private transformType()

in source/nodejs/ac-typed-schema/src/typed-schema.ts [151:357]


	private transformType(type: SchemaType) {
		try {
			if (type instanceof SchemaEnum) {
				var enumType = type as SchemaEnum;
				var transformed: any = { ...type.original };
				delete transformed.type;
				delete transformed.values;
				delete transformed.classType;
				delete transformed.$schema;
				delete transformed.extends;

				var enums: string[] = [];
				enumType.values.forEach(val => {
					enums.push(val.value);
				});

				// If only allows strict enums but case-insensitive...
				if (!this._enforceEnumCaseSensitivity && !this._allowCustomEnums) {
					var enumRegexs: string[] = [];
					enums.forEach(enumVal => {
						enumRegexs.push("(" + this.toCaseInsensitiveRegex(enumVal) + ")");
					});
					var regex = "^" + enumRegexs.join("|") + "$";

					transformed.anyOf = [
						{
							"enum": enums
						},
						{
							"pattern": regex
						}
					];
				} else if (!this._allowCustomEnums) {
					// Else strict and sensitive
					transformed.enum = enums;
				} else {
					// Else allows any enums, so no need for the regex
					transformed.anyOf = [
						{
							"enum": enums
						},
						{
							"type": "string"
						}
					];
				}

				return transformed;
			} else if (!(type instanceof SchemaClass)) {
				throw new Error("Unknown class type " + type);
			}

			var transformed: any = { ...type.original };
			transformed.type = "object";

			if (!this._allowAdditionalProperties) {
				transformed.additionalProperties = false;
			}

			delete transformed.isAbstract;
			delete transformed.classType;
			delete transformed.$schema;
			delete transformed.extends;
			delete transformed.shorthand;

			transformed.properties = {};
			var overridesProperties = false;
			var properties = new Map<string, SchemaProperty>();

			if (type.extends.length > 0) {

				type.properties.forEach(prop => {
					if (prop.override) {
						// If property exists in base classes
						if (type.getAllExtendedProperties().indexOf(prop.name) !== -1) {
							// Note that JSON schema doesn't support the concept of overriding properties, so instead
							// we need to copy all the properties and NOT inherit.
							overridesProperties = true;
						} else {
							console.warn(`${type.type}.${prop.name} has override set to true, but it doesn't override any base class properties.`);
						}
					} else {
						// If property exists in base classes
						if (type.getAllExtendedProperties().indexOf(prop.name) !== -1) {
							console.warn(`Ignoring ${type.type}.${prop.name} as there's already this property specified in a base class. If you want to keep this property, add "override": "true".`);
						}
					}
				});
			}

			if (overridesProperties) {
				type.getAllProperties().forEach((propVal, key) => {
					properties.set(key, propVal);
				});
			} else {
				properties = type.properties;
			}

			if (properties.size > 0) {
				properties.forEach((propVal, key) => {

					var transformedPropVal: any;
					try {
						transformedPropVal = this.transformPropertyValue(propVal);
					} catch (err) {
						throw new Error("Failed transforming property " + key + "\n\n" + err.stack);
					}

					if (transformedPropVal.required) {
						delete transformedPropVal.required;
						if (!transformed.required) {
							transformed.required = [];
						}
						transformed.required.push(key);
					}

					transformed.properties[key] = transformedPropVal;
				});
			}

			if (!type.isAbstract) {
				// If it's not abstract, we add the type property
				// Note that we don't require it though, it's optional
				var newProperties: any = {};
				newProperties[this._typePropertyName] = {
					"enum": [type.type],
					"description": "Must be `" + type.type + "`"
				};
				transformed.properties = {
					...newProperties,
					...transformed.properties
				};
			}

			if (type.extends.length > 0) {

				if (!overridesProperties) {
					// Have to add placeholders for all the properties
					type.getAllExtendedProperties().forEach(extendedPropKey => {

						// If there's an existing property defined, skip
						var existingProp = type.properties.get(extendedPropKey);
						if (existingProp !== undefined) {
							if (!existingProp.override) {
								console.warn(`Overriding extended property ${extendedPropKey} on type ${type.type}. If this was intentional, add "override": "true" to this property to prevent this warning from appearing.`);
							}
							return;
						}

						transformed.properties[extendedPropKey] = {};
					});

					transformed.allOf = [];
					type.extends.forEach(extended => {
						if (extended.getAllProperties().size > 0) {
							transformed.allOf.push({
								$ref: "#/definitions/Extendable." + extended.type
							});
						}
					});
					if (transformed.allOf.length == 0) {
						delete transformed.allOf;
					}
				}

				// Keep track of implementations
				type.getAllExtended().forEach(extended => {
					if (!this._implementationsOf[extended.type]) {
						this._implementationsOf[extended.type] = [];

						// If extending type isn't abstract, add that as an implementation
						if (!extended.isAbstract) {
							this._implementationsOf[extended.type].push(extended.type);
						}
					}

					if (!type.isAbstract) {
						this._implementationsOf[extended.type].push(type.type);
					}
				});

				delete transformed.extends;
			}

			if (type.shorthand) {
				transformed.anyOf = [
					{
						...transformed.properties[type.shorthand.name]
					},
					{
						type: transformed.type,
						properties: transformed.properties,
						required: transformed.required,
						additionalProperties: false
					}
				];
				delete transformed.type;
				delete transformed.properties;
				delete transformed.required;
				delete transformed.additionalProperties;
			}

			return transformed;
		} catch (err) {
			throw "Failed transforming type " + type.type + "\n\n" + err.stack;
		}
	}