ember-data-model-fragments 7.0.2 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-plan.json +16 -4
- package/CHANGELOG.md +40 -0
- package/README.md +95 -31
- package/addon/array/fragment.js +19 -0
- package/addon/array/stateful.js +9 -45
- package/addon/attributes/array.js +55 -43
- package/addon/attributes/fragment-array.js +58 -46
- package/addon/attributes/fragment-owner.js +12 -2
- package/addon/attributes/fragment.js +61 -45
- package/addon/cache/fragment-cache.js +323 -24
- package/addon/cache/fragment-record-data-proxy.js +3 -1
- package/addon/cache/fragment-state-manager.js +283 -70
- package/addon/ext.js +52 -216
- package/addon/fragment.js +129 -48
- package/addon/index.js +15 -4
- package/addon/schema-service.js +190 -0
- package/addon/serializer.js +21 -0
- package/addon/serializers/fragment.js +85 -0
- package/addon/serializers/json-api.js +85 -0
- package/addon/serializers/rest.js +83 -0
- package/addon/serializers/utils.js +253 -0
- package/addon/store.js +301 -0
- package/addon/transforms/fragment.js +3 -7
- package/addon/util/fragment-cache.js +59 -0
- package/package.json +76 -65
- package/addon/record-data.js +0 -1131
- package/app/initializers/model-fragments.js +0 -11
- package/record-data.d.ts +0 -4
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
function getType(resource) {
|
|
2
|
+
return typeof resource === 'string' ? resource : resource.type;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function isFragmentAttribute(meta) {
|
|
6
|
+
return (
|
|
7
|
+
typeof meta === 'object' &&
|
|
8
|
+
meta !== null &&
|
|
9
|
+
'kind' in meta &&
|
|
10
|
+
meta.isFragment === true &&
|
|
11
|
+
(meta.kind === 'fragment' ||
|
|
12
|
+
meta.kind === 'fragment-array' ||
|
|
13
|
+
meta.kind === 'array')
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function transformFragmentMeta(name, meta) {
|
|
18
|
+
return {
|
|
19
|
+
name,
|
|
20
|
+
key: name,
|
|
21
|
+
kind: 'attribute',
|
|
22
|
+
type: meta.type,
|
|
23
|
+
options: {
|
|
24
|
+
...meta.options,
|
|
25
|
+
isFragment: true,
|
|
26
|
+
fragmentKind: meta.kind,
|
|
27
|
+
modelName: meta.modelName,
|
|
28
|
+
},
|
|
29
|
+
isAttribute: true,
|
|
30
|
+
isFragment: true,
|
|
31
|
+
modelName: meta.modelName,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function mergedCacheFields(fields) {
|
|
36
|
+
const cacheFields = new Map();
|
|
37
|
+
|
|
38
|
+
fields.forEach((field, key) => {
|
|
39
|
+
if (field.kind === '@id' || field.kind === '@hash') {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
cacheFields.set(field.sourceKey || field.name || key, field);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return cacheFields;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default class FragmentSchemaService {
|
|
50
|
+
constructor(store, schema) {
|
|
51
|
+
this.store = store;
|
|
52
|
+
this._schema = schema;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_fragmentDefinitionsFor(resource) {
|
|
56
|
+
const type = getType(resource);
|
|
57
|
+
const modelClass = this.store.modelFor(type);
|
|
58
|
+
const definitions = Object.create(null);
|
|
59
|
+
|
|
60
|
+
modelClass.eachComputedProperty((name, meta) => {
|
|
61
|
+
if (isFragmentAttribute(meta)) {
|
|
62
|
+
definitions[name] = transformFragmentMeta(name, meta);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return definitions;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_mergedFields(resource) {
|
|
70
|
+
const fields = new Map(this._schema.fields(resource));
|
|
71
|
+
const fragments = this._fragmentDefinitionsFor(resource);
|
|
72
|
+
|
|
73
|
+
Object.keys(fragments).forEach((name) => {
|
|
74
|
+
fields.set(name, fragments[name]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return fields;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
resourceTypes() {
|
|
81
|
+
return this._schema.resourceTypes();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
hasResource(resource) {
|
|
85
|
+
return this._schema.hasResource(resource);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
hasTrait(type) {
|
|
89
|
+
return this._schema.hasTrait(type);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
resourceHasTrait(resource, trait) {
|
|
93
|
+
return this._schema.resourceHasTrait(resource, trait);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fields(resource) {
|
|
97
|
+
return this._mergedFields(resource);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
cacheFields(resource) {
|
|
101
|
+
return mergedCacheFields(this._mergedFields(resource));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
transformation(field) {
|
|
105
|
+
return this._schema.transformation(field);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
hashFn(field) {
|
|
109
|
+
return this._schema.hashFn(field);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
derivation(field) {
|
|
113
|
+
return this._schema.derivation(field);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
resource(resource) {
|
|
117
|
+
const schema = this._schema.resource(resource);
|
|
118
|
+
const fields = this._mergedFields(resource);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
...schema,
|
|
122
|
+
fields: Array.from(fields.values()),
|
|
123
|
+
cacheFields: mergedCacheFields(fields),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
registerResources(schemas) {
|
|
128
|
+
this._schema.registerResources(schemas);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
registerResource(schema) {
|
|
132
|
+
this._schema.registerResource(schema);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
registerTransformation(transform) {
|
|
136
|
+
this._schema.registerTransformation(transform);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
registerDerivation(derivation) {
|
|
140
|
+
this._schema.registerDerivation(derivation);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
registerHashFn(hashFn) {
|
|
144
|
+
this._schema.registerHashFn(hashFn);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
registerTrait(trait) {
|
|
148
|
+
this._schema.registerTrait?.(trait);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
attributesDefinitionFor(resource) {
|
|
152
|
+
const attributes = this._schema.attributesDefinitionFor
|
|
153
|
+
? { ...this._schema.attributesDefinitionFor(resource) }
|
|
154
|
+
: Object.create(null);
|
|
155
|
+
const fragments = this._fragmentDefinitionsFor(resource);
|
|
156
|
+
|
|
157
|
+
return Object.assign(attributes, fragments);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
relationshipsDefinitionFor(resource) {
|
|
161
|
+
return this._schema.relationshipsDefinitionFor?.(resource);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
doesTypeExist(type) {
|
|
165
|
+
return this._schema.doesTypeExist?.(type) ?? this.hasResource({ type });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
CAUTION_MEGA_DANGER_ZONE_registerExtension(extension) {
|
|
169
|
+
this._schema.CAUTION_MEGA_DANGER_ZONE_registerExtension?.(extension);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
CAUTION_MEGA_DANGER_ZONE_resourceExtensions(resource) {
|
|
173
|
+
return this._schema.CAUTION_MEGA_DANGER_ZONE_resourceExtensions?.(resource);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
CAUTION_MEGA_DANGER_ZONE_objectExtensions(field, resolvedType) {
|
|
177
|
+
return this._schema.CAUTION_MEGA_DANGER_ZONE_objectExtensions?.(
|
|
178
|
+
field,
|
|
179
|
+
resolvedType,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
CAUTION_MEGA_DANGER_ZONE_arrayExtensions(field) {
|
|
184
|
+
return this._schema.CAUTION_MEGA_DANGER_ZONE_arrayExtensions?.(field);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
CAUTION_MEGA_DANGER_ZONE_hasExtension(extension) {
|
|
188
|
+
return this._schema.CAUTION_MEGA_DANGER_ZONE_hasExtension?.(extension);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports for ember-data-model-fragments serializers.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
*
|
|
6
|
+
* ```js
|
|
7
|
+
* // For JSONSerializer (default)
|
|
8
|
+
* import FragmentSerializer from 'ember-data-model-fragments/serializer';
|
|
9
|
+
*
|
|
10
|
+
* // For RESTSerializer
|
|
11
|
+
* import { FragmentRESTSerializer } from 'ember-data-model-fragments/serializer';
|
|
12
|
+
*
|
|
13
|
+
* // For JSONAPISerializer
|
|
14
|
+
* import { FragmentJSONAPISerializer } from 'ember-data-model-fragments/serializer';
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export { default } from './serializers/fragment';
|
|
19
|
+
export { default as FragmentSerializer } from './serializers/fragment';
|
|
20
|
+
export { default as FragmentRESTSerializer } from './serializers/rest';
|
|
21
|
+
export { default as FragmentJSONAPISerializer } from './serializers/json-api';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import JSONSerializer from '@ember-data/serializer/json';
|
|
2
|
+
import {
|
|
3
|
+
fragmentTransformFor,
|
|
4
|
+
fragmentApplyTransforms,
|
|
5
|
+
fragmentExtractAttributes,
|
|
6
|
+
fragmentSerialize,
|
|
7
|
+
} from './utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
FragmentSerializer is the base serializer class for ember-data-model-fragments.
|
|
11
|
+
Extends JSONSerializer.
|
|
12
|
+
|
|
13
|
+
To use fragment serialization properly, your serializers should extend FragmentSerializer:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// app/serializers/application.js
|
|
17
|
+
import FragmentSerializer from 'ember-data-model-fragments/serializer';
|
|
18
|
+
|
|
19
|
+
export default class ApplicationSerializer extends FragmentSerializer {}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
@class FragmentSerializer
|
|
23
|
+
@extends JSONSerializer
|
|
24
|
+
@public
|
|
25
|
+
*/
|
|
26
|
+
export default class FragmentSerializer extends JSONSerializer {
|
|
27
|
+
serialize(snapshot, options) {
|
|
28
|
+
return fragmentSerialize(
|
|
29
|
+
this,
|
|
30
|
+
snapshot,
|
|
31
|
+
super.serialize(snapshot, options),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
Enables fragment properties to have custom transforms based on the fragment
|
|
37
|
+
type, so that deserialization does not have to happen on the fly
|
|
38
|
+
|
|
39
|
+
@method transformFor
|
|
40
|
+
@param {String} attributeType - The attribute type to get the transform for
|
|
41
|
+
@return {Transform}
|
|
42
|
+
@public
|
|
43
|
+
*/
|
|
44
|
+
transformFor(attributeType) {
|
|
45
|
+
return fragmentTransformFor(
|
|
46
|
+
this,
|
|
47
|
+
attributeType,
|
|
48
|
+
JSONSerializer.prototype.transformFor,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
Override applyTransforms to handle polymorphic fragments with a typeKey function
|
|
54
|
+
|
|
55
|
+
@method applyTransforms
|
|
56
|
+
@param {Class} typeClass - The model class
|
|
57
|
+
@param {Object} data - The data to apply transforms to
|
|
58
|
+
@return {Object} The transformed data
|
|
59
|
+
@public
|
|
60
|
+
*/
|
|
61
|
+
applyTransforms(typeClass, data) {
|
|
62
|
+
return fragmentApplyTransforms(this, typeClass, data);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
Override extractAttributes to include fragment attributes.
|
|
67
|
+
The default implementation only iterates modelClass.eachAttribute which
|
|
68
|
+
doesn't include fragment attributes (they're computed properties with
|
|
69
|
+
isFragment: true metadata).
|
|
70
|
+
|
|
71
|
+
@method extractAttributes
|
|
72
|
+
@param {Class} modelClass - The model class
|
|
73
|
+
@param {Object} resourceHash - The raw resource data from the server
|
|
74
|
+
@return {Object} The extracted attributes
|
|
75
|
+
@public
|
|
76
|
+
*/
|
|
77
|
+
extractAttributes(modelClass, resourceHash) {
|
|
78
|
+
return fragmentExtractAttributes(
|
|
79
|
+
this,
|
|
80
|
+
modelClass,
|
|
81
|
+
resourceHash,
|
|
82
|
+
JSONSerializer.prototype.extractAttributes,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import JSONAPISerializer from '@ember-data/serializer/json-api';
|
|
2
|
+
import {
|
|
3
|
+
fragmentTransformFor,
|
|
4
|
+
fragmentApplyTransforms,
|
|
5
|
+
fragmentExtractAttributesJSONAPI,
|
|
6
|
+
fragmentSerialize,
|
|
7
|
+
} from './utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
FragmentJSONAPISerializer is the base serializer class for ember-data-model-fragments
|
|
11
|
+
when using JSONAPISerializer.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// app/serializers/application.js
|
|
15
|
+
import { FragmentJSONAPISerializer } from 'ember-data-model-fragments/serializer';
|
|
16
|
+
|
|
17
|
+
export default class ApplicationSerializer extends FragmentJSONAPISerializer {}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
@class FragmentJSONAPISerializer
|
|
21
|
+
@extends JSONAPISerializer
|
|
22
|
+
@public
|
|
23
|
+
*/
|
|
24
|
+
export default class FragmentJSONAPISerializer extends JSONAPISerializer {
|
|
25
|
+
serialize(snapshot, options) {
|
|
26
|
+
return fragmentSerialize(
|
|
27
|
+
this,
|
|
28
|
+
snapshot,
|
|
29
|
+
super.serialize(snapshot, options),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
Enables fragment properties to have custom transforms based on the fragment
|
|
35
|
+
type, so that deserialization does not have to happen on the fly
|
|
36
|
+
|
|
37
|
+
@method transformFor
|
|
38
|
+
@param {String} attributeType - The attribute type to get the transform for
|
|
39
|
+
@return {Transform}
|
|
40
|
+
@public
|
|
41
|
+
*/
|
|
42
|
+
transformFor(attributeType) {
|
|
43
|
+
return fragmentTransformFor(
|
|
44
|
+
this,
|
|
45
|
+
attributeType,
|
|
46
|
+
JSONAPISerializer.prototype.transformFor,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
Override applyTransforms to handle polymorphic fragments with a typeKey function
|
|
52
|
+
|
|
53
|
+
@method applyTransforms
|
|
54
|
+
@param {Class} typeClass - The model class
|
|
55
|
+
@param {Object} data - The data to apply transforms to
|
|
56
|
+
@return {Object} The transformed data
|
|
57
|
+
@public
|
|
58
|
+
*/
|
|
59
|
+
applyTransforms(typeClass, data) {
|
|
60
|
+
return fragmentApplyTransforms(this, typeClass, data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
Override extractAttributes to include fragment attributes.
|
|
65
|
+
The default implementation only iterates modelClass.eachAttribute which
|
|
66
|
+
doesn't include fragment attributes (they're computed properties with
|
|
67
|
+
isFragment: true metadata).
|
|
68
|
+
|
|
69
|
+
For JSON:API, attributes are nested under resourceHash.attributes.
|
|
70
|
+
|
|
71
|
+
@method extractAttributes
|
|
72
|
+
@param {Class} modelClass - The model class
|
|
73
|
+
@param {Object} resourceHash - The raw resource data from the server
|
|
74
|
+
@return {Object} The extracted attributes
|
|
75
|
+
@public
|
|
76
|
+
*/
|
|
77
|
+
extractAttributes(modelClass, resourceHash) {
|
|
78
|
+
return fragmentExtractAttributesJSONAPI(
|
|
79
|
+
this,
|
|
80
|
+
modelClass,
|
|
81
|
+
resourceHash,
|
|
82
|
+
JSONAPISerializer.prototype.extractAttributes,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import RESTSerializer from '@ember-data/serializer/rest';
|
|
2
|
+
import {
|
|
3
|
+
fragmentTransformFor,
|
|
4
|
+
fragmentApplyTransforms,
|
|
5
|
+
fragmentExtractAttributes,
|
|
6
|
+
fragmentSerialize,
|
|
7
|
+
} from './utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
FragmentRESTSerializer is the base serializer class for ember-data-model-fragments
|
|
11
|
+
when using RESTSerializer.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// app/serializers/application.js
|
|
15
|
+
import { FragmentRESTSerializer } from 'ember-data-model-fragments/serializer';
|
|
16
|
+
|
|
17
|
+
export default class ApplicationSerializer extends FragmentRESTSerializer {}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
@class FragmentRESTSerializer
|
|
21
|
+
@extends RESTSerializer
|
|
22
|
+
@public
|
|
23
|
+
*/
|
|
24
|
+
export default class FragmentRESTSerializer extends RESTSerializer {
|
|
25
|
+
serialize(snapshot, options) {
|
|
26
|
+
return fragmentSerialize(
|
|
27
|
+
this,
|
|
28
|
+
snapshot,
|
|
29
|
+
super.serialize(snapshot, options),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
Enables fragment properties to have custom transforms based on the fragment
|
|
35
|
+
type, so that deserialization does not have to happen on the fly
|
|
36
|
+
|
|
37
|
+
@method transformFor
|
|
38
|
+
@param {String} attributeType - The attribute type to get the transform for
|
|
39
|
+
@return {Transform}
|
|
40
|
+
@public
|
|
41
|
+
*/
|
|
42
|
+
transformFor(attributeType) {
|
|
43
|
+
return fragmentTransformFor(
|
|
44
|
+
this,
|
|
45
|
+
attributeType,
|
|
46
|
+
RESTSerializer.prototype.transformFor,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
Override applyTransforms to handle polymorphic fragments with a typeKey function
|
|
52
|
+
|
|
53
|
+
@method applyTransforms
|
|
54
|
+
@param {Class} typeClass - The model class
|
|
55
|
+
@param {Object} data - The data to apply transforms to
|
|
56
|
+
@return {Object} The transformed data
|
|
57
|
+
@public
|
|
58
|
+
*/
|
|
59
|
+
applyTransforms(typeClass, data) {
|
|
60
|
+
return fragmentApplyTransforms(this, typeClass, data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
Override extractAttributes to include fragment attributes.
|
|
65
|
+
The default implementation only iterates modelClass.eachAttribute which
|
|
66
|
+
doesn't include fragment attributes (they're computed properties with
|
|
67
|
+
isFragment: true metadata).
|
|
68
|
+
|
|
69
|
+
@method extractAttributes
|
|
70
|
+
@param {Class} modelClass - The model class
|
|
71
|
+
@param {Object} resourceHash - The raw resource data from the server
|
|
72
|
+
@return {Object} The extracted attributes
|
|
73
|
+
@public
|
|
74
|
+
*/
|
|
75
|
+
extractAttributes(modelClass, resourceHash) {
|
|
76
|
+
return fragmentExtractAttributes(
|
|
77
|
+
this,
|
|
78
|
+
modelClass,
|
|
79
|
+
resourceHash,
|
|
80
|
+
RESTSerializer.prototype.extractAttributes,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|