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

updates to lifetimes service and CacheHandler install #8511

Merged
merged 3 commits into from
Mar 29, 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
3 changes: 2 additions & 1 deletion packages/-ember-data/addon/-private/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import ObjectProxy from '@ember/object/proxy';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
import BaseStore from '@ember-data/store';
import BaseStore, { CacheHandler } from '@ember-data/store';

export class Store extends BaseStore {
constructor(args: Record<string, unknown>) {
super(args);
this.requestManager = new RequestManager();
this.requestManager.use([LegacyNetworkHandler, Fetch]);
this.requestManager.useCache(CacheHandler);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,22 @@ type SerializerWithParseErrors = MinimumSerializerInterface & {
extractErrors?(store: Store, modelClass: ShimModelClass, error: AdapterErrors, recordId: string | null): unknown;
};

const PotentialLegacyOperations = new Set([
'findRecord',
'findAll',
'query',
'queryRecord',
'findBelongsTo',
'findHasMany',
'updateRecord',
'createRecord',
'deleteRecord',
]);

export const LegacyNetworkHandler: Handler = {
request<T>(context: StoreRequestContext, next: NextFn<T>): Promise<T> {
// if we are not a legacy request, move on
if (context.request.url || !context.request.op) {
if (context.request.url || !context.request.op || !PotentialLegacyOperations.has(context.request.op)) {
return next(context.request) as unknown as Promise<T>;
}

Expand Down
6 changes: 4 additions & 2 deletions packages/request/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ export default class extends RequestManager {
To have a request service unique to a Store:

```ts
import Store from '@ember-data/store';
import Store, { CacheHandler } from '@ember-data/store';
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';

Expand All @@ -338,6 +338,7 @@ class extends Store {
constructor(args) {
super(args);
this.requestManager.use([Fetch]);
this.requestManager.useCache(CacheHandler);
}
}
```
Expand All @@ -347,7 +348,7 @@ class extends Store {
If using the package [ember-data](https://github.com/emberjs/data/tree/main/packages/-ember-data), the following configuration will automatically be done in order to preserve the legacy [Adapter](https://github.com/emberjs/data/tree/main/packages/adapter) and [Serializer](https://github.com/emberjs/data/tree/main/packages/serializer) behavior. Additional handlers or a service injection like the above would need to be done by the consuming application in order to make broader use of `RequestManager`.

```ts
import Store from '@ember-data/store';
import Store, { CacheHandler } from '@ember-data/store';
import RequestManager from '@ember-data/request';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';

Expand All @@ -357,6 +358,7 @@ export default class extends Store {
constructor(args) {
super(args);
this.requestManager.use([LegacyNetworkHandler]);
this.requestManager.useCache(CacheHandler);
}
}
```
Expand Down
37 changes: 22 additions & 15 deletions packages/request/src/-private/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,18 @@ import { DEBUG } from '@ember-data/env';
import { Context } from './context';
import type { ImmutableHeaders, RequestInfo } from './types';

const ValidKeys = new Map<string, string | string[]>([
const BODY_TYPES = {
type: 'string',
klass: ['Blob', 'ArrayBuffer', 'TypedArray', 'DataView', 'FormData', 'URLSearchParams', 'ReadableStream'],
};
const ValidKeys = new Map<string, string | string[] | typeof BODY_TYPES>([
['records', 'array'],
['data', 'json'],
['body', BODY_TYPES],
['disableTestWaiter', 'boolean'],
['options', 'object'],
['cacheOptions', 'object'],
[
'op',
[
'findAll',
'query',
'queryRecord',
'findRecord',
'updateRecord',
'deleteRecord',
'createRecord',
'findBelongsTo',
'findHasMany',
],
],
['op', 'string'],
['store', 'object'],
['url', 'string'],
['cache', ['default', 'force-cache', 'no-cache', 'no-store', 'only-if-cached', 'reload']],
Expand Down Expand Up @@ -204,6 +196,21 @@ function validateKey(key: string, value: unknown, errors: string[]) {
return;
}
if (schema) {
if (schema === BODY_TYPES) {
if (typeof value === 'string' || value instanceof ReadableStream) {
return;
}
let type = niceTypeOf(value);
if (schema.klass.includes(type)) {
return;
}
errors.push(
`InvalidValue: key 'body' should be a string or one of '${schema.klass.join("', '")}', received ${
'<a value of type ' + niceTypeOf(value) + '>'
}`
);
return;
}
if (Array.isArray(schema)) {
if (!schema.includes(value as string)) {
errors.push(
Expand Down
6 changes: 4 additions & 2 deletions packages/request/src/-private/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export default class extends RequestManager {
To have a request service unique to a Store:

```ts
import Store from '@ember-data/store';
import Store, { CacheHandler } from '@ember-data/store';
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';

Expand All @@ -285,6 +285,7 @@ class extends Store {
constructor(args) {
super(args);
this.requestManager.use([Fetch]);
this.requestManager.useCache(CacheHandler);
}
}
```
Expand All @@ -294,7 +295,7 @@ class extends Store {
If using the package [ember-data](https://github.com/emberjs/data/tree/main/packages/-ember-data), the following configuration will automatically be done in order to preserve the legacy [Adapter](https://github.com/emberjs/data/tree/main/packages/adapter) and [Serializer](https://github.com/emberjs/data/tree/main/packages/serializer) behavior. Additional handlers or a service injection like the above would need to be done by the consuming application in order to make broader use of `RequestManager`.

```ts
import Store from '@ember-data/store';
import Store, { CacheHandler } from '@ember-data/store';
import RequestManager from '@ember-data/request';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';

Expand All @@ -304,6 +305,7 @@ export default class extends Store {
constructor(args) {
super(args);
this.requestManager.use([LegacyNetworkHandler]);
this.requestManager.useCache(CacheHandler);
}
}
```
Expand Down
4 changes: 2 additions & 2 deletions packages/store/src/-private/cache-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ function fetchContentAndHydrate<T>(

export const CacheHandler: Handler = {
request<T>(context: StoreRequestContext, next: NextFn<T>): Promise<T> | Future<T> {
// if we are a legacy request, skip cache handling
if (context.request.op && !context.request.url) {
// if we are a legacy request or did not originate from the store, skip cache handling
if (!context.request.store || (context.request.op && !context.request.url)) {
return next(context.request);
}
const { store } = context.request;
Expand Down
2 changes: 2 additions & 0 deletions packages/store/src/-private/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export { default as Store, storeFor } from './store-service';

export { recordIdentifierFor } from './caches/instance-cache';

export { CacheHandler } from './cache-handler';

export {
setIdentifierGenerationMethod,
setIdentifierUpdateMethod,
Expand Down
28 changes: 14 additions & 14 deletions packages/store/src/-private/store-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import type { SchemaDefinitionService } from '@ember-data/types/q/schema-definit
import type { FindOptions } from '@ember-data/types/q/store';
import type { Dict } from '@ember-data/types/q/utils';

import { CacheHandler, type LifetimesService, type StoreRequestInfo } from './cache-handler';
import { type LifetimesService, type StoreRequestInfo } from './cache-handler';
import peekCache, { setCacheFor } from './caches/cache-utils';
import { IdentifierCache } from './caches/identifier-cache';
import {
Expand Down Expand Up @@ -199,7 +199,7 @@ class Store {
* it as a service or by initializing it.
*
* ```ts
* import Store from '@ember-data/store';
* import Store, { CacheHandler } from '@ember-data/store';
* import RequestManager from '@ember-data/request';
* import Fetch from '@ember/data/request/fetch';
*
Expand All @@ -208,6 +208,7 @@ class Store {
* super(...arguments);
* this.requestManager = new RequestManager();
* this.requestManager.use([Fetch]);
* this.requestManager.useCache(CacheHandler);
* }
* }
* ```
Expand All @@ -221,13 +222,22 @@ class Store {
* A Property which an App may set to provide a Lifetimes Service
* to control when a cached request becomes stale.
*
* Note, when defined, these methods will only be invoked if `key` `url` and `method`
* are all present.
*
* `isSoftExpired` will only be invoked if `isHardExpired` returns `false`.
*
* ```ts
* store.lifetimes = {
* // make the request and ignore the current cache state
* isHardExpired() {}
* isHardExpired(key: string, url: string, method?: HTTPMethod): boolean {
* return false;
* }
*
* // make the request in the background if true, return cache state
* isSoftExpired() {}
* isSoftExpired(key: string, url: string, method: HTTPMethod): boolean {
* return false;
* }
* }
* ```
*
Expand All @@ -245,7 +255,6 @@ class Store {
declare _schemaDefinitionService: SchemaDefinitionService;
declare _instanceCache: InstanceCache;

declare _hasRegisteredCacheHandler: boolean;
declare _cbs: { coalesce?: () => void; sync?: () => void; notify?: () => void } | null;
declare _forceShim: boolean;
declare _enableAsyncFlush: boolean | null;
Expand Down Expand Up @@ -278,14 +287,6 @@ class Store {
this._modelFactoryCache = Object.create(null);
}

_registerCacheHandler() {
if (this._hasRegisteredCacheHandler) {
return;
}
this.requestManager.useCache(CacheHandler);
this._hasRegisteredCacheHandler = true;
}

_run(cb: () => void) {
assert(`EmberData should never encounter a nested run`, !this._cbs);
const _cbs: { coalesce?: () => void; sync?: () => void; notify?: () => void } = (this._cbs = {});
Expand Down Expand Up @@ -375,7 +376,6 @@ class Store {
// we lazily set the cache handler when we issue the first request
// because constructor doesn't allow for this to run after
// the user has had the chance to set the prop.
this._registerCacheHandler();
let opts: { store: Store; disableTestWaiter?: boolean } = { store: this };

if (TESTING) {
Expand Down
1 change: 1 addition & 0 deletions packages/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@

export {
Store as default,
CacheHandler,
normalizeModelName,
setIdentifierGenerationMethod,
setIdentifierUpdateMethod,
Expand Down
3 changes: 2 additions & 1 deletion tests/adapter-encapsulation/app/services/store.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import Cache from '@ember-data/json-api';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
import RequestManager from '@ember-data/request';
import Store from '@ember-data/store';
import Store, { CacheHandler } from '@ember-data/store';

export default class DefaultStore extends Store {
constructor() {
super(...arguments);
this.requestManager = new RequestManager();
this.requestManager.use([LegacyNetworkHandler]);
this.requestManager.useCache(CacheHandler);
}
createCache(storeWrapper) {
return new Cache(storeWrapper);
Expand Down
3 changes: 2 additions & 1 deletion tests/graph/app/services/store.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
import RequestManager from '@ember-data/request';
import Fetch from '@ember-data/request/fetch';
import BaseStore from '@ember-data/store';
import BaseStore, { CacheHandler } from '@ember-data/store';

export default class Store extends BaseStore {
constructor(args: Record<string, unknown>) {
super(args);
this.requestManager = new RequestManager();
this.requestManager.use([LegacyNetworkHandler, Fetch]);
this.requestManager.useCache(CacheHandler);
}
}
3 changes: 2 additions & 1 deletion tests/serializer-encapsulation/app/services/store.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import Cache from '@ember-data/json-api';
import { LegacyNetworkHandler } from '@ember-data/legacy-compat';
import RequestManager from '@ember-data/request';
import Store from '@ember-data/store';
import Store, { CacheHandler } from '@ember-data/store';

export default class DefaultStore extends Store {
constructor() {
super(...arguments);
this.requestManager = new RequestManager();
this.requestManager.use([LegacyNetworkHandler]);
this.requestManager.useCache(CacheHandler);
}
createCache(storeWrapper) {
return new Cache(storeWrapper);
Expand Down