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;
}