Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data masking #12042

Merged
merged 20 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fa0e958
[Data masking] Core data masking algorithm (#11686)
jerelmiller May 16, 2024
a89be38
Provide way to opt-in to data masking via the cache (#11862)
jerelmiller May 22, 2024
6ce94bc
[Data masking] Add function to mask fragment data (#11880)
jerelmiller Jun 11, 2024
f557c52
[Data masking] Add ability to globally enable data masking for querie…
jerelmiller Jun 24, 2024
ca911fe
Move check for whether data masking is enabled to `ObservableQuery` (…
jerelmiller Jul 2, 2024
14ea23a
[Data masking] Add `@unmask` directive with field access warnings (#1…
jerelmiller Jul 24, 2024
fd41f96
Extract data masking tests to own file (#11973)
jerelmiller Jul 24, 2024
c28dd60
[Data masking] Warn when passing object to `useFragment`/`watchFragme…
jerelmiller Aug 16, 2024
30eeebb
Fix issue with comparing errors in dataMasking (#12006)
jerelmiller Aug 16, 2024
433f1a7
Always retain `__typename` during data masking (#12016)
jerelmiller Aug 22, 2024
be055fa
[Data masking] Fix bug where masked field could be returned in parent…
jerelmiller Aug 22, 2024
430f6c3
Add data masking to fragments read by `useFragment`/`watchFragment` (…
jerelmiller Aug 27, 2024
d6bb319
[Data masking] Ensure `@unmask` can be used with the fragment registr…
jerelmiller Aug 28, 2024
9695436
[Data masking] Ensure masking algorithm properly handles nulls (#12034)
jerelmiller Aug 28, 2024
0e241ee
[Data masking] Add support for masking `client.query` calls (#12033)
jerelmiller Aug 28, 2024
d7cc9f2
[Data masking] Mask data returned from subscriptions (#12038)
jerelmiller Aug 28, 2024
fa34aa2
[Data masking] Mask data returned from mutations (#12039)
jerelmiller Aug 28, 2024
45b1bc1
Introduce a `MaskedDocumentNode` type and handle masking types (#12064)
jerelmiller Sep 24, 2024
1c0ecbf
Add changeset
jerelmiller Sep 24, 2024
f584167
Clean up Prettier, Size-limit, and Api-Extractor
jerelmiller Sep 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 91 additions & 12 deletions .api-reports/api-report-cache.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export abstract class ApolloCache<TSerialized> implements DataProxy {
abstract evict(options: Cache_2.EvictOptions): boolean;
abstract extract(optimistic?: boolean): TSerialized;
// (undocumented)
fragmentMatches?(fragment: InlineFragmentNode, typename: string): boolean;
// (undocumented)
gc(): string[];
// Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts
//
Expand All @@ -38,15 +40,19 @@ export abstract class ApolloCache<TSerialized> implements DataProxy {
// (undocumented)
identify(object: StoreObject | Reference): string | undefined;
// (undocumented)
lookupFragment(fragmentName: string): FragmentDefinitionNode | null;
// (undocumented)
modify<Entity extends Record<string, any> = Record<string, any>>(options: Cache_2.ModifyOptions<Entity>): boolean;
// (undocumented)
abstract performTransaction(transaction: Transaction<TSerialized>, optimisticId?: string | null): void;
// Warning: (ae-forgotten-export) The symbol "Unmasked" needs to be exported by the entry point index.d.ts
//
// (undocumented)
abstract read<TData = any, TVariables = any>(query: Cache_2.ReadOptions<TVariables, TData>): TData | null;
abstract read<TData = any, TVariables = any>(query: Cache_2.ReadOptions<TVariables, TData>): Unmasked<TData> | null;
// (undocumented)
readFragment<FragmentType, TVariables = any>(options: Cache_2.ReadFragmentOptions<FragmentType, TVariables>, optimistic?: boolean): FragmentType | null;
readFragment<FragmentType, TVariables = any>(options: Cache_2.ReadFragmentOptions<FragmentType, TVariables>, optimistic?: boolean): Unmasked<FragmentType> | null;
// (undocumented)
readQuery<QueryType, TVariables = any>(options: Cache_2.ReadQueryOptions<QueryType, TVariables>, optimistic?: boolean): QueryType | null;
readQuery<QueryType, TVariables = any>(options: Cache_2.ReadQueryOptions<QueryType, TVariables>, optimistic?: boolean): Unmasked<QueryType> | null;
// (undocumented)
recordOptimisticTransaction(transaction: Transaction<TSerialized>, optimisticId: string): void;
// (undocumented)
Expand All @@ -59,9 +65,9 @@ export abstract class ApolloCache<TSerialized> implements DataProxy {
// (undocumented)
transformForLink(document: DocumentNode): DocumentNode;
// (undocumented)
updateFragment<TData = any, TVariables = any>(options: Cache_2.UpdateFragmentOptions<TData, TVariables>, update: (data: TData | null) => TData | null | void): TData | null;
updateFragment<TData = any, TVariables = any>(options: Cache_2.UpdateFragmentOptions<TData, TVariables>, update: (data: Unmasked<TData> | null) => Unmasked<TData> | null | void): Unmasked<TData> | null;
// (undocumented)
updateQuery<TData = any, TVariables = any>(options: Cache_2.UpdateQueryOptions<TData, TVariables>, update: (data: TData | null) => TData | null | void): TData | null;
updateQuery<TData = any, TVariables = any>(options: Cache_2.UpdateQueryOptions<TData, TVariables>, update: (data: Unmasked<TData> | null) => Unmasked<TData> | null | void): Unmasked<TData> | null;
// (undocumented)
abstract watch<TData = any, TVariables = any>(watch: Cache_2.WatchOptions<TData, TVariables>): () => void;
// Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts
Expand Down Expand Up @@ -166,7 +172,7 @@ namespace Cache_2 {
// (undocumented)
dataId?: string;
// (undocumented)
result: TResult;
result: Unmasked<TResult>;
}
import DiffResult = DataProxy.DiffResult;
import ReadQueryOptions = DataProxy.ReadQueryOptions;
Expand Down Expand Up @@ -210,9 +216,22 @@ export const canonicalStringify: ((value: any) => string) & {
// @public (undocumented)
type CanReadFunction = (value: StoreValue) => boolean;

// Warning: (ae-forgotten-export) The symbol "UnionToIntersection" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "UnwrapFragmentRefs" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "RemoveFragmentName" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type CombineFragmentRefs<FragmentRefs extends Record<string, any>> = UnionToIntersection<{
[K in keyof FragmentRefs]-?: UnwrapFragmentRefs<RemoveFragmentName<FragmentRefs[K]>>;
}[keyof FragmentRefs]>;

// @public (undocumented)
export function createFragmentRegistry(...fragments: DocumentNode[]): FragmentRegistryAPI;

// @public (undocumented)
interface DataMasking {
}

// @public (undocumented)
export namespace DataProxy {
// (undocumented)
Expand Down Expand Up @@ -261,7 +280,7 @@ export namespace DataProxy {
// (undocumented)
export interface WriteOptions<TData> {
broadcast?: boolean;
data: TData;
data: Unmasked<TData>;
overwrite?: boolean;
}
// (undocumented)
Expand All @@ -271,8 +290,8 @@ export namespace DataProxy {

// @public
export interface DataProxy {
readFragment<FragmentType, TVariables = any>(options: DataProxy.ReadFragmentOptions<FragmentType, TVariables>, optimistic?: boolean): FragmentType | null;
readQuery<QueryType, TVariables = any>(options: DataProxy.ReadQueryOptions<QueryType, TVariables>, optimistic?: boolean): QueryType | null;
readFragment<FragmentType, TVariables = any>(options: DataProxy.ReadFragmentOptions<FragmentType, TVariables>, optimistic?: boolean): Unmasked<FragmentType> | null;
readQuery<QueryType, TVariables = any>(options: DataProxy.ReadQueryOptions<QueryType, TVariables>, optimistic?: boolean): Unmasked<QueryType> | null;
writeFragment<TData = any, TVariables = any>(options: DataProxy.WriteFragmentOptions<TData, TVariables>): Reference | undefined;
writeQuery<TData = any, TVariables = any>(options: DataProxy.WriteQueryOptions<TData, TVariables>): Reference | undefined;
}
Expand Down Expand Up @@ -517,6 +536,17 @@ export interface FragmentRegistryAPI {
transform<D extends DocumentNode>(document: D): D;
}

// @public (undocumented)
type FragmentType<TData> = [
TData
] extends [{
" $fragmentName"?: infer TKey;
}] ? TKey extends string ? {
" $fragmentRefs"?: {
[key in TKey]: TData;
};
} : never : never;

// @internal
const getApolloCacheMemoryInternals: (() => {
cache: {
Expand Down Expand Up @@ -577,6 +607,8 @@ export class InMemoryCache extends ApolloCache<NormalizedCacheObject> {
// (undocumented)
extract(optimistic?: boolean): NormalizedCacheObject;
// (undocumented)
fragmentMatches(fragment: InlineFragmentNode, typename: string): boolean;
// (undocumented)
gc(options?: {
resetResultCache?: boolean;
resetResultIdentities?: boolean;
Expand All @@ -588,6 +620,8 @@ export class InMemoryCache extends ApolloCache<NormalizedCacheObject> {
// (undocumented)
identify(object: StoreObject | Reference): string | undefined;
// (undocumented)
lookupFragment(fragmentName: string): FragmentDefinitionNode | null;
// (undocumented)
readonly makeVar: typeof makeVar;
// (undocumented)
modify<Entity extends Record<string, any> = Record<string, any>>(options: Cache_2.ModifyOptions<Entity>): boolean;
Expand Down Expand Up @@ -696,6 +730,17 @@ export function makeReference(id: string): Reference;
// @public (undocumented)
export function makeVar<T>(value: T): ReactiveVar<T>;

// Warning: (ae-forgotten-export) The symbol "Prettify" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "RemoveMaskedMarker" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "DataMasking" needs to be exported by the entry point index.d.ts
//
// @public
type MaybeMasked<TData> = TData extends {
__masked?: true;
} ? Prettify<RemoveMaskedMarker<TData>> : DataMasking extends {
enabled: true;
} ? TData : Unmasked<TData>;

// @public (undocumented)
export interface MergeInfo {
// (undocumented)
Expand Down Expand Up @@ -761,6 +806,9 @@ export type Modifiers<T extends Record<string, any> = Record<string, unknown>> =
[FieldName in keyof T]: Modifier<StoreObjectValueMaybeReference<Exclude<T[FieldName], undefined>>>;
}>;

// @public
type NoInfer_2<T> = [T][T extends any ? 0 : never];

// @public
export interface NormalizedCache {
// (undocumented)
Expand Down Expand Up @@ -859,6 +907,11 @@ export type PossibleTypesMap = {
[supertype: string]: string[];
};

// @public (undocumented)
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};

// @public (undocumented)
type Primitive = null | undefined | string | number | boolean | symbol | bigint;

Expand Down Expand Up @@ -920,6 +973,12 @@ export interface Reference {
readonly __ref: string;
}

// @public (undocumented)
type RemoveFragmentName<T> = T extends any ? Omit<T, " $fragmentName"> : T;

// @public (undocumented)
type RemoveMaskedMarker<T> = Omit<T, "__masked">;

// @public (undocumented)
type SafeReadonly<T> = T extends object ? Readonly<T> : T;

Expand Down Expand Up @@ -976,22 +1035,41 @@ export type TypePolicy = {
};
};

// @public (undocumented)
type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;

// @public
type Unmasked<TData> = TData extends object ? UnwrapFragmentRefs<RemoveMaskedMarker<RemoveFragmentName<TData>>> : TData;

// Warning: (ae-forgotten-export) The symbol "CombineFragmentRefs" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
type UnwrapFragmentRefs<TData> = string extends keyof NonNullable<TData> ? TData : " $fragmentRefs" extends keyof NonNullable<TData> ? TData extends {
" $fragmentRefs"?: infer FragmentRefs extends object;
} ? Prettify<{
[K in keyof TData as K extends " $fragmentRefs" ? never : K]: UnwrapFragmentRefs<TData[K]>;
} & CombineFragmentRefs<FragmentRefs>> : never : TData extends object ? {
[K in keyof TData]: UnwrapFragmentRefs<TData[K]>;
} : TData;

// @public
export interface WatchFragmentOptions<TData, TVars> {
fragment: DocumentNode | TypedDocumentNode<TData, TVars>;
fragmentName?: string;
from: StoreObject | Reference | string;
// Warning: (ae-forgotten-export) The symbol "FragmentType" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "NoInfer_2" needs to be exported by the entry point index.d.ts
from: StoreObject | Reference | FragmentType<NoInfer_2<TData>> | string;
optimistic?: boolean;
variables?: TVars;
}

// @public
export type WatchFragmentResult<TData> = {
data: TData;
data: MaybeMasked<TData>;
complete: true;
missing?: never;
} | {
data: DeepPartial<TData>;
data: DeepPartial<MaybeMasked<TData>>;
complete: false;
missing: MissingTree;
};
Expand Down Expand Up @@ -1030,6 +1108,7 @@ interface WriteContext extends ReadMergeModifyContext {

// Warnings were encountered during analysis:
//
// src/cache/core/cache.ts:92:7 - (ae-forgotten-export) The symbol "MaybeMasked" needs to be exported by the entry point index.d.ts
// src/cache/inmemory/policies.ts:92:3 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts
// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts
// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts
Expand Down
Loading