@stonyx/orm 0.2.1-alpha.0 → 0.2.5-alpha.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/package.json +1 -1
- package/src/orm-request.js +68 -8
- package/src/record.js +14 -6
package/package.json
CHANGED
package/src/orm-request.js
CHANGED
|
@@ -114,6 +114,51 @@ function parseInclude(includeParam) {
|
|
|
114
114
|
.map(rel => rel.split('.')); // Parse nested paths: "owner.pets" → ["owner", "pets"]
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function parseFields(query) {
|
|
118
|
+
const fields = new Map();
|
|
119
|
+
if (!query) return fields;
|
|
120
|
+
|
|
121
|
+
for (const [key, value] of Object.entries(query)) {
|
|
122
|
+
const match = key.match(/^fields\[(\w+)\]$/);
|
|
123
|
+
if (match && typeof value === 'string') {
|
|
124
|
+
const modelName = match[1];
|
|
125
|
+
const fieldNames = value.split(',').map(f => f.trim()).filter(f => f);
|
|
126
|
+
fields.set(modelName, new Set(fieldNames));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return fields;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseFilters(query) {
|
|
134
|
+
const filters = [];
|
|
135
|
+
if (!query) return filters;
|
|
136
|
+
|
|
137
|
+
for (const [key, value] of Object.entries(query)) {
|
|
138
|
+
const match = key.match(/^filter\[(.+)\]$/);
|
|
139
|
+
if (match && typeof value === 'string') {
|
|
140
|
+
filters.push({ path: match[1].split('.'), value });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return filters;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function createFilterPredicate(filters) {
|
|
148
|
+
if (filters.length === 0) return null;
|
|
149
|
+
|
|
150
|
+
return (record) => filters.every(({ path, value }) => {
|
|
151
|
+
let current = record;
|
|
152
|
+
|
|
153
|
+
for (const segment of path) {
|
|
154
|
+
if (current == null) return false;
|
|
155
|
+
current = current[segment];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return String(current) === value;
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
117
162
|
export default class OrmRequest extends Request {
|
|
118
163
|
constructor({ model, access }) {
|
|
119
164
|
super(...arguments);
|
|
@@ -123,20 +168,30 @@ export default class OrmRequest extends Request {
|
|
|
123
168
|
|
|
124
169
|
this.handlers = {
|
|
125
170
|
get: {
|
|
126
|
-
[`/${pluralizedModel}`]: (request, { filter }) => {
|
|
171
|
+
[`/${pluralizedModel}`]: (request, { filter: accessFilter }) => {
|
|
127
172
|
const allRecords = Array.from(store.get(model).values());
|
|
128
|
-
const recordsToReturn = filter ? allRecords.filter(filter) : allRecords;
|
|
129
|
-
const data = recordsToReturn.map(record => record.toJSON());
|
|
130
173
|
|
|
174
|
+
const queryFilters = parseFilters(request.query);
|
|
175
|
+
const queryFilterPredicate = createFilterPredicate(queryFilters);
|
|
176
|
+
const fieldsMap = parseFields(request.query);
|
|
177
|
+
const modelFields = fieldsMap.get(pluralizedModel) || fieldsMap.get(model);
|
|
178
|
+
|
|
179
|
+
let recordsToReturn = allRecords;
|
|
180
|
+
if (accessFilter) recordsToReturn = recordsToReturn.filter(accessFilter);
|
|
181
|
+
if (queryFilterPredicate) recordsToReturn = recordsToReturn.filter(queryFilterPredicate);
|
|
182
|
+
|
|
183
|
+
const data = recordsToReturn.map(record => record.toJSON({ fields: modelFields }));
|
|
131
184
|
return buildResponse(data, request.query?.include, recordsToReturn);
|
|
132
185
|
},
|
|
133
186
|
|
|
134
187
|
[`/${pluralizedModel}/:id`]: (request) => {
|
|
135
188
|
const record = store.get(model, getId(request.params));
|
|
189
|
+
if (!record) return 404;
|
|
136
190
|
|
|
137
|
-
|
|
191
|
+
const fieldsMap = parseFields(request.query);
|
|
192
|
+
const modelFields = fieldsMap.get(pluralizedModel) || fieldsMap.get(model);
|
|
138
193
|
|
|
139
|
-
return buildResponse(record.toJSON(), request.query?.include, record);
|
|
194
|
+
return buildResponse(record.toJSON({ fields: modelFields }), request.query?.include, record);
|
|
140
195
|
}
|
|
141
196
|
},
|
|
142
197
|
|
|
@@ -160,14 +215,19 @@ export default class OrmRequest extends Request {
|
|
|
160
215
|
},
|
|
161
216
|
|
|
162
217
|
post: {
|
|
163
|
-
[`/${pluralizedModel}`]: (
|
|
164
|
-
const { attributes } = body?.data || {};
|
|
218
|
+
[`/${pluralizedModel}`]: (request) => {
|
|
219
|
+
const { attributes } = request.body?.data || {};
|
|
165
220
|
|
|
166
221
|
if (!attributes) return 400; // Bad request
|
|
167
222
|
|
|
223
|
+
const fieldsMap = parseFields(request.query);
|
|
224
|
+
const modelFields = fieldsMap.get(pluralizedModel) || fieldsMap.get(model);
|
|
225
|
+
// Check for duplicate ID
|
|
226
|
+
if (attributes.id !== undefined && store.get(model, attributes.id)) return 409; // Conflict
|
|
227
|
+
|
|
168
228
|
const record = createRecord(model, attributes, { serialize: false });
|
|
169
229
|
|
|
170
|
-
return { data: record.toJSON() };
|
|
230
|
+
return { data: record.toJSON({ fields: modelFields }) };
|
|
171
231
|
}
|
|
172
232
|
},
|
|
173
233
|
|
package/src/record.js
CHANGED
|
@@ -48,21 +48,29 @@ export default class Record {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// Formats record for JSON API output
|
|
51
|
-
toJSON() {
|
|
51
|
+
toJSON(options = {}) {
|
|
52
52
|
if (!this.__serialized) throw new Error('Record must be serialized before being converted to JSON');
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
const { __data:data } = this;
|
|
55
|
+
const { fields } = options;
|
|
55
56
|
const relationships = {};
|
|
56
|
-
const attributes = {
|
|
57
|
-
|
|
57
|
+
const attributes = {};
|
|
58
|
+
|
|
59
|
+
for (const [key, value] of Object.entries(data)) {
|
|
60
|
+
if (key === 'id') continue;
|
|
61
|
+
if (fields && !fields.has(key)) continue;
|
|
62
|
+
attributes[key] = value;
|
|
63
|
+
}
|
|
58
64
|
|
|
59
65
|
for (const [key, getter] of getComputedProperties(this.__model)) {
|
|
66
|
+
if (fields && !fields.has(key)) continue;
|
|
60
67
|
attributes[key] = getter.call(this);
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
for (const [
|
|
70
|
+
for (const [key, childRecord] of Object.entries(this.__relationships)) {
|
|
71
|
+
if (fields && !fields.has(key)) continue;
|
|
64
72
|
relationships[key] = {
|
|
65
|
-
data: Array.isArray(childRecord)
|
|
73
|
+
data: Array.isArray(childRecord)
|
|
66
74
|
? childRecord.map(r => ({ type: r.__model.__name, id: r.id }))
|
|
67
75
|
: childRecord ? { type: childRecord.__model.__name, id: childRecord.id } : null
|
|
68
76
|
};
|