1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Schema = void 0;
|
4 | const CustomError = require("./Error");
|
5 | const utils = require("./utils");
|
6 | const Internal = require("./Internal");
|
7 | const Document_1 = require("./Document");
|
8 | const Model_1 = require("./Model");
|
9 | class DynamoDBType {
|
10 | constructor(obj) {
|
11 | Object.keys(obj).forEach((key) => {
|
12 | this[key] = obj[key];
|
13 | });
|
14 | }
|
15 | result(typeSettings) {
|
16 |
|
17 |
|
18 |
|
19 | const type = this.dynamodbType instanceof DynamoDBType ? this.dynamodbType : this;
|
20 | const dynamodbType = (() => {
|
21 | if (this.dynamodbType instanceof DynamoDBType) {
|
22 | return this.dynamodbType.dynamodbType;
|
23 | }
|
24 | else if (typeof this.dynamodbType === "function") {
|
25 | return this.dynamodbType(typeSettings);
|
26 | }
|
27 | else {
|
28 | return this.dynamodbType;
|
29 | }
|
30 | })();
|
31 | const result = {
|
32 | "name": this.name,
|
33 | dynamodbType,
|
34 | "nestedType": this.nestedType,
|
35 | "isOfType": this.jsType.func ? (val) => this.jsType.func(val, typeSettings) : (val) => {
|
36 | return [{ "value": this.jsType, "type": "main" }, { "value": this.dynamodbType instanceof DynamoDBType ? type.jsType : null, "type": "underlying" }].filter((a) => Boolean(a.value)).find((jsType) => typeof jsType.value === "string" ? typeof val === jsType.value : val instanceof jsType.value);
|
37 | },
|
38 | "isSet": false,
|
39 | typeSettings
|
40 | };
|
41 | if (this.dynamicName) {
|
42 | result.dynamicName = () => this.dynamicName(typeSettings);
|
43 | }
|
44 | if (this.customType) {
|
45 | const functions = this.customType.functions(typeSettings);
|
46 | result.customType = Object.assign(Object.assign({}, this.customType), { functions });
|
47 | }
|
48 | const isSetAllowed = typeof type.set === "function" ? type.set(typeSettings) : type.set;
|
49 | if (isSetAllowed) {
|
50 | let typeName;
|
51 | if (type.customDynamoName) {
|
52 | typeName = typeof type.customDynamoName === "function" ? type.customDynamoName(typeSettings) : type.customDynamoName;
|
53 | }
|
54 | else {
|
55 | typeName = type.name;
|
56 | }
|
57 | result.set = {
|
58 | "name": `${this.name} Set`,
|
59 | "isSet": true,
|
60 | "dynamodbType": `${dynamodbType}S`,
|
61 | "isOfType": (val, type, settings = {}) => {
|
62 | if (type === "toDynamo") {
|
63 | return !settings.saveUnknown && Array.isArray(val) && val.every((subValue) => result.isOfType(subValue)) || val instanceof Set && [...val].every((subValue) => result.isOfType(subValue));
|
64 | }
|
65 | else {
|
66 | const setVal = val;
|
67 | return setVal.wrapperName === "Set" && setVal.type === typeName && Array.isArray(setVal.values);
|
68 | }
|
69 | },
|
70 | "toDynamo": (val) => ({ "wrapperName": "Set", "type": typeName, "values": [...val] }),
|
71 | "fromDynamo": (val) => new Set(val.values),
|
72 | typeSettings
|
73 | };
|
74 | if (this.dynamicName) {
|
75 | result.set.dynamicName = () => `${this.dynamicName(typeSettings)} Set`;
|
76 | }
|
77 | if (this.customType) {
|
78 | result.set.customType = {
|
79 | "functions": {
|
80 | "toDynamo": (val) => val.map(result.customType.functions.toDynamo),
|
81 | "fromDynamo": (val) => (Object.assign(Object.assign({}, val), { "values": val.values.map(result.customType.functions.fromDynamo) })),
|
82 | "isOfType": (val, type) => {
|
83 | if (type === "toDynamo") {
|
84 | return Array.isArray(val) && val.every((item) => result.customType.functions.isOfType(item, type));
|
85 | }
|
86 | else {
|
87 | const setVal = val;
|
88 | return setVal.wrapperName === "Set" && setVal.type === typeName && Array.isArray(setVal.values);
|
89 | }
|
90 | }
|
91 | }
|
92 | };
|
93 | }
|
94 | }
|
95 | return result;
|
96 | }
|
97 | }
|
98 | const attributeTypesMain = (() => {
|
99 | const numberType = new DynamoDBType({ "name": "Number", "dynamodbType": "N", "set": true, "jsType": "number" });
|
100 | const stringType = new DynamoDBType({ "name": "String", "dynamodbType": "S", "set": true, "jsType": "string" });
|
101 | const booleanType = new DynamoDBType({ "name": "Boolean", "dynamodbType": "BOOL", "jsType": "boolean" });
|
102 | return [
|
103 | new DynamoDBType({ "name": "Null", "dynamodbType": "NULL", "set": false, "jsType": { "func": (val) => val === null } }),
|
104 | new DynamoDBType({ "name": "Buffer", "dynamodbType": "B", "set": true, "jsType": Buffer, "customDynamoName": "Binary" }),
|
105 | booleanType,
|
106 | new DynamoDBType({ "name": "Array", "dynamodbType": "L", "jsType": { "func": Array.isArray }, "nestedType": true }),
|
107 | new DynamoDBType({ "name": "Object", "dynamodbType": "M", "jsType": { "func": (val) => Boolean(val) && val.constructor === Object && (val.wrapperName !== "Set" || Object.keys(val).length !== 3 || !val.type || !val.values) }, "nestedType": true }),
|
108 | numberType,
|
109 | stringType,
|
110 | new DynamoDBType({ "name": "Date", "dynamodbType": numberType, "customType": {
|
111 | "functions": (typeSettings) => ({
|
112 | "toDynamo": (val) => {
|
113 | if (typeSettings.storage === "seconds") {
|
114 | return Math.round(val.getTime() / 1000);
|
115 | }
|
116 | else {
|
117 | return val.getTime();
|
118 | }
|
119 | },
|
120 | "fromDynamo": (val) => {
|
121 | if (typeSettings.storage === "seconds") {
|
122 | return new Date(val * 1000);
|
123 | }
|
124 | else {
|
125 | return new Date(val);
|
126 | }
|
127 | },
|
128 | "isOfType": (val, type) => {
|
129 | return type === "toDynamo" ? val instanceof Date : typeof val === "number";
|
130 | }
|
131 | })
|
132 | }, "jsType": Date }),
|
133 | new DynamoDBType({ "name": "Combine", "dynamodbType": stringType, "set": false, "jsType": String }),
|
134 | new DynamoDBType({ "name": "Constant", "dynamicName": (typeSettings) => {
|
135 | return `constant ${typeof typeSettings.value} (${typeSettings.value})`;
|
136 | }, "customType": {
|
137 | "functions": (typeSettings) => ({
|
138 | "isOfType": (val) => typeSettings.value === val
|
139 | })
|
140 | }, "jsType": { "func": (val, typeSettings) => val === typeSettings.value }, "dynamodbType": (typeSettings) => {
|
141 | switch (typeof typeSettings.value) {
|
142 | case "string":
|
143 | return stringType.dynamodbType;
|
144 | case "boolean":
|
145 | return booleanType.dynamodbType;
|
146 | case "number":
|
147 | return numberType.dynamodbType;
|
148 | }
|
149 | } }),
|
150 | new DynamoDBType({ "name": "Model", "customDynamoName": (typeSettings) => {
|
151 | const model = typeSettings.model.Model;
|
152 | const hashKey = model.getHashKey();
|
153 | const typeDetails = model.schemas[0].getAttributeTypeDetails(hashKey);
|
154 | return typeDetails.name;
|
155 | }, "dynamicName": (typeSettings) => typeSettings.model.Model.name, "dynamodbType": (typeSettings) => {
|
156 | const model = typeSettings.model.Model;
|
157 | const hashKey = model.getHashKey();
|
158 | const rangeKey = model.getRangeKey();
|
159 | return rangeKey ? "M" : model.schemas[0].getAttributeType(hashKey);
|
160 | }, "set": (typeSettings) => {
|
161 | return !typeSettings.model.Model.getRangeKey();
|
162 | }, "jsType": { "func": (val) => val.prototype instanceof Document_1.Document }, "customType": {
|
163 | "functions": (typeSettings) => ({
|
164 | "toDynamo": (val) => {
|
165 | var _a;
|
166 | const model = typeSettings.model.Model;
|
167 | const hashKey = model.getHashKey();
|
168 | const rangeKey = model.getRangeKey();
|
169 | if (rangeKey) {
|
170 | return {
|
171 | [hashKey]: val[hashKey],
|
172 | [rangeKey]: val[rangeKey]
|
173 | };
|
174 | }
|
175 | else {
|
176 | return (_a = val[hashKey]) !== null && _a !== void 0 ? _a : val;
|
177 | }
|
178 | },
|
179 | "fromDynamo": (val) => val,
|
180 | "isOfType": (val, type) => {
|
181 | var _a;
|
182 | const model = typeSettings.model.Model;
|
183 | const hashKey = model.getHashKey();
|
184 | const rangeKey = model.getRangeKey();
|
185 | if (rangeKey) {
|
186 | return typeof val === "object" && val[hashKey] && val[rangeKey];
|
187 | }
|
188 | else {
|
189 | return utils.dynamoose.getValueTypeCheckResult(model.schemas[0], (_a = val[hashKey]) !== null && _a !== void 0 ? _a : val, hashKey, { type }, {}).isValidType;
|
190 | }
|
191 | }
|
192 | })
|
193 | } })
|
194 | ];
|
195 | })();
|
196 | const attributeTypes = utils.array_flatten(attributeTypesMain.filter((checkType) => !checkType.customType).map((checkType) => checkType.result()).map((a) => [a, a.set])).filter((a) => Boolean(a));
|
197 | class Schema {
|
198 | constructor(object, settings = {}) {
|
199 | if (!object || typeof object !== "object" || Array.isArray(object)) {
|
200 | throw new CustomError.InvalidParameterType("Schema initalization parameter must be an object.");
|
201 | }
|
202 | if (Object.keys(object).length === 0) {
|
203 | throw new CustomError.InvalidParameter("Schema initalization parameter must not be an empty object.");
|
204 | }
|
205 | if (settings.timestamps === true) {
|
206 | settings.timestamps = {
|
207 | "createdAt": "createdAt",
|
208 | "updatedAt": "updatedAt"
|
209 | };
|
210 | }
|
211 | if (settings.timestamps) {
|
212 | const createdAtArray = Array.isArray(settings.timestamps.createdAt) ? settings.timestamps.createdAt : [settings.timestamps.createdAt];
|
213 | const updatedAtArray = Array.isArray(settings.timestamps.updatedAt) ? settings.timestamps.updatedAt : [settings.timestamps.updatedAt];
|
214 | [...createdAtArray, ...updatedAtArray].forEach((prop) => {
|
215 | if (object[prop]) {
|
216 | throw new CustomError.InvalidParameter("Timestamp attributes must not be defined in schema.");
|
217 | }
|
218 | object[prop] = Date;
|
219 | });
|
220 | }
|
221 | let parsedSettings = Object.assign({}, settings);
|
222 | const parsedObject = Object.assign({}, object);
|
223 | utils.object.entries(parsedObject).filter((entry) => entry[1] instanceof Schema).forEach((entry) => {
|
224 | const [key, value] = entry;
|
225 | let newValue = {
|
226 | "type": Object,
|
227 | "schema": value.schemaObject
|
228 | };
|
229 | if (key.endsWith(".schema")) {
|
230 | newValue = value.schemaObject;
|
231 | }
|
232 | const subSettings = Object.assign({}, value.settings);
|
233 | Object.entries(subSettings).forEach((entry) => {
|
234 | const [settingsKey, settingsValue] = entry;
|
235 | switch (settingsKey) {
|
236 | case "saveUnknown":
|
237 | subSettings[settingsKey] = typeof subSettings[settingsKey] === "boolean" ? [`${key}.**`] : settingsValue.map((val) => `${key}.${val}`);
|
238 | break;
|
239 | case "timestamps":
|
240 | subSettings[settingsKey] = Object.entries(subSettings[settingsKey]).reduce((obj, entity) => {
|
241 | const [subKey, subValue] = entity;
|
242 | obj[subKey] = Array.isArray(subValue) ? subValue.map((subValue) => `${key}.${subValue}`) : `${key}.${subValue}`;
|
243 | return obj;
|
244 | }, {});
|
245 | break;
|
246 | }
|
247 | });
|
248 | parsedSettings = utils.merge_objects.main({ "combineMethod": "array_merge_new_arrray" })(parsedSettings, subSettings);
|
249 | utils.object.set(parsedObject, key, newValue);
|
250 | });
|
251 | utils.object.entries(parsedObject).forEach((entry) => {
|
252 | const key = entry[0];
|
253 | const value = entry[1];
|
254 | if (!key.endsWith(".type") && !key.endsWith(".0")) {
|
255 | if (value && value.Model && value.Model instanceof Model_1.Model) {
|
256 | utils.object.set(parsedObject, key, { "type": value });
|
257 | }
|
258 | else if (value && Array.isArray(value)) {
|
259 | value.forEach((item, index) => {
|
260 | if (item && item.Model && item.Model instanceof Model_1.Model) {
|
261 | utils.object.set(parsedObject, `${key}.${index}`, { "type": item });
|
262 | }
|
263 | });
|
264 | }
|
265 | }
|
266 | });
|
267 |
|
268 | this.schemaObject = parsedObject;
|
269 | this.settings = parsedSettings;
|
270 | const checkAttributeNameDots = (object /*, existingKey = ""*/) => {
|
271 | Object.keys(object).forEach((key) => {
|
272 | if (key.includes(".")) {
|
273 | throw new CustomError.InvalidParameter("Attributes must not contain dots.");
|
274 | }
|
275 |
|
276 | if (typeof object[key] === "object" && object[key] !== null && object[key].schema) {
|
277 | checkAttributeNameDots(object[key].schema );
|
278 | }
|
279 | });
|
280 | };
|
281 | checkAttributeNameDots(this.schemaObject);
|
282 | const checkMultipleArraySchemaElements = (key) => {
|
283 | let attributeType = [];
|
284 | try {
|
285 | const tmpAttributeType = this.getAttributeType(key);
|
286 | attributeType = Array.isArray(tmpAttributeType) ? tmpAttributeType : [tmpAttributeType];
|
287 | }
|
288 | catch (e) { }
|
289 | if (attributeType.some((type) => type === "L") && (this.getAttributeValue(key).schema || []).length > 1) {
|
290 | throw new CustomError.InvalidParameter("You must only pass one element into schema array.");
|
291 | }
|
292 | };
|
293 | this.attributes().forEach((key) => checkMultipleArraySchemaElements(key));
|
294 | const hashrangeKeys = this.attributes().reduce((val, key) => {
|
295 | const hashKey = this.getAttributeSettingValue("hashKey", key);
|
296 | const rangeKey = this.getAttributeSettingValue("rangeKey", key);
|
297 | const isHashKey = Array.isArray(hashKey) ? hashKey.every((item) => Boolean(item)) : hashKey;
|
298 | const isRangeKey = Array.isArray(rangeKey) ? rangeKey.every((item) => Boolean(item)) : rangeKey;
|
299 | if (isHashKey) {
|
300 | val.hashKeys.push(key);
|
301 | }
|
302 | if (isRangeKey) {
|
303 | val.rangeKeys.push(key);
|
304 | }
|
305 | if (isHashKey && isRangeKey) {
|
306 | val.hashAndRangeKeyAttributes.push(key);
|
307 | }
|
308 | return val;
|
309 | }, { "hashKeys": [], "rangeKeys": [], "hashAndRangeKeyAttributes": [] });
|
310 | const keyTypes = ["hashKey", "rangeKey"];
|
311 | keyTypes.forEach((keyType) => {
|
312 | if (hashrangeKeys[`${keyType}s`].length > 1) {
|
313 | throw new CustomError.InvalidParameter(`Only one ${keyType} allowed per schema.`);
|
314 | }
|
315 | if (hashrangeKeys[`${keyType}s`].find((key) => key.includes("."))) {
|
316 | throw new CustomError.InvalidParameter(`${keyType} must be at root object and not nested in object or array.`);
|
317 | }
|
318 | });
|
319 | if (hashrangeKeys.hashAndRangeKeyAttributes.length > 0) {
|
320 | throw new CustomError.InvalidParameter(`Attribute ${hashrangeKeys.hashAndRangeKeyAttributes[0]} must not be both hashKey and rangeKey`);
|
321 | }
|
322 | this.attributes().forEach((key) => {
|
323 | const attributeSettingValue = this.getAttributeSettingValue("index", key);
|
324 | if (key.includes(".") && (Array.isArray(attributeSettingValue) ? attributeSettingValue.some((singleValue) => Boolean(singleValue)) : attributeSettingValue)) {
|
325 | throw new CustomError.InvalidParameter("Index must be at root object and not nested in object or array.");
|
326 | }
|
327 | });
|
328 | }
|
329 | async getCreateTableAttributeParams(model) {
|
330 | const hashKey = this.getHashKey();
|
331 | const AttributeDefinitions = [
|
332 | {
|
333 | "AttributeName": hashKey,
|
334 | "AttributeType": this.getSingleAttributeType(hashKey)
|
335 | }
|
336 | ];
|
337 | const AttributeDefinitionsNames = [hashKey];
|
338 | const KeySchema = [
|
339 | {
|
340 | "AttributeName": hashKey,
|
341 | "KeyType": "HASH"
|
342 | }
|
343 | ];
|
344 | const rangeKey = this.getRangeKey();
|
345 | if (rangeKey) {
|
346 | AttributeDefinitions.push({
|
347 | "AttributeName": rangeKey,
|
348 | "AttributeType": this.getSingleAttributeType(rangeKey)
|
349 | });
|
350 | AttributeDefinitionsNames.push(rangeKey);
|
351 | KeySchema.push({
|
352 | "AttributeName": rangeKey,
|
353 | "KeyType": "RANGE"
|
354 | });
|
355 | }
|
356 | utils.array_flatten(await Promise.all([this.getIndexAttributes(), this.getIndexRangeKeyAttributes()])).map((obj) => obj.attribute).forEach((index) => {
|
357 | if (AttributeDefinitionsNames.includes(index)) {
|
358 | return;
|
359 | }
|
360 | AttributeDefinitionsNames.push(index);
|
361 | AttributeDefinitions.push({
|
362 | "AttributeName": index,
|
363 | "AttributeType": this.getSingleAttributeType(index)
|
364 | });
|
365 | });
|
366 | return Object.assign({ AttributeDefinitions,
|
367 | KeySchema }, await this.getIndexes(model));
|
368 | }
|
369 |
|
370 | getSingleAttributeType(key, value, settings) {
|
371 | const attributeType = this.getAttributeType(key, value, settings);
|
372 | if (Array.isArray(attributeType)) {
|
373 | throw new CustomError.InvalidParameter(`You can not have multiple types for attribute definition: ${key}.`);
|
374 | }
|
375 | return attributeType;
|
376 | }
|
377 | getAttributeType(key, value, settings) {
|
378 | try {
|
379 | const typeDetails = this.getAttributeTypeDetails(key);
|
380 | return Array.isArray(typeDetails) ? typeDetails.map((detail) => detail.dynamodbType) : typeDetails.dynamodbType;
|
381 | }
|
382 | catch (e) {
|
383 | if ((settings === null || settings === void 0 ? void 0 : settings.unknownAttributeAllowed) && e.message === `Invalid Attribute: ${key}` && value) {
|
384 | return Object.keys(Document_1.Document.objectToDynamo(value, { "type": "value" }))[0];
|
385 | }
|
386 | else {
|
387 | throw e;
|
388 | }
|
389 | }
|
390 | }
|
391 |
|
392 | async defaultCheck(key, value, settings) {
|
393 | const isValueUndefined = typeof value === "undefined" || value === null;
|
394 | if (settings.defaults && isValueUndefined || settings.forceDefault && await this.getAttributeSettingValue("forceDefault", key)) {
|
395 | const defaultValueRaw = await this.getAttributeSettingValue("default", key);
|
396 | let hasMultipleTypes;
|
397 | try {
|
398 | hasMultipleTypes = Array.isArray(this.getAttributeType(key));
|
399 | }
|
400 | catch (e) {
|
401 | hasMultipleTypes = false;
|
402 | }
|
403 | const defaultValue = Array.isArray(defaultValueRaw) && hasMultipleTypes ? defaultValueRaw[0] : defaultValueRaw;
|
404 | const isDefaultValueUndefined = typeof defaultValue === "undefined" || defaultValue === null;
|
405 | if (!isDefaultValueUndefined) {
|
406 | return defaultValue;
|
407 | }
|
408 | }
|
409 | }
|
410 | getAttributeSettingValue(setting, key, settings = { "returnFunction": false }) {
|
411 | function func(attributeValue) {
|
412 | const defaultPropertyValue = (attributeValue || {})[setting];
|
413 | return typeof defaultPropertyValue === "function" && !settings.returnFunction ? defaultPropertyValue() : defaultPropertyValue;
|
414 | }
|
415 | const attributeValue = this.getAttributeValue(key, { "typeIndexOptionMap": settings.typeIndexOptionMap });
|
416 | if (Array.isArray(attributeValue)) {
|
417 | return attributeValue.map(func);
|
418 | }
|
419 | else {
|
420 | return func(attributeValue);
|
421 | }
|
422 | }
|
423 | getTypePaths(object, settings = { "type": "toDynamo" }) {
|
424 | return Object.entries(object).reduce((result, entry) => {
|
425 | const [key, value] = entry;
|
426 | const fullKey = [settings.previousKey, key].filter((a) => Boolean(a)).join(".");
|
427 | let typeCheckResult;
|
428 | try {
|
429 | typeCheckResult = utils.dynamoose.getValueTypeCheckResult(this, value, fullKey, settings, {});
|
430 | }
|
431 | catch (e) {
|
432 | if (result && settings.includeAllProperties) {
|
433 | result[fullKey] = {
|
434 | "index": 0,
|
435 | "matchCorrectness": 0.5,
|
436 | "entryCorrectness": [0.5]
|
437 | };
|
438 | }
|
439 | return result;
|
440 | }
|
441 | const { typeDetails, matchedTypeDetailsIndex, matchedTypeDetailsIndexes } = typeCheckResult;
|
442 | const hasMultipleTypes = Array.isArray(typeDetails);
|
443 | const isObject = typeof value === "object" && value !== null;
|
444 | if (hasMultipleTypes) {
|
445 | if (matchedTypeDetailsIndexes.length > 1 && isObject) {
|
446 | result[fullKey] = matchedTypeDetailsIndexes.map((index) => {
|
447 | const entryCorrectness = utils.object.entries(value).map((entry) => {
|
448 | const [subKey, subValue] = entry;
|
449 | try {
|
450 | const { isValidType } = utils.dynamoose.getValueTypeCheckResult(this, subValue, `${fullKey}.${subKey}`, settings, { "typeIndexOptionMap": { [fullKey]: index } });
|
451 | return isValidType ? 1 : 0;
|
452 | }
|
453 | catch (e) {
|
454 | return 0.5;
|
455 | }
|
456 | });
|
457 | return {
|
458 | index,
|
459 |
|
460 |
|
461 |
|
462 | "matchCorrectness": Math.min(...entryCorrectness),
|
463 | entryCorrectness
|
464 | };
|
465 | }).sort((a, b) => {
|
466 | if (a.matchCorrectness === b.matchCorrectness) {
|
467 | return b.entryCorrectness.reduce((a, b) => a + b, 0) - a.entryCorrectness.reduce((a, b) => a + b, 0);
|
468 | }
|
469 | else {
|
470 | return b.matchCorrectness - a.matchCorrectness;
|
471 | }
|
472 | }).map((a) => a.index)[0];
|
473 | }
|
474 | if (result[fullKey] === undefined) {
|
475 | result[fullKey] = matchedTypeDetailsIndex;
|
476 | }
|
477 | }
|
478 | else if (settings.includeAllProperties) {
|
479 | const matchCorrectness = typeCheckResult.isValidType ? 1 : 0;
|
480 | result[fullKey] = {
|
481 | "index": 0,
|
482 | matchCorrectness,
|
483 | "entryCorrectness": [matchCorrectness]
|
484 | };
|
485 | }
|
486 | if (isObject) {
|
487 | result = Object.assign(Object.assign({}, result), this.getTypePaths(value, Object.assign(Object.assign({}, settings), { "previousKey": fullKey })));
|
488 | }
|
489 | return result;
|
490 | }, {});
|
491 | }
|
492 | }
|
493 | exports.Schema = Schema;
|
494 | Schema.attributeTypes = {
|
495 | "findDynamoDBType": (type) => attributeTypes.find((checkType) => checkType.dynamodbType === type),
|
496 | "findTypeForValue": (...args) => attributeTypes.find((checkType) => checkType.isOfType(...args))
|
497 | };
|
498 |
|
499 | Schema.prototype.getHashKey = function () {
|
500 | return Object.keys(this.schemaObject).find((key) => this.schemaObject[key].hashKey) || Object.keys(this.schemaObject)[0];
|
501 | };
|
502 | Schema.prototype.getRangeKey = function () {
|
503 | return Object.keys(this.schemaObject).find((key) => this.schemaObject[key].rangeKey);
|
504 | };
|
505 |
|
506 | Schema.prototype.requiredCheck = async function (key, value) {
|
507 | const isRequired = await this.getAttributeSettingValue("required", key);
|
508 | if ((typeof value === "undefined" || value === null) && (Array.isArray(isRequired) ? isRequired.some((val) => Boolean(val)) : isRequired)) {
|
509 | throw new CustomError.ValidationError(`${key} is a required property but has no value when trying to save document`);
|
510 | }
|
511 | };
|
512 | Schema.prototype.getIndexAttributes = async function () {
|
513 | return (await Promise.all(this.attributes()
|
514 | .map(async (attribute) => ({
|
515 | "index": await this.getAttributeSettingValue("index", attribute),
|
516 | attribute
|
517 | }))))
|
518 | .filter((obj) => Array.isArray(obj.index) ? obj.index.some((index) => Boolean(index)) : obj.index)
|
519 | .reduce((accumulator, currentValue) => {
|
520 | if (Array.isArray(currentValue.index)) {
|
521 | currentValue.index.forEach((currentIndex) => {
|
522 | accumulator.push(Object.assign(Object.assign({}, currentValue), { "index": currentIndex }));
|
523 | });
|
524 | }
|
525 | else {
|
526 | accumulator.push(currentValue);
|
527 | }
|
528 | return accumulator;
|
529 | }, []);
|
530 | };
|
531 | Schema.prototype.getIndexRangeKeyAttributes = async function () {
|
532 | const indexes = await this.getIndexAttributes();
|
533 | return indexes.map((index) => index.index.rangeKey).filter((a) => Boolean(a)).map((a) => ({ "attribute": a }));
|
534 | };
|
535 | Schema.prototype.getIndexes = async function (model) {
|
536 | return (await this.getIndexAttributes()).reduce((accumulator, currentValue) => {
|
537 | const indexValue = currentValue.index;
|
538 | const attributeValue = currentValue.attribute;
|
539 | const dynamoIndexObject = {
|
540 | "IndexName": indexValue.name || `${attributeValue}${indexValue.global ? "GlobalIndex" : "LocalIndex"}`,
|
541 | "KeySchema": [],
|
542 | "Projection": { "ProjectionType": "KEYS_ONLY" }
|
543 | };
|
544 | if (indexValue.project || typeof indexValue.project === "undefined" || indexValue.project === null) {
|
545 | dynamoIndexObject.Projection = Array.isArray(indexValue.project) ? { "ProjectionType": "INCLUDE", "NonKeyAttributes": indexValue.project } : { "ProjectionType": "ALL" };
|
546 | }
|
547 | if (indexValue.global) {
|
548 | dynamoIndexObject.KeySchema.push({ "AttributeName": attributeValue, "KeyType": "HASH" });
|
549 | if (indexValue.rangeKey) {
|
550 | dynamoIndexObject.KeySchema.push({ "AttributeName": indexValue.rangeKey, "KeyType": "RANGE" });
|
551 | }
|
552 | const throughputObject = utils.dynamoose.get_provisioned_throughput(indexValue.throughput ? indexValue : model.options.throughput === "ON_DEMAND" ? {} : model.options);
|
553 |
|
554 | if (throughputObject.ProvisionedThroughput) {
|
555 | dynamoIndexObject.ProvisionedThroughput = throughputObject.ProvisionedThroughput;
|
556 | }
|
557 | }
|
558 | else {
|
559 | dynamoIndexObject.KeySchema.push({ "AttributeName": this.getHashKey(), "KeyType": "HASH" });
|
560 | dynamoIndexObject.KeySchema.push({ "AttributeName": attributeValue, "KeyType": "RANGE" });
|
561 | }
|
562 | const accumulatorKey = indexValue.global ? "GlobalSecondaryIndexes" : "LocalSecondaryIndexes";
|
563 | if (!accumulator[accumulatorKey]) {
|
564 | accumulator[accumulatorKey] = [];
|
565 | }
|
566 | accumulator[accumulatorKey].push(dynamoIndexObject);
|
567 | return accumulator;
|
568 | }, {});
|
569 | };
|
570 | Schema.prototype.getSettingValue = function (setting) {
|
571 | return this.settings[setting];
|
572 | };
|
573 | function attributesAction(object) {
|
574 | const typePaths = object && this.getTypePaths(object);
|
575 | const main = (object, existingKey = "") => {
|
576 | return Object.keys(object).reduce((accumulator, key) => {
|
577 | const keyWithExisting = `${existingKey ? `${existingKey}.` : ""}${key}`;
|
578 | accumulator.push(keyWithExisting);
|
579 | let attributeType;
|
580 | try {
|
581 | const tmpAttributeType = this.getAttributeType(keyWithExisting);
|
582 | attributeType = Array.isArray(tmpAttributeType) ? tmpAttributeType : [tmpAttributeType];
|
583 | }
|
584 | catch (e) { }
|
585 |
|
586 | function recursive(type, arrayTypeIndex) {
|
587 | if ((type === "M" || type === "L") && (object[key][arrayTypeIndex] || object[key]).schema) {
|
588 | accumulator.push(...main((object[key][arrayTypeIndex] || object[key]).schema, keyWithExisting));
|
589 | }
|
590 | }
|
591 | if (attributeType) {
|
592 | if (typePaths && typePaths[keyWithExisting] !== undefined) {
|
593 | const index = typePaths[keyWithExisting];
|
594 | const type = attributeType[index];
|
595 | recursive(type, index);
|
596 | }
|
597 | else {
|
598 | attributeType.forEach(recursive);
|
599 | }
|
600 | }
|
601 |
|
602 | return accumulator;
|
603 | }, []);
|
604 | };
|
605 | return main(this.schemaObject);
|
606 | }
|
607 | Schema.prototype.attributes = function (object) {
|
608 | return attributesAction.call(this, object);
|
609 | };
|
610 | Schema.prototype.getAttributeValue = function (key, settings) {
|
611 | const previousKeyParts = [];
|
612 | let result = ((settings === null || settings === void 0 ? void 0 : settings.standardKey) ? key : key.replace(/\.\d+/gu, ".0")).split(".").reduce((result, part) => {
|
613 | if (Array.isArray(result)) {
|
614 | const predefinedIndex = settings && settings.typeIndexOptionMap && settings.typeIndexOptionMap[previousKeyParts.join(".")];
|
615 | if (predefinedIndex !== undefined) {
|
616 | result = result[predefinedIndex];
|
617 | }
|
618 | else {
|
619 | result = result.find((item) => item.schema && item.schema[part]);
|
620 | }
|
621 | }
|
622 | previousKeyParts.push(part);
|
623 | return utils.object.get(result.schema, part);
|
624 | }, { "schema": this.schemaObject });
|
625 | if (Array.isArray(result)) {
|
626 | const predefinedIndex = settings && settings.typeIndexOptionMap && settings.typeIndexOptionMap[previousKeyParts.join(".")];
|
627 | if (predefinedIndex !== undefined) {
|
628 | result = result[predefinedIndex];
|
629 | }
|
630 | }
|
631 | return result;
|
632 | };
|
633 | function retrieveTypeInfo(type, isSet, key, typeSettings) {
|
634 | const foundType = attributeTypesMain.find((checkType) => checkType.name.toLowerCase() === type.toLowerCase());
|
635 | if (!foundType) {
|
636 | throw new CustomError.InvalidType(`${key} contains an invalid type: ${type}`);
|
637 | }
|
638 | const parentType = foundType.result(typeSettings);
|
639 | if (!parentType.set && isSet) {
|
640 | throw new CustomError.InvalidType(`${key} with type: ${type} is not allowed to be a set`);
|
641 | }
|
642 | return isSet ? parentType.set : parentType;
|
643 | }
|
644 |
|
645 | Schema.prototype.getAttributeTypeDetails = function (key, settings = {}) {
|
646 | const standardKey = settings.standardKey ? key : key.replace(/\.\d+/gu, ".0");
|
647 | const val = this.getAttributeValue(standardKey, Object.assign(Object.assign({}, settings), { "standardKey": true }));
|
648 | if (typeof val === "undefined") {
|
649 | throw new CustomError.UnknownAttribute(`Invalid Attribute: ${key}`);
|
650 | }
|
651 | let typeVal = typeof val === "object" && !Array.isArray(val) && val.type ? val.type : val;
|
652 | let typeSettings = {};
|
653 | if (typeof typeVal === "object" && !Array.isArray(typeVal)) {
|
654 | typeSettings = typeVal.settings || {};
|
655 | typeVal = typeVal.value;
|
656 | }
|
657 | const getType = (typeVal) => {
|
658 | let type;
|
659 | const isThisType = typeVal === Internal.Public.this;
|
660 | const isNullType = typeVal === Internal.Public.null;
|
661 | if (typeof typeVal === "function" || isThisType) {
|
662 | if (typeVal.prototype instanceof Document_1.Document || isThisType) {
|
663 | type = "model";
|
664 | if (isThisType) {
|
665 | typeSettings.model = {
|
666 | "Model": {
|
667 | "getHashKey": this.getHashKey.bind(this),
|
668 | "getRangeKey": this.getRangeKey.bind(this),
|
669 | "schemas": [this]
|
670 | }
|
671 | };
|
672 | }
|
673 | else {
|
674 | typeSettings.model = typeVal;
|
675 | }
|
676 | }
|
677 | else {
|
678 | const regexFuncName = /^Function ([^(]+)\(/iu;
|
679 | [, type] = typeVal.toString().match(regexFuncName);
|
680 | }
|
681 | }
|
682 | else if (isNullType) {
|
683 | type = "null";
|
684 | }
|
685 | else {
|
686 | type = typeVal;
|
687 | }
|
688 | return type;
|
689 | };
|
690 | const result = (Array.isArray(typeVal) ? typeVal : [typeVal]).map((item, index) => {
|
691 | item = typeof item === "object" && !Array.isArray(item) && item.type ? item.type : item;
|
692 | if (typeof item === "object" && !Array.isArray(item)) {
|
693 | typeSettings = item.settings || {};
|
694 | item = item.value;
|
695 | }
|
696 | let type = getType(item);
|
697 | const isSet = type.toLowerCase() === "set";
|
698 | if (isSet) {
|
699 | let schemaValue = this.getAttributeSettingValue("schema", key);
|
700 | if (Array.isArray(schemaValue[index])) {
|
701 | schemaValue = schemaValue[index];
|
702 | }
|
703 | const subValue = schemaValue[0];
|
704 | type = getType(typeof subValue === "object" && subValue.type ? subValue.type : subValue);
|
705 | }
|
706 | const returnObject = retrieveTypeInfo(type, isSet, key, typeSettings);
|
707 | return returnObject;
|
708 | });
|
709 | const returnObject = result.length < 2 ? result[0] : result;
|
710 | return returnObject;
|
711 | };
|
712 |
|
\ | No newline at end of file |