Skip to content

Commit

Permalink
saveRecord
Browse files Browse the repository at this point in the history
  • Loading branch information
gitKrystan committed Apr 5, 2024
1 parent 57fb11d commit e3b3bdf
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 5 deletions.
92 changes: 92 additions & 0 deletions packages/legacy-compat/src/builders/save-record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { assert } from '@ember/debug';

import { recordIdentifierFor, storeFor, type StoreRequestInput } from '@ember-data/store';
import type { InstanceCache } from '@ember-data/store/-private/caches/instance-cache';
import type { StableRecordIdentifier } from '@warp-drive/core-types';
import type { Cache } from '@warp-drive/core-types/cache';
import { SkipCache } from '@warp-drive/core-types/request';

export type SaveRecordRequestInput = StoreRequestInput & {
op: 'createRecord' | 'deleteRecord' | 'updateRecord';
data: {
record: StableRecordIdentifier;
options: SaveRecordBuilderOptions;
};
records: [StableRecordIdentifier];
};

export type SaveRecordBuilderOptions = Record<string, unknown>;

function _resourceIsFullDeleted(identifier: StableRecordIdentifier, cache: Cache): boolean {
return cache.isDeletionCommitted(identifier) || (cache.isNew(identifier) && cache.isDeleted(identifier));
}

function resourceIsFullyDeleted(instanceCache: InstanceCache, identifier: StableRecordIdentifier): boolean {
const cache = instanceCache.cache;
return !cache || _resourceIsFullDeleted(identifier, cache);
}

/**
* FIXME: Docs
This function builds a request config for the given type.
When passed to `store.request`, this config will result in the same behavior as a `store.findAll` request.
Additionally, it takes the same options as `store.findAll`.
@since x.x.x
@method query
@public
@param {String} type the name of the resource
@param {object} query a query to be used by the adapter
@param {SaveRecordBuilderOptions} options optional, may include `adapterOptions` hash which will be passed to adapter.query
@return {SaveRecordRequestInput} request config
*/
export function saveRecordBuilder<T>(record: T, options: Record<string, unknown> = {}): SaveRecordRequestInput {
const store = storeFor(record);
assert(`Unable to initiate save for a record in a disconnected state`, store);
const identifier = recordIdentifierFor(record);

if (!identifier) {
// this commonly means we're disconnected
// but just in case we throw here to prevent bad things.
throw new Error(`Record Is Disconnected`);
}
// TODO we used to check if the record was destroyed here
assert(
`Cannot initiate a save request for an unloaded record: ${identifier.lid}`,
store._instanceCache.recordIsLoaded(identifier)
);
if (resourceIsFullyDeleted(store._instanceCache, identifier)) {
throw new Error('cannot build saveRecord request for deleted record');
}

if (!options) {
options = {};
}
let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord';

const cache = store.cache;
if (cache.isNew(identifier)) {
operation = 'createRecord';
} else if (cache.isDeleted(identifier)) {
operation = 'deleteRecord';
}

return {
op: operation,
data: {
options,
record: identifier,
},
records: [identifier],
cacheOptions: { [SkipCache as symbol]: true },
};
}

/*
TODO:
* [] test this
* [] make sure nothing fails bc of willCommit change
* [] cargo cult jsdoc setup from json-api/src/-private/builders/query.ts
*/
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ function saveRecord<T>(context: StoreRequestContext): Promise<T> {

upgradeStore(store);

store.cache.willCommit(identifier, context);

const saveOptions = Object.assign(
{ [SaveOp]: operation as 'updateRecord' | 'deleteRecord' | 'createRecord' },
options
Expand Down
5 changes: 1 addition & 4 deletions packages/store/src/-private/store-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2121,7 +2121,7 @@ class Store extends EmberObject {
if (DEBUG) {
assertDestroyingStore(this, 'saveRecord');
}
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
assert(`Unable to initiate save for a record in a disconnected state`, storeFor(record));
const identifier = recordIdentifierFor(record);
const cache = this.cache;

Expand Down Expand Up @@ -2160,9 +2160,6 @@ class Store extends EmberObject {
cacheOptions: { [SkipCache as symbol]: true },
};

// we lie here on the type because legacy doesn't have enough context
cache.willCommit(identifier, { request } as unknown as StoreRequestContext);

return this.request<T>(request).then((document) => document.content);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
*
* ### Presenting Data from the Cache
*
* Now that we have a source and a cach for our data, we need to configure how
* Now that we have a source and a cache for our data, we need to configure how
* the Store delivers that data back to our application. We do this via the hook
* [instantiateRecord](https://api.emberjs.com/ember-data/release/classes/Store/methods/instantiateRecord%20(hook)?anchor=instantiateRecord%20(hook)),
* which allows us to transform the data for a resource before handing it to the application.
Expand Down

0 comments on commit e3b3bdf

Please sign in to comment.