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

Add @ember-data/legacy-compat/builders #9319

Merged
merged 18 commits into from
Apr 9, 2024
Merged
2 changes: 1 addition & 1 deletion packages/legacy-compat/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const config = {
base.rules(),
imports.rules(),
isolation.rules({
allowedImports: ['@ember/debug', '@ember/application'],
allowedImports: ['@ember/debug', '@ember/string', '@ember/application'],
}),
{}
),
Expand Down
1 change: 1 addition & 0 deletions packages/legacy-compat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"@ember-data/json-api": "workspace:5.4.0-alpha.52",
"@ember-data/request": "workspace:5.4.0-alpha.52",
"@ember-data/store": "workspace:5.4.0-alpha.52",
"@ember/string": "^3.1.1",
"@warp-drive/core-types": "workspace:0.0.0-alpha.38"
},
"peerDependenciesMeta": {
Expand Down
2 changes: 1 addition & 1 deletion packages/legacy-compat/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default {
plugins: [
// These are the modules that users should be able to import from your
// addon. Anything not listed here may get optimized away.
addon.publicEntrypoints(['index.js', '-private.js']),
addon.publicEntrypoints(['index.js', 'builders.js', '-private.js']),

nodeResolve({ extensions: ['.ts'] }),
babel({
Expand Down
23 changes: 23 additions & 0 deletions packages/legacy-compat/src/builders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
Builders for migrating from `store` methods to `store.request`.

These builders enable you to migrate your codebase to using the correct syntax for `store.request` while temporarily preserving legacy behaviors.
This is useful for quickly upgrading an entire app to a unified syntax while a longer incremental migration is made to shift off of adapters and serializers.
To that end, these builders are deprecated and will be removed in a future version of Ember Data.

@module @ember-data/legacy-compat/builders
@main @ember-data/legacy-compat/builders
@deprecated
*/

export { findAllBuilder as findAll } from './builders/find-all';
export type { FindAllBuilderOptions, FindAllRequestInput } from './builders/find-all';
gitKrystan marked this conversation as resolved.
Show resolved Hide resolved

export { findRecordBuilder as findRecord } from './builders/find-record';
export type { FindRecordBuilderOptions, FindRecordRequestInput } from './builders/find-record';

export { queryBuilder as query, queryRecordBuilder as queryRecord } from './builders/query';
export type { QueryBuilderOptions, QueryRecordRequestInput, QueryRequestInput } from './builders/query';

export { saveRecordBuilder as saveRecord } from './builders/save-record';
export type { SaveRecordBuilderOptions, SaveRecordRequestInput } from './builders/save-record';
62 changes: 62 additions & 0 deletions packages/legacy-compat/src/builders/find-all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @module @ember-data/legacy-compat/builders
*/
import { assert } from '@ember/debug';

import type { StoreRequestInput } from '@ember-data/store';
import type { FindAllOptions } from '@ember-data/store/-types/q/store';
import type { TypeFromInstance } from '@warp-drive/core-types/record';
import { SkipCache } from '@warp-drive/core-types/request';

import { normalizeModelName } from './utils';

export type FindAllRequestInput = StoreRequestInput & {
op: 'findAll';
data: {
type: string;
options: FindAllBuilderOptions;
};
};

export type FindAllBuilderOptions = FindAllOptions;

/**
This function builds a request config to perform a `findAll` request 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`.
gitKrystan marked this conversation as resolved.
Show resolved Hide resolved

All `@ember-data/legacy-compat` builders exist to enable you to migrate your codebase to using the correct syntax for `store.request` while temporarily preserving legacy behaviors.
This is useful for quickly upgrading an entire app to a unified syntax while a longer incremental migration is made to shift off of adapters and serializers.
To that end, these builders are deprecated and will be removed in a future version of Ember Data.

@deprecated
gitKrystan marked this conversation as resolved.
Show resolved Hide resolved
@method findAll
@public
@static
@for @ember-data/legacy-compat/builders
@param {string} type the name of the resource
@param {object} query a query to be used by the adapter
@param {FindAllBuilderOptions} [options] optional, may include `adapterOptions` hash which will be passed to adapter.findAll
@return {FindAllRequestInput} request config
*/
export function findAllBuilder<T>(type: TypeFromInstance<T>, options?: FindAllBuilderOptions): FindAllRequestInput;
export function findAllBuilder(type: string, options?: FindAllBuilderOptions): FindAllRequestInput;
export function findAllBuilder<T>(
type: TypeFromInstance<T> | string,
options: FindAllBuilderOptions = {}
): FindAllRequestInput {
gitKrystan marked this conversation as resolved.
Show resolved Hide resolved
assert(`You need to pass a model name to the findAll builder`, type);
assert(
`Model name passed to the findAll builder must be a dasherized string instead of ${type}`,
typeof type === 'string'
);

return {
op: 'findAll',
data: {
type: normalizeModelName(type),
options: options || {},
},
cacheOptions: { [SkipCache as symbol]: true },
};
}
125 changes: 125 additions & 0 deletions packages/legacy-compat/src/builders/find-record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* @module @ember-data/legacy-compat/builders
*/
import { assert } from '@ember/debug';

import type { StoreRequestInput } from '@ember-data/store';
import { constructResource, ensureStringId } from '@ember-data/store/-private';
import type { BaseFinderOptions, FindRecordOptions } from '@ember-data/store/-types/q/store';
import type { TypeFromInstance } from '@warp-drive/core-types/record';
import { SkipCache } from '@warp-drive/core-types/request';
import type { ResourceIdentifierObject } from '@warp-drive/core-types/spec/raw';

import { isMaybeIdentifier, normalizeModelName } from './utils';

export type FindRecordRequestInput = StoreRequestInput & {
op: 'findRecord';
data: {
record: ResourceIdentifierObject;
gitKrystan marked this conversation as resolved.
Show resolved Hide resolved
options: FindRecordBuilderOptions;
};
};

export type FindRecordBuilderOptions = Omit<FindRecordOptions, 'preload'>;

/**
This function builds a request config to find the record for a given identifier or type and id combination.
When passed to `store.request`, this config will result in the same behavior as a `store.findRecord` request.
Additionally, it takes the same options as `store.findRecord`, with the exception of `preload` (which is unsupported).

**Example 1**

```app/routes/post.js
import Route from '@ember/routing/route';
import { findRecord } from '@ember-data/legacy-compat/builders';

export default class PostRoute extends Route {
model({ post_id }) {
return this.store.request(findRecord('post', post_id));
gitKrystan marked this conversation as resolved.
Show resolved Hide resolved
}
}
```

**Example 2**

`findRecord` can be called with a single identifier argument instead of the combination
of `type` (modelName) and `id` as separate arguments. You may recognize this combo as
the typical pairing from [JSON:API](https://jsonapi.org/format/#document-resource-object-identification)

```app/routes/post.js
import Route from '@ember/routing/route';
import { findRecord } from '@ember-data/legacy-compat/builders';

export default class PostRoute extends Route {
model({ post_id: id }) {
return this.store.request(findRecord({ type: 'post', id }).content;
}
}
```

All `@ember-data/legacy-compat` builders exist to enable you to migrate your codebase to using the correct syntax for `store.request` while temporarily preserving legacy behaviors.
This is useful for quickly upgrading an entire app to a unified syntax while a longer incremental migration is made to shift off of adapters and serializers.
To that end, these builders are deprecated and will be removed in a future version of Ember Data.

@deprecated
@method findRecord
@public
@static
@for @ember-data/legacy-compat/builders
@param {string|object} type - either a string representing the name of the resource or a ResourceIdentifier object containing both the type (a string) and the id (a string) for the record or an lid (a string) of an existing record
@param {string|number|object} id - optional object with options for the request only if the first param is a ResourceIdentifier, else the string id of the record to be retrieved
@param {FindRecordBuilderOptions} [options] - if the first param is a string this will be the optional options for the request. See examples for available options.
@return {FindRecordRequestInput} request config
*/
export function findRecordBuilder<T>(
resource: TypeFromInstance<T>,
id: string,
options?: FindRecordBuilderOptions
): FindRecordRequestInput;
export function findRecordBuilder(
resource: string,
id: string,
options?: FindRecordBuilderOptions
): FindRecordRequestInput;
export function findRecordBuilder<T>(
resource: ResourceIdentifierObject<TypeFromInstance<T>>,
options?: FindRecordBuilderOptions
): FindRecordRequestInput;
export function findRecordBuilder(
resource: ResourceIdentifierObject,
options?: FindRecordBuilderOptions
): FindRecordRequestInput;
export function findRecordBuilder(
resource: string | ResourceIdentifierObject,
idOrOptions?: string | BaseFinderOptions,
options?: FindRecordBuilderOptions
): FindRecordRequestInput {
assert(
`You need to pass a modelName or resource identifier as the first argument to the findRecord builder`,
resource
);
if (isMaybeIdentifier(resource)) {
options = idOrOptions as BaseFinderOptions | undefined;
} else {
assert(
`You need to pass a modelName or resource identifier as the first argument to the findRecord builder (passed ${resource})`,
typeof resource === 'string'
);
const type = normalizeModelName(resource);
const normalizedId = ensureStringId(idOrOptions as string | number);
resource = constructResource(type, normalizedId);
}

options = options || {};

assert('findRecord builder does not support options.preload', !(options as FindRecordOptions).preload);

return {
op: 'findRecord' as const,
data: {
record: resource,
options,
},
cacheOptions: { [SkipCache as symbol]: true },
};
}
125 changes: 125 additions & 0 deletions packages/legacy-compat/src/builders/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* @module @ember-data/legacy-compat/builders
*/
import { assert } from '@ember/debug';

import type { StoreRequestInput } from '@ember-data/store';
import type { QueryOptions } from '@ember-data/store/-types/q/store';
import type { TypeFromInstance } from '@warp-drive/core-types/record';
import { SkipCache } from '@warp-drive/core-types/request';

import { normalizeModelName } from './utils';

export type QueryRequestInput = StoreRequestInput & {
op: 'query';
data: {
type: string;
query: Record<string, unknown>;
options: QueryBuilderOptions;
};
};

export type QueryBuilderOptions = QueryOptions;

/**
This function builds a request config for a given type and query object.
When passed to `store.request`, this config will result in the same behavior as a `store.query` request.
Additionally, it takes the same options as `store.query`.

All `@ember-data/legacy-compat` builders exist to enable you to migrate your codebase to using the correct syntax for `store.request` while temporarily preserving legacy behaviors.
This is useful for quickly upgrading an entire app to a unified syntax while a longer incremental migration is made to shift off of adapters and serializers.
To that end, these builders are deprecated and will be removed in a future version of Ember Data.

@deprecated
@method query
@public
@static
@for @ember-data/legacy-compat/builders
@param {string} type the name of the resource
@param {object} query a query to be used by the adapter
@param {QueryBuilderOptions} [options] optional, may include `adapterOptions` hash which will be passed to adapter.query
@return {QueryRequestInput} request config
*/
export function queryBuilder<T>(
type: TypeFromInstance<T>,
query: Record<string, unknown>,
options?: QueryBuilderOptions
): QueryRequestInput;
export function queryBuilder(
type: string,
query: Record<string, unknown>,
options?: QueryBuilderOptions
): QueryRequestInput;
export function queryBuilder(
type: string,
query: Record<string, unknown>,
options: QueryBuilderOptions = {}
): QueryRequestInput {
assert(`You need to pass a model name to the query builder`, type);
assert(`You need to pass a query hash to the query builder`, query);
assert(
`Model name passed to the query builder must be a dasherized string instead of ${type}`,
typeof type === 'string'
);

return {
op: 'query' as const,
data: {
type: normalizeModelName(type),
query,
options: options,
},
cacheOptions: { [SkipCache as symbol]: true },
};
}

export type QueryRecordRequestInput = StoreRequestInput & {
op: 'queryRecord';
data: {
type: string;
query: Record<string, unknown>;
options: QueryBuilderOptions;
};
};

/**
This function builds a request config for a given type and query object.
When passed to `store.request`, this config will result in the same behavior as a `store.queryRecord` request.
Additionally, it takes the same options as `store.queryRecord`.

All `@ember-data/legacy-compat` builders exist to enable you to migrate your codebase to using the correct syntax for `store.request` while temporarily preserving legacy behaviors.
This is useful for quickly upgrading an entire app to a unified syntax while a longer incremental migration is made to shift off of adapters and serializers.
To that end, these builders are deprecated and will be removed in a future version of Ember Data.

@deprecated
@method queryRecord
@public
@static
@for @ember-data/legacy-compat/builders
@param {string} type the name of the resource
@param {object} query a query to be used by the adapter
@param {QueryBuilderOptions} [options] optional, may include `adapterOptions` hash which will be passed to adapter.query
@return {QueryRecordRequestInput} request config
*/
export function queryRecordBuilder(
modelName: string,
query: Record<string, unknown>,
options?: QueryBuilderOptions
): QueryRecordRequestInput {
assert(`You need to pass a model name to the queryRecord builder`, modelName);
assert(`You need to pass a query hash to the queryRecord builder`, query);
assert(
`Model name passed to the queryRecord builder must be a dasherized string instead of ${modelName}`,
typeof modelName === 'string'
);

return {
op: 'queryRecord',
data: {
type: normalizeModelName(modelName),
query,
options: options || {},
},
cacheOptions: { [SkipCache as symbol]: true },
};
}
Loading
Loading