diff --git a/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts b/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts index f0590f71e39..c850b59da5c 100644 --- a/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts +++ b/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts @@ -141,7 +141,7 @@ function getAuthRules({ export const multiAuthStrategy: ( amplifyContext: AmplifyContext, ) => AuthModeStrategy = - (amplifyContext: AmplifyContext) => + () => async ({ schema, modelName }) => { let currentUser; try { diff --git a/packages/datastore/src/datastore/datastore.ts b/packages/datastore/src/datastore/datastore.ts index 5f403b5646f..8882e9a4446 100644 --- a/packages/datastore/src/datastore/datastore.ts +++ b/packages/datastore/src/datastore/datastore.ts @@ -221,6 +221,8 @@ const buildSeedPredicate = ( }; // exporting syncClasses for testing outbox.test.ts +// TODO(eslint): refactor not to export non-constant +// eslint-disable-next-line import/no-mutable-exports export let syncClasses: TypeConstructorMap; let userClasses: TypeConstructorMap; let dataStoreClasses: TypeConstructorMap; @@ -490,7 +492,7 @@ const checkSchemaCodegenVersion = (codegenVersion: string) => { let isValid = false; try { const versionParts = codegenVersion.split('.'); - const [major, minor, patch, patchrevision] = versionParts; + const [major, minor] = versionParts; isValid = Number(major) === majorVersion && Number(minor) >= minorVersion; } catch (err) { console.log(`Error parsing codegen version: ${codegenVersion}\n${err}`); @@ -548,12 +550,12 @@ export declare type ModelInstanceCreator = typeof modelInstanceCreator; const instancesMetadata = new WeakSet>(); function modelInstanceCreator( - modelConstructor: PersistentModelConstructor, + ModelConstructor: PersistentModelConstructor, init: Partial, ): T { instancesMetadata.add(init); - return new modelConstructor(init as ModelInit>); + return new ModelConstructor(init as ModelInit>); } const validateModelFields = @@ -652,6 +654,7 @@ const validateModelFields = } } } else if (!isRequired && v === undefined) { + // no-op for this branch but still to filter this branch out } else if (typeof v !== jsType && v !== null) { throw new Error( `Field ${name} should be of type ${jsType}, ${typeof v} received. ${v}`, @@ -905,7 +908,7 @@ const createModelClass = ( { source }, ); } - (draft as Object)[key] = source[key]; + (draft as object)[key] = source[key]; }); const modelValidator = validateModelFields(modelDefinition); @@ -994,7 +997,7 @@ const createModelClass = ( // Avoid validation error when processing AppSync response with nested // selection set. Nested entitites lack version field and can not be validated // TODO: explore a more reliable method to solve this - if (model.hasOwnProperty('_version')) { + if (Object.prototype.hasOwnProperty.call(model, '_version')) { const modelConstructor = Object.getPrototypeOf(model || {}) .constructor as PersistentModelConstructor; @@ -1040,7 +1043,7 @@ const createModelClass = ( // if the memos already has a result for this field, we'll use it. // there is no "cache" invalidation of any kind; memos are permanent to // keep an immutable perception of the instance. - if (!instanceMemos.hasOwnProperty(field)) { + if (!Object.prototype.hasOwnProperty.call(instanceMemos, field)) { // before we populate the memo, we need to know where to look for relatives. // today, this only supports DataStore. Models aren't managed elsewhere in Amplify. if (getAttachment(this) === ModelAttachment.DataStore) { @@ -1052,12 +1055,14 @@ const createModelClass = ( relationship.remoteModelConstructor as PersistentModelConstructor, base => base.and(q => { - return relationship.remoteJoinFields.map((field, index) => { - // TODO: anything we can use instead of `any` here? - return (q[field] as T[typeof field]).eq( - this[relationship.localJoinFields[index]], - ); - }); + return relationship.remoteJoinFields.map( + (joinField, index) => { + // TODO: anything we can use instead of `any` here? + return (q[joinField] as T[typeof joinField]).eq( + this[relationship.localJoinFields[index]], + ); + }, + ); }), ); @@ -1301,14 +1306,14 @@ async function checkSchemaVersion( storage: Storage, version: string, ): Promise { - const Setting = + const SettingCtor = dataStoreClasses.Setting as PersistentModelConstructor; const modelDefinition = schema.namespaces[DATASTORE].models.Setting; await storage.runExclusive(async s => { const [schemaVersionSetting] = await s.query( - Setting, + SettingCtor, ModelPredicateCreator.createFromAST(modelDefinition, { and: { key: { eq: SETTING_SCHEMA_VERSION } }, }), @@ -1326,7 +1331,7 @@ async function checkSchemaVersion( } } else { await s.save( - modelInstanceCreator(Setting, { + modelInstanceCreator(SettingCtor, { key: SETTING_SCHEMA_VERSION, value: JSON.stringify(version), }), @@ -1404,8 +1409,8 @@ class DataStore { private errorHandler!: (error: SyncError) => void; private fullSyncInterval!: number; private initialized?: Promise; - private initReject!: Function; - private initResolve!: Function; + private initReject!: () => void; + private initResolve!: () => void; private maxRecordsToSync!: number; private storage?: Storage; private sync?: SyncEngine; @@ -1513,9 +1518,9 @@ class DataStore { this.state = DataStoreState.Starting; if (this.initialized === undefined) { logger.debug('Starting DataStore'); - this.initialized = new Promise((res, rej) => { - this.initResolve = res; - this.initReject = rej; + this.initialized = new Promise((resolve, reject) => { + this.initResolve = resolve; + this.initReject = reject; }); } else { await this.initialized; @@ -1831,12 +1836,7 @@ class DataStore { : undefined; const [savedModel] = await this.storage.runExclusive(async s => { - const saved = await s.save( - model, - producedCondition, - undefined, - patchesTuple, - ); + await s.save(model, producedCondition, undefined, patchesTuple); return s.query( modelConstructor, @@ -2074,10 +2074,10 @@ class DataStore { if (modelOrConstructor && modelConstructor === undefined) { const model = modelOrConstructor as T; - const modelConstructor = - model && (Object.getPrototypeOf(model) as Object).constructor; + const resolvedModelConstructor = + model && (Object.getPrototypeOf(model) as object).constructor; - if (isValidModelConstructor(modelConstructor)) { + if (isValidModelConstructor(resolvedModelConstructor)) { if (identifierOrCriteria) { logger.warn('idOrCriteria is ignored when using a model instance', { model, @@ -2085,7 +2085,7 @@ class DataStore { }); } - return this.observe(modelConstructor, model.id); + return this.observe(resolvedModelConstructor, model.id); } else { const msg = 'The model is not an instance of a PersistentModelConstructor'; @@ -2282,10 +2282,11 @@ class DataStore { // to have visibility into items that move from in-set to out-of-set. // We need to explicitly remove those items from the existing snapshot. handle = this.observe(model).subscribe( - ({ element, model, opType }) => + ({ element, model: observedModel, opType }) => this.runningProcesses.isOpen && this.runningProcesses.add(async () => { - const itemModelDefinition = getModelDefinition(model)!; + const itemModelDefinition = + getModelDefinition(observedModel)!; const idOrPk = getIdentifierValue( itemModelDefinition, element, @@ -2320,7 +2321,7 @@ class DataStore { } const isSynced = - this.sync?.getModelSyncedStatus(model) ?? false; + this.sync?.getModelSyncedStatus(observedModel) ?? false; const limit = itemsChanged.size - deletedItemIds.length >= @@ -2409,8 +2410,11 @@ class DataStore { * @param itemsToSort A array of model type. */ const sortItems = (itemsToSort: T[]): void => { - const modelDefinition = getModelDefinition(model); - const pagination = this.processPagination(modelDefinition!, options); + const sortingModelDefinition = getModelDefinition(model); + const pagination = this.processPagination( + sortingModelDefinition!, + options, + ); const sortPredicates = ModelSortPredicateCreator.getPredicates( pagination!.sort!, @@ -2456,8 +2460,6 @@ class DataStore { const { DataStore: configDataStore, authModeStrategyType: configAuthModeStrategyType, - conflictHandler: configConflictHandler, - errorHandler: configErrorHandler, maxRecordsToSync: configMaxRecordsToSync, syncPageSize: configSyncPageSize, fullSyncInterval: configFullSyncInterval, diff --git a/packages/datastore/src/index.ts b/packages/datastore/src/index.ts index 02022b78064..1e721b5e50f 100644 --- a/packages/datastore/src/index.ts +++ b/packages/datastore/src/index.ts @@ -13,6 +13,7 @@ export { DataStoreClass, initSchema, ModelInstanceCreator, + // eslint-disable-next-line import/export AsyncCollection, AsyncItem, } from './datastore/datastore'; @@ -34,4 +35,5 @@ export const utils = { isModelConstructor, }; +// eslint-disable-next-line import/export export * from './types'; diff --git a/packages/datastore/src/predicates/index.ts b/packages/datastore/src/predicates/index.ts index 75ffec64c4b..ef547da94f3 100644 --- a/packages/datastore/src/predicates/index.ts +++ b/packages/datastore/src/predicates/index.ts @@ -100,7 +100,8 @@ export const PredicateAll = Symbol('A predicate that matches all records'); export class Predicates { public static get ALL(): typeof PredicateAll { - const predicate = c => c as ProducerModelPredicate; + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const predicate = >(c => c); predicatesAllSet.add(predicate); diff --git a/packages/datastore/src/predicates/next.ts b/packages/datastore/src/predicates/next.ts index 03fcb0a37cd..78e44763fda 100644 --- a/packages/datastore/src/predicates/next.ts +++ b/packages/datastore/src/predicates/next.ts @@ -115,7 +115,7 @@ export class FieldCondition { * @param extract Not used. Present only to fulfill the `UntypedCondition` interface. * @returns A new, identitical `FieldCondition`. */ - copy(extract?: GroupCondition): [FieldCondition, GroupCondition | undefined] { + copy(): [FieldCondition, GroupCondition | undefined] { return [ new FieldCondition(this.field, this.operator, [...this.operands]), undefined, @@ -193,7 +193,8 @@ export class FieldCondition { * @param storage N/A. If ever implemented, the storage adapter to query. * @returns N/A. If ever implemented, return items from `storage` that match. */ - async fetch(storage: StorageAdapter): Promise[]> { + async fetch(): Promise[]> { + // eslint-disable-next-line prefer-promise-reject-errors return Promise.reject('No implementation needed [yet].'); } @@ -796,16 +797,16 @@ export function recursivePredicateFor( registerPredicateInternals(baseCondition, link); const copyLink = () => { - const [query, newTail] = baseCondition.copy(tailCondition); + const [copiedQuery, newTail] = baseCondition.copy(tailCondition); const newLink = recursivePredicateFor( ModelType, allowRecursion, undefined, - query, + copiedQuery, newTail, ); - return { query, newTail, newLink }; + return { query: copiedQuery, newTail, newLink }; }; // Adds .or() and .and() methods to the link. @@ -814,7 +815,7 @@ export function recursivePredicateFor( link[op] = (builder: RecursiveModelPredicateAggregateExtender) => { // or() and and() will return a copy of the original link // to head off mutability concerns. - const { query, newTail } = copyLink(); + const { query: copiedLinkQuery, newTail } = copyLink(); const childConditions = builder( recursivePredicateFor(ModelType, allowRecursion), @@ -838,7 +839,7 @@ export function recursivePredicateFor( ); // FinalPredicate - return registerPredicateInternals(query); + return registerPredicateInternals(copiedLinkQuery); }; }); @@ -848,7 +849,7 @@ export function recursivePredicateFor( ): PredicateInternalsKey => { // not() will return a copy of the original link // to head off mutability concerns. - const { query, newTail } = copyLink(); + const { query: copiedLinkQuery, newTail } = copyLink(); // unlike and() and or(), the customer will supply a "singular" child predicate. // the difference being: not() does not accept an array of predicate-like objects. @@ -862,7 +863,7 @@ export function recursivePredicateFor( // A `FinalModelPredicate`. // Return a thing that can no longer be extended, but instead used to `async filter(items)` // or query storage: `.__query.fetch(storage)`. - return registerPredicateInternals(query); + return registerPredicateInternals(copiedLinkQuery); }; // For each field on the model schema, we want to add a getter @@ -890,7 +891,7 @@ export function recursivePredicateFor( [operator]: (...operands: any[]) => { // build off a fresh copy of the existing `link`, just in case // the same link is being used elsewhere by the customer. - const { query, newTail } = copyLink(); + const { query: copiedLinkQuery, newTail } = copyLink(); // normalize operands. if any of the values are `undefiend`, use // `null` instead, because that's what will be stored cross-platform. @@ -907,7 +908,7 @@ export function recursivePredicateFor( // A `FinalModelPredicate`. // Return a thing that can no longer be extended, but instead used to `async filter(items)` // or query storage: `.__query.fetch(storage)`. - return registerPredicateInternals(query); + return registerPredicateInternals(copiedLinkQuery); }, }; }, {}); diff --git a/packages/datastore/src/predicates/sort.ts b/packages/datastore/src/predicates/sort.ts index 3971659c525..aadb5146cf0 100644 --- a/packages/datastore/src/predicates/sort.ts +++ b/packages/datastore/src/predicates/sort.ts @@ -21,33 +21,29 @@ export class ModelSortPredicateCreator { const { name: modelName } = modelDefinition; const fieldNames = new Set(Object.keys(modelDefinition.fields)); - let handler: ProxyHandler>; - const predicate = new Proxy( - {} as SortPredicate, - (handler = { - get(_target, propertyKey, receiver: SortPredicate) { - const field = propertyKey as keyof T; + const predicate = new Proxy({} as SortPredicate, { + get(_target, propertyKey, receiver: SortPredicate) { + const field = propertyKey as keyof T; - if (!fieldNames.has(field)) { - throw new Error( - `Invalid field for model. field: ${String( - field, - )}, model: ${modelName}`, - ); - } + if (!fieldNames.has(field)) { + throw new Error( + `Invalid field for model. field: ${String( + field, + )}, model: ${modelName}`, + ); + } - const result = (sortDirection: SortDirection) => { - ModelSortPredicateCreator.sortPredicateGroupsMap - .get(receiver) - ?.push({ field, sortDirection }); + const result = (sortDirection: SortDirection) => { + ModelSortPredicateCreator.sortPredicateGroupsMap + .get(receiver) + ?.push({ field, sortDirection }); - return receiver; - }; + return receiver; + }; - return result; - }, - }), - ); + return result; + }, + }); ModelSortPredicateCreator.sortPredicateGroupsMap.set(predicate, []); diff --git a/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts b/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts index 458db1d84f2..b903c55dc2f 100644 --- a/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts +++ b/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts @@ -26,9 +26,13 @@ import { StorageAdapterBase } from './StorageAdapterBase'; export class AsyncStorageAdapter extends StorageAdapterBase { protected db!: AsyncStorageDatabase; - // no-ops for this adapter - protected async preSetUpChecks() {} - protected async preOpCheck() {} + protected async preSetUpChecks() { + // no-ops for AsyncStorageAdapter + } + + protected async preOpCheck() { + // no-ops for AsyncStorageAdapter + } /** * Open AsyncStorage database @@ -79,16 +83,20 @@ export class AsyncStorageAdapter extends StorageAdapterBase { const keyValuesPath = this.getIndexKeyValuesPath(model); - const { instance } = connectedModels.find(({ instance }) => { - const instanceKeyValuesPath = this.getIndexKeyValuesPath(instance); + const { instance } = connectedModels.find( + ({ instance: connectedModelInstance }) => { + const instanceKeyValuesPath = this.getIndexKeyValuesPath( + connectedModelInstance, + ); - return keysEqual([instanceKeyValuesPath], [keyValuesPath]); - })!; + return keysEqual([instanceKeyValuesPath], [keyValuesPath]); + }, + )!; batch.push(instance); } - return await this.db.batchSave(storeName, batch, keys); + return this.db.batchSave(storeName, batch, keys); } protected async _get(storeName: string, keyArr: string[]): Promise { @@ -112,12 +120,15 @@ export class AsyncStorageAdapter extends StorageAdapterBase { const result: [T, OpType.INSERT | OpType.UPDATE][] = []; for await (const resItem of connectionStoreNames) { - const { storeName, item, instance, keys } = resItem; + const { storeName: storeNameForRestItem, item, instance, keys } = resItem; const itemKeyValues: string[] = keys.map(key => item[key]); - const fromDB = (await this._get(storeName, itemKeyValues)) as T; - const opType: OpType = fromDB ? OpType.UPDATE : OpType.INSERT; + const fromDBForRestItem = (await this._get( + storeNameForRestItem, + itemKeyValues, + )) as T; + const opType: OpType = fromDBForRestItem ? OpType.UPDATE : OpType.INSERT; if ( keysEqual(itemKeyValues, modelKeyValues) || @@ -125,7 +136,7 @@ export class AsyncStorageAdapter extends StorageAdapterBase { ) { await this.db.save( item, - storeName, + storeNameForRestItem, keys, itemKeyValues.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR), ); @@ -174,7 +185,7 @@ export class AsyncStorageAdapter extends StorageAdapterBase { return this.getAll(storeName); })()) as T[]; - return await this.load(namespaceName, modelConstructor.name, records); + return this.load(namespaceName, modelConstructor.name, records); } private async getByKey( @@ -187,7 +198,7 @@ export class AsyncStorageAdapter extends StorageAdapterBase { private async getAll( storeName: string, ): Promise { - return await this.db.getAll(storeName); + return this.db.getAll(storeName); } private async filterOnPredicate( diff --git a/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts b/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts index a63557d1293..f7592f53640 100644 --- a/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts +++ b/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts @@ -74,12 +74,12 @@ class AsyncStorageDatabase { if (id === undefined) { // It is an old entry (without ulid). Need to migrate to new key format - const id = ulidOrId; + const resolvedId = ulidOrId; const newUlid = this.getMonotonicFactory(storeName)(); - const oldKey = this.getLegacyKeyForItem(storeName, id); - const newKey = this.getKeyForItem(storeName, id, newUlid); + const oldKey = this.getLegacyKeyForItem(storeName, resolvedId); + const newKey = this.getKeyForItem(storeName, resolvedId, newUlid); const item = await this.storage.getItem(oldKey); @@ -272,16 +272,17 @@ class AsyncStorageDatabase { const [itemId, ulid] = firstOrLast === QueryOne.FIRST ? (() => { - let id: string, ulid: string; - for ([id, ulid] of collection) break; // Get first element of the set + let resolvedId: string, resolvedUlid: string; + // eslint-disable-next-line no-unreachable-loop + for ([resolvedId, resolvedUlid] of collection) break; // Get first element of the set - return [id!, ulid!]; + return [resolvedId!, resolvedUlid!]; })() : (() => { - let id: string, ulid: string; - for ([id, ulid] of collection); // Get last element of the set + let resolvedId: string, resolvedUlid: string; + for ([resolvedId, resolvedUlid] of collection); // Get last element of the set - return [id!, ulid!]; + return [resolvedId!, resolvedUlid!]; })(); const itemKey = this.getKeyForItem(storeName, itemId, ulid); diff --git a/packages/datastore/src/storage/adapter/InMemoryStore.ts b/packages/datastore/src/storage/adapter/InMemoryStore.ts index 0ea858f59dc..c275ee0e85e 100644 --- a/packages/datastore/src/storage/adapter/InMemoryStore.ts +++ b/packages/datastore/src/storage/adapter/InMemoryStore.ts @@ -9,7 +9,11 @@ export class InMemoryStore { multiGet = async (keys: string[]) => { return keys.reduce( - (res, k) => (res.push([k, this.db.get(k)!]), res), + (res, k) => { + res.push([k, this.db.get(k)!]); + + return res; + }, [] as [string, string][], ); }; diff --git a/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts b/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts index 5b099451a86..6e8a6986160 100644 --- a/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts +++ b/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts @@ -79,7 +79,7 @@ class IndexedDBAdapter extends StorageAdapterBase { * @returns IDB Database instance */ protected async initDb(): Promise { - return await idb.openDB(this.dbName, DB_VERSION, { + return idb.openDB(this.dbName, DB_VERSION, { upgrade: async (db, oldVersion, newVersion, txn) => { // create new database if (oldVersion === 0) { @@ -228,22 +228,25 @@ class IndexedDBAdapter extends StorageAdapterBase { const result: [T, OpType.INSERT | OpType.UPDATE][] = []; for await (const resItem of connectionStoreNames) { - const { storeName, item, instance, keys } = resItem; - const store = tx.objectStore(storeName); + const { storeName: storeNameForRestItem, item, instance, keys } = resItem; + const storeForRestItem = tx.objectStore(storeNameForRestItem); const itemKeyValues: string[] = keys.map(key => item[key]); - const fromDB = (await this._get(store, itemKeyValues)) as T; - const opType: OpType = fromDB ? OpType.UPDATE : OpType.INSERT; + const fromDBForRestItem = (await this._get( + storeForRestItem, + itemKeyValues, + )) as T; + const opType: OpType = fromDBForRestItem ? OpType.UPDATE : OpType.INSERT; if ( keysEqual(itemKeyValues, modelKeyValues) || opType === OpType.INSERT ) { - const key = await store + const key = await storeForRestItem .index('byPk') .getKey(this.canonicalKeyPath(itemKeyValues)); - await store.put(item, key); + await storeForRestItem.put(item, key); result.push([instance, opType]); } } @@ -304,7 +307,7 @@ class IndexedDBAdapter extends StorageAdapterBase { return this.getAll(storeName); })()) as T[]; - return await this.load(namespaceName, modelConstructor.name, records); + return this.load(namespaceName, modelConstructor.name, records); } async queryOne( @@ -361,11 +364,15 @@ class IndexedDBAdapter extends StorageAdapterBase { const key = await index.getKey(this.canonicalKeyPath(keyValues)); if (!_deleted) { - const { instance } = connectedModels.find(({ instance }) => { - const instanceKeyValues = this.getIndexKeyValuesFromModel(instance); + const { instance } = connectedModels.find( + ({ instance: connectedModelInstance }) => { + const instanceKeyValues = this.getIndexKeyValuesFromModel( + connectedModelInstance, + ); - return keysEqual(instanceKeyValues, keyValues); - })!; + return keysEqual(instanceKeyValues, keyValues); + }, + )!; result.push([ instance as unknown as T, @@ -426,12 +433,11 @@ class IndexedDBAdapter extends StorageAdapterBase { // #region platform-specific helper methods private async checkPrivate() { - const isPrivate = await isPrivateMode().then(isPrivate => { - return isPrivate; - }); + const isPrivate = await isPrivateMode(); if (isPrivate) { logger.error("IndexedDB not supported in this browser's private mode"); + // eslint-disable-next-line prefer-promise-reject-errors return Promise.reject( "IndexedDB not supported in this browser's private mode", ); @@ -497,7 +503,7 @@ class IndexedDBAdapter extends StorageAdapterBase { private async getAll( storeName: string, ): Promise { - return await this.db.getAll(storeName); + return this.db.getAll(storeName); } /** @@ -571,7 +577,7 @@ class IndexedDBAdapter extends StorageAdapterBase { isPredicateGroup(predicateObjs[0]) && (predicateObjs[0] as PredicatesGroup).type !== 'not' ) { - type = (predicateObjs[0] as PredicatesGroup).type; + ({ type } = predicateObjs[0] as PredicatesGroup); predicateObjs = (predicateObjs[0] as PredicatesGroup).predicates; } diff --git a/packages/datastore/src/storage/adapter/StorageAdapterBase.ts b/packages/datastore/src/storage/adapter/StorageAdapterBase.ts index cb418456b00..cf77ea75242 100644 --- a/packages/datastore/src/storage/adapter/StorageAdapterBase.ts +++ b/packages/datastore/src/storage/adapter/StorageAdapterBase.ts @@ -82,9 +82,9 @@ export abstract class StorageAdapterBase implements Adapter { await this.preSetUpChecks(); if (!this.initPromise) { - this.initPromise = new Promise((res, rej) => { - this.resolve = res; - this.reject = rej; + this.initPromise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; }); } else { await this.initPromise; @@ -200,14 +200,14 @@ export abstract class StorageAdapterBase implements Adapter { const set = new Set(); const connectionStoreNames = Object.values(connectedModels).map( ({ modelName, item, instance }) => { - const storeName = getStorename(namespaceName, modelName); - set.add(storeName); + const resolvedStoreName = getStorename(namespaceName, modelName); + set.add(resolvedStoreName); const keys = getIndexKeys( this.schema.namespaces[namespaceName], modelName, ); - return { storeName, item, instance, keys }; + return { storeName: resolvedStoreName, item, instance, keys }; }, ); diff --git a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts index 95de512f4bd..f4313eee9a2 100644 --- a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts +++ b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts @@ -1,6 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Adapter } from '..'; +// eslint-disable-next-line import/no-named-as-default import AsyncStorageAdapter from '../AsyncStorageAdapter'; const getDefaultAdapter: () => Adapter = () => { diff --git a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts index 2507f38dea2..d4053fb29fe 100644 --- a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts +++ b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts @@ -4,6 +4,7 @@ import { isBrowser, isWebWorker } from '@aws-amplify/core/internals/utils'; import { Adapter } from '..'; import IndexedDBAdapter from '../IndexedDBAdapter'; +// eslint-disable-next-line import/no-named-as-default import AsyncStorageAdapter from '../AsyncStorageAdapter'; const getDefaultAdapter: () => Adapter = () => { diff --git a/packages/datastore/src/storage/relationship.ts b/packages/datastore/src/storage/relationship.ts index 02ad9c7f3d6..7cb92dfbf4f 100644 --- a/packages/datastore/src/storage/relationship.ts +++ b/packages/datastore/src/storage/relationship.ts @@ -213,6 +213,7 @@ export class ModelRelationship { // This case is theoretically unnecessary going forward. return [this.explicitRemoteAssociation.targetName!]; } else if (this.explicitRemoteAssociation?.targetNames) { + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain return this.explicitRemoteAssociation?.targetNames!; } else if (this.localAssociatedWith) { return this.localAssociatedWith; diff --git a/packages/datastore/src/storage/storage.ts b/packages/datastore/src/storage/storage.ts index 2005692cb52..5b8d23747be 100644 --- a/packages/datastore/src/storage/storage.ts +++ b/packages/datastore/src/storage/storage.ts @@ -88,9 +88,9 @@ class StorageClass implements StorageFacade { let resolve: (value?: void | PromiseLike) => void; let reject: (value?: void | PromiseLike) => void; - this.initialized = new Promise((res, rej) => { - resolve = res; - reject = rej; + this.initialized = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; }); this.adapter!.setUp( @@ -156,7 +156,7 @@ class StorageClass implements StorageFacade { const element = updateMutationInput || savedElement; - const modelConstructor = (Object.getPrototypeOf(savedElement) as Object) + const modelConstructor = (Object.getPrototypeOf(savedElement) as object) .constructor as PersistentModelConstructor; this.pushStream.next({ @@ -230,7 +230,7 @@ class StorageClass implements StorageFacade { } deleted.forEach(model => { - const modelConstructor = (Object.getPrototypeOf(model) as Object) + const resolvedModelConstructor = (Object.getPrototypeOf(model) as object) .constructor as PersistentModelConstructor; let theCondition: PredicatesGroup | undefined; @@ -243,7 +243,7 @@ class StorageClass implements StorageFacade { } this.pushStream.next({ - model: modelConstructor, + model: resolvedModelConstructor, opType: OpType.DELETE, element: model, mutator, @@ -264,7 +264,7 @@ class StorageClass implements StorageFacade { throw new Error('Storage adapter is missing'); } - return await this.adapter.query(modelConstructor, predicate, pagination); + return this.adapter.query(modelConstructor, predicate, pagination); } async queryOne( @@ -276,7 +276,7 @@ class StorageClass implements StorageFacade { throw new Error('Storage adapter is missing'); } - return await this.adapter.queryOne(modelConstructor, firstOrLast); + return this.adapter.queryOne(modelConstructor, firstOrLast); } observe( diff --git a/packages/datastore/src/sync/datastoreConnectivity.ts b/packages/datastore/src/sync/datastoreConnectivity.ts index 5e2fde5c091..10395ed753e 100644 --- a/packages/datastore/src/sync/datastoreConnectivity.ts +++ b/packages/datastore/src/sync/datastoreConnectivity.ts @@ -1,12 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Observable, Observer, SubscriptionLike } from 'rxjs'; -import { ConsoleLogger } from '@aws-amplify/core'; import { ReachabilityMonitor } from './datastoreReachability'; -const logger = new ConsoleLogger('DataStore'); - const RECONNECTING_IN = 5000; // 5s this may be configurable in the future interface ConnectionStatus { diff --git a/packages/datastore/src/sync/index.ts b/packages/datastore/src/sync/index.ts index dffb6d9d87e..3575caab2a8 100644 --- a/packages/datastore/src/sync/index.ts +++ b/packages/datastore/src/sync/index.ts @@ -27,7 +27,6 @@ import { NamespaceResolver, OpType, OptionallyManagedIdentifier, - PersistentModel, PersistentModelConstructor, SchemaModel, SchemaNamespace, @@ -161,12 +160,12 @@ export class SyncEngine { this.syncQueriesObservableStartSleeping = resolve; }); - const MutationEvent = this.modelClasses + const MutationEventCtor = this.modelClasses .MutationEvent as PersistentModelConstructor; this.outbox = new MutationEventOutbox( this.schema, - MutationEvent, + MutationEventCtor, modelInstanceCreator, ownSymbol, ); @@ -197,7 +196,7 @@ export class SyncEngine { this.userModelClasses, this.outbox, this.modelInstanceCreator, - MutationEvent, + MutationEventCtor, this.amplifyConfig, this.authModeStrategy, errorHandler, @@ -226,196 +225,187 @@ export class SyncEngine { // this is awaited at the bottom. so, we don't need to register // this explicitly with the context. it's already contained. - const startPromise = new Promise( - (doneStarting, failedStarting) => { - this.datastoreConnectivity.status().subscribe( - async ({ online }) => - this.runningProcesses.isOpen && - this.runningProcesses.add(async onTerminate => { - // From offline to online - if (online && !this.online) { - this.online = online; - - observer.next({ - type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, - data: { - active: this.online, - }, + const startPromise = new Promise((resolve, reject) => { + const doneStarting = resolve; + const failedStarting = reject; + + this.datastoreConnectivity.status().subscribe( + async ({ online }) => + this.runningProcesses.isOpen && + this.runningProcesses.add(async onTerminate => { + // From offline to online + if (online && !this.online) { + this.online = online; + + observer.next({ + type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, + data: { + active: this.online, + }, + }); + + this.stopDisruptionListener = this.startDisruptionListener(); + // #region GraphQL Subscriptions + const [ctlSubsObservable, dataSubsObservable] = + this.subscriptionsProcessor.start(); + + try { + await new Promise((_resolve, _reject) => { + onTerminate.then(_reject); + const ctlSubsSubscription = ctlSubsObservable.subscribe({ + next: msg => { + if (msg === CONTROL_MSG.CONNECTED) { + _resolve(); + } + }, + error: err => { + _reject(err); + const handleDisconnect = this.disconnectionHandler(); + handleDisconnect(err); + }, + }); + + subscriptions.push(ctlSubsSubscription); }); + } catch (err) { + observer.error(err); + failedStarting(); - let ctlSubsObservable: Observable; - let dataSubsObservable: Observable< - [TransformerMutationType, SchemaModel, PersistentModel] - >; - - this.stopDisruptionListener = - this.startDisruptionListener(); - // #region GraphQL Subscriptions - [ctlSubsObservable, dataSubsObservable] = - this.subscriptionsProcessor.start(); - - try { - await new Promise((resolve, reject) => { - onTerminate.then(reject); - const ctlSubsSubscription = ctlSubsObservable.subscribe( - { - next: msg => { - if (msg === CONTROL_MSG.CONNECTED) { - resolve(); - } - }, - error: err => { - reject(err); - const handleDisconnect = - this.disconnectionHandler(); - handleDisconnect(err); - }, - }, - ); + return; + } - subscriptions.push(ctlSubsSubscription); - }); - } catch (err) { - observer.error(err); - failedStarting(); + logger.log('Realtime ready'); - return; - } + observer.next({ + type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED, + }); - logger.log('Realtime ready'); + // #endregion - observer.next({ - type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED, - }); + // #region Base & Sync queries + try { + await new Promise((_resolve, _reject) => { + const syncQuerySubscription = + this.syncQueriesObservable().subscribe({ + next: message => { + const { type } = message; - // #endregion + if ( + type === + ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY + ) { + _resolve(); + } - // #region Base & Sync queries - try { - await new Promise((resolve, reject) => { - const syncQuerySubscription = - this.syncQueriesObservable().subscribe({ - next: message => { - const { type } = message; + observer.next(message); + }, + complete: () => { + _resolve(); + }, + error: error => { + _reject(error); + }, + }); - if ( - type === - ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY - ) { - resolve(); - } + if (syncQuerySubscription) { + subscriptions.push(syncQuerySubscription); + } + }); + } catch (error) { + observer.error(error); + failedStarting(); - observer.next(message); - }, - complete: () => { - resolve(); - }, - error: error => { - reject(error); + return; + } + // #endregion + + // #region process mutations (outbox) + subscriptions.push( + this.mutationsProcessor + .start() + .subscribe(({ modelDefinition, model: item, hasMore }) => + this.runningProcesses.add(async () => { + const modelConstructor = this.userModelClasses[ + modelDefinition.name + ] as PersistentModelConstructor; + + const model = this.modelInstanceCreator( + modelConstructor, + item, + ); + + await this.storage.runExclusive(storage => + this.modelMerger.merge( + storage, + model, + modelDefinition, + ), + ); + + observer.next({ + type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED, + data: { + model: modelConstructor, + element: model, }, }); - if (syncQuerySubscription) { - subscriptions.push(syncQuerySubscription); - } - }); - } catch (error) { - observer.error(error); - failedStarting(); - - return; - } - // #endregion - - // #region process mutations (outbox) - subscriptions.push( - this.mutationsProcessor - .start() - .subscribe( - ({ modelDefinition, model: item, hasMore }) => - this.runningProcesses.add(async () => { - const modelConstructor = this.userModelClasses[ - modelDefinition.name - ] as PersistentModelConstructor; - - const model = this.modelInstanceCreator( - modelConstructor, - item, - ); - - await this.storage.runExclusive(storage => - this.modelMerger.merge( - storage, - model, - modelDefinition, - ), - ); - - observer.next({ - type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED, - data: { - model: modelConstructor, - element: model, - }, - }); - - observer.next({ - type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS, - data: { - isEmpty: !hasMore, - }, - }); - }, 'mutation processor event'), - ), - ); - // #endregion - - // #region Merge subscriptions buffer - subscriptions.push( - dataSubsObservable!.subscribe( - ([_transformerMutationType, modelDefinition, item]) => - this.runningProcesses.add(async () => { - const modelConstructor = this.userModelClasses[ - modelDefinition.name - ] as PersistentModelConstructor; - - const model = this.modelInstanceCreator( - modelConstructor, - item, - ); - - await this.storage.runExclusive(storage => - this.modelMerger.merge( - storage, - model, - modelDefinition, - ), - ); - }, 'subscription dataSubsObservable event'), + observer.next({ + type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS, + data: { + isEmpty: !hasMore, + }, + }); + }, 'mutation processor event'), ), - ); - // #endregion - } else if (!online) { - this.online = online; - - observer.next({ - type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, - data: { - active: this.online, - }, - }); - - subscriptions.forEach(sub => { - sub.unsubscribe(); - }); - subscriptions = []; - } + ); + // #endregion + + // #region Merge subscriptions buffer + subscriptions.push( + dataSubsObservable!.subscribe( + ([_transformerMutationType, modelDefinition, item]) => + this.runningProcesses.add(async () => { + const modelConstructor = this.userModelClasses[ + modelDefinition.name + ] as PersistentModelConstructor; + + const model = this.modelInstanceCreator( + modelConstructor, + item, + ); + + await this.storage.runExclusive(storage => + this.modelMerger.merge( + storage, + model, + modelDefinition, + ), + ); + }, 'subscription dataSubsObservable event'), + ), + ); + // #endregion + } else if (!online) { + this.online = online; + + observer.next({ + type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, + data: { + active: this.online, + }, + }); + + subscriptions.forEach(sub => { + sub.unsubscribe(); + }); + subscriptions = []; + } - doneStarting(); - }, 'datastore connectivity event'), - ); - }, - ); + doneStarting(); + }, 'datastore connectivity event'), + ); + }); this.storage .observe(null, null, ownSymbol) @@ -507,14 +497,7 @@ export class SyncEngine { 'sync/index getModelsMetadataWithNextFullSync', ) ).map( - ({ - namespace, - model, - lastSync, - lastFullSync, - fullSyncInterval, - lastSyncPredicate, - }) => { + ({ namespace, model, lastSync, lastFullSync, fullSyncInterval }) => { const nextFullSync = lastFullSync! + fullSyncInterval; const syncFrom = !lastFullSync || nextFullSync < currentTimeStamp @@ -567,7 +550,7 @@ export class SyncEngine { let start: number; let syncDuration: number; let lastStartedAt: number; - await new Promise((resolve, reject) => { + await new Promise((resolve, _reject) => { if (!this.runningProcesses.isOpen) resolve(); onTerminate.then(() => { resolve(); @@ -777,16 +760,16 @@ export class SyncEngine { // TLDR; this is a lot of complexity here for a sleep(), // but, it's not clear to me yet how to support an // extensible, centralized cancelable `sleep()` elegantly. - await this.runningProcesses.add(async onTerminate => { - let sleepTimer; + await this.runningProcesses.add(async onRunningProcessTerminate => { + let _sleepTimer; let unsleep; - const sleep = new Promise(_unsleep => { - unsleep = _unsleep; - sleepTimer = setTimeout(unsleep, msNextFullSync); + const sleep = new Promise(resolve => { + unsleep = resolve; + _sleepTimer = setTimeout(unsleep, msNextFullSync); }); - onTerminate.then(() => { + onRunningProcessTerminate.then(() => { terminated = true; this.syncQueriesObservableStartSleeping(); unsleep(); @@ -937,10 +920,10 @@ export class SyncEngine { } private async getModelsMetadata(): Promise { - const ModelMetadata = this.modelClasses + const ModelMetadataCtor = this.modelClasses .ModelMetadata as PersistentModelConstructor; - const modelsMetadata = await this.storage.query(ModelMetadata); + const modelsMetadata = await this.storage.query(ModelMetadataCtor); return modelsMetadata; } @@ -949,18 +932,22 @@ export class SyncEngine { namespace: string, model: string, ): Promise { - const ModelMetadata = this.modelClasses + const ModelMetadataCtor = this.modelClasses .ModelMetadata as PersistentModelConstructor; const predicate = ModelPredicateCreator.createFromAST( - this.schema.namespaces[SYNC].models[ModelMetadata.name], + this.schema.namespaces[SYNC].models[ModelMetadataCtor.name], { and: [{ namespace: { eq: namespace } }, { model: { eq: model } }] }, ); - const [modelMetadata] = await this.storage.query(ModelMetadata, predicate, { - page: 0, - limit: 1, - }); + const [modelMetadata] = await this.storage.query( + ModelMetadataCtor, + predicate, + { + page: 0, + limit: 1, + }, + ); return modelMetadata; } diff --git a/packages/datastore/src/sync/merger.ts b/packages/datastore/src/sync/merger.ts index 01bed587d69..0cd5dde2989 100644 --- a/packages/datastore/src/sync/merger.ts +++ b/packages/datastore/src/sync/merger.ts @@ -67,7 +67,7 @@ class ModelMerger { const page = [...itemsMap.values()]; - return await storage.batchSave(modelConstructor, page, this.ownSymbol); + return storage.batchSave(modelConstructor, page, this.ownSymbol); } } diff --git a/packages/datastore/src/sync/outbox.ts b/packages/datastore/src/sync/outbox.ts index 07fba9991dc..b555e47b5dd 100644 --- a/packages/datastore/src/sync/outbox.ts +++ b/packages/datastore/src/sync/outbox.ts @@ -27,7 +27,7 @@ class MutationEventOutbox { constructor( private readonly schema: InternalSchema, - private readonly MutationEvent: PersistentModelConstructor, + private readonly _MutationEvent: PersistentModelConstructor, private readonly modelInstanceCreator: ModelInstanceCreator, private readonly ownSymbol: symbol, ) {} @@ -53,7 +53,7 @@ class MutationEventOutbox { ); // Check if there are any other records with same id - const [first] = await s.query(this.MutationEvent, predicate); + const [first] = await s.query(this._MutationEvent, predicate); // No other record with same modelId, so enqueue if (first === undefined) { @@ -67,7 +67,7 @@ class MutationEventOutbox { if (first.operation === TransformerMutationType.CREATE) { if (incomingMutationType === TransformerMutationType.DELETE) { - await s.delete(this.MutationEvent, predicate); + await s.delete(this._MutationEvent, predicate); } else { // first gets updated with the incoming mutation's data, condition intentionally skipped @@ -75,7 +75,7 @@ class MutationEventOutbox { // data loss, since update mutations only include changed fields const merged = this.mergeUserFields(first, mutationEvent); await s.save( - this.MutationEvent.copyOf(first, draft => { + this._MutationEvent.copyOf(first, draft => { draft.data = merged.data; }), undefined, @@ -92,7 +92,7 @@ class MutationEventOutbox { merged = this.mergeUserFields(first, mutationEvent); // delete all for model - await s.delete(this.MutationEvent, predicate); + await s.delete(this._MutationEvent, predicate); } merged = merged! || mutationEvent; @@ -128,7 +128,7 @@ class MutationEventOutbox { * @param storage */ public async peek(storage: StorageFacade): Promise { - const head = await storage.queryOne(this.MutationEvent, QueryOne.FIRST); + const head = await storage.queryOne(this._MutationEvent, QueryOne.FIRST); this.inProgressMutationEventId = head ? head.id : undefined!; @@ -146,7 +146,7 @@ class MutationEventOutbox { const modelId = getIdentifierValue(userModelDefinition, model); const mutationEvents = await storage.query( - this.MutationEvent, + this._MutationEvent, ModelPredicateCreator.createFromAST(mutationEventModelDefinition, { and: { modelId: { eq: modelId } }, }), @@ -156,7 +156,7 @@ class MutationEventOutbox { } public async getModelIds(storage: StorageFacade): Promise> { - const mutationEvents = await storage.query(this.MutationEvent); + const mutationEvents = await storage.query(this._MutationEvent); const result = new Set(); @@ -225,7 +225,7 @@ class MutationEventOutbox { ); const outdatedMutations = await storage.query( - this.MutationEvent, + this._MutationEvent, predicate, ); @@ -238,16 +238,16 @@ class MutationEventOutbox { const newData = { ...oldData, _version, _lastChangedAt }; - return this.MutationEvent.copyOf(m, draft => { + return this._MutationEvent.copyOf(m, draft => { draft.data = JSON.stringify(newData); }); }); - await storage.delete(this.MutationEvent, predicate); + await storage.delete(this._MutationEvent, predicate); await Promise.all( - reconciledMutations.map( - async m => await storage.save(m, undefined, this.ownSymbol), + reconciledMutations.map(async m => + storage.save(m, undefined, this.ownSymbol), ), ); } @@ -275,7 +275,7 @@ class MutationEventOutbox { ...currentData, }); - return this.modelInstanceCreator(this.MutationEvent, { + return this.modelInstanceCreator(this._MutationEvent, { ...current, data, }); diff --git a/packages/datastore/src/sync/processors/errorMaps.ts b/packages/datastore/src/sync/processors/errorMaps.ts index 5bd14736749..1714c7288b5 100644 --- a/packages/datastore/src/sync/processors/errorMaps.ts +++ b/packages/datastore/src/sync/processors/errorMaps.ts @@ -63,6 +63,7 @@ function unwrapObservableError(observableError: any) { const { errors: [error], } = ({ + // eslint-disable-next-line no-empty-pattern errors: [], } = observableError); diff --git a/packages/datastore/src/sync/processors/mutation.ts b/packages/datastore/src/sync/processors/mutation.ts index e7b390f3594..bc38e26e14e 100644 --- a/packages/datastore/src/sync/processors/mutation.ts +++ b/packages/datastore/src/sync/processors/mutation.ts @@ -84,7 +84,7 @@ class MutationProcessor { private readonly userClasses: TypeConstructorMap, private readonly outbox: MutationEventOutbox, private readonly modelInstanceCreator: ModelInstanceCreator, - private readonly MutationEvent: PersistentModelConstructor, + private readonly _MutationEvent: PersistentModelConstructor, private readonly amplifyConfig: Record = {}, private readonly authModeStrategy: AuthModeStrategy, private readonly errorHandler: ErrorHandler, @@ -217,7 +217,7 @@ class MutationProcessor { data, condition, modelConstructor, - this.MutationEvent, + this._MutationEvent, head, operationAuthModes[authModeAttempts], onTerminate, @@ -237,6 +237,7 @@ class MutationProcessor { }`, ); try { + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -262,7 +263,7 @@ class MutationProcessor { }`, ); - return await authModeRetry(); + return authModeRetry(); } }; @@ -315,30 +316,30 @@ class MutationProcessor { data: string, condition: string, modelConstructor: PersistentModelConstructor, - MutationEvent: PersistentModelConstructor, + MutationEventCtor: PersistentModelConstructor, mutationEvent: MutationEvent, authMode: GraphQLAuthMode, onTerminate: Promise, ): Promise< [GraphQLResult>, string, SchemaModel] > { - return await retry( + return retry( async ( - model: string, - operation: TransformerMutationType, - data: string, - condition: string, - modelConstructor: PersistentModelConstructor, - MutationEvent: PersistentModelConstructor, - mutationEvent: MutationEvent, + retriedModel: string, + retriedOperation: TransformerMutationType, + retriedData: string, + retriedCondition: string, + retriedModelConstructor: PersistentModelConstructor, + retiredMutationEventCtor: PersistentModelConstructor, + retiredMutationEvent: MutationEvent, ) => { const [query, variables, graphQLCondition, opName, modelDefinition] = this.createQueryVariables( namespaceName, - model, - operation, - data, - condition, + retriedModel, + retriedOperation, + retriedData, + retriedCondition, ); const authToken = await getTokenForCustomAuth( @@ -354,7 +355,7 @@ class MutationProcessor { }; let attempt = 0; - const opType = this.opTypeFromTransformerOperation(operation); + const opType = this.opTypeFromTransformerOperation(retriedOperation); const customUserAgentDetails: CustomUserAgentDetails = { category: Category.DataStore, @@ -402,20 +403,20 @@ class MutationProcessor { } else { try { retryWith = await this.conflictHandler!({ - modelConstructor, + modelConstructor: retriedModelConstructor, localModel: this.modelInstanceCreator( - modelConstructor, + retriedModelConstructor, variables.input, ), remoteModel: this.modelInstanceCreator( - modelConstructor, + retriedModelConstructor, error.data, ), operation: opType, attempts: attempt, }); - } catch (err) { - logger.warn('conflict trycatch', err); + } catch (caughtErr) { + logger.warn('conflict trycatch', caughtErr); continue; } } @@ -423,13 +424,13 @@ class MutationProcessor { if (retryWith === DISCARD) { // Query latest from server and notify merger - const [[, opName, query]] = buildGraphQLOperation( + const [[, builtOpName, builtQuery]] = buildGraphQLOperation( this.schema.namespaces[namespaceName], modelDefinition, 'GET', ); - const authToken = await getTokenForCustomAuth( + const newAuthToken = await getTokenForCustomAuth( authMode, this.amplifyConfig, ); @@ -437,10 +438,10 @@ class MutationProcessor { const serverData = (await this.amplifyContext.InternalAPI.graphql( { - query, + query: builtQuery, variables: { id: variables.input.id }, authMode, - authToken, + authToken: newAuthToken, }, undefined, customUserAgentDetails, @@ -448,7 +449,7 @@ class MutationProcessor { // onTerminate cancel graphql() - return [serverData, opName, modelDefinition]; + return [serverData, builtOpName, modelDefinition]; } const namespace = this.schema.namespaces[namespaceName]; @@ -459,12 +460,12 @@ class MutationProcessor { namespace.relationships!, modelDefinition, opType, - modelConstructor, + retriedModelConstructor, retryWith, graphQLCondition, - MutationEvent, + retiredMutationEventCtor, this.modelInstanceCreator, - mutationEvent.id, + retiredMutationEvent.id, ); await this.storage.save(updatedMutation); @@ -477,19 +478,23 @@ class MutationProcessor { 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', localModel: variables.input, message: error.message, - operation, + operation: retriedOperation, errorType: getMutationErrorType(error), errorInfo: error.errorInfo, process: ProcessName.mutate, cause: error, remoteModel: error.data - ? this.modelInstanceCreator(modelConstructor, error.data) + ? this.modelInstanceCreator( + retriedModelConstructor, + error.data, + ) : null!, }); - } catch (err) { - logger.warn('Mutation error handler failed with:', err); + } catch (caughtErr) { + logger.warn('Mutation error handler failed with:', caughtErr); } finally { // Return empty tuple, dequeues the mutation + // eslint-disable-next-line no-unsafe-finally return error.data ? [ { data: { [opName]: error.data } }, @@ -505,6 +510,7 @@ class MutationProcessor { throw new NonRetryableError(err); } } + // eslint-disable-next-line no-unmodified-loop-condition } while (tryWith); }, [ @@ -513,7 +519,7 @@ class MutationProcessor { data, condition, modelConstructor, - MutationEvent, + MutationEventCtor, mutationEvent, ], safeJitteredBackoff, @@ -599,7 +605,7 @@ class MutationProcessor { // scalar fields / non-model types if (operation === TransformerMutationType.UPDATE) { - if (!parsedData.hasOwnProperty(name)) { + if (!Object.prototype.hasOwnProperty.call(parsedData, name)) { // for update mutations - strip out a field if it's unchanged continue; } diff --git a/packages/datastore/src/sync/processors/subscription.ts b/packages/datastore/src/sync/processors/subscription.ts index 2815dbbfccc..ac3760255d0 100644 --- a/packages/datastore/src/sync/processors/subscription.ts +++ b/packages/datastore/src/sync/processors/subscription.ts @@ -163,6 +163,7 @@ class SubscriptionProcessor { const validGroup = (authMode === 'oidc' || authMode === 'userPool') && + // eslint-disable-next-line array-callback-return groupAuthRules.find(groupAuthRule => { // validate token against groupClaim if (oidcTokenPayload) { @@ -232,7 +233,7 @@ class SubscriptionProcessor { } private hubQueryCompletionListener( - completed: Function, + completed: () => void, capsule: HubCapsule<'datastore', { event: string }>, ) { const { @@ -369,7 +370,7 @@ class SubscriptionProcessor { }; if (addFilter && predicatesGroup) { - variables.filter = + (variables as any).filter = predicateToGraphQLFilter(predicatesGroup); } @@ -433,13 +434,12 @@ class SubscriptionProcessor { return; } - const predicatesGroup = + const resolvedPredicatesGroup = ModelPredicateCreator.getPredicates( this.syncPredicates.get(modelDefinition)!, false, ); - // @ts-ignore const { [opName]: record } = data; // checking incoming subscription against syncPredicate. @@ -449,7 +449,7 @@ class SubscriptionProcessor { if ( this.passesPredicateValidation( record, - predicatesGroup!, + resolvedPredicatesGroup!, ) ) { this.pushToBuffer( @@ -464,6 +464,7 @@ class SubscriptionProcessor { const { errors: [{ message = '' } = {}], } = ({ + // eslint-disable-next-line no-empty-pattern errors: [], } = subscriptionError); @@ -549,6 +550,7 @@ class SubscriptionProcessor { logger.warn('subscriptionError', message); try { + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -588,11 +590,11 @@ class SubscriptionProcessor { (async () => { let boundFunction: any; let removeBoundFunctionListener: () => void; - await new Promise(res => { - subscriptionReadyCallback = res; + await new Promise(resolve => { + subscriptionReadyCallback = resolve; boundFunction = this.hubQueryCompletionListener.bind( this, - res, + resolve, ); removeBoundFunctionListener = Hub.listen( 'api', diff --git a/packages/datastore/src/sync/processors/sync.ts b/packages/datastore/src/sync/processors/sync.ts index 70efa0e74f5..319e153cb50 100644 --- a/packages/datastore/src/sync/processors/sync.ts +++ b/packages/datastore/src/sync/processors/sync.ts @@ -176,7 +176,7 @@ class SyncProcessor { }. Retrying with authMode: ${readAuthModes[authModeAttempts]}`, ); - return await authModeRetry(); + return authModeRetry(); } }; @@ -219,8 +219,8 @@ class SyncProcessor { > > > { - return await jitteredExponentialRetry( - async (query, variables) => { + return jitteredExponentialRetry( + async (retriedQuery, retriedVariables) => { try { const authToken = await getTokenForCustomAuth( authMode, @@ -234,8 +234,8 @@ class SyncProcessor { return await this.amplifyContext.InternalAPI.graphql( { - query, - variables, + query: retriedQuery, + variables: retriedVariables, authMode, authToken, }, @@ -280,6 +280,7 @@ class SyncProcessor { await Promise.all( otherErrors.map(async err => { try { + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -400,7 +401,8 @@ class SyncProcessor { parentPromises.get(`${namespace}_${parent}`), ); - const promise = new Promise(async res => { + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async resolve => { await Promise.all(promises); do { @@ -414,7 +416,7 @@ class SyncProcessor { `Sync processor has been stopped, terminating sync for ${modelDefinition.name}`, ); - res(); + resolve(); return; } @@ -440,6 +442,7 @@ class SyncProcessor { )); } catch (error) { try { + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -481,7 +484,7 @@ class SyncProcessor { }); } while (!done); - res(); + resolve(); }); parentPromises.set( diff --git a/packages/datastore/src/sync/utils.ts b/packages/datastore/src/sync/utils.ts index 45dd4e43fe7..2e538ab0efa 100644 --- a/packages/datastore/src/sync/utils.ts +++ b/packages/datastore/src/sync/utils.ts @@ -45,13 +45,13 @@ import { MutationEvent } from './'; const logger = new ConsoleLogger('DataStore'); -enum GraphQLOperationType { - LIST = 'query', - CREATE = 'mutation', - UPDATE = 'mutation', - DELETE = 'mutation', - GET = 'query', -} +const GraphQLOperationType = { + LIST: 'query', + CREATE: 'mutation', + UPDATE: 'mutation', + DELETE: 'mutation', + GET: 'query', +}; export enum TransformerMutationType { CREATE = 'Create', @@ -120,7 +120,9 @@ function getOwnerFields( if (isSchemaModelWithAttributes(modelDefinition)) { modelDefinition.attributes!.forEach(attr => { if (attr.properties && attr.properties.rules) { - const rule = attr.properties.rules.find(rule => rule.allow === 'owner'); + const rule = attr.properties.rules.find( + currentRule => currentRule.allow === 'owner', + ); if (rule && rule.ownerField) { ownerFields.push(rule.ownerField); } @@ -177,11 +179,12 @@ function getConnectionFields( // Need to retrieve relations in order to get connected model keys const [relations] = establishRelationAndKeys(namespace); - const connectedModelName = - modelDefinition.fields[name].type.model; + const connectedModelName = ( + modelDefinition.fields[name].type as any + ).model; const byPkIndex = relations[connectedModelName].indexes.find( - ([name]) => name === 'byPk', + ([currentName]) => currentName === 'byPk', ); const keyFields = byPkIndex && byPkIndex[1]; const keyFieldSelectionSet = keyFields?.join(' '); @@ -212,17 +215,18 @@ function getNonModelFields( if (isNonModelFieldType(type)) { const typeDefinition = namespace.nonModels![type.nonModel]; const scalarFields = Object.values(getScalarFields(typeDefinition)).map( - ({ name }) => name, + ({ name: currentName }) => currentName, ); const nested: string[] = []; Object.values(typeDefinition.fields).forEach(field => { - const { type, name } = field; + const { type: fieldType, name: fieldName } = field; - if (isNonModelFieldType(type)) { - const typeDefinition = namespace.nonModels![type.nonModel]; + if (isNonModelFieldType(fieldType)) { + const nonModelTypeDefinition = + namespace.nonModels![fieldType.nonModel]; nested.push( - `${name} { ${generateSelectionSet(namespace, typeDefinition)} }`, + `${fieldName} { ${generateSelectionSet(namespace, nonModelTypeDefinition)} }`, ); } }); @@ -900,8 +904,8 @@ export function getForbiddenError(error) { } export function resolveServiceErrorStatusCode(error: unknown): number | null { - if (error?.$metadata?.httpStatusCode) { - return Number(error?.$metadata?.httpStatusCode); + if ((error as any)?.$metadata?.httpStatusCode) { + return Number((error as any)?.$metadata?.httpStatusCode); } else if ((error as GraphQLError)?.originalError) { return resolveServiceErrorStatusCode( (error as GraphQLError)?.originalError, diff --git a/packages/datastore/src/types.ts b/packages/datastore/src/types.ts index 66ec74e965a..df534a879f5 100644 --- a/packages/datastore/src/types.ts +++ b/packages/datastore/src/types.ts @@ -252,6 +252,7 @@ export enum GraphQLScalarType { AWSIPAddress, } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace GraphQLScalarType { export function getJSType( scalar: keyof Omit< @@ -338,7 +339,7 @@ export interface ModelFieldType { model: string; modelConstructor?: ModelMeta; } -export function isModelFieldType( +export function isModelFieldType<_ extends PersistentModel>( obj: any, ): obj is ModelFieldType { const modelField: keyof ModelFieldType = 'model'; @@ -536,14 +537,6 @@ type OptionalRelativesOf = type OmitOptionalRelatives = Omit>; type PickOptionalRelatives = Pick>; -type OmitOptionalFields = Omit< - T, - KeysOfSuperType | OptionalRelativesOf ->; -type PickOptionalFields = Pick< - T, - KeysOfSuperType | OptionalRelativesOf ->; export interface DefaultPersistentModelMetaData { identifier: ManagedIdentifier<{ id: string }, 'id'>; @@ -576,6 +569,7 @@ export type MetadataReadOnlyFields< // This type makes optional some identifiers in the constructor init object (e.g. OptionallyManagedIdentifier) export type ModelInitBase< T extends PersistentModel, + // eslint-disable-next-line @typescript-eslint/ban-types M extends PersistentModelMetaData = {}, > = Omit< T, @@ -590,6 +584,7 @@ export type ModelInitBase< export type ModelInit< T extends PersistentModel, + // eslint-disable-next-line @typescript-eslint/ban-types M extends PersistentModelMetaData = {}, > = { [P in keyof OmitOptionalRelatives>]: SettableFieldType< @@ -615,6 +610,7 @@ type DeepWritable = { export type MutableModel< T extends PersistentModel, + // eslint-disable-next-line @typescript-eslint/ban-types M extends PersistentModelMetaData = {}, // This provides Intellisense with ALL of the properties, regardless of read-only // but will throw a linting error if trying to overwrite a read-only property @@ -1202,7 +1198,7 @@ export type ModelPredicateAggregateExtender = ( ) => PredicateInternalsKey[]; export type ValuePredicate< - RT extends PersistentModel, + _RT extends PersistentModel, MT extends MatchableTypes, > = { [K in AllFieldOperators]: K extends 'between' diff --git a/packages/datastore/src/util.ts b/packages/datastore/src/util.ts index 060e0ea5b56..cd33533ebef 100644 --- a/packages/datastore/src/util.ts +++ b/packages/datastore/src/util.ts @@ -156,10 +156,11 @@ export const validatePredicateField = ( return value >= operand; case 'gt': return value > operand; - case 'between': + case 'between': { const [min, max] = operand as [T, T]; return value >= min && value <= max; + } case 'beginsWith': return ( !isNullOrUndefined(value) && @@ -224,7 +225,9 @@ export const traverseModel = ( instance: T; }[] = []; - const newInstance = modelConstructor.copyOf(instance, () => {}); + const newInstance = modelConstructor.copyOf(instance, () => { + // no-op + }); result.unshift({ modelName: srcModelName, @@ -255,6 +258,7 @@ let privateModeCheckResult; export const isPrivateMode = () => { return new Promise(resolve => { const dbname = amplifyUuid(); + // eslint-disable-next-line prefer-const let db; const isPrivate = () => { @@ -328,18 +332,18 @@ export const isSafariCompatabilityMode: () => Promise = async () => { }; dbOpenRequest.onsuccess = () => { - const db = dbOpenRequest.result; - resolve(db); + const openedDb = dbOpenRequest.result; + resolve(openedDb); }; dbOpenRequest.onupgradeneeded = (event: any) => { - const db = event?.target?.result; + const upgradedDb = event?.target?.result; - db.onerror = () => { + upgradedDb.onerror = () => { resolve(false); }; - const store = db.createObjectStore(storeName, { + const store = upgradedDb.createObjectStore(storeName, { autoIncrement: true, }); @@ -376,6 +380,7 @@ export const isSafariCompatabilityMode: () => Promise = async () => { }); if (db && typeof db.close === 'function') { + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await db.close(); } @@ -747,6 +752,7 @@ export class DeferredPromise { public resolve: (value: string | PromiseLike) => void; public reject: () => void; constructor() { + // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; this.promise = new Promise( (resolve: (value: string | PromiseLike) => void, reject) => { @@ -763,7 +769,10 @@ export class DeferredCallbackResolver { private maxInterval: number; private timer: ReturnType; private raceInFlight = false; - private callback = () => {}; + private callback = () => { + // no-op + }; + private errorHandler: (error: string) => void; private defaultErrorHandler = ( msg = 'DeferredCallbackResolver error', @@ -778,7 +787,7 @@ export class DeferredCallbackResolver { } private startTimer(): void { - this.timerPromise = new Promise((resolve, reject) => { + this.timerPromise = new Promise((resolve, _reject) => { this.timer = setTimeout(() => { resolve(LimitTimerRaceResolvedValues.TIMER); }, this.maxInterval); @@ -803,6 +812,7 @@ export class DeferredCallbackResolver { this.raceInFlight = false; this.limitPromise = new DeferredPromise(); + // eslint-disable-next-line no-unsafe-finally return winner!; } } @@ -1039,7 +1049,8 @@ export const establishRelationAndKeys = ( relationType: connectionType, targetName: fieldAttribute.association!.targetName, targetNames: fieldAttribute.association!.targetNames, - associatedWith: fieldAttribute.association!.associatedWith, + // eslint-disable-next-line dot-notation + associatedWith: fieldAttribute.association!['associatedWith'], }); if (connectionType === 'BELONGS_TO') { @@ -1109,6 +1120,7 @@ export const getIndex = ( src: string, ): string | undefined => { let indexName; + // eslint-disable-next-line array-callback-return rel.some((relItem: RelationType) => { if (relItem.modelName === src) { const targetNames = extractTargetNamesFromSrc(relItem);