powershell/llcsharp/model/interface.ts (233 lines of code) (raw):
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KnownMediaType, JsonType, getPolymorphicBases } from '@azure-tools/codemodel-v3';
import { Expression, ExpressionOrLiteral, Interface, Namespace, OneOrMoreStatements, Variable, Access, InterfaceProperty, Attribute, StringExpression, LiteralExpression, Property, TypeDeclaration } from '@azure-tools/codegen-csharp';
import { ClientRuntime } from '../clientruntime';
import { Schema } from '../code-model';
import { Schema as NewSchema, Language, ObjectSchema } from '@autorest/codemodel';
import { State } from '../generator';
import { EnhancedTypeDeclaration } from '../schema/extended-type-declaration';
import { ModelClass } from './model-class';
import { TypeContainer } from '@azure-tools/codegen-csharp';
import { DeepPartial } from '@azure-tools/codegen';
import { values } from '@azure-tools/linq';
import { VirtualProperty as NewVirtualProperty, VirtualProperties as NewVirtualProperties, Mutability } from '../../utils/schema';
import { isEnumImplementation, addPSArgumentCompleterAttribute } from '../../cmdlets/class';
import { ArrayOf } from '../exports';
export function addInfoAttribute(targetProperty: Property, pType: TypeDeclaration, isRequired: boolean, isReadOnly: boolean, description: string, serializedName: string) {
let pt = <any>pType;
while (pt.elementType) {
switch (pt.elementType.schema.type) {
case JsonType.Object:
if (pt.elementType.schema.details.csharp.interfaceImplementation) {
pt = {
declaration: pt.elementType.schema.details.csharp.interfaceImplementation.declaration,
schema: pt.elementType.schema,
};
} else {
// arg! it's not done yet. Hope it's not polymorphic itself.
pt = {
declaration: `${pt.elementType.schema.details.csharp.namespace}.${pt.elementType.schema.details.csharp.interfaceName}`,
schema: pt.elementType.schema,
};
}
break;
case JsonType.Array:
pt = pt.elementType;
break;
default:
pt = pt.elementType;
break;
}
}
const ptypes = new Array<string>();
if (pt.schema && pt.schema.details.csharp.byReference) {
ptypes.push(`typeof(${pt.schema.details.csharp.namespace}.${pt.schema.details.csharp.interfaceName}_Reference)`);
// do we need polymorphic types for by-resource ? Don't think so.
} else {
ptypes.push(`typeof(${pt.declaration})`);
if (pt.schema && pt.schema.details.csharp.classImplementation && pt.schema.details.csharp.classImplementation.discriminators) {
ptypes.push(...[...pt.schema.details.csharp.classImplementation.discriminators.values()].map(each => `typeof(${each.modelInterface.fullName})`));
}
}
targetProperty.add(new Attribute(ClientRuntime.InfoAttribute, {
parameters: [
new LiteralExpression(`\nRequired = ${isRequired}`),
new LiteralExpression(`\nReadOnly = ${isReadOnly}`),
new LiteralExpression(`\nDescription = ${new StringExpression(description).value}`),
new LiteralExpression(`\nSerializedName = ${new StringExpression(serializedName).value}`),
new LiteralExpression(`\nPossibleTypes = new [] { ${ptypes.join(',').replace(/\?/g, '').replace(/undefined\./g, '')} }`),
]
}));
}
export function newAddInfoAttribute(targetProperty: Property, pType: TypeDeclaration, isRequired: boolean, isReadOnly: boolean, mutability: Mutability, description: string, serializedName: string) {
let pt = <any>pType;
while (pt.elementType) {
switch (pt.elementType.schema.type) {
case JsonType.Object:
if (pt.elementType.schema.language.csharp.interfaceImplementation) {
pt = {
declaration: pt.elementType.schema.language.csharp.interfaceImplementation.declaration,
schema: pt.elementType.schema,
};
} else {
// arg! it's not done yet. Hope it's not polymorphic itself.
pt = {
declaration: `${pt.elementType.schema.language.csharp.namespace}.${pt.elementType.schema.language.csharp.interfaceName}`,
schema: pt.elementType.schema,
};
}
break;
case JsonType.Array:
pt = pt.elementType;
break;
default:
pt = pt.elementType;
break;
}
}
const ptypes = new Array<string>();
if (pt.schema && pt.schema.language.csharp.byReference) {
ptypes.push(`typeof(${pt.schema.language.csharp.namespace}.${pt.schema.language.csharp.interfaceName}_Reference)`);
// do we need polymorphic types for by-resource ? Don't think so.
} else {
ptypes.push(`typeof(${pt.declaration})`);
if (pt.schema && pt.schema.language.csharp.classImplementation && pt.schema.language.csharp.classImplementation.discriminators) {
ptypes.push(...[...pt.schema.language.csharp.classImplementation.discriminators.values()].map(each => `typeof(${each.modelInterface.fullName})`));
}
}
targetProperty.add(new Attribute(ClientRuntime.InfoAttribute, {
parameters: [
new LiteralExpression(`\nRequired = ${isRequired}`),
new LiteralExpression(`\nReadOnly = ${isReadOnly}`),
new LiteralExpression(`\nRead = ${mutability.read}`),
new LiteralExpression(`\nCreate = ${mutability.create}`),
new LiteralExpression(`\nUpdate = ${mutability.update}`),
new LiteralExpression(`\nDescription = ${new StringExpression(description ?? '').value}`),
new LiteralExpression(`\nSerializedName = ${new StringExpression(serializedName).value}`),
new LiteralExpression(`\nPossibleTypes = new [] { ${ptypes.join(',').replace(/\?/g, '').replace(/undefined\./g, '')} }`),
]
}));
}
export class ModelInterface extends Interface implements EnhancedTypeDeclaration {
get schema(): NewSchema {
return this.classImplementation.schema;
}
get defaultOfType() {
return this.classImplementation.defaultOfType;
}
get convertObjectMethod() {
return this.classImplementation.convertObjectMethod;
}
deserializeFromContainerMember(mediaType: KnownMediaType, container: ExpressionOrLiteral, serializedName: string, defaultValue: Expression): Expression {
return this.classImplementation.deserializeFromContainerMember(mediaType, container, serializedName, defaultValue);
}
deserializeFromNode(mediaType: KnownMediaType, node: ExpressionOrLiteral, defaultValue: Expression): Expression {
return this.classImplementation.deserializeFromNode(mediaType, node, defaultValue);
}
/** emits an expression to deserialize content from a string */
deserializeFromString(mediaType: KnownMediaType, content: ExpressionOrLiteral, defaultValue: Expression): Expression | undefined {
return this.classImplementation.deserializeFromString(mediaType, content, defaultValue);
}
/** emits an expression to deserialize content from a content/response */
deserializeFromResponse(mediaType: KnownMediaType, content: ExpressionOrLiteral, defaultValue: Expression): Expression | undefined {
return this.classImplementation.deserializeFromResponse(mediaType, content, defaultValue);
}
/** emits an expression serialize this to a HttpContent */
serializeToContent(mediaType: KnownMediaType, value: ExpressionOrLiteral, mode: Expression): Expression {
return this.classImplementation.serializeToContent(mediaType, value, mode);
}
serializeToNode(mediaType: KnownMediaType, value: ExpressionOrLiteral, serializedName: string, mode: Expression): Expression {
return this.classImplementation.serializeToNode(mediaType, value, serializedName, mode);
}
serializeToContainerMember(mediaType: KnownMediaType, value: ExpressionOrLiteral, container: Variable, serializedName: string, mode: Expression): OneOrMoreStatements {
return this.classImplementation.serializeToContainerMember(mediaType, value, container, serializedName, mode);
}
get isXmlAttribute(): boolean {
return this.classImplementation.isXmlAttribute;
}
public isNullable = true;
get isRequired(): boolean {
return this.classImplementation.isRequired;
}
public validatePresence(eventListener: Variable, property: Variable): OneOrMoreStatements {
return this.classImplementation.validatePresence(eventListener, property);
}
public validateValue(eventListener: Variable, property: Variable): OneOrMoreStatements {
return this.classImplementation.validateValue(eventListener, property);
}
get hasHeaderProperties(): boolean {
// disabled
// return this.classImplementation.hasHeaderProperties;
return false;
}
constructor(parent: TypeContainer, interfaceName: string, public classImplementation: ModelClass, public state: State, objectInitializer?: DeepPartial<ModelInterface>) {
super(parent, interfaceName);
this.partial = true;
this.apply(objectInitializer);
}
get isInternal(): boolean {
return this.accessModifier === Access.Internal;
}
init() {
(<any>this).init = () => { }; // only allow a single init!
this.schema.language.csharp = this.schema.language.csharp || new Language();
const implData = (this.schema.language.csharp = this.schema.language.csharp || {});
//implData.interfaceImplementation = this;
this.description = `${this.schema.language.csharp.description}`;
const virtualProperties: NewVirtualProperties = this.schema.language.csharp.virtualProperties || {
owned: [],
inherited: [],
inlined: []
};
if (this.schema.language.csharp.virtualProperties) {
for (const virtualProperty of values(virtualProperties.owned)) {
if (virtualProperty.private && !this.isInternal) {
continue;
}
const modelProperty = virtualProperty.property;
const internalSet = !!(!this.isInternal && (modelProperty.readOnly || !!virtualProperty.readOnly || (<any>modelProperty.language.csharp).constantValue));
const isRequired = !!(virtualProperty.required && virtualProperty.read && virtualProperty.create && virtualProperty.update);
const mutability = { read: !!virtualProperty.read, update: !!virtualProperty.update, create: !!virtualProperty.create };
const pType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(<NewSchema>modelProperty.schema, isRequired, this.state.path('schema'));
const p = this.add(new InterfaceProperty(virtualProperty.name, pType, {
description: modelProperty.language.default.description,
setAccess: internalSet ? Access.Internal : Access.Public
}));
this.addInfoAttribute(p, pType, isRequired, internalSet, mutability, modelProperty.language.default.description, modelProperty.serializedName);
if (isEnumImplementation(modelProperty.schema)) {
addPSArgumentCompleterAttribute(p, modelProperty.schema);
} else if (pType instanceof ArrayOf && isEnumImplementation((<any>modelProperty.schema).elementType)) {
addPSArgumentCompleterAttribute(p, (<any>modelProperty.schema).elementType);
}
if (!this.isInternal && (<any>modelProperty.language.csharp).constantValue !== undefined) {
p.setAccess = Access.Internal;
}
}
for (const virtualProperty of values(virtualProperties.inlined)) {
// don't publicly expose the 'private' properties.
if (virtualProperty.private && !this.isInternal) {
continue;
}
const modelProperty = virtualProperty.property;
const isRequired = !!(virtualProperty.required && virtualProperty.read && virtualProperty.create && virtualProperty.update);
const mutability = { read: !!virtualProperty.read, update: !!virtualProperty.update, create: !!virtualProperty.create };
const pType = this.state.project.modelsNamespace.NewResolveTypeDeclaration(<NewSchema>modelProperty.schema, isRequired, this.state.path('schema'));
const internalSet = !!(!this.isInternal && (modelProperty.readOnly || !!virtualProperty.readOnly || (<any>modelProperty.language.csharp).constantValue));
const p = this.add(new InterfaceProperty(virtualProperty.name, pType, {
description: modelProperty.language.default.description,
setAccess: internalSet ? Access.Internal : Access.Public
}));
this.addInfoAttribute(p, pType, isRequired, internalSet, mutability, modelProperty.language.default.description, modelProperty.serializedName);
if (isEnumImplementation(modelProperty.schema)) {
addPSArgumentCompleterAttribute(p, modelProperty.schema);
} else if (pType instanceof ArrayOf && isEnumImplementation((<any>modelProperty.schema).elementType)) {
addPSArgumentCompleterAttribute(p, (<any>modelProperty.schema).elementType);
}
}
}
if (!this.isInternal) {
// mark it as json serializable
if (!this.schema.language.csharp.isHeaderModel) {
if (this.state.project.jsonSerialization) {
this.interfaces.push(ClientRuntime.IJsonSerializable);
}
if (this.state.project.xmlSerialization) {
this.interfaces.push(ClientRuntime.IXmlSerializable);
}
}
}
return this;
}
addInfoAttribute(p: Property, pType: TypeDeclaration, isRequired: boolean, internalSet: boolean, mutability: Mutability, description: string, serializedName: string) {
if (!this.isInternal) {
return newAddInfoAttribute(p, pType, isRequired, internalSet, mutability, description, serializedName);
}
}
}