Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 64 additions & 57 deletions powershell/cmdlets/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Schema as NewSchema, SchemaType, ArraySchema, SchemaResponse, HttpParameter, ObjectSchema } from '@azure-tools/codemodel';
import { Schema as NewSchema, SchemaType, ArraySchema, SchemaResponse, HttpParameter, ObjectSchema, DictionarySchema } from '@azure-tools/codemodel';
import { command, getAllProperties, JsonType, http, getAllPublicVirtualProperties, getVirtualPropertyFromPropertyName, ParameterLocation, getAllVirtualProperties, VirtualParameter, VirtualProperty } from '@azure-tools/codemodel-v3';
import { CommandOperation, VirtualParameter as NewVirtualParameter } from '../utils/command-operation';
import { getAllProperties as NewGetAllProperties, getAllPublicVirtualProperties as NewGetAllPublicVirtualProperties, getVirtualPropertyFromPropertyName as NewGetVirtualPropertyFromPropertyName, VirtualProperty as NewVirtualProperty } from '../utils/schema';
Expand Down Expand Up @@ -2412,21 +2412,23 @@ export class NewCmdletClass extends Class {
// }
cmdletParameter.add(new Attribute(AllowEmptyCollectionAttribute));
}
//skip-for-time-being
// if (vSchema.additionalProperties) {
// // we have to figure out if this is a standalone dictionary or a hybrid object/dictionary.
// // if it's a hybrid, we have to create another parameter like -<XXX>AdditionalProperties and have that dump the contents into the dictionary
// // if it's a standalone dictionary, we can just use hashtable instead
// if (length(vSchema.properties) === 0) {
// // it's a pure dictionary
// // add an attribute for changing the exported type.
// cmdletParameter.add(new Attribute(ExportAsAttribute, { parameters: [`typeof(${System.Collections.Hashtable})`] }));
// } else {
// // it's a hybrid. We need to add an additional property that puts its items into the target container

// }

// }
const dictSchema = vSchema.type === SchemaType.Dictionary ? vSchema :
vSchema.type === SchemaType.Object ? (<ObjectSchema>vSchema).parents?.immediate?.find((s) => s.type === SchemaType.Dictionary) :
undefined;
if (dictSchema) {
// we have to figure out if this is a standalone dictionary or a hybrid object/dictionary.
// if it's a hybrid, we have to create another parameter like -<XXX>AdditionalProperties and have that dump the contents into the dictionary
// if it's a standalone dictionary, we can just use hashtable instead
if (length((<ObjectSchema>vSchema).properties) === 0) {
// it's a pure dictionary
// add an attribute for changing the exported type.
cmdletParameter.add(new Attribute(ExportAsAttribute, { parameters: [`typeof(${System.Collections.Hashtable})`] }));
} else {
// it's a hybrid. We need to add an additional property that puts its items into the target container

}

}

const desc = (vParam.description || '.').replace(/[\r?\n]/gm, '');
cmdletParameter.description = desc;
Expand Down Expand Up @@ -2478,31 +2480,34 @@ export class NewCmdletClass extends Class {

this.add(cmdletParameter);
}
// skip-for-time-being
// if (parameter.schema.additionalProperties) {
// // if there is an additional properties on this type
// // add a hashtable parameter for additionalProperties
// let apPropName = '';
// const options = ['AdditionalProperties', 'MoreProperties', 'ExtendedProperties', 'Properties'];
// for (const n of options) {
// if (this.properties.find(each => each.name === n)) {
// continue;
// }
// apPropName = n;
// break;
// }

// this.apProp = this.add(new Property(apPropName, System.Collections.Hashtable));
// this.apProp.add(new Attribute(ParameterAttribute, {
// parameters: ['Mandatory = false', 'HelpMessage = "Additional Parameters"']
// }));
// this.bodyParameterInfo = {
// type: {
// declaration: parameter.schema.details.csharp.fullname
// },
// valueType: parameter.schema.additionalProperties === true ? System.Object : this.state.project.schemaDefinitionResolver.resolveTypeDeclaration(<Schema>parameter.schema.additionalProperties, true, this.state)
// };
// }
const paramDictSchema = parameter.schema.type === SchemaType.Dictionary ? parameter.schema :
parameter.schema.type === SchemaType.Object ? (<ObjectSchema>parameter.schema).parents?.immediate?.find((s) => s.type === SchemaType.Dictionary) :
undefined;
if (paramDictSchema) {
// if there is an additional properties on this type
// add a hashtable parameter for additionalProperties
let apPropName = '';
const options = ['AdditionalProperties', 'MoreProperties', 'ExtendedProperties', 'Properties'];
for (const n of options) {
if (this.properties.find(each => each.name === n)) {
continue;
}
apPropName = n;
break;
}

this.apProp = this.add(new Property(apPropName, System.Collections.Hashtable));
this.apProp.add(new Attribute(ParameterAttribute, {
parameters: ['Mandatory = false', 'HelpMessage = "Additional Parameters"']
}));
this.bodyParameterInfo = {
type: {
declaration: parameter.schema.language.csharp?.fullname
},
valueType: (<DictionarySchema>paramDictSchema).elementType.type === SchemaType.Any ? System.Object :
this.state.project.schemaDefinitionResolver.resolveTypeDeclaration((<DictionarySchema>paramDictSchema).elementType, true, this.state)
};
}

this.bodyParameter = expandedBodyParameter;
continue;
Expand Down Expand Up @@ -2550,22 +2555,24 @@ export class NewCmdletClass extends Class {
description: vParam.description
}));

// skip-for-time-being
// if (vSchema.additionalProperties) {
// // we have to figure out if this is a standalone dictionary or a hybrid object/dictionary.
// // if it's a hybrid, we have to create another parameter like -<XXX>AdditionalProperties and have that dump the contents into the dictionary
// // if it's a standalone dictionary, we can just use hashtable instead
// if (length(vSchema.properties) === 0) {
// // it's a pure dictionary
// // change the property type to hashtable.
// // add an attribute to change the exported type.
// regularCmdletParameter.add(new Attribute(ExportAsAttribute, { parameters: [`typeof(${System.Collections.Hashtable})`] }));
// } else {
// // it's a hybrid. We need to add an additional property that puts its items into the target container

// }

// }
const dictSchema = vSchema.type === SchemaType.Dictionary ? vSchema :
vSchema.type === SchemaType.Object ? (<ObjectSchema>vSchema).parents?.immediate?.find((s) => s.type === SchemaType.Dictionary) :
undefined;
if (dictSchema) {
// we have to figure out if this is a standalone dictionary or a hybrid object/dictionary.
// if it's a hybrid, we have to create another parameter like -<XXX>AdditionalProperties and have that dump the contents into the dictionary
// if it's a standalone dictionary, we can just use hashtable instead
if (length((<ObjectSchema>vSchema).properties) === 0) {
// it's a pure dictionary
// change the property type to hashtable.
// add an attribute to change the exported type.
regularCmdletParameter.add(new Attribute(ExportAsAttribute, { parameters: [`typeof(${System.Collections.Hashtable})`] }));
} else {
// it's a hybrid. We need to add an additional property that puts its items into the target container

}

}

this.thingsToSerialize.push(regularCmdletParameter);

Expand Down
112 changes: 110 additions & 2 deletions powershell/llcsharp/model/model-class-dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Field, System, Property, toExpression, dotnet, Parameter, ParameterModifier, Method, Class, TypeDeclaration, Indexer, Access, Variable, Expression, If, And, ForEach, LocalVariable, ImplicitCastOperator } from '@azure-tools/codegen-csharp';
import { ModelClass } from './model-class';
import { EnhancedTypeDeclaration } from '../schema/extended-type-declaration';
import { ModelClass, NewModelClass } from './model-class';
import { EnhancedTypeDeclaration, NewEnhancedTypeDeclaration } from '../schema/extended-type-declaration';
import { ClientRuntime } from '../clientruntime';
import { getAllVirtualProperties } from '@azure-tools/codemodel-v3';
import { DeepPartial } from '@azure-tools/codegen';
import { DictionarySchema, ObjectSchema, SchemaType, Schema } from '@azure-tools/codemodel';

export class DictionaryImplementation extends Class {
private get state() { return this.modelClass.state; }
Expand Down Expand Up @@ -114,3 +115,110 @@ export class DictionaryImplementation extends Class {
return `${super.fileName}.dictionary`;
}
}

export class NewDictionaryImplementation extends Class {
private get state() { return this.modelClass.state; }
private get schema() { return this.modelClass.schema; }
public valueType!: TypeDeclaration | NewEnhancedTypeDeclaration;
public ownsDictionary = false;

constructor(protected modelClass: NewModelClass, objectInitializer?: DeepPartial<NewDictionaryImplementation>) {
super(modelClass.namespace, modelClass.name);
this.apply(objectInitializer);
}

init(valueType?: TypeDeclaration, accessViaMember?: Expression) {
if (valueType && accessViaMember) {
this.valueType = valueType;
this.implementIDictionary(this, '', System.String, valueType, accessViaMember);

}
else {
const dictSchema = (<Schema>this.schema).type === SchemaType.Dictionary ? this.schema : this.schema.parents?.immediate?.find((s) => s.type === SchemaType.Dictionary);
if (dictSchema) {
this.ownsDictionary = true;
this.valueType = (<DictionarySchema>dictSchema).elementType.type === SchemaType.Any ? System.Object : this.state.project.modelsNamespace.NewResolveTypeDeclaration((<DictionarySchema>dictSchema).elementType, true, this.state);
this.modelClass.modelInterface.interfaces.push(this.implementIDictionary(this, 'additionalProperties', System.String, this.valueType));
}
}

return this;
}

addSerialization() {
if (this.modelClass.jsonSerializer) {
// add serializer methods.
// this.modelClass.jsonSerializer


}
}

implementIDictionary(targetClass: Class, name: string, keyType: TypeDeclaration, valueType: TypeDeclaration, accessViaMember?: Expression) {
const containerInterfaceType = { declaration: `${ClientRuntime}.IAssociativeArray<${valueType.declaration}>`, allProperties: [] };
const dictionaryInterfaceType = System.Collections.Generic.IDictionary(keyType, valueType);
const itemType = System.Collections.Generic.KeyValuePair(keyType, valueType);

// add the interface to the list of interfaces for the class
targetClass.interfaces.push(containerInterfaceType);

// the backing field
const dictionaryType = System.Collections.Generic.Dictionary(keyType, valueType);

accessViaMember = accessViaMember || targetClass.add(new Field(`__${name}`, dictionaryType, { access: Access.Protected, initialValue: dictionaryType.new() }));

const indexer = targetClass.add(new Indexer(keyType, valueType, { get: toExpression(`${accessViaMember}[index]`), set: toExpression(`${accessViaMember}[index] = value`) }));

// the parameters used in methods.

const pKey = new Parameter('key', keyType);
const pValue = new Parameter('value', valueType);
const pOutValue = new Parameter('value', valueType, { modifier: ParameterModifier.Out });

targetClass.add(new Property(`${containerInterfaceType.declaration}.Keys`, System.Collections.Generic.IEnumerable(keyType), { get: toExpression(`${accessViaMember}.Keys`), getAccess: Access.Explicit }));
targetClass.add(new Property(`${containerInterfaceType.declaration}.Values`, System.Collections.Generic.IEnumerable(valueType), { get: toExpression(`${accessViaMember}.Values`), getAccess: Access.Explicit }));
targetClass.add(new Property(`${containerInterfaceType.declaration}.Count`, dotnet.Int, { get: toExpression(`${accessViaMember}.Count`), getAccess: Access.Explicit }));
if (name) {
targetClass.add(new ImplicitCastOperator(dictionaryType, targetClass, `source.${accessViaMember}`));
targetClass.add(new Property(`${containerInterfaceType.declaration}.AdditionalProperties`, dictionaryInterfaceType, { get: toExpression(`${accessViaMember}`), getAccess: Access.Explicit }));
} else {
targetClass.add(new Property(`${containerInterfaceType.declaration}.AdditionalProperties`, dictionaryInterfaceType, { get: toExpression(`${accessViaMember}.AdditionalProperties`), getAccess: Access.Explicit }));
}
targetClass.add(new Method('Add', dotnet.Void, { parameters: [pKey, pValue], body: toExpression(`${accessViaMember}.Add( ${pKey}, ${pValue})`), access: Access.Public }));
targetClass.add(new Method('Clear', dotnet.Void, { body: toExpression(`${accessViaMember}.Clear()`), access: Access.Public }));

targetClass.add(new Method('ContainsKey', dotnet.Bool, { parameters: [pKey], body: toExpression(`${accessViaMember}.ContainsKey( ${pKey})`), access: Access.Public }));

targetClass.add(new Method('Remove', dotnet.Bool, { parameters: [pKey], body: toExpression(`${accessViaMember}.Remove( ${pKey})`), access: Access.Public }));

targetClass.add(new Method('TryGetValue', dotnet.Bool, { parameters: [pKey, pOutValue], body: toExpression(`${accessViaMember}.TryGetValue( ${pKey}, out ${pOutValue})`), access: Access.Public }));

const all = getAllVirtualProperties(this.schema.language.csharp?.virtualProperties);
const exclusions = all.map(each => `"${each.name}"`).join(',');

// add a CopyFrom that takes an IDictionary or PSObject and copies the values into this dictionary
for (const pDictType of [System.Collections.IDictionary, { declaration: 'global::System.Management.Automation.PSObject' }]) {
const pDict = new Parameter('source', pDictType);
targetClass.add(new Method('CopyFrom', dotnet.Void, {
parameters: [pDict], body: function* () {
yield If(pDict.IsNotNull, function* () {

yield ForEach('property', ` Microsoft.Rest.ClientRuntime.PowerShell.TypeConverterExtensions.GetFilteredProperties(${pDict.value}, ${System.Collections.Generic.HashSet(System.String).new()} { ${exclusions} } )`, function* () {

yield If(And('null != property.Key', 'null != property.Value'), function* () {
yield `this.${accessViaMember}.Add(property.Key.ToString(), global::System.Management.Automation.LanguagePrimitives.ConvertTo<${valueType.declaration}>( property.Value));`;
});
});
});
}
}));
}

// return dictionaryInterfaceType;
return containerInterfaceType;
}

public get fileName(): string {
return `${super.fileName}.dictionary`;
}
}
11 changes: 7 additions & 4 deletions powershell/llcsharp/model/model-class-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Schema as NewSchema, ObjectSchema } from '@azure-tools/codemodel';
import { Schema as NewSchema, ObjectSchema, SchemaType } from '@azure-tools/codemodel';
import { KnownMediaType, HeaderProperty, HeaderPropertyType, getAllProperties } from '@azure-tools/codemodel-v3';
import { getAllProperties as newGetAllProperties } from '@azure-tools/codemodel';
import { EOL, DeepPartial, } from '@azure-tools/codegen';
Expand All @@ -25,7 +25,7 @@ import { ClientRuntime } from '../clientruntime';

import { dotnet } from '@azure-tools/codegen-csharp';
import { ModelClass, NewModelClass } from './model-class';
import { EnhancedTypeDeclaration } from '../schema/extended-type-declaration';
import { EnhancedTypeDeclaration, NewEnhancedTypeDeclaration } from '../schema/extended-type-declaration';
import { popTempVar, pushTempVar } from '../schema/primitive';

import { ModelProperty } from './property';
Expand Down Expand Up @@ -310,8 +310,11 @@ export class NewJsonSerializableClass extends Class {

for (const each of values(modelClass.backingFields)) {
serializeStatements.add(`${each.field.value}?.ToJson(${container}, ${mode.use});`);
const sch = (<EnhancedTypeDeclaration>each.typeDeclaration).schema;
if (!(sch instanceof NewSchema) && (sch.additionalProperties)) {
const sch = (<NewEnhancedTypeDeclaration>each.typeDeclaration).schema;
const dictSchema = sch.type === SchemaType.Dictionary ? sch :
sch.type === SchemaType.Object ? (<ObjectSchema>sch).parents?.immediate.find((s) => s.type === SchemaType.Dictionary) :
undefined;
if (dictSchema) {
deserializeStatements.add(`${each.field.value} = new ${each.className}(json${this.excludes});`);
} else {
deserializeStatements.add(`${each.field.value} = new ${each.className}(json);`);
Expand Down
Loading