UNPKG

36.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Schema = void 0;
4const CustomError = require("./Error");
5const utils = require("./utils");
6const Internal = require("./Internal");
7const Document_1 = require("./Document");
8const Model_1 = require("./Model");
9class DynamoDBType {
10 constructor(obj) {
11 Object.keys(obj).forEach((key) => {
12 this[key] = obj[key];
13 });
14 }
15 result(typeSettings) {
16 // Can't use variable below to check type, see TypeScript issue link below for more information
17 // https://github.com/microsoft/TypeScript/issues/37855
18 // const isSubType = this.dynamodbType instanceof DynamoDBType; // Represents underlying DynamoDB type for custom types
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; // TODO: Probably bad practice here, should figure out how to do this better.
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; // TODO: Probably bad practice here, should figure out how to do this better.
88 return setVal.wrapperName === "Set" && setVal.type === typeName && Array.isArray(setVal.values);
89 }
90 }
91 }
92 };
93 }
94 }
95 return result;
96 }
97}
98const 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); // This has no potiental of being an array because a hashKey is not allowed to have multiple type options
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})();
196const attributeTypes = utils.array_flatten(attributeTypesMain.filter((checkType) => !checkType.customType).map((checkType) => checkType.result()).map((a) => [a, a.set])).filter((a) => Boolean(a));
197class 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 // Anytime `this.schemaObject` is modified, `this[internalCache].attributes` must be set to undefined or null
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 // TODO: lots of `as` statements in the two lines below. We should clean that up.
276 if (typeof object[key] === "object" && object[key] !== null && object[key].schema) {
277 checkAttributeNameDots(object[key].schema /*, key*/);
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) { } // eslint-disable-line no-empty
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 // 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
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 // This function will take in an attribute and value, and returns the default value if it should be applied.
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 // 1 = full match
460 // 0.5 = attributes don't exist
461 // 0 = types don't match
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}
493exports.Schema = Schema;
494Schema.attributeTypes = {
495 "findDynamoDBType": (type) => attributeTypes.find((checkType) => checkType.dynamodbType === type),
496 "findTypeForValue": (...args) => attributeTypes.find((checkType) => checkType.isOfType(...args))
497};
498// TODO: in the two functions below I don't think we should be using as. We should try to clean that up.
499Schema.prototype.getHashKey = function () {
500 return Object.keys(this.schemaObject).find((key) => this.schemaObject[key].hashKey) || Object.keys(this.schemaObject)[0];
501};
502Schema.prototype.getRangeKey = function () {
503 return Object.keys(this.schemaObject).find((key) => this.schemaObject[key].rangeKey);
504};
505// 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.
506Schema.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};
512Schema.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};
531Schema.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};
535Schema.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 // TODO: fix up the two lines below. Using too many `as` statements.
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};
570Schema.prototype.getSettingValue = function (setting) {
571 return this.settings[setting];
572};
573function 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) { } // eslint-disable-line no-empty
585 // TODO: using too many `as` statements in the few lines below. Clean that up.
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}
607Schema.prototype.attributes = function (object) {
608 return attributesAction.call(this, object);
609};
610Schema.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};
633function 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// TODO: using too many `as` statements in the function below. We should clean this up.
645Schema.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//# sourceMappingURL=Schema.js.map
\No newline at end of file