UNPKG

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