function getSchemaForModel()

in packages/typespec-powershell/src/utils/modelUtils.ts [720:1029]


function getSchemaForModel(
  dpgContext: SdkContext,
  model: Model,
  options?: GetSchemaOptions
) {
  const {
    usage,
    needRef,
    isRequestBody,
    mediaTypes: contentTypes
  } = options ?? {};
  if (isArrayModelType(dpgContext.program, model)) {
    return getSchemaForArrayModel(dpgContext, model, options);
  }

  const program = dpgContext.program;
  const overridedModelName =
    getFriendlyName(program, model) ?? getWireName(dpgContext, model);
  const fullNamespaceName =
    overridedModelName ??
    getModelNamespaceName(dpgContext, model.namespace!)
      .map((nsName) => {
        return pascalCase(deconstruct(nsName));
      })
      .join("") + model.name;
  // ToDo: need to confirm which one should be
  // let name = dpgContext.rlcOptions?.enableModelNamespace
  // ? fullNamespaceName
  // : model.name;
  let name = model.name;
  if (
    !overridedModelName &&
    model.templateMapper &&
    model.templateMapper.args &&
    model.templateMapper.args.length > 0 &&
    getPagedResult(program, model)
  ) {
    const templateTypes = model.templateMapper.args.filter((it) =>
      isType(it)
    ) as Type[];
    name =
      templateTypes
        .map((it) => {
          switch (it.kind) {
            case "Model":
              return it.name;
            case "String":
              return it.value;
            default:
              return "";
          }
        })
        .join("") + "List";
  }

  const modelSchema = new ObjectSchema(overridedModelName ?? name, getDoc(program, model) || "");
  // normalized the output name
  // modelSchema.name = normalizeName(
  //   modelSchema.name,
  //   NameType.Interface,
  //   true /** shouldGuard */
  // );
  // by xiaogang, skip ArmResourceBase
  if (model.baseModel && model.baseModel.name !== "ArmResourceBase") {
    modelSchema.parents = {
      all: [
        getSchemaForType(dpgContext, model.baseModel, {
          usage,
          needRef: true
        })
      ],
      immediate: [
        getSchemaForType(dpgContext, model.baseModel, {
          usage,
          needRef: true
        })
      ]
    };
  }
  modelSchema.language.default.name = pascalCase(deconstruct(modelSchema.language.default.name));
  if (isRecordModelType(program, model)) {
    return getSchemaForRecordModel(dpgContext, model, { usage });
  }
  // ToDo: by xiaogang
  // modelSchema.typeName = modelSchema.name;
  // if (usage && usage.includes(SchemaContext.Output)) {
  //   modelSchema.outputTypeName = modelSchema.name + "Output";
  // }

  // if (isAzureCoreErrorType(model)) {
  //   modelSchema.fromCore = true;
  // }

  if (getPagedResult(program, model)) {
    const paged = extractPagedMetadataNested(program, model);
    if (paged && paged.itemsProperty) {
      const items = paged.itemsProperty as unknown as Model;
      if (items && items.templateMapper && items.templateMapper.args) {
        const templateTypes = items.templateMapper.args.filter((it) =>
          isType(it)
        ) as Type[];
        const templateName = templateTypes
          ?.map((it) => {
            switch (it.kind) {
              case "Model":
                return it.name;
              case "String":
                return it.value;
              default:
                return "";
            }
          })
          .join("");
        // ToDo by xiaogang
        // if (
        //   paged.itemsProperty.name === "value" &&
        //   paged.nextLinkProperty?.name === "nextLink"
        // ) {
        //   modelSchema.alias = `Paged<${templateName}>`;
        //   modelSchema.outputAlias = `Paged<${templateName}Output>`;
        // }
      }
    }
  }
  modelSchema.properties = [];

  // getSchemaOrRef on all children to push them into components.schemas
  const discriminator = getDiscriminator(program, model);
  // should respect needRef for derived models unless there's a discriminator in base model
  const derivedModels = model.derivedModels.filter((dm) => {
    return includeDerivedModel(dm, discriminator ? false : needRef);
  });

  for (const child of derivedModels) {
    // Delay schema generation of those models to avoiding circular reference
    delayedModelSet.add(child);
    // const childSchema = getSchemaForType(dpgContext, child, {
    //   usage,
    //   needRef: true
    // });
    // for (const [name, prop] of child.properties) {
    //   if (name === discriminator?.propertyName) {
    //     const propSchema = getSchemaForType(dpgContext, prop.type, {
    //       usage,
    //       needRef: !isAnonymousModelType(prop.type),
    //       relevantProperty: prop
    //     });
    //     childSchema.discriminatorValue = propSchema.type.replace(/"/g, "");
    //     break;
    //   }
    // }
    // modelSchema.children?.all?.push(childSchema);
    // modelSchema.children?.immediate?.push(childSchema);
  }

  // Enable option `isPolyParent` and discriminator only when it has valid children
  if (
    discriminator &&
    derivedModels &&
    derivedModels.length > 0
  ) {
    if (!validateDiscriminator(program, discriminator, derivedModels)) {
      // appropriate diagnostic is generated in the validate function
      return {};
    }

    const { propertyName } = discriminator;
    modelSchema.discriminatorValue = propertyName;
    // ToDo: need to confirm whether still need this.
    // modelSchema.isPolyParent = true;
  }

  // applyExternalDocs(model, modelSchema);
  // by xiaogang, skip needRef
  // if (needRef) {
  //   return modelSchema;
  // }
  // by xiagang, seems no need to inherit a dictionary
  if (isRecordModelType(program, model)) {
    modelSchema.parents = {
      all: [getSchemaForRecordModel(dpgContext, model, { usage })],
      immediate: [getSchemaForRecordModel(dpgContext, model, { usage })]
    };
  }
  for (const [propName, prop] of model.properties) {
    const encodedName = resolveEncodedName(program, prop, "application/json");
    const restApiName = getWireName(dpgContext, prop);
    const name = encodedName ?? restApiName ?? propName;
    if (!isSchemaProperty(program, prop)) {
      continue;
    }

    const propSchema = getSchemaForType(dpgContext, prop, {
      usage,
      needRef: isAnonymousModelType(prop.type) ? false : true,
      relevantProperty: prop,
      isParentRequestBody: isRequestBody,
      isRequestBody: false,
      mediaTypes: contentTypes
    });

    let propertyDescription;
    if (propSchema) {
      propertyDescription = getFormattedPropertyDoc(
        program,
        prop,
        propSchema
      );
      propSchema.usage = usage;
      // Use the description from ModelProperty not derived from Model Type
      (<Schema>propSchema).language = (<Schema>propSchema).language || {};
      (<Schema>propSchema).language.default = (<Schema>propSchema).language.default || {};
      (<Schema>propSchema).language.default.description = (<Schema>propSchema).language.default.description || propertyDescription || "";
      (<Schema>propSchema).language.default.name = (<Schema>propSchema).language.default.name || name;
    }
    // ToDo: need to confirm there is no duplicated properties.
    const property = new Property(name, getDoc(program, prop) || "", propSchema || new ObjectSchema(name, ""));
    if (!prop.optional) {
      property.required = true;
    }
    const vis = getSdkVisibility(program, prop);
    if (vis) {
      if (vis.includes(Visibility.Read)) {
        if (vis.length === 1) {
          property.readOnly = true;
        }
      }
      if (vis.length > 0) {
        property.extensions = property.extensions || {};
        property.extensions['x-ms-mutability'] = vis.map(v => Visibility[v].toLowerCase());
      }
    }
    if (propSchema === undefined && prop.type.kind === "Model") {
      property.extensions = property.extensions || {};
      property.extensions['circle-ref'] = pascalCase(deconstruct(prop.type.name));
    }
    let isDiscriminatorInChild = false;
    if (modelSchema.parents && modelSchema.parents.all) {
      modelSchema.parents.all.forEach((parent) => {
        if (parent.type === "object" && (<ObjectSchema>parent).discriminator?.property.serializedName === propName) {
          isDiscriminatorInChild = true;
        }
      });
    }
    if (!isDiscriminatorInChild) {
      modelSchema.properties.push(property);
    } else {
      // If discriminator value is union variant, it will be constant type
      // Otherwise, it will be sealed choice type
      modelSchema.discriminatorValue = propSchema.type === 'constant' ? (<ConstantSchema>propSchema).value.value : (<SealedChoiceSchema>propSchema).choices[0].value.toString();
    }
    if (discriminator && propName === discriminator.propertyName) {
      property.isDiscriminator = true;
      modelSchema.discriminator = new M4Discriminator(property);
    }
    // if this property is a discriminator property, remove it to keep autorest validation happy
    //const { propertyName } = getDiscriminator(program, model) || {};
    // ToDo: by xiaoang, skip polymorphism for the time being.
    // if (
    //   propertyName &&
    //   name === `"${propertyName}"` &&
    //   modelSchema.discriminator
    // ) {
    //   modelSchema.discriminator.type = propSchema.typeName ?? propSchema.type;
    //   continue;
    // }

    // Apply decorators on the property to the type's schema
    // const newPropSchema = applyIntrinsicDecorators(program, prop, propSchema);
    // if (newPropSchema === undefined) {
    //   continue;
    // }
    // Use the description from ModelProperty not devired from Model Type
    // newPropSchema.description = propertyDescription;

    // Should the property be marked as readOnly?
    // const vis = getVisibility(program, prop);
    // if (vis && vis.includes("read")) {
    //   //const mutability = [];
    //   if (vis.includes("read")) {
    //     if (vis.length > 1) {
    //       // ToDo: by xiaogang, skip it since it is not required by autorest.powershell
    //       //mutability.push(SchemaContext.Output);
    //     } else {
    //       newPropSchema["readOnly"] = true;
    //     }
    //   }
    //   if (vis.includes("write") || vis.includes("create")) {
    //     // ToDo: by xiaogang
    //     //mutability.push(SchemaContext.Input);
    //   }

    //   // if (mutability.length > 0) {
    //   //   newPropSchema["usage"] = mutability;
    //   // }
    // }
    // ToDo: skip for the time being, we need to use newPropSchema finally.
    // modelSchema.properties = modelSchema.properties?.filter(p => p.language.default.name != name);
    // modelSchema.properties.push(newPropSchema);
  }
  // Add discriminator property if it is not already present
  if (discriminator && !modelSchema.discriminator) {
    const discriminatorProperty = new Property(discriminator.propertyName, `Discriminator property for ${modelSchema.language.default.name}.`, new StringSchema("string", ""));
    discriminatorProperty.isDiscriminator = true;
    discriminatorProperty.required = true;
    modelSchema.discriminator = new M4Discriminator(discriminatorProperty);
    modelSchema.properties.push(discriminatorProperty);
  }
  return modelSchema;
}