text: "use strict";
  // Copyright IBM Corp. 2018. All Rights Reserved.
  // Node module: @loopback/repository-json-schema
  // This file is licensed under the MIT License.
  // License text available at https://opensource.org/licenses/MIT
  Object.defineProperty(exports, "__esModule", { value: true });
  const context_1 = require("@loopback/context");
  const repository_1 = require("@loopback/repository");
  const keys_1 = require("./keys");
  /**
   * Gets the JSON Schema of a TypeScript model/class by seeing if one exists
   * in a cache. If not, one is generated and then cached.
   * @param ctor Contructor of class to get JSON Schema from
   */
  function getJsonSchema(ctor) {
      // NOTE(shimks) currently impossible to dynamically update
      const jsonSchema = context_1.MetadataInspector.getClassMetadata(keys_1.JSON_SCHEMA_KEY, ctor);
      if (jsonSchema) {
          return jsonSchema;
      }
      else {
          const newSchema = modelToJsonSchema(ctor);
          context_1.MetadataInspector.defineMetadata(keys_1.JSON_SCHEMA_KEY.key, newSchema, ctor);
          return newSchema;
      }
  }
  exports.getJsonSchema = getJsonSchema;
  /**
   * Gets the wrapper function of primitives string, number, and boolean
   * @param type Name of type
   */
  function stringTypeToWrapper(type) {
      if (typeof type === 'function') {
          return type;
      }
      type = type.toLowerCase();
      let wrapper;
      switch (type) {
          case 'number': {
              wrapper = Number;
              break;
          }
          case 'string': {
              wrapper = String;
              break;
          }
          case 'boolean': {
              wrapper = Boolean;
              break;
          }
          case 'array': {
              wrapper = Array;
              break;
          }
          case 'object': {
              wrapper = Object;
              break;
          }
          case 'date': {
              wrapper = Date;
              break;
          }
          case 'buffer': {
              wrapper = Buffer;
              break;
          }
          default: {
              throw new Error('Unsupported type: ' + type);
          }
      }
      return wrapper;
  }
  exports.stringTypeToWrapper = stringTypeToWrapper;
  /**
   * Determines whether a given string or constructor is array type or not
   * @param type Type as string or wrapper
   */
  function isArrayType(type) {
      return type === Array || type === 'array';
  }
  exports.isArrayType = isArrayType;
  /**
   * Converts property metadata into a JSON property definition
   * @param meta
   */
  function metaToJsonProperty(meta) {
      // tslint:disable-next-line:no-any
      const propDef = {};
      let result;
      let propertyType = meta.type;
      if (isArrayType(propertyType) && meta.itemType) {
          if (Array.isArray(meta.itemType)) {
              throw new Error('itemType as an array is not supported');
          }
          result = { type: 'array', items: propDef };
          propertyType = meta.itemType;
      }
      else {
          result = propDef;
      }
      const wrappedType = stringTypeToWrapper(propertyType);
      const resolvedType = repository_1.resolveType(wrappedType);
      if (resolvedType === Date) {
          Object.assign(propDef, {
              type: 'string',
              format: 'date-time',
          });
      }
      else if (repository_1.isBuiltinType(resolvedType)) {
          Object.assign(propDef, {
              type: resolvedType.name.toLowerCase(),
          });
      }
      else {
          Object.assign(propDef, { $ref: `#/definitions/${resolvedType.name}` });
      }
      if (meta.description) {
          Object.assign(propDef, {
              description: meta.description,
          });
      }
      return result;
  }
  exports.metaToJsonProperty = metaToJsonProperty;
  // NOTE(shimks) no metadata for: union, optional, nested array, any, enum,
  // string literal, anonymous types, and inherited properties
  /**
   * Converts a TypeScript class into a JSON Schema using TypeScript's
   * reflection API
   * @param ctor Constructor of class to convert from
   */
  function modelToJsonSchema(ctor) {
      const meta = repository_1.ModelMetadataHelper.getModelMetadata(ctor);
      const result = {};
      // returns an empty object if metadata is an empty object
      if (!(meta instanceof repository_1.ModelDefinition)) {
          return {};
      }
      result.title = meta.title || ctor.name;
      if (meta.description) {
          result.description = meta.description;
      }
      for (const p in meta.properties) {
          if (!meta.properties[p].type) {
              continue;
          }
          result.properties = result.properties || {};
          result.properties[p] = result.properties[p] || {};
          const metaProperty = Object.assign({}, meta.properties[p]);
          // populating "properties" key
          result.properties[p] = metaToJsonProperty(metaProperty);
          // handling 'required' metadata
          if (metaProperty.required) {
              result.required = result.required || [];
              result.required.push(p);
          }
          // populating JSON Schema 'definitions'
          // shimks: ugly type casting; this should be replaced by logic to throw
          // error if itemType/type is not a string or a function
          const resolvedType = repository_1.resolveType(metaProperty.type);
          const referenceType = isArrayType(resolvedType)
              ? // shimks: ugly type casting; this should be replaced by logic to throw
                  // error if itemType/type is not a string or a function
                  repository_1.resolveType(metaProperty.itemType)
              : resolvedType;
          if (typeof referenceType !== 'function' || repository_1.isBuiltinType(referenceType)) {
              continue;
          }
          const propSchema = getJsonSchema(referenceType);
          if (propSchema && Object.keys(propSchema).length > 0) {
              result.definitions = result.definitions || {};
              // delete nested definition
              if (propSchema.definitions) {
                  for (const key in propSchema.definitions) {
                      result.definitions[key] = propSchema.definitions[key];
                  }
                  delete propSchema.definitions;
              }
              result.definitions[referenceType.name] = propSchema;
          }
      }
      return result;
  }
  exports.modelToJsonSchema = modelToJsonSchema;
  //# sourceMappingURL=build-schema.js.map