Skip to content

Commit

Permalink
[v6] Refactor parent and additionalProperties processing (#642)
Browse files Browse the repository at this point in the history
* Refactor parent and additionalProperties processing

* don't destructure additionalProperties out of mapper type

* Use "Any" mapper instead of object

* remove not needed filter

* Add CosmosDB, Monitor and Graphrbac to smoke tests
  • Loading branch information
joheredi authored May 12, 2020
1 parent 0541767 commit 6174e3c
Show file tree
Hide file tree
Showing 82 changed files with 25,391 additions and 68 deletions.
6 changes: 3 additions & 3 deletions src/generators/mappersGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ export function writeMapper(writer: CodeBlockWriter, mapper: Mapper) {
.block(() => {
// Write all type properties that don't need special handling
writeObjectProps(restType, writer);
// Write ptype roperties that need special handling
writePolymorphicDiscrimitator(writer, polymorphicDiscriminator);
// Write type properties that need special handling
writePolymorphicDiscriminator(writer, polymorphicDiscriminator);
writeModelProperties(writer, parents, modelProperties);
});
});
Expand Down Expand Up @@ -225,7 +225,7 @@ function writeModelProperties(
});
}

function writePolymorphicDiscrimitator(
function writePolymorphicDiscriminator(
writer: CodeBlockWriter,
polymorphicDiscriminator?: PolymorphicDiscriminator
) {
Expand Down
43 changes: 29 additions & 14 deletions src/transforms/mapperTransforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import { normalizeName, NameType } from "../utils/nameUtils";
import { extractHeaders } from "../utils/extractHeaders";
import { KnownMediaType } from "@azure-tools/codegen";
import { ClientOptions } from "../models/clientDetails";
import {
getSchemaParents,
getAdditionalProperties
} from "../utils/schemaHelpers";
import { ObjectDetails } from "../models/modelDetails";

interface PipelineValue {
schema: Schema;
Expand All @@ -53,15 +58,14 @@ const pipe = (
...fns: Array<(pipelineValue: PipelineValue) => PipelineValue>
) => (x: PipelineValue) => fns.reduce((v, f) => (!v.isHandled ? f(v) : v), x);

let uberParents: string[] = [];

export type ModelProperties = { [propertyName: string]: Mapper | string[] };

export interface EntityOptions {
serializedName?: string;
required?: boolean;
readOnly?: boolean;
hasXmlMetadata?: boolean;
uberParents?: string[];
}

export interface MapperInput {
Expand All @@ -71,6 +75,7 @@ export interface MapperInput {

export async function transformMappers(
codeModel: CodeModel,
uberParents: ObjectDetails[],
{ mediaTypes }: ClientOptions
): Promise<Mapper[]> {
const clientName = getLanguageMetadata(codeModel.language).name;
Expand All @@ -79,12 +84,16 @@ export async function transformMappers(
return [];
}

const uberParentsNames = uberParents.map(up => up.name);
const hasXmlMetadata = mediaTypes?.has(KnownMediaType.Xml);
return [
...codeModel.schemas.objects,
...extractHeaders(codeModel.operationGroups, clientName)
].map(objectSchema =>
transformMapper({ schema: objectSchema, options: { hasXmlMetadata } })
transformMapper({
schema: objectSchema,
options: { hasXmlMetadata, uberParents: uberParentsNames }
})
);
}

Expand Down Expand Up @@ -253,8 +262,11 @@ function getXmlMetadata(
};
}

function getAdditionalProperties(allParents: Schema[]): Mapper | undefined {
return allParents.some(p => p.type === SchemaType.Dictionary)
function buildAdditionalProperties(
objectSchema: ObjectSchema
): Mapper | undefined {
const additionalProperties = getAdditionalProperties(objectSchema);
return additionalProperties
? {
type: {
name: MapperType.Object
Expand All @@ -264,10 +276,8 @@ function getAdditionalProperties(allParents: Schema[]): Mapper | undefined {
}

function isUberParent(objectSchema: ObjectSchema) {
const { discriminator, parents, children } = objectSchema;
return (
discriminator && !parents && children && children.all && children.all.length
);
const { parents, children } = objectSchema;
return !parents && children && children.all && children.all.length;
}

function transformObjectMapper(pipelineValue: PipelineValue) {
Expand All @@ -281,21 +291,24 @@ function transformObjectMapper(pipelineValue: PipelineValue) {
const { discriminator, discriminatorValue } = objectSchema;

let modelProperties = processProperties(objectSchema.properties, options);
const parents = objectSchema.parents ? objectSchema.parents.all : [];
const immediateParents = objectSchema.parents
? objectSchema.parents.immediate
: [];
const parents = getSchemaParents(objectSchema);
const immediateParents = getSchemaParents(
objectSchema,
true /** immediateOnly */
);
const parentsRefs = immediateParents
.map(p => getMapperClassName(p))
.filter(p => p !== className);

const additionalProperties = getAdditionalProperties(parents);
const additionalProperties = buildAdditionalProperties(objectSchema);

modelProperties = {
...modelProperties,
...(parentsRefs && parentsRefs.length && { parentsRefs })
};

const uberParents = options?.uberParents || [];

// If we find a new uber parent, store it
if (uberParents.indexOf(className) < 0 && isUberParent(objectSchema)) {
uberParents.push(className);
Expand Down Expand Up @@ -717,6 +730,8 @@ export function getMapperTypeFromSchema(type: SchemaType, format?: string) {
return MapperType.Number;
case SchemaType.Object:
return MapperType.Object;
case SchemaType.Any:
return "any";
default:
throw new Error(`There is no known Mapper type for schema type ${type}`);
}
Expand Down
74 changes: 40 additions & 34 deletions src/transforms/objectTransforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import {
import { getLanguageMetadata } from "../utils/languageHelpers";
import { normalizeName, NameType } from "../utils/nameUtils";
import { PropertyDetails } from "../models/modelDetails";
import { getTypeForSchema } from "../utils/schemaHelpers";
import {
getTypeForSchema,
getSchemaParents,
getAdditionalProperties
} from "../utils/schemaHelpers";
import { extractHeaders } from "../utils/extractHeaders";

export function transformObjects(
Expand Down Expand Up @@ -61,7 +65,7 @@ export function transformObject(
: []
};

return getAdditionalObjectDetails(objectDetails, schema, uberParents);
return getAdditionalObjectDetails(objectDetails, uberParents);
}

export function transformProperty({
Expand Down Expand Up @@ -100,7 +104,8 @@ function getObjectKind(schema: ObjectSchema): ObjectKind {
return ObjectKind.Polymorphic;
}

if (schema.parents && schema.parents.immediate.length) {
const immediateParents = getSchemaParents(schema, true /** immediateOnly */);
if (immediateParents.length) {
return ObjectKind.Extended;
}

Expand All @@ -111,8 +116,10 @@ function getObjectDetailsWithHierarchy(
objectsDetails: ObjectDetails[]
): ObjectDetails[] {
return objectsDetails.map(current => {
const parentsSchema =
current.schema.parents && current.schema.parents.immediate;
const parentsSchema = getSchemaParents(
current.schema,
true /** immediateOnly */
);
const childrenSchema =
current.schema.children && current.schema.children.immediate;
let parents: ObjectDetails[] = extractHierarchy(
Expand All @@ -125,9 +132,9 @@ function getObjectDetailsWithHierarchy(
objectsDetails,
current
);
const hasAdditionalProperties =
!!parentsSchema &&
parentsSchema.some(p => p.type === SchemaType.Dictionary);
const hasAdditionalProperties = Boolean(
getAdditionalProperties(current.schema, true /** immediateOnly */)
);

return {
...current,
Expand All @@ -147,28 +154,25 @@ function extractHierarchy(
return [];
}

return schemas
.filter(s => s.type === SchemaType.Object)
.map(r => {
const relativeName = normalizeName(
getLanguageMetadata(r.language).name,
NameType.Interface,
true /** shouldGuard */
);
const relative = objectsDetails.find(o => o.name === relativeName);
return schemas.map(r => {
const relativeName = normalizeName(
getLanguageMetadata(r.language).name,
NameType.Interface,
true /** shouldGuard */
);
const relative = objectsDetails.find(o => o.name === relativeName);

if (!relative) {
throw new Error(
`Expected relative ${relativeName} of ${current.name} but couldn't find it in transformed objects`
);
}
return relative;
});
if (!relative) {
throw new Error(
`Expected relative ${relativeName} of ${current.name} but couldn't find it in transformed objects`
);
}
return relative;
});
}

function getAdditionalObjectDetails(
objectDetails: ObjectDetails,
schema: ObjectSchema,
uberParents: ObjectDetails[]
): ObjectDetails {
switch (objectDetails.kind) {
Expand All @@ -177,25 +181,27 @@ function getAdditionalObjectDetails(
case ObjectKind.Polymorphic:
return transformPolymorphicObject(
objectDetails as PolymorphicObjectDetails,
schema,
uberParents
);
case ObjectKind.Extended:
return transformComposedObject(objectDetails, schema);
return transformComposedObject(objectDetails);
default:
throw new Error(`Unexpected ObjectKind ${objectDetails.kind}`);
}
}

function transformComposedObject(
objectDetails: ObjectDetails,
schema: ObjectSchema
objectDetails: ObjectDetails
): ComposedObjectDetails {
if (!schema.parents) {
const immediateParents = getSchemaParents(
objectDetails.schema,
true /** immediateOnly */
);
if (immediateParents.length < 1) {
throw new Error(`Expected object ${objectDetails.name} to have parents`);
}

const parentNames = schema.parents.immediate.map(parent => {
const parentNames = immediateParents.map(parent => {
const name = getLanguageMetadata(parent.language).name;
return `${normalizeName(
name,
Expand All @@ -212,11 +218,11 @@ function transformComposedObject(

function transformPolymorphicObject(
objectDetails: PolymorphicObjectDetails,
schema: ObjectSchema,
uberParents: ObjectDetails[]
): PolymorphicObjectDetails {
let uberParent: ObjectSchema | undefined = schema;
const allParents = schema.parents && schema.parents.all;
const schema = objectDetails.schema;
let uberParent: ObjectSchema | undefined = objectDetails.schema;
const allParents = getSchemaParents(schema);
if (allParents && allParents.length) {
const uberParentSchema = allParents.find(p => {
// TODO: Reconsider names to reduce issues with normalization, can we switch to serialized?
Expand Down
6 changes: 4 additions & 2 deletions src/transforms/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Host } from "@azure-tools/autorest-extension-base";
import { transformBaseUrl } from "./urlTransforms";
import { normalizeModelWithExtensions } from "./extensions";
import { transformGroups } from "./groupTransforms";
import { getSchemaParents } from "../utils/schemaHelpers";

export async function transformChoices(codeModel: CodeModel) {
const choices = [
Expand Down Expand Up @@ -82,7 +83,7 @@ export async function transformCodeModel(
] = await Promise.all([
transformObjects(codeModel, uberParents),
transformGroups(codeModel),
transformMappers(codeModel, options),
transformMappers(codeModel, uberParents, options),
transformChoices(codeModel),
transformParameters(codeModel, options),
transformBaseUrl(codeModel)
Expand All @@ -105,6 +106,7 @@ export async function transformCodeModel(

/**
* This function gets all top level objects with children, aka UberParents
* An UberParent is an object schema that has no parents but is extended
* @param codeModel CodeModel
*/
async function getUberParents(codeModel: CodeModel): Promise<ObjectDetails[]> {
Expand All @@ -118,7 +120,7 @@ async function getUberParents(codeModel: CodeModel): Promise<ObjectDetails[]> {
const name = getLanguageMetadata(object.language).name;
const isPresent = uberParents.some(up => up.name === name);
const hasChildren = object.children && object.children.all.length;
const hasParents = object.parents && object.parents.all.length;
const hasParents = getSchemaParents(object).length > 0;

if (hasChildren && !hasParents && !isPresent) {
const baseObject = transformObject(object, uberParents);
Expand Down
43 changes: 42 additions & 1 deletion src/utils/schemaHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,53 @@ import {
ObjectSchema,
ArraySchema,
DictionarySchema,
SchemaResponse
SchemaResponse,
ComplexSchema
} from "@azure-tools/codemodel";
import { getStringForValue } from "./valueHelpers";
import { getLanguageMetadata } from "./languageHelpers";
import { normalizeName, NameType, normalizeTypeName } from "./nameUtils";

/**
* Extracts parents from an ObjectSchema
* by default it extracts all parameters unless
* immediateOnly = true is passed
*/
export function getSchemaParents(
{ parents }: ObjectSchema,
immediateOnly = false
) {
if (!parents) {
return [];
}

const allParents: ComplexSchema[] = immediateOnly
? parents.immediate
: parents.all;

return allParents.filter(p => p.type === SchemaType.Object);
}

/**
* Extracts the additional properties for an object schema
* if immediateOnly is true, it will only extract additionalProperties defined directly
* in the schema, otherwise it will get it from any of its parents
*/
export function getAdditionalProperties(
{ parents }: ObjectSchema,
immediateOnly = false
): ComplexSchema | undefined {
if (!parents) {
return undefined;
}

const allParents: ComplexSchema[] = immediateOnly
? parents.immediate
: parents.all;

return allParents.find(p => p.type === SchemaType.Dictionary);
}

/**
* Helper function which given a schema returns type information for useful for generating Typescript code
* @param schema schema to extract type information from
Expand Down
Loading

0 comments on commit 6174e3c

Please sign in to comment.