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

feat: docs, tests and fixes for create/update/deleteRecord builders #8849

Merged
merged 16 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type FindRecordOptions = ConstrainedRequestOptions & {

/**
* Builds request options to fetch a single resource by a known id or identifier
* configured for the url and header expectations of most JSON:API APIs.
* configured for the url and header expectations of most ActiveRecord APIs.
*
* **Basic Usage**
*
Expand Down
142 changes: 140 additions & 2 deletions packages/active-record/src/-private/builders/save-record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,58 @@ function isExisting(identifier: StableRecordIdentifier): identifier is StableExi
return 'id' in identifier && identifier.id !== null && 'type' in identifier && identifier.type !== null;
}

/**
* Builds request options to delete record for resources,
* configured for the url, method and header expectations of ActiveRecord APIs.
*
* **Basic Usage**
*
* ```ts
* import { deleteRecord } from '@ember-data/active-record/request';
*
* const person = this.store.peekRecord('person', '1');
*
* // mark record as deleted
* store.deleteRecord(person);
*
* // persist deletion
* const data = await store.request(deleteRecord(person));
* ```
*
* **Supplying Options to Modify the Request Behavior**
*
* The following options are supported:
*
* - `host` - The host to use for the request, defaults to the `host` configured with `setBuildURLConfig`.
* - `namespace` - The namespace to use for the request, defaults to the `namespace` configured with `setBuildURLConfig`.
* - `resourcePath` - The resource path to use for the request, defaults to pluralizing the supplied type
* - `reload` - Whether to forcibly reload the request if it is already in the store, not supplying this
* option will delegate to the store's lifetimes service, defaulting to `false` if none is configured.
* - `backgroundReload` - Whether to reload the request if it is already in the store, but to also resolve the
* promise with the cached value, not supplying this option will delegate to the store's lifetimes service,
* defaulting to `false` if none is configured.
* - `urlParamsSetting` - an object containing options for how to serialize the query params (see `buildQueryParams`)
*
* ```ts
* import { deleteRecord } from '@ember-data/active-record/request';
*
* const person = this.store.peekRecord('person', '1');
*
* // mark record as deleted
* store.deleteRecord(person);
*
* // persist deletion
* const options = deleteRecord(person, { namespace: 'api/v1' });
* const data = await store.request(options);
* ```
*
* @method deleteRecord
* @public
* @static
* @for @ember-data/active-record/request
* @param record
* @param options
*/
export function deleteRecord(record: unknown, options: ConstrainedRequestOptions = {}): DeleteRequestOptions {
const identifier = recordIdentifierFor(record);
assert(`Expected to be given a record instance`, identifier);
Expand Down Expand Up @@ -52,10 +104,51 @@ export function deleteRecord(record: unknown, options: ConstrainedRequestOptions
};
}

/**
* Builds request options to create new record for resources,
* configured for the url, method and header expectations of most ActiveRecord APIs.
*
* **Basic Usage**
*
* ```ts
* import { createRecord } from '@ember-data/active-record/request';
*
* const person = this.store.createRecord('person', { name: 'Ted' });
* const data = await store.request(createRecord(person));
* ```
*
* **Supplying Options to Modify the Request Behavior**
*
* The following options are supported:
*
* - `host` - The host to use for the request, defaults to the `host` configured with `setBuildURLConfig`.
* - `namespace` - The namespace to use for the request, defaults to the `namespace` configured with `setBuildURLConfig`.
* - `resourcePath` - The resource path to use for the request, defaults to pluralizing the supplied type
* - `reload` - Whether to forcibly reload the request if it is already in the store, not supplying this
* option will delegate to the store's lifetimes service, defaulting to `false` if none is configured.
* - `backgroundReload` - Whether to reload the request if it is already in the store, but to also resolve the
* promise with the cached value, not supplying this option will delegate to the store's lifetimes service,
* defaulting to `false` if none is configured.
* - `urlParamsSetting` - an object containing options for how to serialize the query params (see `buildQueryParams`)
*
* ```ts
* import { createRecord } from '@ember-data/active-record/request';
*
* const person = this.store.createRecord('person', { name: 'Ted' });
* const options = createRecord(person, { namespace: 'api/v1' });
* const data = await store.request(options);
* ```
*
* @method createRecord
* @public
* @static
* @for @ember-data/active-record/request
* @param record
* @param options
*/
export function createRecord(record: unknown, options: ConstrainedRequestOptions = {}): CreateRequestOptions {
const identifier = recordIdentifierFor(record);
assert(`Expected to be given a record instance`, identifier);
assert(`Cannot delete a record that does not have an associated type and id.`, isExisting(identifier));

const urlOptions: CreateRecordUrlOptions = {
identifier: identifier,
Expand All @@ -80,13 +173,58 @@ export function createRecord(record: unknown, options: ConstrainedRequestOptions
};
}

/**
* Builds request options to update existing record for resources,
* configured for the url, method and header expectations of most ActiveRecord APIs.
*
* **Basic Usage**
*
* ```ts
* import { updateRecord } from '@ember-data/active-record/request';
*
* const person = this.store.peekRecord('person', '1');
* person.name = 'Chris';
* const data = await store.request(updateRecord(person));
* ```
*
* **Supplying Options to Modify the Request Behavior**
*
* The following options are supported:
*
* - `patch` - Allows caller to specify whether to use a PATCH request instead of a PUT request, defaults to `false`.
* - `host` - The host to use for the request, defaults to the `host` configured with `setBuildURLConfig`.
* - `namespace` - The namespace to use for the request, defaults to the `namespace` configured with `setBuildURLConfig`.
* - `resourcePath` - The resource path to use for the request, defaults to pluralizing the supplied type
* - `reload` - Whether to forcibly reload the request if it is already in the store, not supplying this
* option will delegate to the store's lifetimes service, defaulting to `false` if none is configured.
* - `backgroundReload` - Whether to reload the request if it is already in the store, but to also resolve the
* promise with the cached value, not supplying this option will delegate to the store's lifetimes service,
* defaulting to `false` if none is configured.
* - `urlParamsSetting` - an object containing options for how to serialize the query params (see `buildQueryParams`)
*
* ```ts
* import { updateRecord } from '@ember-data/active-record/request';
*
* const person = this.store.peekRecord('person', '1');
* person.name = 'Chris';
* const options = updateRecord(person, { patch: true });
* const data = await store.request(options);
* ```
*
* @method updateRecord
* @public
* @static
* @for @ember-data/active-record/request
* @param record
* @param options
*/
export function updateRecord(
record: unknown,
options: ConstrainedRequestOptions & { patch?: boolean } = {}
): UpdateRequestOptions {
const identifier = recordIdentifierFor(record);
assert(`Expected to be given a record instance`, identifier);
assert(`Cannot delete a record that does not have an associated type and id.`, isExisting(identifier));
assert(`Cannot update a record that does not have an associated type and id.`, isExisting(identifier));

const urlOptions: UpdateRecordUrlOptions = {
identifier: identifier,
Expand Down
142 changes: 140 additions & 2 deletions packages/json-api/src/-private/builders/save-record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,58 @@ function isExisting(identifier: StableRecordIdentifier): identifier is StableExi
return 'id' in identifier && identifier.id !== null && 'type' in identifier && identifier.type !== null;
}

/**
* Builds request options to delete record for resources,
* configured for the url, method and header expectations of most JSON:API APIs.
*
* **Basic Usage**
*
* ```ts
* import { deleteRecord } from '@ember-data/json-api/request';
*
* const person = this.store.peekRecord('person', '1');
*
* // mark record as deleted
* store.deleteRecord(person);
*
* // persist deletion
* const data = await store.request(deleteRecord(person));
* ```
*
* **Supplying Options to Modify the Request Behavior**
*
* The following options are supported:
*
* - `host` - The host to use for the request, defaults to the `host` configured with `setBuildURLConfig`.
* - `namespace` - The namespace to use for the request, defaults to the `namespace` configured with `setBuildURLConfig`.
* - `resourcePath` - The resource path to use for the request, defaults to pluralizing the supplied type
* - `reload` - Whether to forcibly reload the request if it is already in the store, not supplying this
* option will delegate to the store's lifetimes service, defaulting to `false` if none is configured.
* - `backgroundReload` - Whether to reload the request if it is already in the store, but to also resolve the
* promise with the cached value, not supplying this option will delegate to the store's lifetimes service,
* defaulting to `false` if none is configured.
* - `urlParamsSetting` - an object containing options for how to serialize the query params (see `buildQueryParams`)
*
* ```ts
* import { deleteRecord } from '@ember-data/json-api/request';
*
* const person = this.store.peekRecord('person', '1');
*
* // mark record as deleted
* store.deleteRecord(person);
*
* // persist deletion
* const options = deleteRecord(person, { namespace: 'api/v1' });
* const data = await store.request(options);
* ```
*
* @method deleteRecord
* @public
* @static
* @for @ember-data/json-api/request
* @param record
* @param options
*/
export function deleteRecord(record: unknown, options: ConstrainedRequestOptions = {}): DeleteRequestOptions {
const identifier = recordIdentifierFor(record);
assert(`Expected to be given a record instance`, identifier);
Expand Down Expand Up @@ -52,10 +104,51 @@ export function deleteRecord(record: unknown, options: ConstrainedRequestOptions
};
}

/**
* Builds request options to create new record for resources,
* configured for the url, method and header expectations of most JSON:API APIs.
*
* **Basic Usage**
*
* ```ts
* import { createRecord } from '@ember-data/json-api/request';
*
* const person = this.store.createRecord('person', { name: 'Ted' });
Baltazore marked this conversation as resolved.
Show resolved Hide resolved
* const data = await store.request(createRecord(person));
* ```
*
* **Supplying Options to Modify the Request Behavior**
*
* The following options are supported:
*
* - `host` - The host to use for the request, defaults to the `host` configured with `setBuildURLConfig`.
* - `namespace` - The namespace to use for the request, defaults to the `namespace` configured with `setBuildURLConfig`.
* - `resourcePath` - The resource path to use for the request, defaults to pluralizing the supplied type
* - `reload` - Whether to forcibly reload the request if it is already in the store, not supplying this
* option will delegate to the store's lifetimes service, defaulting to `false` if none is configured.
* - `backgroundReload` - Whether to reload the request if it is already in the store, but to also resolve the
* promise with the cached value, not supplying this option will delegate to the store's lifetimes service,
* defaulting to `false` if none is configured.
* - `urlParamsSetting` - an object containing options for how to serialize the query params (see `buildQueryParams`)
*
* ```ts
* import { createRecord } from '@ember-data/json-api/request';
*
* const person = this.store.createRecord('person', { name: 'Ted' });
* const options = createRecord(person, { namespace: 'api/v1' });
* const data = await store.request(options);
* ```
*
* @method createRecord
* @public
* @static
* @for @ember-data/json-api/request
* @param record
* @param options
*/
export function createRecord(record: unknown, options: ConstrainedRequestOptions = {}): CreateRequestOptions {
const identifier = recordIdentifierFor(record);
assert(`Expected to be given a record instance`, identifier);
assert(`Cannot delete a record that does not have an associated type and id.`, isExisting(identifier));

const urlOptions: CreateRecordUrlOptions = {
identifier: identifier,
Expand All @@ -81,13 +174,58 @@ export function createRecord(record: unknown, options: ConstrainedRequestOptions
};
}

/**
* Builds request options to update existing record for resources,
* configured for the url, method and header expectations of most JSON:API APIs.
*
* **Basic Usage**
*
* ```ts
* import { updateRecord } from '@ember-data/json-api/request';
*
* const person = this.store.peekRecord('person', '1');
* person.name = 'Chris';
* const data = await store.request(updateRecord(person));
* ```
*
* **Supplying Options to Modify the Request Behavior**
*
* The following options are supported:
*
* - `patch` - Allows caller to specify whether to use a PATCH request instead of a PUT request, defaults to `false`.
* - `host` - The host to use for the request, defaults to the `host` configured with `setBuildURLConfig`.
* - `namespace` - The namespace to use for the request, defaults to the `namespace` configured with `setBuildURLConfig`.
* - `resourcePath` - The resource path to use for the request, defaults to pluralizing the supplied type
* - `reload` - Whether to forcibly reload the request if it is already in the store, not supplying this
* option will delegate to the store's lifetimes service, defaulting to `false` if none is configured.
* - `backgroundReload` - Whether to reload the request if it is already in the store, but to also resolve the
* promise with the cached value, not supplying this option will delegate to the store's lifetimes service,
* defaulting to `false` if none is configured.
* - `urlParamsSetting` - an object containing options for how to serialize the query params (see `buildQueryParams`)
*
* ```ts
* import { updateRecord } from '@ember-data/json-api/request';
*
* const person = this.store.peekRecord('person', '1');
* person.name = 'Chris';
* const options = updateRecord(person, { patch: true });
* const data = await store.request(options);
* ```
*
* @method updateRecord
* @public
* @static
* @for @ember-data/json-api/request
* @param record
* @param options
*/
export function updateRecord(
record: unknown,
options: ConstrainedRequestOptions & { patch?: boolean } = {}
): UpdateRequestOptions {
const identifier = recordIdentifierFor(record);
assert(`Expected to be given a record instance`, identifier);
assert(`Cannot delete a record that does not have an associated type and id.`, isExisting(identifier));
assert(`Cannot update a record that does not have an associated type and id.`, isExisting(identifier));

const urlOptions: UpdateRecordUrlOptions = {
identifier: identifier,
Expand Down
1 change: 1 addition & 0 deletions packages/json-api/src/-private/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,7 @@ export default class JSONAPICache implements Cache {

if (cached.isNew) {
// > Note: Graph removal handled by unloadRecord
cached.isDeletionCommitted = true;
cached.isDeleted = true;
cached.isNew = false;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/model/src/-private/model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type BelongsToReference from './references/belongs-to';
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
import type { LegacySupport } from './legacy-relationships-support';
import type { Cache } from '@ember-data/types/q/cache';
import type RecordState from './record-state';

export type ModelCreateArgs = {
_createProps: Record<string, unknown>;
Expand All @@ -26,6 +27,8 @@ export type ModelCreateArgs = {
class Model extends EmberObject {
store: Store;
errors: Errors;
currentState: RecordState;
adapterError?: Error;
toString(): string;
save(): Promise<this>;
hasMany(key: string): HasManyReference;
Expand Down
Loading
Loading