Skip to content

Commit

Permalink
[CHORE ts] type Snapshot (#6801)
Browse files Browse the repository at this point in the history
* [CHORE ts] type Snapshot

* clarify returns

* clarify types

* cleanup core-store

* remove comment

* cleanup

* fuller types

* more cleanup

* cleanup

* appease igor

* comment interfaces

* better narrowing

* cleanup

* fix production tests

* fix check

* regenerate lock

* skip lockfile for ember-observer
  • Loading branch information
runspired authored Dec 7, 2019
1 parent b365f2b commit ae527fd
Show file tree
Hide file tree
Showing 9 changed files with 1,100 additions and 1,188 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"test-external:ember-m3": "./bin/test-external-partner-project.js ember-m3 https://github.com/hjdivad/ember-m3.git",
"test-external:ember-data-change-tracker": "./bin/test-external-partner-project.js ember-data-change-tracker https://github.com/danielspaniel/ember-data-change-tracker.git",
"test-external:model-fragments": "./bin/test-external-partner-project.js model-fragments https://github.com/lytics/ember-data-model-fragments.git",
"test-external:ember-observer": "./bin/test-external-partner-project.js ember-observer https://github.com/emberobserver/client.git",
"test-external:ember-observer": "./bin/test-external-partner-project.js ember-observer https://github.com/emberobserver/client.git --noLockFile",
"test-external:travis-web": "./bin/test-external-partner-project.js travis-web https://github.com/travis-ci/travis-web.git",
"test-external:storefront": "./bin/test-external-partner-project.js storefront https://github.com/embermap/ember-data-storefront.git",
"test-external:factory-guy": "./bin/test-external-partner-project.js factory-guy https://github.com/danielspaniel/ember-data-factory-guy.git",
Expand Down
7 changes: 3 additions & 4 deletions packages/-ember-data/tests/integration/snapshot-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,7 @@ module('integration/snapshot - Snapshot', function(hooks) {
});
let post = store.peekRecord('post', 1);
let snapshot = post._createSnapshot();

assert.throws(
assert.expectAssertion(
() => {
snapshot.attr('unknown');
},
Expand Down Expand Up @@ -452,7 +451,7 @@ module('integration/snapshot - Snapshot', function(hooks) {
let post = store.peekRecord('post', 1);
let snapshot = post._createSnapshot();

assert.throws(
assert.expectAssertion(
() => {
snapshot.belongsTo('unknown');
},
Expand Down Expand Up @@ -982,7 +981,7 @@ module('integration/snapshot - Snapshot', function(hooks) {
let post = store.peekRecord('post', 1);
let snapshot = post._createSnapshot();

assert.throws(
assert.expectAssertion(
() => {
snapshot.hasMany('unknown');
},
Expand Down
63 changes: 39 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,30 @@ 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 PrivateSnapshot = import('./snapshot').PrivateSnapshot;
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 +1150,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);

assertIdentifierHasId(identifier);

let promise = this._fetchManager.scheduleFetch(identifier, options, generateStackTrace);
return promise.then(
payload => {
if (IDENTIFIERS) {
Expand Down Expand Up @@ -2481,7 +2486,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 PrivateSnapshot)._internalModel;
let adapter = this.adapterFor(internalModel.modelName);
let operation;

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

_internalModelForResource(resource: RecordIdentifier): InternalModel {
_internalModelForResource(resource: ResourceIdentifierObject): InternalModel {
return internalModelFactoryFor(this).getByResource(resource);
}

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

function assertIdentifierHasId(
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.`);
}
}
57 changes: 43 additions & 14 deletions packages/store/addon/-private/system/fetch-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@ import { normalizeResponseHelper } from './store/serializer-response';
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 { 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';

// TODO @runspired symbol shouldn't be in ts-interfaces
// as it is runtime code
import { symbol } from '../ts-interfaces/utils/symbol';

type CoreStore = import('./core-store').default;
type FindRecordQuery = import('../ts-interfaces/fetch-manager').FindRecordQuery;
type SaveRecordMutation = import('../ts-interfaces/fetch-manager').SaveRecordMutation;
type Request = import('../ts-interfaces/fetch-manager').Request;
type CollectionResourceDocument = import('../ts-interfaces/ember-data-json-api').CollectionResourceDocument;
type SingleResourceDocument = import('../ts-interfaces/ember-data-json-api').SingleResourceDocument;
type RecordIdentifier = import('../ts-interfaces/identifier').RecordIdentifier;
type ExistingRecordIdentifier = import('../ts-interfaces/identifier').ExistingRecordIdentifier;
type Dict<T> = import('../ts-interfaces/utils').Dict<T>;
type PrivateSnapshot = import('./snapshot').PrivateSnapshot;

function payloadIsNotBlank(adapterPayload): boolean {
if (Array.isArray(adapterPayload)) {
return true;
Expand All @@ -27,7 +37,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 +107,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 PrivateSnapshot)._internalModel;
let modelName = snapshot.modelName;
let store = this._store;
let modelClass = store.modelFor(modelName);
Expand Down Expand Up @@ -162,7 +174,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 @@ -317,7 +329,10 @@ export default class FetchManager {

for (let i = 0, l = expectedSnapshots.length; i < l; i++) {
let snapshot = expectedSnapshots[i];
assertIsString(snapshot.id);

// We know id is a string because you can't fetch
// without one.
if (!found[snapshot.id]) {
missingSnapshots.push(snapshot);
}
Expand All @@ -339,14 +354,18 @@ 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];
assertIsString(snapshot.id);
// 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
let pair = seeking[snapshot.id];

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 +447,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 +470,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 Expand Up @@ -480,3 +501,11 @@ export default class FetchManager {
this.isDestroyed = true;
}
}

function assertIsString(id: string | null): asserts id is string {
if (DEBUG) {
if (typeof id !== 'string') {
throw new Error(`Cannot fetch record without an id`);
}
}
}
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

0 comments on commit ae527fd

Please sign in to comment.