From e71425d9c382ac46d8e715dbc69ddf182740a060 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 13 Sep 2019 21:54:33 -0700 Subject: [PATCH 01/19] [DOC serializer] implements MinimumSerializerInterface --- packages/adapter/addon/-private/index.js | 1 + .../-private/utils/serialize-into-hash.js | 11 + packages/adapter/addon/json-api.js | 7 +- packages/adapter/addon/rest.js | 14 +- packages/serializer/tsconfig.json | 10 +- .../store/addon/-private/system/core-store.ts | 10 +- .../addon/-private/system/fetch-manager.ts | 11 +- .../-private/system/model/internal-model.ts | 3 + .../minimum-serializer-interface.ts | 242 ++++++++++++++++++ 9 files changed, 291 insertions(+), 18 deletions(-) create mode 100644 packages/adapter/addon/-private/utils/serialize-into-hash.js create mode 100644 packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts diff --git a/packages/adapter/addon/-private/index.js b/packages/adapter/addon/-private/index.js index e244a107c58..4d1d18fb8df 100644 --- a/packages/adapter/addon/-private/index.js +++ b/packages/adapter/addon/-private/index.js @@ -7,3 +7,4 @@ export { determineBodyPromise } from './utils/determine-body-promise'; export { serializeQueryParams } from './utils/serialize-query-params'; export { default as fetch } from './utils/fetch'; export { default as BuildURLMixin } from './build-url-mixin'; +export { default as serializeIntoHash } from './utils/serialize-into-hash'; diff --git a/packages/adapter/addon/-private/utils/serialize-into-hash.js b/packages/adapter/addon/-private/utils/serialize-into-hash.js new file mode 100644 index 00000000000..ecdde068a24 --- /dev/null +++ b/packages/adapter/addon/-private/utils/serialize-into-hash.js @@ -0,0 +1,11 @@ +export default function serializeIntoHash(store, modelClass, snapshot, options = { includeId: true }) { + const serializer = store.serializerFor(modelClass.modelName); + + if (typeof serializer.serializeIntoHash === 'function') { + const data = {}; + serializer.serializeIntoHash(data, type, snapshot, options); + return data; + } + + return serializer.serialize(snapshot, options); +} diff --git a/packages/adapter/addon/json-api.js b/packages/adapter/addon/json-api.js index d86da31278a..a241d8b9efe 100644 --- a/packages/adapter/addon/json-api.js +++ b/packages/adapter/addon/json-api.js @@ -4,6 +4,7 @@ import { dasherize } from '@ember/string'; import RESTAdapter from './rest'; import { pluralize } from 'ember-inflector'; +import { serializeIntoHash } from './-private'; /** The `JSONAPIAdapter` is the default adapter used by Ember Data. It @@ -228,12 +229,8 @@ const JSONAPIAdapter = RESTAdapter.extend({ return pluralize(dasherized); }, - // TODO: Remove this once we have a better way to override HTTP verbs. updateRecord(store, type, snapshot) { - let data = {}; - let serializer = store.serializerFor(type.modelName); - - serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); + const data = serializeIntoHash(store, type, snapshot); let url = this.buildURL(type.modelName, snapshot.id, snapshot, 'updateRecord'); diff --git a/packages/adapter/addon/rest.js b/packages/adapter/addon/rest.js index 0e2cb6bd0ce..a95a35e6f8f 100644 --- a/packages/adapter/addon/rest.js +++ b/packages/adapter/addon/rest.js @@ -23,6 +23,7 @@ import AdapterError, { } from '@ember-data/adapter/error'; import { warn } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; +import { serializeIntoHash } from './-private'; const Promise = EmberPromise; const hasJQuery = typeof jQuery !== 'undefined'; @@ -725,13 +726,11 @@ const RESTAdapter = Adapter.extend(BuildURLMixin, { @return {Promise} promise */ createRecord(store, type, snapshot) { - let data = {}; - let serializer = store.serializerFor(type.modelName); let url = this.buildURL(type.modelName, null, snapshot, 'createRecord'); - serializer.serializeIntoHash(data, type, snapshot, { includeId: true }); + const data = serializeIntoHash(store, type, snapshot); - return this.ajax(url, 'POST', { data: data }); + return this.ajax(url, 'POST', { data }); }, /** @@ -751,15 +750,12 @@ const RESTAdapter = Adapter.extend(BuildURLMixin, { @return {Promise} promise */ updateRecord(store, type, snapshot) { - let data = {}; - let serializer = store.serializerFor(type.modelName); - - serializer.serializeIntoHash(data, type, snapshot); + const data = serializeIntoHash(store, type, snapshot, {}); let id = snapshot.id; let url = this.buildURL(type.modelName, id, snapshot, 'updateRecord'); - return this.ajax(url, 'PUT', { data: data }); + return this.ajax(url, 'PUT', { data }); }, /** diff --git a/packages/serializer/tsconfig.json b/packages/serializer/tsconfig.json index cd1e2d61179..60288ddb7cb 100644 --- a/packages/serializer/tsconfig.json +++ b/packages/serializer/tsconfig.json @@ -29,6 +29,14 @@ "*": ["types/*"] } }, - "include": ["app/**/*", "addon/**/*", "tests/**/*", "types/**/*", "test-support/**/*", "addon-test-support/**/*"], + "include": [ + "app/**/*", + "addon/**/*", + "tests/**/*", + "types/**/*", + "test-support/**/*", + "addon-test-support/**/*", + "../store/addon/minimum-serializer-interface.ts" + ], "exclude": ["node_modules"] } diff --git a/packages/store/addon/-private/system/core-store.ts b/packages/store/addon/-private/system/core-store.ts index d2342badec1..eea95396edd 100644 --- a/packages/store/addon/-private/system/core-store.ts +++ b/packages/store/addon/-private/system/core-store.ts @@ -80,6 +80,7 @@ import Reference from './references/reference'; import { Dict } from '../ts-interfaces/utils'; import constructResource from '../utils/construct-resource'; +import { errorsArrayToHash } from './errors-utils'; const emberRun = emberRunLoop.backburner; const { ENV } = Ember; @@ -3582,7 +3583,14 @@ function _commit(adapter, store, operation, snapshot) { }, function (error) { if (error instanceof InvalidError) { - let parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id); + let parsedErrors; + + if (typeof serializer.extractErrors === 'function') { + parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id); + } else { + parsedErrors = errorsArrayToHash(error.errors); + } + store.recordWasInvalid(internalModel, parsedErrors, error); } else { store.recordWasError(internalModel, error); diff --git a/packages/store/addon/-private/system/fetch-manager.ts b/packages/store/addon/-private/system/fetch-manager.ts index 415a5f1f3f4..bb6ceb1cdc5 100644 --- a/packages/store/addon/-private/system/fetch-manager.ts +++ b/packages/store/addon/-private/system/fetch-manager.ts @@ -8,7 +8,6 @@ import { normalizeResponseHelper } from './store/serializer-response'; import { InvalidError } from '@ember-data/adapter/error'; import coerceId from './coerce-id'; import { A } from '@ember/array'; - import { _findHasMany, _findBelongsTo, _findAll, _query, _queryRecord } from './store/finders'; import RequestCache from './request-cache'; import { CollectionResourceDocument, SingleResourceDocument } from '../ts-interfaces/ember-data-json-api'; @@ -18,6 +17,7 @@ import { symbol } from '../ts-interfaces/utils/symbol'; import Store from './ds-model-store'; import recordDataFor from './record-data-for'; import CoreStore from './core-store'; +import { errorsArrayToHash } from './errors-utils'; function payloadIsNotBlank(adapterPayload): boolean { if (Array.isArray(adapterPayload)) { @@ -135,7 +135,14 @@ export default class FetchManager { }, function(error) { if (error instanceof InvalidError) { - let parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id); + let parsedErrors = error.errors; + + if (typeof serializer.extractErrors === 'function') { + parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id); + } else { + parsedErrors = errorsArrayToHash(error.errors); + } + throw { error, parsedErrors }; } else { throw { error }; diff --git a/packages/store/addon/-private/system/model/internal-model.ts b/packages/store/addon/-private/system/model/internal-model.ts index 6c4a3c2a9af..bb3c27d3804 100644 --- a/packages/store/addon/-private/system/model/internal-model.ts +++ b/packages/store/addon/-private/system/model/internal-model.ts @@ -1469,6 +1469,9 @@ export default class InternalModel { this.addErrorMessageToAttribute(attribute, parsedErrors[attribute]); } } + } else { + // TODO recordData.getErrors code branch + throw new Error(`Support for RecordData.getErrors is not yet present`); } let jsonApiErrors: JsonApiValidationError[] = errorsHashToArray(parsedErrors); diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts new file mode 100644 index 00000000000..fad66e1624f --- /dev/null +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -0,0 +1,242 @@ +/** + ## Overview + + In order to properly manage and present your data, `EmberData` + needs to understand the structure of data it receives. + + `Serializers` convert data between the format used by an API + and the format `EmberData` understands. + + Data received from an API response is `"normalized"` into + [JSON:API](https://jsonapi.org/) (the format used internally + by `EmberData`), while data sent to an API is `"serialized"` + into the format the API expects. + + ### Implementing a Serializer + + There are only two required serializer methods, one for + normalizing data in `JSON:API`, and another for serializing + records via `Snapshot`s into the expected API. + + To implement a serializer, export a class implementing the + [MinimumSerializerInterface](MinimumSerializerInterface) from + the `app/serializers/` directory. An example is below. + + ```ts + import EmberObject from '@ember/object'; + + export default class ApplicationSerializer extends EmberObject { + normalizeResponse(_, __, rawPayload) { + return rawPayload; + } + serialize(snapshot, options) { + const serializedResource = { + id: snapshot.id(), + type: snapshot.modelName, + attributes: snapshot.attributes() + }; + + return serializedResource; + } + } + ``` + + + #### Serializer Resolution + + How to achieve per-model + How to achieve per-API + How to achieve single + + ### Using a Serializer + + ### Default Serializers + + @module @ember-data/serializer + @main @ember-data/serializer + @class MinimumSerializerInterface + @public +*/ + +import { Object as JSONObject } from 'json-typescript'; +import Store from '../system/core-store'; +import { JsonApiDocument, SingleResourceDocument } from './ember-data-json-api'; +import Snapshot from '../system/snapshot'; +import ShimModelClass from '../system/model/shim-model-class'; + +type OptionsHash = Record; + +interface Serializer { + /** + * This method is responsible for normalizing the value resolved from the promise returned + * by an Adapter request into the format expected by the `Store`. + * + * The output should be a [JSON:API](https://jsonapi.org/) document with the following + * additional restrictions: + * + * - `type` should be formatted in the `singular` `dasherized` `lowercase` form + * - `members` (the property names of attributes and relationships) should be formatted + * to match their definition in the corresponding `Model` definition. Typically this + * will be `camelCase`. + * + * @method normalizeResponse + * @param {Store} store - the store service that initiated the request being normalized + * @param {ShimModelClass} schema - An object with methods for accessing information about + * the type, attributes and relationships of the primary type associated with the request. + * @param {JSONObject} rawPayload - The raw JSON response data returned from an API request. + * This correlates to the value the promise returned by the adapter method that performed + * the request resolved to. + * @param {string|null} id - For a `findRecord` request, this is the `id` initially provided + * in the call to `store.findRecord`. Else this value is `null`. + * @param {'findRecord' | 'queryRecord' | 'findAll' | 'findBelongsTo' | 'findHasMany' | 'findMany' | 'query' | 'createRecord' | 'deleteRecord' | 'updateRecord'} requestType - The + * type of request the Adapter had been asked to perform. + * + * @returns {JsonApiDocument} - a document following the structure of a `JSON:API` Document. + */ + normalizeResponse( + store: Store, + schema: ShimModelClass, + rawPayload: JSONObject, + id: string | null, + requestType: + | 'findRecord' + | 'queryRecord' + | 'findAll' + | 'findBelongsTo' + | 'findHasMany' + | 'findMany' + | 'query' + | 'createRecord' + | 'deleteRecord' + | 'updateRecord' + ): JsonApiDocument; + + /** + * This method is responsible for serializing an individual record + * via a [Snapshot](Snapshot) into the format expected by the API. + * + * This method is called by `snapshot.serialize()`. + * + * When using `Model`, this method is called by `record.serialize()`. + * + * When using `JSONAPIAdapter` or `RESTAdapter` this method is called + * by `updateRecord` and `createRecord` if `Serializer.serializeIntoHash` + * is not implemented. + * + * @method serialize + * @param {Snapshot} snapshot - A Snapshot for the record to serialize + * @param {object} options + */ + serialize(snapshot: Snapshot, options?: OptionsHash): JSONObject; + + /** + * This method is intended to normalize data into a `JsonApiDocument` representing + * with a data member containing a single `resource`. + * + * This method is called by the `Store` when `store.normalize(modelName, payload)` is + * called. It is recommended to use `store.serializerFor(modelName).normalizeResponse` + * over `store.normalize`. + * + * This method may be called when also using the `RESTSerializer` + * when `serializer.pushPayload` is called by `store.pushPayload`. + * It is recommended to use `store.push` over `store.pushPayload` after normalizing + * the payload directly. + * + * Example: + * ```js + * function pushPayload(store, modelName, rawPayload) { + * const ModelClass = store.modelFor(modelName); + * const serializer = store.serializerFor(modelName); + * const jsonApiPayload = serializer.normalizeResponse(store, ModelClass, rawPayload, null, 'query'); + * + * return store.push(jsonApiPayload); + * } + * ``` + * + * This method may be called when also using the `JSONAPISerializer` + * when normalizing included records. If mixing serializer usage in this way + * we recommend implementing this method, but caution that it may lead + * to unexpected mixing of formats. + * + * This method may also be called when normalizing embedded relationships when + * using the `EmbeddedRecordsMixin`. If using this mixin in a serializer in + * your application we recommend implementing this method, but caution that + * it may lead to unexpected mixing of formats. + * + * @method normalize [OPTIONAL] + * @param {ShimModelClass} schema - An object with methods for accessing information about + * the type, attributes and relationships of the primary type associated with the request. + * @param {JSONObject} rawPayload - Some raw JSON data to be normalized into a `JSON:API` resource. + * @param {string} [prop] - When called by the `EmbeddedRecordsMixin` this param will be the + * property at which the object provided as rawPayload was found. + * @returns {SingleResourceDocument} - A `JSON:API` document containing a single `resource` as + * its primary data. + */ + normalize?(schema: ShimModelClass, rawPayload: JSONObject, prop?: string): SingleResourceDocument; + + /** + * When using `JSONAPIAdapter` or `RESTAdapter` this method is called + * by `adapter.updateRecord` and `adapter.createRecord` if `Serializer.serializeIntoHash` + * is not implemented. + * + * You can use this method to customize the root keys serialized into the payload. + * The hash property should be modified by reference. + * + * For instance, your API may expect resources to be keyed by underscored type in the payload: + * + * ```js + * { + * _user: { + * type: 'user', + * id: '1' + * } + * } + * ``` + * + * Which when using these adapters can be achieved by implementing this method similar + * to the following: + * + * ```js + * serializeIntoHash(hash, ModelClass, snapshot, options) { + * hash[`_${snapshot.modelName}`] = this.serialize(snapshot, options).data; + * } + * ``` + * + * @method serializeIntoHash [OPTIONAL] + * @param hash - an top most object of the request payload onto + * which to append the serialized record + * @param {ShimModelClass} schema - An object with methods for accessing information about + * the type, attributes and relationships of the primary type associated with the request. + * @param {Snapshot} snapshot - A Snapshot for the record to serialize + * @param options + */ + serializeIntoHash?(hash: object, schema: ShimModelClass, snapshot: Snapshot, options?: OptionsHash): void; + + /** + * This method is called by `store.pushPayload` and should be implemented if + * you want to use that method. + * + * It is recommended to use `store.push` over `store.pushPayload` after normalizing + * the payload directly. + * + * Example: + * ```js + * function pushPayload(store, modelName, rawPayload) { + * const ModelClass = store.modelFor(modelName); + * const serializer = store.serializerFor(modelName); + * const jsonApiPayload = serializer.normalizeResponse(store, ModelClass, rawPayload, null, 'query'); + * + * return store.push(jsonApiPayload); + * } + * ``` + * + * @method pushPayload [OPTIONAL] + * @param {Store} store - the store service that initiated the request being normalized + * @param {JSONObject} rawPayload - The raw JSON response data returned from an API request. + * This JSON should be in the API format expected by the serializer. + * @returns {JsonApiDocument} - a document following the structure of a `JSON:API` Document. + */ + pushPayload?(store: Store, rawPayload: JSONObject): JsonApiDocument; +} + +export default Serializer; From 7738533e7bab14765994845b42520ac291cacdd1 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 13 Sep 2019 22:23:01 -0700 Subject: [PATCH 02/19] expand docs --- .../minimum-serializer-interface.ts | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index fad66e1624f..83ed9c934ee 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -44,14 +44,43 @@ #### Serializer Resolution - How to achieve per-model - How to achieve per-API - How to achieve single + The instances of serializers defined in `app/serializers/` can be looked up + via `store.serializerFor(name)`. + + It is recommended that applications define only a single `application` adapter and serializer + where possible. + + `serializerFor` first attempts to find a serializer with an exact match on `name`, + then falls back to checking for the presence of a serializer named `application`. + + Because most requests in `ember-data` occur from the perspective of a primary `type` + (or `modelName`) typically `serializerFor` will be used to find a serializer with a name + matching that of the primary resource `type` for the request, falling back to the `application` + serializer for those types that do not have a defined serializer. This is often described + as a `per-model` or `per-type` strategy for defining serializers. However, because APIs + rarely format payloads per-type but rather per-API-version, this may not be a desired strategy. + + If you have multiple API formats and the per-type strategy is not viable, one strategy is to + write an `application` adapter and serializer that make use of `options` to specify the desired + format when making a request. ### Using a Serializer + Any serializer in `app/serializers` can be looked up by `name` using `store.serializerFor(name)`. + ### Default Serializers + It is recommended that apps write their own serializer to best suit the needs of their API and + application. + + However, for applications whose APIs are *very close to* or *exactly* the `REST` or `JSON:API` + format the `@ember-data/serializer` package contains implementations these applications can + extend. It also contains a simly `JSONSerializer` for serializing to/from very simple JSON objects. + + Many applications will find writing their own serializer to be more performant and less + complex than extending these classes even when their API format is very close to that expected + by these serializers. + @module @ember-data/serializer @main @ember-data/serializer @class MinimumSerializerInterface From 8cc270a31054d155b4188aab01eb38b6b542e1d5 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Fri, 13 Sep 2019 22:24:15 -0700 Subject: [PATCH 03/19] fix variable name --- packages/adapter/addon/-private/utils/serialize-into-hash.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter/addon/-private/utils/serialize-into-hash.js b/packages/adapter/addon/-private/utils/serialize-into-hash.js index ecdde068a24..daa20881f70 100644 --- a/packages/adapter/addon/-private/utils/serialize-into-hash.js +++ b/packages/adapter/addon/-private/utils/serialize-into-hash.js @@ -3,7 +3,7 @@ export default function serializeIntoHash(store, modelClass, snapshot, options = if (typeof serializer.serializeIntoHash === 'function') { const data = {}; - serializer.serializeIntoHash(data, type, snapshot, options); + serializer.serializeIntoHash(data, modelClass, snapshot, options); return data; } From 6b6c8274289c0156966fb26925eb7d0dd8a6c106 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 14 Sep 2019 12:50:22 -0700 Subject: [PATCH 04/19] fix tests --- .../node-tests/fixtures/expected.js | 527 +++++++++--------- .../-private/system/model/internal-model.ts | 3 - .../minimum-serializer-interface.ts | 9 + 3 files changed, 274 insertions(+), 265 deletions(-) diff --git a/packages/-ember-data/node-tests/fixtures/expected.js b/packages/-ember-data/node-tests/fixtures/expected.js index 0fe1dca017d..63d4b26ffab 100644 --- a/packages/-ember-data/node-tests/fixtures/expected.js +++ b/packages/-ember-data/node-tests/fixtures/expected.js @@ -1,263 +1,266 @@ module.exports = { - "classitems": [ - "buildURL", - "_buildURL", - "urlForFindRecord", - "urlForFindAll", - "urlForQuery", - "urlForQueryRecord", - "urlForFindMany", - "urlForFindHasMany", - "urlForFindBelongsTo", - "urlForCreateRecord", - "urlForUpdateRecord", - "urlForDeleteRecord", - "urlPrefix", - "pathForType", - "errorsHashToArray", - "errorsArrayToHash", - "_registerHandlers", - "errorsByAttributeName", - "errorsFor", - "messages", - "content", - "unknownProperty", - "length", - "isEmpty", - "add", - "_add", - "_findOrCreateMessages", - "remove", - "_remove", - "_clear", - "has", - "isLoading", - "isLoaded", - "hasDirtyAttributes", - "isSaving", - "isDeleted", - "isNew", - "isValid", - "dirtyType", - "isError", - "isReloading", - "id", - "currentState", - "_internalModel", - "recordData", - "store", - "errors", - "adapterError", - "serialize", - "toJSON", - "ready", - "didLoad", - "didUpdate", - "didCreate", - "didDelete", - "becameInvalid", - "becameError", - "rolledBack", - "send", - "transitionTo", - "deleteRecord", - "destroyRecord", - "unloadRecord", - "_notifyProperties", - "changedAttributes", - "rollbackAttributes", - "save", - "reload", - "trigger", - "belongsTo", - "hasMany", - "_debugInfo", - "eachRelationship", - "data", - "create", - "modelName", - "typeForRelationship", - "inverseFor", - "relationships", - "relationshipNames", - "relatedTypes", - "relationshipsByName", - "fields", - "eachRelatedType", - "attributes", - "transformedAttributes", - "eachAttribute", - "eachTransformedAttribute", - "toString", - "_setInternalModels", - "isUpdating", - "type", - "objectAtContent", - "update", - "_pushInternalModels", - "removeInternalModel", - "_unregisterFromManager", - "push", - "value", - "load", - "remoteType", - "ids", - "link", - "meta", - "diffArray", - "retrieve", - "clear", - "get", - "models", - "metadata", - "promise", - "isPolymorphic", - "relationship", - "createRecord", - "normalizeModelName", - "liveRecordArrayFor", - "createRecordArray", - "createAdapterPopulatedRecordArray", - "unregisterRecordArray", - "_snapshots", - "_recordArray", - "adapterOptions", - "include", - "snapshots", - "record", - "attr", - "init", - "adapter", - "defaultAdapter", - "_generateId", - "find", - "findRecord", - "findByIds", - "_fetchRecord", - "getReference", - "peekRecord", - "_reloadRecord", - "hasRecordForId", - "recordForId", - "findMany", - "findHasMany", - "findBelongsTo", - "query", - "queryRecord", - "findAll", - "_fetchAll", - "_didUpdateAll", - "peekAll", - "unloadAll", - "scheduleSave", - "flushPendingSave", - "didSaveRecord", - "recordWasInvalid", - "recordWasError", - "setRecordId", - "_load", - "modelFor", - "pushPayload", - "normalize", - "adapterFor", - "serializerFor", - "VERSION", - "ajaxOptions", - "coalesceFindRequests", - "sortQueryParams", - "namespace", - "host", - "headers", - "updateRecord", - "groupRecordsForFindMany", - "handleResponse", - "isSuccess", - "isInvalid", - "ajax", - "_ajaxRequest", - "_najaxRequest", - "parseErrorResponse", - "normalizeErrorResponse", - "generatedDetailedMessage", - "serializeBelongsTo", - "serializeHasMany", - "removeEmbeddedForeignKey", - "_extractEmbeddedRecords", - "_extractEmbeddedHasMany", - "_extractEmbeddedBelongsTo", - "_normalizeEmbeddedRelationship", - "_normalizeDocumentHelper", - "_normalizeRelationshipDataHelper", - "_normalizeResourceHelper", - "_normalizeResponse", - "extractRelationship", - "extractRelationships", - "_extractType", - "modelNameFromPayloadKey", - "payloadKeyFromModelName", - "keyForAttribute", - "keyForRelationship", - "primaryKey", - "attrs", - "applyTransforms", - "normalizeResponse", - "normalizeFindRecordResponse", - "normalizeQueryRecordResponse", - "normalizeFindAllResponse", - "normalizeFindBelongsToResponse", - "normalizeFindHasManyResponse", - "normalizeFindManyResponse", - "normalizeQueryResponse", - "normalizeCreateRecordResponse", - "normalizeDeleteRecordResponse", - "normalizeUpdateRecordResponse", - "normalizeSaveResponse", - "normalizeSingleResponse", - "normalizeArrayResponse", - "extractId", - "extractAttributes", - "extractPolymorphicRelationship", - "normalizeRelationships", - "normalizeUsingDeclaredMapping", - "_getMappedKey", - "_canSerialize", - "_mustSerialize", - "shouldSerializeHasMany", - "serializeIntoHash", - "serializeAttribute", - "serializePolymorphicType", - "extractMeta", - "extractErrors", - "keyForLink", - "transformFor", - "keyForPolymorphicType", - "_normalizeArray", - "deserialize", - "defaultSerializer", - "generateIdForRecord", - "shouldReloadRecord", - "shouldReloadAll", - "shouldBackgroundReloadRecord", - "shouldBackgroundReloadAll", - "pluralize", - "singularize", - "enableCache", - "purgedCache", - "disableCache;", - "plural", - "singular", - "uncountable", - "irregular", - "inflect", - "columnNameToDesc", - "columnsForType", - "detect", - "getFilters", - "getRecordColor", - "getRecordColumnValues", - "getRecordFilterValues", - "getRecordKeywords", - "getRecords", - "observerRecord" - ] -} + classitems: [ + 'buildURL', + '_buildURL', + 'urlForFindRecord', + 'urlForFindAll', + 'urlForQuery', + 'urlForQueryRecord', + 'urlForFindMany', + 'urlForFindHasMany', + 'urlForFindBelongsTo', + 'urlForCreateRecord', + 'urlForUpdateRecord', + 'urlForDeleteRecord', + 'urlPrefix', + 'pathForType', + 'errorsHashToArray', + 'errorsArrayToHash', + '_registerHandlers', + 'errorsByAttributeName', + 'errorsFor', + 'messages', + 'content', + 'unknownProperty', + 'length', + 'isEmpty', + 'add', + '_add', + '_findOrCreateMessages', + 'remove', + '_remove', + '_clear', + 'has', + 'isLoading', + 'isLoaded', + 'hasDirtyAttributes', + 'isSaving', + 'isDeleted', + 'isNew', + 'isValid', + 'dirtyType', + 'isError', + 'isReloading', + 'id', + 'currentState', + '_internalModel', + 'recordData', + 'store', + 'errors', + 'adapterError', + 'serialize', + 'toJSON', + 'ready', + 'didLoad', + 'didUpdate', + 'didCreate', + 'didDelete', + 'becameInvalid', + 'becameError', + 'rolledBack', + 'send', + 'transitionTo', + 'deleteRecord', + 'destroyRecord', + 'unloadRecord', + '_notifyProperties', + 'changedAttributes', + 'rollbackAttributes', + 'save', + 'reload', + 'trigger', + 'belongsTo', + 'hasMany', + '_debugInfo', + 'eachRelationship', + 'data', + 'create', + 'modelName', + 'typeForRelationship', + 'inverseFor', + 'relationships', + 'relationshipNames', + 'relatedTypes', + 'relationshipsByName', + 'fields', + 'eachRelatedType', + 'attributes', + 'transformedAttributes', + 'eachAttribute', + 'eachTransformedAttribute', + 'toString', + '_setInternalModels', + 'isUpdating', + 'type', + 'objectAtContent', + 'update', + '_pushInternalModels', + 'removeInternalModel', + '_unregisterFromManager', + 'push', + 'value', + 'load', + 'remoteType', + 'ids', + 'link', + 'meta', + 'diffArray', + 'retrieve', + 'clear', + 'get', + 'models', + 'metadata', + 'promise', + 'isPolymorphic', + 'relationship', + 'createRecord', + 'normalizeModelName', + 'liveRecordArrayFor', + 'createRecordArray', + 'createAdapterPopulatedRecordArray', + 'unregisterRecordArray', + '_snapshots', + '_recordArray', + 'adapterOptions', + 'include', + 'snapshots', + 'record', + 'attr', + 'init', + 'adapter', + 'defaultAdapter', + '_generateId', + 'find', + 'findRecord', + 'findByIds', + '_fetchRecord', + 'getReference', + 'peekRecord', + '_reloadRecord', + 'hasRecordForId', + 'recordForId', + 'findMany', + 'findHasMany', + 'findBelongsTo', + 'query', + 'queryRecord', + 'findAll', + '_fetchAll', + '_didUpdateAll', + 'peekAll', + 'unloadAll', + 'scheduleSave', + 'flushPendingSave', + 'didSaveRecord', + 'recordWasInvalid', + 'recordWasError', + 'setRecordId', + '_load', + 'modelFor', + 'pushPayload', + 'normalize', + 'adapterFor', + 'serializerFor', + 'VERSION', + 'ajaxOptions', + 'coalesceFindRequests', + 'sortQueryParams', + 'namespace', + 'host', + 'headers', + 'updateRecord', + 'groupRecordsForFindMany', + 'handleResponse', + 'isSuccess', + 'isInvalid', + 'ajax', + '_ajaxRequest', + '_najaxRequest', + 'parseErrorResponse', + 'normalizeErrorResponse', + 'generatedDetailedMessage', + 'serializeBelongsTo', + 'serializeHasMany', + 'removeEmbeddedForeignKey', + '_extractEmbeddedRecords', + '_extractEmbeddedHasMany', + '_extractEmbeddedBelongsTo', + '_normalizeEmbeddedRelationship', + '_normalizeDocumentHelper', + '_normalizeRelationshipDataHelper', + '_normalizeResourceHelper', + '_normalizeResponse', + 'extractRelationship', + 'extractRelationships', + '_extractType', + 'modelNameFromPayloadKey', + 'payloadKeyFromModelName', + 'keyForAttribute', + 'keyForRelationship', + 'primaryKey', + 'attrs', + 'applyTransforms', + 'normalizeResponse', + 'normalizeFindRecordResponse', + 'normalizeQueryRecordResponse', + 'normalizeFindAllResponse', + 'normalizeFindBelongsToResponse', + 'normalizeFindHasManyResponse', + 'normalizeFindManyResponse', + 'normalizeQueryResponse', + 'normalizeCreateRecordResponse', + 'normalizeDeleteRecordResponse', + 'normalizeUpdateRecordResponse', + 'normalizeSaveResponse', + 'normalizeSingleResponse', + 'normalizeArrayResponse', + 'extractId', + 'extractAttributes', + 'extractPolymorphicRelationship', + 'normalizeRelationships', + 'normalizeUsingDeclaredMapping', + '_getMappedKey', + '_canSerialize', + '_mustSerialize', + 'shouldSerializeHasMany', + 'serializeIntoHash', + 'serializeAttribute', + 'serializePolymorphicType', + 'extractMeta', + 'extractErrors', + 'keyForLink', + 'transformFor', + 'keyForPolymorphicType', + '_normalizeArray', + 'deserialize', + 'defaultSerializer', + 'generateIdForRecord', + 'shouldReloadRecord', + 'shouldReloadAll', + 'shouldBackgroundReloadRecord', + 'shouldBackgroundReloadAll', + 'pluralize', + 'singularize', + 'enableCache', + 'purgedCache', + 'disableCache;', + 'plural', + 'singular', + 'uncountable', + 'irregular', + 'inflect', + 'columnNameToDesc', + 'columnsForType', + 'detect', + 'getFilters', + 'getRecordColor', + 'getRecordColumnValues', + 'getRecordFilterValues', + 'getRecordKeywords', + 'getRecords', + 'observerRecord', + 'pushPayload [OPTIONAL]', + 'serializeIntoHash [OPTIONAL]', + 'normalize [OPTIONAL]', + ], +}; diff --git a/packages/store/addon/-private/system/model/internal-model.ts b/packages/store/addon/-private/system/model/internal-model.ts index bb3c27d3804..6c4a3c2a9af 100644 --- a/packages/store/addon/-private/system/model/internal-model.ts +++ b/packages/store/addon/-private/system/model/internal-model.ts @@ -1469,9 +1469,6 @@ export default class InternalModel { this.addErrorMessageToAttribute(attribute, parsedErrors[attribute]); } } - } else { - // TODO recordData.getErrors code branch - throw new Error(`Support for RecordData.getErrors is not yet present`); } let jsonApiErrors: JsonApiValidationError[] = errorsHashToArray(parsedErrors); diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 83ed9c934ee..562892b8ba0 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -109,6 +109,7 @@ interface Serializer { * will be `camelCase`. * * @method normalizeResponse + * @public * @param {Store} store - the store service that initiated the request being normalized * @param {ShimModelClass} schema - An object with methods for accessing information about * the type, attributes and relationships of the primary type associated with the request. @@ -153,6 +154,7 @@ interface Serializer { * is not implemented. * * @method serialize + * @public * @param {Snapshot} snapshot - A Snapshot for the record to serialize * @param {object} options */ @@ -193,6 +195,8 @@ interface Serializer { * it may lead to unexpected mixing of formats. * * @method normalize [OPTIONAL] + * @public + * @optional * @param {ShimModelClass} schema - An object with methods for accessing information about * the type, attributes and relationships of the primary type associated with the request. * @param {JSONObject} rawPayload - Some raw JSON data to be normalized into a `JSON:API` resource. @@ -232,12 +236,15 @@ interface Serializer { * ``` * * @method serializeIntoHash [OPTIONAL] + * @public + * @optional * @param hash - an top most object of the request payload onto * which to append the serialized record * @param {ShimModelClass} schema - An object with methods for accessing information about * the type, attributes and relationships of the primary type associated with the request. * @param {Snapshot} snapshot - A Snapshot for the record to serialize * @param options + * @returns {void} */ serializeIntoHash?(hash: object, schema: ShimModelClass, snapshot: Snapshot, options?: OptionsHash): void; @@ -260,6 +267,8 @@ interface Serializer { * ``` * * @method pushPayload [OPTIONAL] + * @public + * @optional * @param {Store} store - the store service that initiated the request being normalized * @param {JSONObject} rawPayload - The raw JSON response data returned from an API request. * This JSON should be in the API format expected by the serializer. From f54efce07dd99751a340596e9221826be3b5b0ed Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Sat, 14 Sep 2019 12:50:42 -0700 Subject: [PATCH 05/19] fix import --- packages/serializer/tsconfig.json | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/serializer/tsconfig.json b/packages/serializer/tsconfig.json index 60288ddb7cb..cd1e2d61179 100644 --- a/packages/serializer/tsconfig.json +++ b/packages/serializer/tsconfig.json @@ -29,14 +29,6 @@ "*": ["types/*"] } }, - "include": [ - "app/**/*", - "addon/**/*", - "tests/**/*", - "types/**/*", - "test-support/**/*", - "addon-test-support/**/*", - "../store/addon/minimum-serializer-interface.ts" - ], + "include": ["app/**/*", "addon/**/*", "tests/**/*", "types/**/*", "test-support/**/*", "addon-test-support/**/*"], "exclude": ["node_modules"] } From 40349f04eb0534344dd82391bfd5173b16ae165b Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 16 Sep 2019 06:06:03 -0700 Subject: [PATCH 06/19] Update packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts --- .../-private/ts-interfaces/minimum-serializer-interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 562892b8ba0..80b6b64b498 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -75,7 +75,7 @@ However, for applications whose APIs are *very close to* or *exactly* the `REST` or `JSON:API` format the `@ember-data/serializer` package contains implementations these applications can - extend. It also contains a simly `JSONSerializer` for serializing to/from very simple JSON objects. + extend. It also contains a simple `JSONSerializer` for serializing to/from very basic JSON objects. Many applications will find writing their own serializer to be more performant and less complex than extending these classes even when their API format is very close to that expected From 28699a41ba95177667738da4d9124836c9afc48c Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 17 Sep 2019 10:20:40 -0700 Subject: [PATCH 07/19] Update packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts Co-Authored-By: Robert Jackson --- .../-private/ts-interfaces/minimum-serializer-interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 80b6b64b498..f6bb4e300b9 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -238,7 +238,7 @@ interface Serializer { * @method serializeIntoHash [OPTIONAL] * @public * @optional - * @param hash - an top most object of the request payload onto + * @param hash - a top most object of the request payload onto * which to append the serialized record * @param {ShimModelClass} schema - An object with methods for accessing information about * the type, attributes and relationships of the primary type associated with the request. From 1fdf9186c10683f22820775df97fb8113a475636 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 17 Sep 2019 10:55:55 -0700 Subject: [PATCH 08/19] documentation updates --- .../minimum-serializer-interface.ts | 65 ++++++++++++++----- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index f6bb4e300b9..952e2a5b2df 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -4,8 +4,8 @@ In order to properly manage and present your data, `EmberData` needs to understand the structure of data it receives. - `Serializers` convert data between the format used by an API - and the format `EmberData` understands. + `Serializers` convert data between the server's API format and + the format `EmberData` understands. Data received from an API response is `"normalized"` into [JSON:API](https://jsonapi.org/) (the format used internally @@ -15,8 +15,9 @@ ### Implementing a Serializer There are only two required serializer methods, one for - normalizing data in `JSON:API`, and another for serializing - records via `Snapshot`s into the expected API. + normalizing data from the server API format into `JSON:API`, and + another for serializing records via `Snapshot`s into the expected + server API format. To implement a serializer, export a class implementing the [MinimumSerializerInterface](MinimumSerializerInterface) from @@ -26,9 +27,10 @@ import EmberObject from '@ember/object'; export default class ApplicationSerializer extends EmberObject { - normalizeResponse(_, __, rawPayload) { + normalizeResponse(store, schema, rawPayload) { return rawPayload; } + serialize(snapshot, options) { const serializedResource = { id: snapshot.id(), @@ -53,6 +55,14 @@ `serializerFor` first attempts to find a serializer with an exact match on `name`, then falls back to checking for the presence of a serializer named `application`. + ```ts + store.serializerFor('author'); + + // lookup paths (in order) => + // app/serializers/author.js + // app/serializers/application.js + ``` + Because most requests in `ember-data` occur from the perspective of a primary `type` (or `modelName`) typically `serializerFor` will be used to find a serializer with a name matching that of the primary resource `type` for the request, falling back to the `application` @@ -100,13 +110,16 @@ interface Serializer { * This method is responsible for normalizing the value resolved from the promise returned * by an Adapter request into the format expected by the `Store`. * - * The output should be a [JSON:API](https://jsonapi.org/) document with the following - * additional restrictions: + * The output should be a [JSON:API Document](https://jsonapi.org/format/#document-structure) + * with the following additional restrictions: * * - `type` should be formatted in the `singular` `dasherized` `lowercase` form * - `members` (the property names of attributes and relationships) should be formatted * to match their definition in the corresponding `Model` definition. Typically this * will be `camelCase`. + * - [`lid`](https://github.com/emberjs/rfcs/blob/master/text/0403-ember-data-identifiers.md) is + * a valid optional sibling to `id` and `type` in both [Resources](https://jsonapi.org/format/#document-resource-objects) + * and [Resource Identifier Objects](https://jsonapi.org/format/#document-resource-identifier-objects) * * @method normalizeResponse * @public @@ -121,7 +134,7 @@ interface Serializer { * @param {'findRecord' | 'queryRecord' | 'findAll' | 'findBelongsTo' | 'findHasMany' | 'findMany' | 'query' | 'createRecord' | 'deleteRecord' | 'updateRecord'} requestType - The * type of request the Adapter had been asked to perform. * - * @returns {JsonApiDocument} - a document following the structure of a `JSON:API` Document. + * @returns {JsonApiDocument} - a document following the structure of a [JSON:API Document](https://jsonapi.org/format/#document-structure). */ normalizeResponse( store: Store, @@ -161,8 +174,16 @@ interface Serializer { serialize(snapshot: Snapshot, options?: OptionsHash): JSONObject; /** - * This method is intended to normalize data into a `JsonApiDocument` representing - * with a data member containing a single `resource`. + * This method is intended to normalize data into a [JSON:API Document](https://jsonapi.org/format/#document-structure) + * with a data member containing a single [Resource](https://jsonapi.org/format/#document-resource-objects). + * + * - `type` should be formatted in the `singular` `dasherized` `lowercase` form + * - `members` (the property names of attributes and relationships) should be formatted + * to match their definition in the corresponding `Model` definition. Typically this + * will be `camelCase`. + * - [`lid`](https://github.com/emberjs/rfcs/blob/master/text/0403-ember-data-identifiers.md) is + * a valid optional sibling to `id` and `type` in both [Resources](https://jsonapi.org/format/#document-resource-objects) + * and [Resource Identifier Objects](https://jsonapi.org/format/#document-resource-identifier-objects) * * This method is called by the `Store` when `store.normalize(modelName, payload)` is * called. It is recommended to use `store.serializerFor(modelName).normalizeResponse` @@ -199,11 +220,12 @@ interface Serializer { * @optional * @param {ShimModelClass} schema - An object with methods for accessing information about * the type, attributes and relationships of the primary type associated with the request. - * @param {JSONObject} rawPayload - Some raw JSON data to be normalized into a `JSON:API` resource. + * @param {JSONObject} rawPayload - Some raw JSON data to be normalized into a [JSON:API Resource](https://jsonapi.org/format/#document-resource-objects). * @param {string} [prop] - When called by the `EmbeddedRecordsMixin` this param will be the * property at which the object provided as rawPayload was found. - * @returns {SingleResourceDocument} - A `JSON:API` document containing a single `resource` as - * its primary data. + * @returns {SingleResourceDocument} - A [JSON:API Document](https://jsonapi.org/format/#document-structure) + * containing a single [JSON:API Resource](https://jsonapi.org/format/#document-resource-objects) + * as its primary data. */ normalize?(schema: ShimModelClass, rawPayload: JSONObject, prop?: string): SingleResourceDocument; @@ -249,12 +271,23 @@ interface Serializer { serializeIntoHash?(hash: object, schema: ShimModelClass, snapshot: Snapshot, options?: OptionsHash): void; /** - * This method is called by `store.pushPayload` and should be implemented if - * you want to use that method. + * This method allows for normalization of data when `store.pushPayload` is called + * and should be implemented if you want to use that method. * * It is recommended to use `store.push` over `store.pushPayload` after normalizing * the payload directly. * + * The output should be a [JSON:API Document](https://jsonapi.org/format/#document-structure) + * with the following additional restrictions: + * + * - `type` should be formatted in the `singular` `dasherized` `lowercase` form + * - `members` (the property names of attributes and relationships) should be formatted + * to match their definition in the corresponding `Model` definition. Typically this + * will be `camelCase`. + * - [`lid`](https://github.com/emberjs/rfcs/blob/master/text/0403-ember-data-identifiers.md) is + * a valid optional sibling to `id` and `type` in both [Resources](https://jsonapi.org/format/#document-resource-objects) + * and [Resource Identifier Objects](https://jsonapi.org/format/#document-resource-identifier-objects) + * * Example: * ```js * function pushPayload(store, modelName, rawPayload) { @@ -272,7 +305,7 @@ interface Serializer { * @param {Store} store - the store service that initiated the request being normalized * @param {JSONObject} rawPayload - The raw JSON response data returned from an API request. * This JSON should be in the API format expected by the serializer. - * @returns {JsonApiDocument} - a document following the structure of a `JSON:API` Document. + * @returns {JsonApiDocument} - a document following the structure of a [JSON:API Document](https://jsonapi.org/format/#document-structure) */ pushPayload?(store: Store, rawPayload: JSONObject): JsonApiDocument; } From d3f5955e9259f8408ab63a646736cef26b2d1dcd Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 13:51:05 -0700 Subject: [PATCH 09/19] fix Dict type and use instead of Record --- .../tests/integration/identifiers/scenarios-test.ts | 6 +++--- packages/store/addon/-private/identifiers/cache.ts | 4 ++-- packages/store/addon/-private/system/core-store.ts | 4 ++-- packages/store/addon/-private/system/identity-map.ts | 2 +- .../addon/-private/system/internal-model-map.ts | 6 +++--- .../addon/-private/system/model/internal-model.ts | 8 ++++---- .../-private/ts-interfaces/ember-data-json-api.ts | 12 ++++++------ .../ts-interfaces/minimum-serializer-interface.ts | 3 ++- packages/store/addon/-private/ts-interfaces/utils.ts | 2 +- 9 files changed, 24 insertions(+), 23 deletions(-) diff --git a/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts b/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts index 45526ace9c7..bd96050e203 100644 --- a/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts +++ b/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts @@ -30,8 +30,8 @@ if (IDENTIFIERS) { let store; let calls; let secondaryCache: { - id: Dict; - username: Dict; + id: Dict; + username: Dict; }; class TestSerializer extends Serializer { normalizeResponse(_, __, payload) { @@ -232,7 +232,7 @@ if (IDENTIFIERS) { module('Secondary Cache using an attribute as an alternate id', function(hooks) { let store; let calls; - let secondaryCache: Dict; + let secondaryCache: Dict; class TestSerializer extends Serializer { normalizeResponse(_, __, payload) { return payload; diff --git a/packages/store/addon/-private/identifiers/cache.ts b/packages/store/addon/-private/identifiers/cache.ts index c16ca8283d2..bb0afeaab2d 100644 --- a/packages/store/addon/-private/identifiers/cache.ts +++ b/packages/store/addon/-private/identifiers/cache.ts @@ -30,8 +30,8 @@ interface KeyOptions { _allIdentifiers: StableRecordIdentifier[]; } -type IdentifierMap = Dict; -type TypeMap = Dict; +type IdentifierMap = Dict; +type TypeMap = Dict; export type MergeMethod = ( targetIdentifier: StableRecordIdentifier, matchedIdentifier: StableRecordIdentifier, diff --git a/packages/store/addon/-private/system/core-store.ts b/packages/store/addon/-private/system/core-store.ts index eea95396edd..54e0c6832cf 100644 --- a/packages/store/addon/-private/system/core-store.ts +++ b/packages/store/addon/-private/system/core-store.ts @@ -3016,7 +3016,7 @@ abstract class CoreStore extends Service { return internalModelFactoryFor(this).lookup(resource); } - serializeRecord(record: Record, options?: Dict): unknown { + serializeRecord(record: Record, options?: Dict): unknown { if (CUSTOM_MODEL_CLASS) { let identifier = recordIdentifierFor(record); let internalModel = internalModelFactoryFor(this).peek(identifier); @@ -3027,7 +3027,7 @@ abstract class CoreStore extends Service { } } - saveRecord(record: Record, options?: Dict): RSVP.Promise { + saveRecord(record: Record, options?: Dict): RSVP.Promise { if (CUSTOM_MODEL_CLASS) { let identifier = recordIdentifierFor(record); let internalModel = internalModelFactoryFor(this).peek(identifier); diff --git a/packages/store/addon/-private/system/identity-map.ts b/packages/store/addon/-private/system/identity-map.ts index f689f7e5847..e4cfcdb3b2c 100644 --- a/packages/store/addon/-private/system/identity-map.ts +++ b/packages/store/addon/-private/system/identity-map.ts @@ -13,7 +13,7 @@ import { Dict } from '../ts-interfaces/utils'; @private */ export default class IdentityMap { - private _map: Dict = Object.create(null); + private _map: Dict = Object.create(null); /** Retrieves the `InternalModelMap` for a given modelName, diff --git a/packages/store/addon/-private/system/internal-model-map.ts b/packages/store/addon/-private/system/internal-model-map.ts index 85c2a4e0006..ee55c0dbdf3 100644 --- a/packages/store/addon/-private/system/internal-model-map.ts +++ b/packages/store/addon/-private/system/internal-model-map.ts @@ -17,9 +17,9 @@ import { Dict } from '../ts-interfaces/utils'; @private */ export default class InternalModelMap { - private _idToModel: Dict = Object.create(null); + private _idToModel: Dict = Object.create(null); private _models: InternalModel[] = []; - private _metadata: Dict | null = null; + private _metadata: Dict | null = null; constructor(public modelName: string) {} @@ -104,7 +104,7 @@ export default class InternalModelMap { * @property metadata * @type Object */ - get metadata(): Dict { + get metadata(): Dict { return this._metadata || (this._metadata = Object.create(null)); } diff --git a/packages/store/addon/-private/system/model/internal-model.ts b/packages/store/addon/-private/system/model/internal-model.ts index 6c4a3c2a9af..115c6d63172 100644 --- a/packages/store/addon/-private/system/model/internal-model.ts +++ b/packages/store/addon/-private/system/model/internal-model.ts @@ -119,14 +119,14 @@ export default class InternalModel { __recordArrays: any; _references: any; _recordReference: any; - _manyArrayCache: Dict = Object.create(null); + _manyArrayCache: Dict = Object.create(null); // The previous ManyArrays for this relationship which will be destroyed when // we create a new ManyArray, but in the interim the retained version will be // updated if inverse internal models are unloaded. - _retainedManyArrayCache: Dict = Object.create(null); - _relationshipPromisesCache: Dict> = Object.create(null); - _relationshipProxyCache: Dict = Object.create(null); + _retainedManyArrayCache: Dict = Object.create(null); + _relationshipPromisesCache: Dict> = Object.create(null); + _relationshipProxyCache: Dict = Object.create(null); currentState: any; error: any; diff --git a/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts b/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts index d5c39f50e9e..7fadb3b0937 100644 --- a/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts +++ b/packages/store/addon/-private/ts-interfaces/ember-data-json-api.ts @@ -5,7 +5,7 @@ import { Dict } from './utils'; @module @ember-data/store */ -export type Meta = Dict; +export type Meta = Dict; /** * Serves as a reference to a `Resource` but does not contain @@ -74,15 +74,15 @@ export type ResourceIdentifierObject = ExistingResourceIdentifierObject | NewRes * Contains the data for an existing resource in JSON:API format */ export interface ExistingResourceObject extends ExistingResourceIdentifierObject { - meta?: Dict; - attributes?: Dict; + meta?: Dict; + attributes?: Dict; // these are lossy, need improved typing - relationships?: Dict; - links?: Dict; + relationships?: Dict; + links?: Dict; } interface Document { - meta?: Dict; + meta?: Dict; included?: ExistingResourceObject[]; } diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 952e2a5b2df..627c9912084 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -102,8 +102,9 @@ import Store from '../system/core-store'; import { JsonApiDocument, SingleResourceDocument } from './ember-data-json-api'; import Snapshot from '../system/snapshot'; import ShimModelClass from '../system/model/shim-model-class'; +import { Dict } from './utils'; -type OptionsHash = Record; +type OptionsHash = Dict; interface Serializer { /** diff --git a/packages/store/addon/-private/ts-interfaces/utils.ts b/packages/store/addon/-private/ts-interfaces/utils.ts index 3c6af61e96a..14db6f0faff 100644 --- a/packages/store/addon/-private/ts-interfaces/utils.ts +++ b/packages/store/addon/-private/ts-interfaces/utils.ts @@ -2,4 +2,4 @@ @module @ember-data/store */ -export type Dict = { [KK in K]: V }; +export type Dict = { [key: string]: V }; From 2a689c3252fff8e7b113c0d43ff57e78fe3fab1e Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 14:44:20 -0700 Subject: [PATCH 10/19] reorder default serializer section --- .../ts-interfaces/minimum-serializer-interface.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 627c9912084..acdee540cb9 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -80,10 +80,7 @@ ### Default Serializers - It is recommended that apps write their own serializer to best suit the needs of their API and - application. - - However, for applications whose APIs are *very close to* or *exactly* the `REST` or `JSON:API` + For applications whose APIs are *very close to* or *exactly* the `REST` or `JSON:API` format the `@ember-data/serializer` package contains implementations these applications can extend. It also contains a simple `JSONSerializer` for serializing to/from very basic JSON objects. @@ -91,6 +88,9 @@ complex than extending these classes even when their API format is very close to that expected by these serializers. + It is recommended that apps write their own serializer to best suit the needs of their API and + application. + @module @ember-data/serializer @main @ember-data/serializer @class MinimumSerializerInterface From 9ae9e34dd91b8db516c3dbba00129af2ef986aaa Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 14:49:49 -0700 Subject: [PATCH 11/19] clarify primary resource type --- .../ts-interfaces/minimum-serializer-interface.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index acdee540cb9..cd8aae53eea 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -63,12 +63,13 @@ // app/serializers/application.js ``` - Because most requests in `ember-data` occur from the perspective of a primary `type` - (or `modelName`) typically `serializerFor` will be used to find a serializer with a name - matching that of the primary resource `type` for the request, falling back to the `application` - serializer for those types that do not have a defined serializer. This is often described - as a `per-model` or `per-type` strategy for defining serializers. However, because APIs - rarely format payloads per-type but rather per-API-version, this may not be a desired strategy. + Because most requests in `ember-data` are made with respect to a particular `type` (or `modelName`) + (e.g., "get me the full collection of **books**" or "get me the **employee** whose id is 37") typically + `serializerFor` will be used to find a serializer with a name matching that of the primary resource + `type` for the request, falling back to the `application` serializer for those types that do not have + a defined serializer. This is often described as a `per-model` or `per-type` strategy for defining + serializers. However, because APIs rarely format payloads per-type but rather per-API-version, this + may not be a desired strategy. If you have multiple API formats and the per-type strategy is not viable, one strategy is to write an `application` adapter and serializer that make use of `options` to specify the desired From d99d768734ec524734969f8c9ee3444e4bf098a6 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 14:51:39 -0700 Subject: [PATCH 12/19] reorder single-application-serializer recommendation to have rationale occur first --- .../minimum-serializer-interface.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index cd8aae53eea..450bd2a72cf 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -49,9 +49,6 @@ The instances of serializers defined in `app/serializers/` can be looked up via `store.serializerFor(name)`. - It is recommended that applications define only a single `application` adapter and serializer - where possible. - `serializerFor` first attempts to find a serializer with an exact match on `name`, then falls back to checking for the presence of a serializer named `application`. @@ -63,13 +60,18 @@ // app/serializers/application.js ``` - Because most requests in `ember-data` are made with respect to a particular `type` (or `modelName`) - (e.g., "get me the full collection of **books**" or "get me the **employee** whose id is 37") typically - `serializerFor` will be used to find a serializer with a name matching that of the primary resource - `type` for the request, falling back to the `application` serializer for those types that do not have - a defined serializer. This is often described as a `per-model` or `per-type` strategy for defining - serializers. However, because APIs rarely format payloads per-type but rather per-API-version, this - may not be a desired strategy. + Most requests in `ember-data` are made with respect to a particular `type` (or `modelName`) + (e.g., "get me the full collection of **books**" or "get me the **employee** whose id is 37"). We + refer to this as the *"primary"* resource `type`. + + Typically `serializerFor` will be used to find a serializer with a name matching that of the primary + resource `type` for the request, falling back to the `application` serializer for those types that + do not have a defined serializer. This is often described as a `per-model` or `per-type` strategy + for defining serializers. However, because APIs rarely format payloads per-type but rather + per-API-version, this may not be a desired strategy. + + It is recommended that applications define only a single `application` adapter and serializer + where possible. If you have multiple API formats and the per-type strategy is not viable, one strategy is to write an `application` adapter and serializer that make use of `options` to specify the desired From 61fedd677c6d3c2c0d6449110cc81f86b2075182 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 15:08:59 -0700 Subject: [PATCH 13/19] cleanup serializerFor description --- .../ts-interfaces/minimum-serializer-interface.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 450bd2a72cf..01f4bc0ebf8 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -19,9 +19,9 @@ another for serializing records via `Snapshot`s into the expected server API format. - To implement a serializer, export a class implementing the - [MinimumSerializerInterface](MinimumSerializerInterface) from - the `app/serializers/` directory. An example is below. + To implement a serializer, export a class that conforms to the structure + described by the [MinimumSerializerInterface](MinimumSerializerInterface) + from the `app/serializers/` directory. An example is below. ```ts import EmberObject from '@ember/object'; @@ -46,8 +46,9 @@ #### Serializer Resolution - The instances of serializers defined in `app/serializers/` can be looked up - via `store.serializerFor(name)`. + `store.serializerFor(name)` will lookup serializers defined in + `app/serializers` and return an instance. If no serializer is found, an + error will be thrown. `serializerFor` first attempts to find a serializer with an exact match on `name`, then falls back to checking for the presence of a serializer named `application`. From 8e4bc77e38f14445655e21796722cf23a937515c Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 15:15:16 -0700 Subject: [PATCH 14/19] clean up pushPayload rec --- .../ts-interfaces/minimum-serializer-interface.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 01f4bc0ebf8..031ff454c37 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -279,9 +279,6 @@ interface Serializer { * This method allows for normalization of data when `store.pushPayload` is called * and should be implemented if you want to use that method. * - * It is recommended to use `store.push` over `store.pushPayload` after normalizing - * the payload directly. - * * The output should be a [JSON:API Document](https://jsonapi.org/format/#document-structure) * with the following additional restrictions: * @@ -293,7 +290,11 @@ interface Serializer { * a valid optional sibling to `id` and `type` in both [Resources](https://jsonapi.org/format/#document-resource-objects) * and [Resource Identifier Objects](https://jsonapi.org/format/#document-resource-identifier-objects) * - * Example: + * If you need better control over normalization or want access to the records being added or updated + * in the store, we recommended using `store.push` over `store.pushPayload` after normalizing + * the payload directly. This can even take advantage of an existing serializer for the format + * the data is in, for example: + * * ```js * function pushPayload(store, modelName, rawPayload) { * const ModelClass = store.modelFor(modelName); From bee8fd28b9ee4807bde47d89934219c69635bc9f Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 16:36:09 -0700 Subject: [PATCH 15/19] Update packages/store/addon/-private/ts-interfaces/utils.ts Co-Authored-By: Mike North --- packages/store/addon/-private/ts-interfaces/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/addon/-private/ts-interfaces/utils.ts b/packages/store/addon/-private/ts-interfaces/utils.ts index 14db6f0faff..abc3e6121d6 100644 --- a/packages/store/addon/-private/ts-interfaces/utils.ts +++ b/packages/store/addon/-private/ts-interfaces/utils.ts @@ -2,4 +2,4 @@ @module @ember-data/store */ -export type Dict = { [key: string]: V }; +export type Dict = { [key: string]: V | undefined }; From 1c450f60b7f511089f93927e7acbac8f7f18a6ee Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 16:37:25 -0700 Subject: [PATCH 16/19] Update packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts Co-Authored-By: Mike North --- .../-private/ts-interfaces/minimum-serializer-interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 031ff454c37..5f45b169c1c 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -174,7 +174,7 @@ interface Serializer { * @method serialize * @public * @param {Snapshot} snapshot - A Snapshot for the record to serialize - * @param {object} options + * @param {object} [options] */ serialize(snapshot: Snapshot, options?: OptionsHash): JSONObject; From b51feab0ff5b1694643c13af32c80f0502af0aaa Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 16:37:37 -0700 Subject: [PATCH 17/19] Update packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts Co-Authored-By: Mike North --- .../-private/ts-interfaces/minimum-serializer-interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts index 5f45b169c1c..f6819d9c2d7 100644 --- a/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts +++ b/packages/store/addon/-private/ts-interfaces/minimum-serializer-interface.ts @@ -270,7 +270,7 @@ interface Serializer { * @param {ShimModelClass} schema - An object with methods for accessing information about * the type, attributes and relationships of the primary type associated with the request. * @param {Snapshot} snapshot - A Snapshot for the record to serialize - * @param options + * @param [options] * @returns {void} */ serializeIntoHash?(hash: object, schema: ShimModelClass, snapshot: Snapshot, options?: OptionsHash): void; From fecca1577d50ed8347d725b4e52d4aa8922e7230 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 16:50:08 -0700 Subject: [PATCH 18/19] revert Dict change, refactor later --- packages/store/addon/-private/ts-interfaces/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/addon/-private/ts-interfaces/utils.ts b/packages/store/addon/-private/ts-interfaces/utils.ts index abc3e6121d6..14db6f0faff 100644 --- a/packages/store/addon/-private/ts-interfaces/utils.ts +++ b/packages/store/addon/-private/ts-interfaces/utils.ts @@ -2,4 +2,4 @@ @module @ember-data/store */ -export type Dict = { [key: string]: V | undefined }; +export type Dict = { [key: string]: V }; From ca9da440aa5cc1aa38f200ad6af1ef485bb6e16f Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 24 Sep 2019 16:58:10 -0700 Subject: [PATCH 19/19] Confident --- .../integration/identifiers/scenarios-test.ts | 8 +-- .../store/addon/-private/identifiers/cache.ts | 6 +- .../store/addon/-private/system/core-store.ts | 62 +++++++++++-------- .../addon/-private/system/identity-map.ts | 4 +- .../-private/system/internal-model-map.ts | 8 +-- .../-private/system/model/internal-model.ts | 10 +-- .../addon/-private/ts-interfaces/utils.ts | 3 +- 7 files changed, 57 insertions(+), 44 deletions(-) diff --git a/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts b/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts index bd96050e203..07337492994 100644 --- a/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts +++ b/packages/-ember-data/tests/integration/identifiers/scenarios-test.ts @@ -13,7 +13,7 @@ import Adapter from '@ember-data/adapter'; import Serializer from '@ember-data/serializer'; import { resolve, all } from 'rsvp'; import { ExistingResourceObject } from '@ember-data/store/-private/ts-interfaces/ember-data-json-api'; -import { Dict } from '@ember-data/store/-private/ts-interfaces/utils'; +import { ConfidentDict } from '@ember-data/store/-private/ts-interfaces/utils'; import { StableRecordIdentifier } from '@ember-data/store/-private/ts-interfaces/identifier'; import { identifierCacheFor } from '@ember-data/store/-private'; import { set } from '@ember/object'; @@ -30,8 +30,8 @@ if (IDENTIFIERS) { let store; let calls; let secondaryCache: { - id: Dict; - username: Dict; + id: ConfidentDict; + username: ConfidentDict; }; class TestSerializer extends Serializer { normalizeResponse(_, __, payload) { @@ -232,7 +232,7 @@ if (IDENTIFIERS) { module('Secondary Cache using an attribute as an alternate id', function(hooks) { let store; let calls; - let secondaryCache: Dict; + let secondaryCache: ConfidentDict; class TestSerializer extends Serializer { normalizeResponse(_, __, payload) { return payload; diff --git a/packages/store/addon/-private/identifiers/cache.ts b/packages/store/addon/-private/identifiers/cache.ts index bb0afeaab2d..49bc2aea3f0 100644 --- a/packages/store/addon/-private/identifiers/cache.ts +++ b/packages/store/addon/-private/identifiers/cache.ts @@ -1,6 +1,6 @@ import { DEBUG } from '@glimmer/env'; import { warn } from '@ember/debug'; -import { Dict } from '../ts-interfaces/utils'; +import { ConfidentDict } from '../ts-interfaces/utils'; import { ResourceIdentifierObject, ExistingResourceObject } from '../ts-interfaces/ember-data-json-api'; import { StableRecordIdentifier, @@ -30,8 +30,8 @@ interface KeyOptions { _allIdentifiers: StableRecordIdentifier[]; } -type IdentifierMap = Dict; -type TypeMap = Dict; +type IdentifierMap = ConfidentDict; +type TypeMap = ConfidentDict; export type MergeMethod = ( targetIdentifier: StableRecordIdentifier, matchedIdentifier: StableRecordIdentifier, diff --git a/packages/store/addon/-private/system/core-store.ts b/packages/store/addon/-private/system/core-store.ts index 54e0c6832cf..b181bfa2187 100644 --- a/packages/store/addon/-private/system/core-store.ts +++ b/packages/store/addon/-private/system/core-store.ts @@ -104,7 +104,10 @@ const HAS_ADAPTER_PACKAGE = has('@ember-data/adapter'); function deprecateTestRegistration(factoryType: 'adapter', factoryName: '-json-api'): void; function deprecateTestRegistration(factoryType: 'serializer', factoryName: '-json-api' | '-rest' | '-default'): void; -function deprecateTestRegistration(factoryType: 'serializer' | 'adapter', factoryName: '-json-api' | '-rest' | '-default'): void { +function deprecateTestRegistration( + factoryType: 'serializer' | 'adapter', + factoryName: '-json-api' | '-rest' | '-default' +): void { deprecate( `You looked up the ${factoryType} "${factoryName}" but it was not found. Likely this means you are using a legacy ember-qunit moduleFor helper. Add "needs: ['${factoryType}:${factoryName}']", "integration: true", or refactor to modern syntax to resolve this deprecation.`, false, @@ -1270,8 +1273,8 @@ abstract class CoreStore extends Service { if (missingInternalModels.length) { warn( 'Ember Data expected to find records with the following ids in the adapter response but they were missing: [ "' + - missingInternalModels.map(r => r.id).join('", "') + - '" ]', + missingInternalModels.map(r => r.id).join('", "') + + '" ]', false, { id: 'ds.store.missing-records-from-adapter', @@ -1292,9 +1295,9 @@ abstract class CoreStore extends Service { if (pair) { pair.resolver.reject( error || - new Error( - `Expected: '${internalModel}' to be present in the adapter provided payload, but it was not found.` - ) + new Error( + `Expected: '${internalModel}' to be present in the adapter provided payload, but it was not found.` + ) ); } } @@ -1332,12 +1335,12 @@ abstract class CoreStore extends Service { } if (totalInGroup > 1) { - (function (groupedInternalModels) { + (function(groupedInternalModels) { _findMany(adapter, store, modelName, ids, groupedInternalModels, optionsMap) - .then(function (foundInternalModels) { + .then(function(foundInternalModels) { handleFoundRecords(foundInternalModels, groupedInternalModels); }) - .catch(function (error) { + .catch(function(error) { rejectInternalModels(groupedInternalModels, error); }); })(groupedInternalModels); @@ -3361,11 +3364,15 @@ abstract class CoreStore extends Service { let adapter = this.adapterFor(modelName); let serializerName = get(adapter, 'defaultSerializer'); - deprecate(`store.serializerFor("${modelName}") resolved the "${serializerName}" serializer via the deprecated \`adapter.defaultSerializer\` property.\n\n\tPreviously, if no application or type-specific serializer was specified, the store would attempt to lookup a serializer via the \`defaultSerializer\` property on the type's adapter. This behavior is deprecated in favor of explicitly defining a type-specific serializer or application serializer`, !serializerName, { - id: 'ember-data:default-serializer', - until: '4.0', - url: 'https://deprecations.emberjs.com/ember-data/v3.x#toc_ember-data:default-serializers' - }); + deprecate( + `store.serializerFor("${modelName}") resolved the "${serializerName}" serializer via the deprecated \`adapter.defaultSerializer\` property.\n\n\tPreviously, if no application or type-specific serializer was specified, the store would attempt to lookup a serializer via the \`defaultSerializer\` property on the type's adapter. This behavior is deprecated in favor of explicitly defining a type-specific serializer or application serializer`, + !serializerName, + { + id: 'ember-data:default-serializer', + until: '4.0', + url: 'https://deprecations.emberjs.com/ember-data/v3.x#toc_ember-data:default-serializers', + } + ); serializer = serializerName ? _serializerCache[serializerName] || owner.lookup(`serializer:${serializerName}`) @@ -3409,11 +3416,15 @@ abstract class CoreStore extends Service { serializer && deprecateTestRegistration('serializer', '-default'); } - deprecate(`store.serializerFor("${modelName}") resolved the "-default" serializer via the deprecated "-default" lookup fallback.\n\n\tPreviously, when no type-specific serializer, application serializer, or adapter.defaultSerializer had been defined by the app, the "-default" serializer would be used which defaulted to the \`JSONSerializer\`. This behavior is deprecated in favor of explicitly defining an application or type-specific serializer`, !serializer, { - id: 'ember-data:default-serializer', - until: '4.0', - url: 'https://deprecations.emberjs.com/ember-data/v3.x#toc_ember-data:default-serializers' - }); + deprecate( + `store.serializerFor("${modelName}") resolved the "-default" serializer via the deprecated "-default" lookup fallback.\n\n\tPreviously, when no type-specific serializer, application serializer, or adapter.defaultSerializer had been defined by the app, the "-default" serializer would be used which defaulted to the \`JSONSerializer\`. This behavior is deprecated in favor of explicitly defining an application or type-specific serializer`, + !serializer, + { + id: 'ember-data:default-serializer', + until: '4.0', + url: 'https://deprecations.emberjs.com/ember-data/v3.x#toc_ember-data:default-serializers', + } + ); assert( `No serializer was found for '${modelName}' and no 'application' serializer was found as a fallback`, @@ -3449,12 +3460,12 @@ abstract class CoreStore extends Service { if (shouldTrack) { throw new Error( 'Async Request leaks detected. Add a breakpoint here and set `store.generateStackTracesForTrackedRequests = true;`to inspect traces for leak origins:\n\t - ' + - tracked.map(o => o.label).join('\n\t - ') + tracked.map(o => o.label).join('\n\t - ') ); } else { warn( 'Async Request leaks detected. Add a breakpoint here and set `store.generateStackTracesForTrackedRequests = true;`to inspect traces for leak origins:\n\t - ' + - tracked.map(o => o.label).join('\n\t - '), + tracked.map(o => o.label).join('\n\t - '), false, { id: 'ds.async.leak.detected', @@ -3507,14 +3518,15 @@ abstract class CoreStore extends Service { defineProperty( CoreStore.prototype, 'defaultAdapter', - computed('adapter', function () { + computed('adapter', function() { deprecate( - `store.adapterFor(modelName) resolved the ("${this.adapter || '-json-api'}") adapter via the deprecated \`store.defaultAdapter\` property.\n\n\tPreviously, applications could define the store's \`adapter\` property which would be used by \`defaultAdapter\` and \`adapterFor\` as a fallback for when an adapter was not found by an exact name match. This behavior is deprecated in favor of explicitly defining an application or type-specific adapter.`, + `store.adapterFor(modelName) resolved the ("${this.adapter || + '-json-api'}") adapter via the deprecated \`store.defaultAdapter\` property.\n\n\tPreviously, applications could define the store's \`adapter\` property which would be used by \`defaultAdapter\` and \`adapterFor\` as a fallback for when an adapter was not found by an exact name match. This behavior is deprecated in favor of explicitly defining an application or type-specific adapter.`, false, { id: 'ember-data:default-adapter', until: '4.0', - url: 'https://deprecations.emberjs.com/ember-data/v3.x#toc_ember-data:default-adapter' + url: 'https://deprecations.emberjs.com/ember-data/v3.x#toc_ember-data:default-adapter', } ); let adapter = this.adapter || '-json-api'; @@ -3581,7 +3593,7 @@ function _commit(adapter, store, operation, snapshot) { return internalModel; }, - function (error) { + function(error) { if (error instanceof InvalidError) { let parsedErrors; diff --git a/packages/store/addon/-private/system/identity-map.ts b/packages/store/addon/-private/system/identity-map.ts index e4cfcdb3b2c..01063192552 100644 --- a/packages/store/addon/-private/system/identity-map.ts +++ b/packages/store/addon/-private/system/identity-map.ts @@ -1,5 +1,5 @@ import InternalModelMap from './internal-model-map'; -import { Dict } from '../ts-interfaces/utils'; +import { ConfidentDict } from '../ts-interfaces/utils'; /** @module @ember-data/store @@ -13,7 +13,7 @@ import { Dict } from '../ts-interfaces/utils'; @private */ export default class IdentityMap { - private _map: Dict = Object.create(null); + private _map: ConfidentDict = Object.create(null); /** Retrieves the `InternalModelMap` for a given modelName, diff --git a/packages/store/addon/-private/system/internal-model-map.ts b/packages/store/addon/-private/system/internal-model-map.ts index ee55c0dbdf3..3d0c48dfcf6 100644 --- a/packages/store/addon/-private/system/internal-model-map.ts +++ b/packages/store/addon/-private/system/internal-model-map.ts @@ -1,6 +1,6 @@ import { assert } from '@ember/debug'; import InternalModel from './model/internal-model'; -import { Dict } from '../ts-interfaces/utils'; +import { ConfidentDict } from '../ts-interfaces/utils'; /** @module @ember-data/store @@ -17,9 +17,9 @@ import { Dict } from '../ts-interfaces/utils'; @private */ export default class InternalModelMap { - private _idToModel: Dict = Object.create(null); + private _idToModel: ConfidentDict = Object.create(null); private _models: InternalModel[] = []; - private _metadata: Dict | null = null; + private _metadata: ConfidentDict | null = null; constructor(public modelName: string) {} @@ -104,7 +104,7 @@ export default class InternalModelMap { * @property metadata * @type Object */ - get metadata(): Dict { + get metadata(): ConfidentDict { return this._metadata || (this._metadata = Object.create(null)); } diff --git a/packages/store/addon/-private/system/model/internal-model.ts b/packages/store/addon/-private/system/model/internal-model.ts index 115c6d63172..7248644cbc0 100644 --- a/packages/store/addon/-private/system/model/internal-model.ts +++ b/packages/store/addon/-private/system/model/internal-model.ts @@ -23,7 +23,7 @@ import { default as recordDataFor, relationshipStateFor } from '../record-data-f import RecordData from '../../ts-interfaces/record-data'; import { JsonApiResource, JsonApiValidationError } from '../../ts-interfaces/record-data-json-api'; import { Record } from '../../ts-interfaces/record'; -import { Dict } from '../../ts-interfaces/utils'; +import { ConfidentDict } from '../../ts-interfaces/utils'; import { IDENTIFIERS, RECORD_DATA_ERRORS, @@ -119,14 +119,14 @@ export default class InternalModel { __recordArrays: any; _references: any; _recordReference: any; - _manyArrayCache: Dict = Object.create(null); + _manyArrayCache: ConfidentDict = Object.create(null); // The previous ManyArrays for this relationship which will be destroyed when // we create a new ManyArray, but in the interim the retained version will be // updated if inverse internal models are unloaded. - _retainedManyArrayCache: Dict = Object.create(null); - _relationshipPromisesCache: Dict> = Object.create(null); - _relationshipProxyCache: Dict = Object.create(null); + _retainedManyArrayCache: ConfidentDict = Object.create(null); + _relationshipPromisesCache: ConfidentDict> = Object.create(null); + _relationshipProxyCache: ConfidentDict = Object.create(null); currentState: any; error: any; diff --git a/packages/store/addon/-private/ts-interfaces/utils.ts b/packages/store/addon/-private/ts-interfaces/utils.ts index 14db6f0faff..43d92465412 100644 --- a/packages/store/addon/-private/ts-interfaces/utils.ts +++ b/packages/store/addon/-private/ts-interfaces/utils.ts @@ -2,4 +2,5 @@ @module @ember-data/store */ -export type Dict = { [key: string]: V }; +export type ConfidentDict = { [key: string]: V }; +export type Dict = { [key: string]: V | undefined };