From 743bf8ffd2b386ef4675a72da5e90624cad8eb6e Mon Sep 17 00:00:00 2001 From: Jon Wire Date: Wed, 10 Nov 2021 10:21:06 -0600 Subject: [PATCH] fixed observeQuery signature to work with updated query and observe --- packages/datastore/src/datastore/datastore.ts | 250 +++++++++--------- 1 file changed, 119 insertions(+), 131 deletions(-) diff --git a/packages/datastore/src/datastore/datastore.ts b/packages/datastore/src/datastore/datastore.ts index a88a795d1e6..a70c7e94842 100644 --- a/packages/datastore/src/datastore/datastore.ts +++ b/packages/datastore/src/datastore/datastore.ts @@ -135,7 +135,7 @@ const isValidModelConstructor = ( return isModelConstructor(obj) && modelNamespaceMap.has(obj); }; -const namespaceResolver: NamespaceResolver = modelConstructor => +const namespaceResolver: NamespaceResolver = (modelConstructor) => modelNamespaceMap.get(modelConstructor); // exporting syncClasses for testing outbox.test.ts @@ -182,7 +182,7 @@ const initSchema = (userSchema: Schema) => { version: userSchema.version, }; - Object.keys(schema.namespaces).forEach(namespace => { + Object.keys(schema.namespaces).forEach((namespace) => { const [relations, keys] = establishRelationAndKeys( schema.namespaces[namespace] ); @@ -192,23 +192,23 @@ const initSchema = (userSchema: Schema) => { const modelAssociations = new Map(); - Object.values(schema.namespaces[namespace].models).forEach(model => { + Object.values(schema.namespaces[namespace].models).forEach((model) => { const connectedModels: string[] = []; Object.values(model.fields) .filter( - field => + (field) => field.association && field.association.connectionType === 'BELONGS_TO' && (field.type).model !== model.name ) - .forEach(field => + .forEach((field) => connectedModels.push((field.type).model) ); modelAssociations.set(model.name, connectedModels); - Object.values(model.fields).forEach(field => { + Object.values(model.fields).forEach((field) => { if ( typeof field.type === 'object' && !Object.getOwnPropertyDescriptor( @@ -253,12 +253,12 @@ const initSchema = (userSchema: Schema) => { for (const modelName of Array.from(modelAssociations.keys())) { const parents = modelAssociations.get(modelName); - if (parents.every(x => result.has(x))) { + if (parents.every((x) => result.has(x))) { result.set(modelName, parents); } } - Array.from(result.keys()).forEach(x => modelAssociations.delete(x)); + Array.from(result.keys()).forEach((x) => modelAssociations.delete(x)); } schema.namespaces[namespace].modelTopologicalOrdering = result; @@ -267,9 +267,9 @@ const initSchema = (userSchema: Schema) => { return userClasses; }; -const createTypeClasses: ( - namespace: SchemaNamespace -) => TypeConstructorMap = namespace => { +const createTypeClasses: (namespace: SchemaNamespace) => TypeConstructorMap = ( + namespace +) => { const classes: TypeConstructorMap = {}; Object.entries(namespace.models).forEach(([modelName, modelDefinition]) => { @@ -303,108 +303,101 @@ function modelInstanceCreator( return new modelConstructor(init); } -const validateModelFields = (modelDefinition: SchemaModel | SchemaNonModel) => ( - k: string, - v: any -) => { - const fieldDefinition = modelDefinition.fields[k]; +const validateModelFields = + (modelDefinition: SchemaModel | SchemaNonModel) => (k: string, v: any) => { + const fieldDefinition = modelDefinition.fields[k]; - if (fieldDefinition !== undefined) { - const { - type, - isRequired, - isArrayNullable, - name, - isArray, - } = fieldDefinition; + if (fieldDefinition !== undefined) { + const { type, isRequired, isArrayNullable, name, isArray } = + fieldDefinition; - if ( - ((!isArray && isRequired) || (isArray && !isArrayNullable)) && - (v === null || v === undefined) - ) { - throw new Error(`Field ${name} is required`); - } + if ( + ((!isArray && isRequired) || (isArray && !isArrayNullable)) && + (v === null || v === undefined) + ) { + throw new Error(`Field ${name} is required`); + } - if (isGraphQLScalarType(type)) { - const jsType = GraphQLScalarType.getJSType(type); - const validateScalar = GraphQLScalarType.getValidationFunction(type); + if (isGraphQLScalarType(type)) { + const jsType = GraphQLScalarType.getJSType(type); + const validateScalar = GraphQLScalarType.getValidationFunction(type); - if (type === 'AWSJSON') { - if (typeof v === jsType) { - return; - } - if (typeof v === 'string') { - try { - JSON.parse(v); + if (type === 'AWSJSON') { + if (typeof v === jsType) { return; - } catch (error) { - throw new Error(`Field ${name} is an invalid JSON object. ${v}`); + } + if (typeof v === 'string') { + try { + JSON.parse(v); + return; + } catch (error) { + throw new Error(`Field ${name} is an invalid JSON object. ${v}`); + } } } - } - if (isArray) { - let errorTypeText: string = jsType; - if (!isRequired) { - errorTypeText = `${jsType} | null | undefined`; - } + if (isArray) { + let errorTypeText: string = jsType; + if (!isRequired) { + errorTypeText = `${jsType} | null | undefined`; + } + + if (!Array.isArray(v) && !isArrayNullable) { + throw new Error( + `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}` + ); + } + + if ( + !isNullOrUndefined(v) && + (<[]>v).some((e) => + isNullOrUndefined(e) ? isRequired : typeof e !== jsType + ) + ) { + const elemTypes = (<[]>v) + .map((e) => (e === null ? 'null' : typeof e)) + .join(','); + + throw new Error( + `All elements in the ${name} array should be of type ${errorTypeText}, [${elemTypes}] received. ${v}` + ); + } + + if (validateScalar && !isNullOrUndefined(v)) { + const validationStatus = (<[]>v).map((e) => { + if (!isNullOrUndefined(e)) { + return validateScalar(e); + } else if (isNullOrUndefined(e) && !isRequired) { + return true; + } else { + return false; + } + }); - if (!Array.isArray(v) && !isArrayNullable) { + if (!validationStatus.every((s) => s)) { + throw new Error( + `All elements in the ${name} array should be of type ${type}, validation failed for one or more elements. ${v}` + ); + } + } + } else if (!isRequired && v === undefined) { + return; + } else if (typeof v !== jsType && v !== null) { throw new Error( - `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}` + `Field ${name} should be of type ${jsType}, ${typeof v} received. ${v}` ); - } - - if ( + } else if ( !isNullOrUndefined(v) && - (<[]>v).some(e => - isNullOrUndefined(e) ? isRequired : typeof e !== jsType - ) + validateScalar && + !validateScalar(v) ) { - const elemTypes = (<[]>v) - .map(e => (e === null ? 'null' : typeof e)) - .join(','); - throw new Error( - `All elements in the ${name} array should be of type ${errorTypeText}, [${elemTypes}] received. ${v}` + `Field ${name} should be of type ${type}, validation failed. ${v}` ); } - - if (validateScalar && !isNullOrUndefined(v)) { - const validationStatus = (<[]>v).map(e => { - if (!isNullOrUndefined(e)) { - return validateScalar(e); - } else if (isNullOrUndefined(e) && !isRequired) { - return true; - } else { - return false; - } - }); - - if (!validationStatus.every(s => s)) { - throw new Error( - `All elements in the ${name} array should be of type ${type}, validation failed for one or more elements. ${v}` - ); - } - } - } else if (!isRequired && v === undefined) { - return; - } else if (typeof v !== jsType && v !== null) { - throw new Error( - `Field ${name} should be of type ${jsType}, ${typeof v} received. ${v}` - ); - } else if ( - !isNullOrUndefined(v) && - validateScalar && - !validateScalar(v) - ) { - throw new Error( - `Field ${name} should be of type ${type}, validation failed. ${v}` - ); } } - } -}; + }; const castInstanceType = ( modelDefinition: SchemaModel | SchemaNonModel, @@ -459,11 +452,10 @@ const createModelClass = ( (draft: Draft) => { initializeInstance(init, modelDefinition, draft); - const modelInstanceMetadata: ModelInstanceMetadata = instancesMetadata.has( - init - ) - ? (init) - : {}; + const modelInstanceMetadata: ModelInstanceMetadata = + instancesMetadata.has(init) + ? (init) + : {}; const { id: _id, _version, @@ -508,7 +500,7 @@ const createModelClass = ( let patches; const model = produce( source, - draft => { + (draft) => { fn(>(draft as unknown)); draft.id = source.id; const modelValidator = validateModelFields(modelDefinition); @@ -518,7 +510,7 @@ const createModelClass = ( modelValidator(k, parsedValue); }); }, - p => (patches = p) + (p) => (patches = p) ); if (patches.length) { @@ -533,7 +525,7 @@ const createModelClass = ( // to gain access to `modelInstanceCreator` and `clazz` for persisting IDs from server to client. static fromJSON(json: T | T[]) { if (Array.isArray(json)) { - return json.map(init => this.fromJSON(init)); + return json.map((init) => this.fromJSON(init)); } const instance = modelInstanceCreator(clazz, json); @@ -607,11 +599,10 @@ const createModelClass = ( return instanceMemos[field]; } const associatedWith = association.associatedWith; - const relatedModel: PersistentModelConstructor = getModelConstructorByModelName( - USER, - relatedModelName - ); - const result = await instance.query(relatedModel, c => + const relatedModel: PersistentModelConstructor< + typeof relatedModelName + > = getModelConstructorByModelName(USER, relatedModelName); + const result = await instance.query(relatedModel, (c) => c[associatedWith].eq(this.id) ); const asyncResult = new AsyncCollection(result); @@ -691,7 +682,7 @@ const checkReadOnlyPropertyOnCreate = ( const modelKeys = Object.keys(draft); const { fields } = modelDefinition; - modelKeys.forEach(key => { + modelKeys.forEach((key) => { if (fields[key] && fields[key].isReadOnly) { throw new Error(`${key} is read-only.`); } @@ -702,7 +693,7 @@ const checkReadOnlyPropertyOnUpdate = ( patches: Patch[], modelDefinition: SchemaModel ) => { - const patchArray = patches.map(p => [p.path[0], p.value]); + const patchArray = patches.map((p) => [p.path[0], p.value]); const { fields } = modelDefinition; patchArray.forEach(([key, val]) => { @@ -789,16 +780,15 @@ async function checkSchemaVersion( storage: Storage, version: string ): Promise { - const Setting = dataStoreClasses.Setting as PersistentModelConstructor< - Setting - >; + const Setting = + dataStoreClasses.Setting as PersistentModelConstructor; const modelDefinition = schema.namespaces[DATASTORE].models.Setting; - await storage.runExclusive(async s => { + await storage.runExclusive(async (s) => { const [schemaVersionSetting] = await s.query( Setting, - ModelPredicateCreator.createFromExisting(modelDefinition, c => + ModelPredicateCreator.createFromExisting(modelDefinition, (c) => // @ts-ignore Argument of type '"eq"' is not assignable to parameter of type 'never'. c.key('eq', SETTING_SCHEMA_VERSION) ), @@ -879,10 +869,8 @@ class DataStore { private sync: SyncEngine; private syncPageSize: number; private syncExpressions: SyncExpression[]; - private syncPredicates: WeakMap< - SchemaModel, - ModelPredicate - > = new WeakMap>(); + private syncPredicates: WeakMap> = + new WeakMap>(); private sessionId: string; private storageAdapter: Adapter; @@ -960,7 +948,7 @@ class DataStore { data, }); }, - error: err => { + error: (err) => { logger.warn('Sync error', err); this.initReject(); }, @@ -1080,7 +1068,7 @@ class DataStore { condition ); - const [savedModel] = await this.storage.runExclusive(async s => { + const [savedModel] = await this.storage.runExclusive(async (s) => { await s.save(model, producedCondition, undefined, patchesTuple); return s.query( @@ -1292,7 +1280,7 @@ class DataStore { }); if (typeof idOrCriteria === 'string') { - const buildIdPredicate = seed => seed.id.eq(idOrCriteria); + const buildIdPredicate = (seed) => seed.id.eq(idOrCriteria); executivePredicate = buildIdPredicate(buildSeedPredicate()).__query; } else if (modelConstructor && typeof idOrCriteria === 'function') { executivePredicate = (idOrCriteria as SingularModelPredicateExtender)( @@ -1300,7 +1288,7 @@ class DataStore { ).__query; } - return new Observable>(observer => { + return new Observable>((observer) => { let source: ZenObservable.Subscription; (async () => { @@ -1310,7 +1298,7 @@ class DataStore { .observe(modelConstructor) .filter(({ model }) => namespaceResolver(model) === USER) .subscribe({ - next: async item => { + next: async (item) => { if ( !executivePredicate || (await executivePredicate.matches(item.element)) @@ -1318,7 +1306,7 @@ class DataStore { observer.next(item as SubscriptionMessage); } }, - error: err => observer.error(err), + error: (err) => observer.error(err), complete: () => observer.complete(), }); })(); @@ -1334,15 +1322,15 @@ class DataStore { observeQuery: { ( modelConstructor: PersistentModelConstructor, - criteria?: ProducerModelPredicate | typeof PredicateAll, + criteria?: SingularModelPredicateExtender | typeof PredicateAll, paginationProducer?: ProducerPaginationInput ): Observable>; } = ( model: PersistentModelConstructor, - criteria?: ProducerModelPredicate | typeof PredicateAll, + criteria?: SingularModelPredicateExtender | typeof PredicateAll, options?: ProducerPaginationInput ): Observable> => { - return new Observable>(observer => { + return new Observable>((observer) => { const items = new Map(); const itemsChanged = new Map(); let deletedItemIds: string[] = []; @@ -1351,7 +1339,7 @@ class DataStore { (async () => { try { // first, query and return any locally-available records - (await this.query(model, criteria, options)).forEach(item => + (await this.query(model, criteria, options)).forEach((item) => items.set(item.id, item) ); @@ -1401,10 +1389,10 @@ class DataStore { } items.clear(); - itemsArray.forEach(item => items.set(item.id, item)); + itemsArray.forEach((item) => items.set(item.id, item)); // remove deleted items from the final result set - deletedItemIds.forEach(id => items.delete(id)); + deletedItemIds.forEach((id) => items.delete(id)); return { items: Array.from(items.values()),