private processSchemaImpl()

in packages/extensions/modelerfour/src/modeler/modelerfour.ts [1090:1340]


  private processSchemaImpl(schema: OpenAPI.Schema, name: string): Schema {
    if (this.trap.has(schema)) {
      throw new Error(
        `RECURSING!  Saw schema ${schema.title || schema["x-ms-metadata"]?.name || name} more than once.`,
      );
    }
    this.trap.add(schema);

    // handle enums differently early
    if (isSchemaAnEnum(schema, this.input)) {
      return this.processChoiceSchema(name, schema);
    }

    if (isSchemaBinary(schema)) {
      // handle inconsistency in file format handling.
      this.session.warning(
        `'The schema ${schema?.["x-ms-metadata"]?.name || name} with 'type: ${schema.type}', format: ${
          schema.format
        }' will be treated as a binary blob for binary media types.`,
        ["Modeler", "Superflous type information"],
        schema,
      );
      schema.type = OpenAPI.JsonType.String;
      schema.format = StringFormat.Binary;
    }

    // if they haven't set the schema.type then we're going to have to guess what
    // they meant to do.
    switch (schema.type) {
      case undefined:
      case null:
        if (schema.properties) {
          // if the model has properties, then we're going to assume they meant to say JsonType.object
          // but we're going to warn them anyway.

          this.session.warning(
            `The schema '${
              schema?.["x-ms-metadata"]?.name || name
            }' with an undefined type and declared properties is a bit ambiguous. This has been auto-corrected to 'type:object'`,
            ["Modeler", "MissingType"],
            schema,
          );
          schema.type = OpenAPI.JsonType.Object;
          break;
        }

        if (schema.additionalProperties) {
          // this looks like it's going to be a dictionary
          // we'll mark it as object and let the processObjectSchema sort it out.
          this.session.warning(
            `The schema '${
              schema?.["x-ms-metadata"]?.name || name
            }' with an undefined type and additionalProperties is a bit ambiguous. This has been auto-corrected to 'type:object'`,
            ["Modeler"],
            schema,
          );
          schema.type = OpenAPI.JsonType.Object;
          break;
        }

        if (schema.allOf || schema.anyOf || schema.oneOf) {
          // The schema does not have properties or additionalProperties, but it does have allOf/anyOf/oneOf.
          // The prior logic auto-corrected this to type: object, but that's not always appropriate.
          // Check the child schemas and bypass the auto-correct if any are clearly not type: object.

          // Return true if the schema has an explicit type that is not type: object.
          const notTypeObject = (e: Refable<OpenAPI.Schema>): boolean => {
            const s = this.resolve(e).instance;
            return !!s.type && s.type !== OpenAPI.JsonType.Object;
          };
          let bypassAutoCorrect = schema.allOf && schema.allOf.some(notTypeObject);
          bypassAutoCorrect ||= schema.anyOf && schema.anyOf.some(notTypeObject);
          bypassAutoCorrect ||= schema.oneOf && schema.oneOf.some(notTypeObject);
          if (!bypassAutoCorrect) {
            this.session.warning(
              `The schema '${
                schema?.["x-ms-metadata"]?.name || name
              }' with an undefined type and 'allOf'/'anyOf'/'oneOf' is a bit ambiguous. This has been auto-corrected to 'type:object'`,
              ["Modeler", "MissingType"],
              schema,
            );
            schema.type = OpenAPI.JsonType.Object;
            break;
          }
        }

        {
          // no type info at all!?
          // const err = `The schema '${name}' has no type or format information whatsoever. ${this.location(schema)}`;
          this.session.warning(
            `The schema '${
              schema?.["x-ms-metadata"]?.name || name
            }' has no type or format information whatsoever. ${this.location(schema)}`,
            ["Modeler", "MissingType"],
            schema,
          );
          // throw Error(err);
          return this.anySchema;
        }
    }

    // ok, figure out what kind of schema this is.
    switch (schema.type) {
      case JsonType.Array:
        switch (schema.format) {
          case undefined:
            return this.processArraySchema(name, schema);
          default:
            this.session.error(
              `Array schema '${schema?.["x-ms-metadata"]?.name || name}' with unknown format: '${
                schema.format
              } ' is not valid`,
              ["Modeler"],
              schema,
            );
        }
        break;

      case JsonType.Boolean:
        switch (schema.format) {
          case undefined:
            return this.processBooleanSchema(name, schema);
          default:
            this.session.error(
              `Boolean schema '${name}' with unknown format: '${schema.format}' is not valid`,
              ["Modeler"],
              schema,
            );
        }
        break;

      case JsonType.Integer:
        schema.format = schema.format ? schema.format.toLowerCase() : schema.format;
        switch (schema.format) {
          case IntegerFormat.UnixTime:
            return this.processUnixTimeSchema(name, schema);

          case IntegerFormat.Int64:
          case IntegerFormat.Int32:
          case IntegerFormat.None:
          case undefined:
            return this.processIntegerSchema(name, schema);

          case NumberFormat.Double:
          case NumberFormat.Float:
          case NumberFormat.Decimal:
            return this.processNumberSchema(name, schema);

          default:
            // According to the OpenAPI v3 spec, an unexpected format should be ignored,
            // so treat this as an `integer` with no format.
            this.session.warning(
              `Integer schema '${name}' with unknown format: '${schema.format}' is not valid.  Treating it as 'int32'.`,
              ["Modeler", "UnknownFormatType"],
              schema,
            );
            return this.processIntegerSchema(name, schema);
        }

      case JsonType.Number:
        switch (schema.format) {
          case undefined:
          case NumberFormat.None:
          case NumberFormat.Double:
          case NumberFormat.Float:
          case NumberFormat.Decimal:
            return this.processNumberSchema(name, schema);

          case IntegerFormat.Int64:
          case IntegerFormat.Int32:
            return this.processIntegerSchema(name, schema);

          default:
            this.session.warning(
              `Number schema '${name}' with unknown format: '${schema.format}'. Will ignore.`,
              ["Modeler", "UnknownFormatType"],
              schema,
            );
            return this.processIntegerSchema(name, schema);
        }
        break;

      case JsonType.Object:
        return this.processObjectSchema(name, schema);

      case JsonType.String:
        switch (schema.format) {
          // member should be byte array
          // on wire format should be base64url
          case StringFormat.Base64Url:
          case StringFormat.Byte:
          case StringFormat.Certificate:
            return this.processByteArraySchema(name, schema);

          case StringFormat.Binary:
            // represent as a binary
            // wire format is stream of bytes
            // This is actually a different kind of response or request
            // and should not be treated as a trivial 'type'
            return this.processBinarySchema(name, schema);

          case StringFormat.Char:
            // a single character
            return this.processCharacterSchema(name, schema);

          case StringFormat.Date:
            return this.processDateSchema(name, schema);

          case StringFormat.Time:
            return this.processTimeSchema(name, schema);

          case StringFormat.DateTime:
          case StringFormat.DateTimeRfc1123:
            return this.processDateTimeSchema(name, schema);

          case StringFormat.Duration:
            return this.processDurationSchema(name, schema);

          case StringFormat.Uuid:
            return this.processUuidSchema(name, schema);

          case StringFormat.Url:
          case StringFormat.Uri:
            return this.processUriSchema(name, schema);
          case StringFormat.ArmId:
            return this.processArmId(name, schema);

          case StringFormat.Password:
            return this.processCredentialSchema(name, schema);

          case StringFormat.OData:
            return this.processOdataSchema(name, schema);

          case StringFormat.None:
          case undefined:
          case null:
            return this.processStringSchema(name, schema);

          default:
            // console.error(`String schema '${name}' with unknown format: '${schema.format}' is treated as simple string.`);
            return this.processStringSchema(name, schema);

          //              this.session.error(`String schema '${name}' with unknown format: '${schema.format}' is not valid`, ['Modeler'], schema);
        }
    }
    this.session.error(
      `The model ${name} does not have a recognized schema type '${schema.type}' ${JSON.stringify(schema)} `,
      ["Modeler", "UnknownSchemaType"],
    );
    throw new Error(`Unrecognized schema type:'${schema.type}' / format: ${schema.format} ${JSON.stringify(schema)} `);
  }