Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[CHORE ts] type Snapshot #6801

Merged
merged 17 commits into from
Dec 7, 2019
64 changes: 40 additions & 24 deletions packages/store/addon/-private/system/core-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { default as RSVP, all, resolve, Promise, defer } from 'rsvp';
import Service from '@ember/service';
import { typeOf, isPresent, isNone } from '@ember/utils';

import { addSymbol } from '../ts-interfaces/utils/symbol';
import require from 'require';
import Ember from 'ember';
import { assert, warn, inspect } from '@ember/debug';
Expand Down Expand Up @@ -54,34 +53,15 @@ import {
HAS_SERIALIZER_PACKAGE,
} from '@ember-data/private-build-infra';

import { RecordInstance } from '../ts-interfaces/record-instance';
import { JsonApiRelationship } from '../ts-interfaces/record-data-json-api';
import { ResourceIdentifierObject } from '../ts-interfaces/ember-data-json-api';

import promiseRecord from '../utils/promise-record';
import { identifierCacheFor, IdentifierCache } from '../identifiers/cache';
import { internalModelFactoryFor, setRecordIdentifier, recordIdentifierFor } from './store/internal-model-factory';
import { RecordIdentifier, StableRecordIdentifier } from '../ts-interfaces/identifier';
import { RecordReference, HasManyReference, BelongsToReference } from './references';
import { Backburner } from '@ember/runloop/-private/backburner';
import Snapshot from './snapshot';
import {
EmptyResourceDocument,
SingleResourceDocument,
CollectionResourceDocument,
JsonApiDocument,
ExistingResourceObject,
} from '../ts-interfaces/ember-data-json-api';
import { RequestPromise } from './request-cache';
import { PromiseProxy } from '../ts-interfaces/promise-proxies';
import { DSModel } from '../ts-interfaces/ds-model';
import NotificationManager from './record-notification-manager';
import { AttributesSchema } from '../ts-interfaces/record-data-schemas';
import { SchemaDefinitionService } from '../ts-interfaces/schema-definition-service';
import ShimModelClass, { getShimClass } from './model/shim-model-class';
import { RecordDataRecordWrapper } from '../ts-interfaces/record-data-record-wrapper';
import { RecordData } from '../ts-interfaces/record-data';
import { Dict } from '../ts-interfaces/utils';

import constructResource from '../utils/construct-resource';
import { errorsArrayToHash } from './errors-utils';
Expand All @@ -91,8 +71,31 @@ import {
DEPRECATE_LEGACY_TEST_REGISTRATIONS,
} from '@ember-data/private-build-infra/deprecations';

// TODO this comes from ts-interfaces but it is a function we ship
// so needs to be moved somewhere else
import { addSymbol } from '../ts-interfaces/utils/symbol';

type JsonApiRelationship = import('../ts-interfaces/record-data-json-api').JsonApiRelationship;
type ResourceIdentifierObject = import('../ts-interfaces/ember-data-json-api').ResourceIdentifierObject;
type EmptyResourceDocument = import('../ts-interfaces/ember-data-json-api').EmptyResourceDocument;
type SingleResourceDocument = import('../ts-interfaces/ember-data-json-api').SingleResourceDocument;
type CollectionResourceDocument = import('../ts-interfaces/ember-data-json-api').CollectionResourceDocument;
type JsonApiDocument = import('../ts-interfaces/ember-data-json-api').JsonApiDocument;
type ExistingResourceObject = import('../ts-interfaces/ember-data-json-api').ExistingResourceObject;

type RecordIdentifier = import('../ts-interfaces/identifier').RecordIdentifier;
type StableRecordIdentifier = import('../ts-interfaces/identifier').StableRecordIdentifier;
type StableExistingRecordIdentifier = import('../ts-interfaces/identifier').StableExistingRecordIdentifier;
type RecordInstance = import('../ts-interfaces/record-instance').RecordInstance;
type RecordData = import('../ts-interfaces/record-data').RecordData;
type DSModel = import('../ts-interfaces/ds-model').DSModel;
type PromiseProxy<T> = import('../ts-interfaces/promise-proxies').PromiseProxy<T>;
type Dict<T> = import('../ts-interfaces/utils').Dict<T>;
type RecordDataRecordWrapper = import('../ts-interfaces/record-data-record-wrapper').RecordDataRecordWrapper;
type AttributesSchema = import('../ts-interfaces/record-data-schemas').AttributesSchema;
type SchemaDefinitionService = import('../ts-interfaces/schema-definition-service').SchemaDefinitionService;

type Relationship = import('@ember-data/record-data/-private').Relationship;
type RelationshipRecordData = import('@ember-data/record-data/-private/ts-interfaces/relationship-record-data').RelationshipRecordData;

const emberRun = emberRunLoop.backburner;

Expand Down Expand Up @@ -1148,7 +1151,10 @@ abstract class CoreStore extends Service {
// TODO remove this once we dont rely on state machine
internalModel.loadingData();
let identifier = internalModel.identifier;
let promise = this._fetchManager.scheduleFetch(internalModel.identifier, options, generateStackTrace);

assertIdentifierIsExisting(identifier);

let promise = this._fetchManager.scheduleFetch(identifier, options, generateStackTrace);
return promise.then(
payload => {
if (IDENTIFIERS) {
Expand Down Expand Up @@ -2481,7 +2487,9 @@ abstract class CoreStore extends Service {
let pendingItem = pending[i];
let snapshot = pendingItem.snapshot;
let resolver = pendingItem.resolver;
let internalModel = snapshot._internalModel;
// TODO We have to cast due to our reliance on this private property
// this will be refactored away once we change our pending API to be identifier based
let internalModel = ((snapshot as unknown) as { _internalModel: InternalModel })._internalModel;
runspired marked this conversation as resolved.
Show resolved Hide resolved
let adapter = this.adapterFor(internalModel.modelName);
let operation;

Expand Down Expand Up @@ -3034,7 +3042,7 @@ abstract class CoreStore extends Service {
return internalModel.reloadBelongsTo(key, options);
}

_internalModelForResource(resource: RecordIdentifier): InternalModel {
_internalModelForResource(resource: ResourceIdentifierObject): InternalModel {
runspired marked this conversation as resolved.
Show resolved Hide resolved
return internalModelFactoryFor(this).getByResource(resource);
}

Expand Down Expand Up @@ -3725,3 +3733,11 @@ function internalModelForRelatedResource(
const identifier = cache.getOrCreateRecordIdentifier(resource);
return store._internalModelForResource(identifier);
}

function assertIdentifierIsExisting(
identifier: StableRecordIdentifier
): asserts identifier is StableExistingRecordIdentifier {
if (DEBUG && identifier.id !== null) {
throw new Error(`Attempted to schedule a fetch for a record without an id.`);
}
}
37 changes: 26 additions & 11 deletions packages/store/addon/-private/system/fetch-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import coerceId from './coerce-id';
import { A } from '@ember/array';
import RequestCache from './request-cache';
import { CollectionResourceDocument, SingleResourceDocument } from '../ts-interfaces/ember-data-json-api';
import { RecordIdentifier } from '../ts-interfaces/identifier';
import { RecordIdentifier, ExistingRecordIdentifier } from '../ts-interfaces/identifier';
import { FindRecordQuery, SaveRecordMutation, Request } from '../ts-interfaces/fetch-manager';
import { symbol } from '../ts-interfaces/utils/symbol';
import CoreStore from './core-store';
import { errorsArrayToHash } from './errors-utils';
import { Dict } from '../ts-interfaces/utils';

type InternalModel = import('./model/internal-model').default;

function payloadIsNotBlank(adapterPayload): boolean {
if (Array.isArray(adapterPayload)) {
Expand All @@ -27,7 +30,7 @@ const emberRun = emberRunLoop.backburner;
export const SaveOp: unique symbol = symbol('SaveOp');

interface PendingFetchItem {
identifier: RecordIdentifier;
identifier: ExistingRecordIdentifier;
queryRequest: Request;
resolver: RSVP.Deferred<any>;
options: { [k: string]: unknown };
Expand Down Expand Up @@ -97,7 +100,9 @@ export default class FetchManager {
let adapter = this._store.adapterFor(identifier.type);
let operation = options[SaveOp];

let internalModel = snapshot._internalModel;
// TODO We have to cast due to our reliance on this private property
// this will be refactored away once we change our pending API to be identifier based
let internalModel = ((snapshot as unknown) as { _internalModel: InternalModel })._internalModel;
runspired marked this conversation as resolved.
Show resolved Hide resolved
let modelName = snapshot.modelName;
let store = this._store;
let modelClass = store.modelFor(modelName);
Expand Down Expand Up @@ -162,7 +167,7 @@ export default class FetchManager {
}
}

scheduleFetch(identifier: RecordIdentifier, options: any, shouldTrace: boolean): RSVP.Promise<any> {
scheduleFetch(identifier: ExistingRecordIdentifier, options: any, shouldTrace: boolean): RSVP.Promise<any> {
// TODO Probably the store should pass in the query object

let query: FindRecordQuery = {
Expand Down Expand Up @@ -318,7 +323,12 @@ export default class FetchManager {
for (let i = 0, l = expectedSnapshots.length; i < l; i++) {
let snapshot = expectedSnapshots[i];

if (!found[snapshot.id]) {
// TODO @runspired
// We know id is a string because you can't fetch
// without one.
// we should refactor to keying pending by identifier.lid
runspired marked this conversation as resolved.
Show resolved Hide resolved
// so we can eliminate this cast
if (!found[snapshot.id as string]) {
missingSnapshots.push(snapshot);
}
}
Expand All @@ -339,14 +349,17 @@ export default class FetchManager {

rejectFetchedItems(seeking: { [id: string]: PendingFetchItem }, snapshots: Snapshot[], error?) {
for (let i = 0, l = snapshots.length; i < l; i++) {
let identifier = snapshots[i];
let pair = seeking[identifier.id];
let snapshot = snapshots[i];
// TODO refactor to identifier.lid to avoid this cast to string
// we can do this case because you can only fetch an identifier
// that has an ID
runspired marked this conversation as resolved.
Show resolved Hide resolved
let pair = seeking[snapshot.id as string];

if (pair) {
pair.resolver.reject(
error ||
new Error(
`Expected: '<${identifier.modelName}:${identifier.id}>' to be present in the adapter provided payload, but it was not found.`
`Expected: '<${snapshot.modelName}:${snapshot.id}>' to be present in the adapter provided payload, but it was not found.`
)
);
}
Expand Down Expand Up @@ -428,14 +441,14 @@ export default class FetchManager {
let identifiers = new Array(totalItems);
let seeking: { [id: string]: PendingFetchItem } = Object.create(null);

let optionsMap = new WeakMap<RecordIdentifier, Object>();
let optionsMap = new WeakMap<RecordIdentifier, Dict<unknown>>();

for (let i = 0; i < totalItems; i++) {
let pendingItem = pendingFetchItems[i];
let identifier = pendingItem.identifier;
identifiers[i] = identifier;
optionsMap.set(identifier, pendingItem.options);
seeking[identifier.id as string] = pendingItem;
seeking[identifier.id] = pendingItem;
}

if (shouldCoalesce) {
Expand All @@ -451,7 +464,9 @@ export default class FetchManager {
// will once again convert the records to snapshots for adapter.findMany()
let snapshots = new Array<Snapshot>(totalItems);
for (let i = 0; i < totalItems; i++) {
let options = optionsMap.get(identifiers[i]);
// we know options is in the map due to having just set it above
// but TS doesn't know so we cast it
let options = optionsMap.get(identifiers[i]) as Dict<unknown>;
snapshots[i] = new Snapshot(options, identifiers[i], this._store);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/store/addon/-private/system/model/internal-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { RecordReference, BelongsToReference, HasManyReference } from '../refere
import { RecordData } from '../../ts-interfaces/record-data';
import { JsonApiResource, JsonApiValidationError } from '../../ts-interfaces/record-data-json-api';
import { RecordInstance } from '../../ts-interfaces/record-instance';
import { ConfidentDict } from '../../ts-interfaces/utils';
import { ConfidentDict, Dict } from '../../ts-interfaces/utils';
import {
IDENTIFIERS,
RECORD_DATA_ERRORS,
Expand Down Expand Up @@ -967,7 +967,7 @@ export default class InternalModel {
@method createSnapshot
@private
*/
createSnapshot(options) {
createSnapshot(options?: Dict<unknown>): Snapshot {
return new Snapshot(options || {}, this.identifier, this.store);
}

Expand Down
Loading