From 116d848a28ebf192ebe33aaf4762d30ad8d8aa06 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Mon, 15 Jul 2024 09:30:43 -0500 Subject: [PATCH] Interface inherited properties are now generated (#441) Fix mrl --- .changeset/strange-dots-leave.md | 5 + .../ontology/interfaces/FooInterface.ts | 12 + .../ontology/objects/BoundariesUsState.ts | 14 + .../objects/BuilderDeploymentState.ts | 9 + .../ontology/objects/Employee.ts | 36 +++ .../objects/ObjectTypeWithAllPropertyTypes.ts | 93 ++++++ .../ontology/objects/Person.ts | 3 + .../generatedNoCheck/ontology/objects/Todo.ts | 18 ++ .../ontology/objects/Venture.ts | 9 + .../ontology/objects/WeatherStation.ts | 8 + .../ontology/objects/Employee.ts | 18 ++ .../ontology/objects/Office.ts | 18 ++ .../generatedNoCheck/ontology/objects/Todo.ts | 11 + .../ontology/objects/equipment.ts | 6 + .../generatedNoCheck/ontology/objects/Todo.ts | 2 + .../ontology/objects/Todo.ts | 11 + .../api/src/ontology/ObjectTypeDefinition.ts | 13 +- ...ireInterfaceTypeV2ToSdkObjectDefinition.ts | 2 + .../wirePropertyV2ToSdkPropertyDefinition.ts | 2 + packages/generator/package.json | 1 + ...ireInterfaceTypeV2ToSdkObjectConst.test.ts | 276 ++++++++++++++++++ ...BLE_wireInterfaceTypeV2ToSdkObjectConst.ts | 68 ++++- .../generator/src/shared/propertyJsdoc.ts | 46 +++ .../wireObjectTypeV2ToSdkObjectConst.ts | 5 +- packages/generator/src/util/stringify.ts | 4 +- .../generateClientSdkVersionTwoPointZero.ts | 2 +- pnpm-lock.yaml | 3 + 27 files changed, 682 insertions(+), 13 deletions(-) create mode 100644 .changeset/strange-dots-leave.md create mode 100644 packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.test.ts create mode 100644 packages/generator/src/shared/propertyJsdoc.ts diff --git a/.changeset/strange-dots-leave.md b/.changeset/strange-dots-leave.md new file mode 100644 index 000000000..d55cfe40a --- /dev/null +++ b/.changeset/strange-dots-leave.md @@ -0,0 +1,5 @@ +--- +"@osdk/generator": patch +--- + +Interface inherited properties are now generated diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/interfaces/FooInterface.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/interfaces/FooInterface.ts index 3c0a05c5e..85c56ccd2 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/interfaces/FooInterface.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/interfaces/FooInterface.ts @@ -7,9 +7,18 @@ export interface FooInterface VersionBound<$ExpectedClientVersion> { osdkMetadata: typeof $osdkMetadata; description: 'Its a Foo.'; + displayName: 'Foo interface'; links: {}; properties: { + /** + * display name: 'Description', + * description: Description of Description + */ description: PropertyDef<'string', 'nullable', 'single'>; + /** + * display name: 'Name', + * description: Name of Foo + */ name: PropertyDef<'string', 'nullable', 'single'>; }; } @@ -18,15 +27,18 @@ export const FooInterface: FooInterface = { osdkMetadata: $osdkMetadata, apiName: 'FooInterface', description: 'Its a Foo.', + displayName: 'Foo interface', links: {}, properties: { name: { + displayName: 'Name', multiplicity: false, description: 'Name of Foo', type: 'string', nullable: true, }, description: { + displayName: 'Description', multiplicity: false, description: 'Description of Description', type: 'string', diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BoundariesUsState.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BoundariesUsState.ts index b1879e145..012fb4eb8 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BoundariesUsState.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BoundariesUsState.ts @@ -11,9 +11,22 @@ export interface BoundariesUsState primaryKeyApiName: 'usState'; primaryKeyType: 'string'; properties: { + /** + * display name: 'Geometry10M', + * description: geoshape + */ geometry10M: PropertyDef<'geoshape', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ latitude: PropertyDef<'double', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ longitude: PropertyDef<'double', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ usState: PropertyDef<'string', 'non-nullable', 'single'>; }; } @@ -42,6 +55,7 @@ export const BoundariesUsState: BoundariesUsState = { nullable: true, }, geometry10M: { + displayName: 'Geometry10M', multiplicity: false, description: 'geoshape', type: 'geoshape', diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BuilderDeploymentState.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BuilderDeploymentState.ts index 53754c97e..0a9a9ee79 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BuilderDeploymentState.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/BuilderDeploymentState.ts @@ -11,8 +11,17 @@ export interface BuilderDeploymentState primaryKeyApiName: 'skuId'; primaryKeyType: 'string'; properties: { + /** + * (no ontology metadata) + */ currentTimestamp: PropertyDef<'timestamp', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ date: PropertyDef<'datetime', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ skuId: PropertyDef<'string', 'non-nullable', 'single'>; }; } diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Employee.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Employee.ts index 616b5b526..61fab7cdb 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Employee.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Employee.ts @@ -32,17 +32,53 @@ export interface Employee extends ObjectTypeDefinition<'Employee', Employee>, Ve primaryKeyApiName: 'id'; primaryKeyType: 'string'; properties: { + /** + * (no ontology metadata) + */ adUsername: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ businessTitle: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ email: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ employeeNumber: PropertyDef<'double', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ favPlace: PropertyDef<'geopoint', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ firstFullTimeStartDate: PropertyDef<'datetime', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ firstName: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ id: PropertyDef<'string', 'non-nullable', 'single'>; + /** + * (no ontology metadata) + */ jobProfile: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ locationCity: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ locationName: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ locationType: PropertyDef<'string', 'nullable', 'single'>; }; spts: { diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/ObjectTypeWithAllPropertyTypes.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/ObjectTypeWithAllPropertyTypes.ts index a9e4353e1..60b1b36ed 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/ObjectTypeWithAllPropertyTypes.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/ObjectTypeWithAllPropertyTypes.ts @@ -11,36 +11,129 @@ export interface ObjectTypeWithAllPropertyTypes primaryKeyApiName: 'id'; primaryKeyType: 'integer'; properties: { + /** + * (no ontology metadata) + */ attachment: PropertyDef<'attachment', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ attachmentArray: PropertyDef<'attachment', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ boolean: PropertyDef<'boolean', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ booleanArray: PropertyDef<'boolean', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ byte: PropertyDef<'byte', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ byteArray: PropertyDef<'byte', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ date: PropertyDef<'datetime', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ dateArray: PropertyDef<'datetime', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ dateTime: PropertyDef<'timestamp', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ dateTimeArray: PropertyDef<'timestamp', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ decimal: PropertyDef<'decimal', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ decimalArray: PropertyDef<'decimal', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ double: PropertyDef<'double', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ doubleArray: PropertyDef<'double', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ float: PropertyDef<'float', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ floatArray: PropertyDef<'float', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ geoPoint: PropertyDef<'geopoint', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ geoPointArray: PropertyDef<'geopoint', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ geoShape: PropertyDef<'geoshape', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ geoShapeArray: PropertyDef<'geoshape', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ id: PropertyDef<'integer', 'non-nullable', 'single'>; + /** + * (no ontology metadata) + */ integer: PropertyDef<'integer', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ integerArray: PropertyDef<'integer', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ long: PropertyDef<'long', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ longArray: PropertyDef<'long', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ numericTimeseries: PropertyDef<'numericTimeseries', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ short: PropertyDef<'short', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ shortArray: PropertyDef<'short', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ string: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ stringArray: PropertyDef<'string', 'nullable', 'array'>; + /** + * (no ontology metadata) + */ stringTimeseries: PropertyDef<'stringTimeseries', 'nullable', 'single'>; }; } diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Person.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Person.ts index cdd60c353..ac564f995 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Person.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Person.ts @@ -14,6 +14,9 @@ export interface Person extends ObjectTypeDefinition<'Person', Person>, VersionB primaryKeyApiName: 'email'; primaryKeyType: 'string'; properties: { + /** + * (no ontology metadata) + */ email: PropertyDef<'string', 'non-nullable', 'single'>; }; } diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Todo.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Todo.ts index d2c490b6b..1219c760a 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Todo.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Todo.ts @@ -13,10 +13,26 @@ export interface Todo extends ObjectTypeDefinition<'Todo', Todo>, VersionBound<$ primaryKeyApiName: 'id'; primaryKeyType: 'integer'; properties: { + /** + * display name: 'Body', + * description: The text of the todo + */ body: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ complete: PropertyDef<'boolean', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ id: PropertyDef<'integer', 'non-nullable', 'single'>; + /** + * (no ontology metadata) + */ priority: PropertyDef<'integer', 'nullable', 'single'>; + /** + * display name: 'Text' + */ text: PropertyDef<'string', 'nullable', 'single'>; }; } @@ -40,12 +56,14 @@ export const Todo: Todo = { nullable: false, }, body: { + displayName: 'Body', multiplicity: false, description: 'The text of the todo', type: 'string', nullable: true, }, text: { + displayName: 'Text', multiplicity: false, type: 'string', nullable: true, diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Venture.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Venture.ts index 083d6ccc6..868e04916 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Venture.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/Venture.ts @@ -13,8 +13,17 @@ export interface Venture extends ObjectTypeDefinition<'Venture', Venture>, Versi primaryKeyApiName: 'ventureId'; primaryKeyType: 'string'; properties: { + /** + * (no ontology metadata) + */ ventureId: PropertyDef<'string', 'non-nullable', 'single'>; + /** + * (no ontology metadata) + */ ventureName: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ ventureStart: PropertyDef<'datetime', 'nullable', 'single'>; }; } diff --git a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/WeatherStation.ts b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/WeatherStation.ts index 72fc88949..eaa3fcbf9 100644 --- a/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/WeatherStation.ts +++ b/examples-extra/basic/sdk/src/generatedNoCheck/ontology/objects/WeatherStation.ts @@ -11,7 +11,14 @@ export interface WeatherStation primaryKeyApiName: 'stationId'; primaryKeyType: 'string'; properties: { + /** + * display name: 'Geohash', + * description: geopoint + */ geohash: PropertyDef<'geopoint', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ stationId: PropertyDef<'string', 'non-nullable', 'single'>; }; } @@ -30,6 +37,7 @@ export const WeatherStation: WeatherStation = { nullable: false, }, geohash: { + displayName: 'Geohash', multiplicity: false, description: 'geopoint', type: 'geopoint', diff --git a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Employee.ts b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Employee.ts index 52566ccfc..3987625d1 100644 --- a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Employee.ts +++ b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Employee.ts @@ -12,11 +12,29 @@ export interface Employee extends ObjectTypeDefinition<'Employee', Employee>, Ve primaryKeyApiName: 'employeeId'; primaryKeyType: 'integer'; properties: { + /** + * (no ontology metadata) + */ class: PropertyDef<'string', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ employeeId: PropertyDef<'integer', 'non-nullable', 'single'>; + /** + * description: TimeSeries of the status of the employee + */ employeeStatus: PropertyDef<'numericTimeseries', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ fullName: PropertyDef<'string', 'nullable', 'single'>; + /** + * description: The unique of the employee's assigned office. This is some more text. + */ office: PropertyDef<'integer', 'nullable', 'single'>; + /** + * description: The date the employee was hired (most recently, if they were re-hired) + */ startDate: PropertyDef<'datetime', 'nullable', 'single'>; }; } diff --git a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Office.ts b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Office.ts index b95610f47..3090334bd 100644 --- a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Office.ts +++ b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Office.ts @@ -9,11 +9,29 @@ export interface Office extends ObjectTypeDefinition<'Office', Office>, VersionB primaryKeyApiName: 'officeId'; primaryKeyType: 'string'; properties: { + /** + * (no ontology metadata) + */ entrance: PropertyDef<'geopoint', 'nullable', 'single'>; + /** + * description: The individual capacities of meetings rooms in the office + */ meetingRoomCapacities: PropertyDef<'integer', 'nullable', 'array'>; + /** + * description: The Names of meetings rooms in the office + */ meetingRooms: PropertyDef<'string', 'nullable', 'array'>; + /** + * description: The Name of the Office + */ name: PropertyDef<'string', 'nullable', 'single'>; + /** + * description: The occupied area of the Office + */ occupiedArea: PropertyDef<'geoshape', 'nullable', 'single'>; + /** + * (no ontology metadata) + */ officeId: PropertyDef<'string', 'non-nullable', 'single'>; }; } diff --git a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Todo.ts b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Todo.ts index b30c10943..38d0d1ba3 100644 --- a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Todo.ts +++ b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/Todo.ts @@ -9,8 +9,18 @@ export interface Todo extends ObjectTypeDefinition<'Todo', Todo>, VersionBound<$ primaryKeyApiName: 'id'; primaryKeyType: 'string'; properties: { + /** + * (no ontology metadata) + */ id: PropertyDef<'string', 'non-nullable', 'single'>; + /** + * (no ontology metadata) + */ isComplete: PropertyDef<'boolean', 'nullable', 'single'>; + /** + * display name: 'Title', + * description: The text of the todo + */ title: PropertyDef<'string', 'nullable', 'single'>; }; } @@ -29,6 +39,7 @@ export const Todo: Todo = { nullable: false, }, title: { + displayName: 'Title', multiplicity: false, description: 'The text of the todo', type: 'string', diff --git a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/equipment.ts b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/equipment.ts index 5aba871d1..e53705d11 100644 --- a/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/equipment.ts +++ b/examples-extra/docs_example/src/generatedNoCheck/ontology/objects/equipment.ts @@ -8,7 +8,13 @@ export interface equipment extends ObjectTypeDefinition<'equipment', equipment>, primaryKeyApiName: 'equipmentId'; primaryKeyType: 'string'; properties: { + /** + * description: The id of an equipment + */ equipmentId: PropertyDef<'string', 'non-nullable', 'single'>; + /** + * (no ontology metadata) + */ type: PropertyDef<'string', 'nullable', 'single'>; }; } diff --git a/examples-extra/one_dot_one/src/generatedNoCheck/ontology/objects/Todo.ts b/examples-extra/one_dot_one/src/generatedNoCheck/ontology/objects/Todo.ts index 05595278c..34d810180 100644 --- a/examples-extra/one_dot_one/src/generatedNoCheck/ontology/objects/Todo.ts +++ b/examples-extra/one_dot_one/src/generatedNoCheck/ontology/objects/Todo.ts @@ -40,6 +40,7 @@ export interface TodoDef extends ObjectTypeDefinition<'Todo', Todo> { nullable: true; }; body: { + displayName: 'Body'; multiplicity: false; description: 'The text of the todo'; type: 'string'; @@ -72,6 +73,7 @@ export const Todo: TodoDef = { nullable: true, }, body: { + displayName: 'Body', multiplicity: false, description: 'The text of the todo', type: 'string', diff --git a/examples-extra/todoapp/src/generatedNoCheck2/ontology/objects/Todo.ts b/examples-extra/todoapp/src/generatedNoCheck2/ontology/objects/Todo.ts index b30c10943..38d0d1ba3 100644 --- a/examples-extra/todoapp/src/generatedNoCheck2/ontology/objects/Todo.ts +++ b/examples-extra/todoapp/src/generatedNoCheck2/ontology/objects/Todo.ts @@ -9,8 +9,18 @@ export interface Todo extends ObjectTypeDefinition<'Todo', Todo>, VersionBound<$ primaryKeyApiName: 'id'; primaryKeyType: 'string'; properties: { + /** + * (no ontology metadata) + */ id: PropertyDef<'string', 'non-nullable', 'single'>; + /** + * (no ontology metadata) + */ isComplete: PropertyDef<'boolean', 'nullable', 'single'>; + /** + * display name: 'Title', + * description: The text of the todo + */ title: PropertyDef<'string', 'nullable', 'single'>; }; } @@ -29,6 +39,7 @@ export const Todo: Todo = { nullable: false, }, title: { + displayName: 'Title', multiplicity: false, description: 'The text of the todo', type: 'string', diff --git a/packages/api/src/ontology/ObjectTypeDefinition.ts b/packages/api/src/ontology/ObjectTypeDefinition.ts index fb01e3bb0..8622078c1 100644 --- a/packages/api/src/ontology/ObjectTypeDefinition.ts +++ b/packages/api/src/ontology/ObjectTypeDefinition.ts @@ -52,6 +52,7 @@ export type ObjectTypePropertyDefinitionFrom2< export interface ObjectInterfaceBaseDefinition { type: "object" | "interface"; apiName: BrandedApiName; + displayName?: string; description?: string; properties: Record; links: Record< @@ -59,6 +60,13 @@ export interface ObjectInterfaceBaseDefinition { ObjectTypeLinkDefinition >; osdkMetadata?: OsdkMetadata; + + /** + * Represents the "super interfaces" of this object. + * + * Optional because they may not exist on legacy. + */ + implements?: ReadonlyArray; } export interface VersionBound> { @@ -73,11 +81,6 @@ export interface ObjectTypeDefinition< primaryKeyApiName: keyof this["properties"]; primaryKeyType: WirePropertyTypes; - /** - * Optional because they may not exist on legacy. - */ - implements?: string[]; - /** * Optional because they may not exist on legacy. */ diff --git a/packages/generator-converters/src/__UNSTABLE_wireInterfaceTypeV2ToSdkObjectDefinition.ts b/packages/generator-converters/src/__UNSTABLE_wireInterfaceTypeV2ToSdkObjectDefinition.ts index b08e5aead..27611a92c 100644 --- a/packages/generator-converters/src/__UNSTABLE_wireInterfaceTypeV2ToSdkObjectDefinition.ts +++ b/packages/generator-converters/src/__UNSTABLE_wireInterfaceTypeV2ToSdkObjectDefinition.ts @@ -25,7 +25,9 @@ export function __UNSTABLE_wireInterfaceTypeV2ToSdkObjectDefinition( return { type: "interface", apiName: interfaceType.apiName, + displayName: interfaceType.displayName, description: interfaceType.description, + implements: interfaceType.extendsInterfaces, properties: Object.fromEntries( Object.entries(interfaceType.properties).map(( [key, value], diff --git a/packages/generator-converters/src/wirePropertyV2ToSdkPropertyDefinition.ts b/packages/generator-converters/src/wirePropertyV2ToSdkPropertyDefinition.ts index dcf088d3a..18d24785b 100644 --- a/packages/generator-converters/src/wirePropertyV2ToSdkPropertyDefinition.ts +++ b/packages/generator-converters/src/wirePropertyV2ToSdkPropertyDefinition.ts @@ -46,6 +46,7 @@ export function wirePropertyV2ToSdkPropertyDefinition( case "timeseries": case "marking": return { + displayName: input.displayName, multiplicity: false, description: input.description, type: objectPropertyTypeToSdkPropertyDefinition(input.dataType), @@ -53,6 +54,7 @@ export function wirePropertyV2ToSdkPropertyDefinition( }; case "array": { return { + displayName: input.displayName, multiplicity: true, description: input.description, type: objectPropertyTypeToSdkPropertyDefinition(input.dataType), diff --git a/packages/generator/package.json b/packages/generator/package.json index e45d49b09..82136aaaa 100644 --- a/packages/generator/package.json +++ b/packages/generator/package.json @@ -35,6 +35,7 @@ "@osdk/api": "workspace:~", "@osdk/gateway": "workspace:~", "@osdk/generator-converters": "workspace:~", + "fast-deep-equal": "^3.1.3", "fetch-retry": "^6.0.0", "prettier": "^3.0.3", "prettier-plugin-organize-imports": "^3.2.4", diff --git a/packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.test.ts b/packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.test.ts new file mode 100644 index 000000000..d0786bdc5 --- /dev/null +++ b/packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.test.ts @@ -0,0 +1,276 @@ +/* + * Copyright 2024 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { InterfaceType, SharedPropertyType } from "@osdk/gateway/types"; +import { format } from "prettier"; +import { describe, expect, it } from "vitest"; +import type { WireOntologyDefinition } from "../WireOntologyDefinition.js"; +import { __UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst } from "./UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.js"; + +function simpleSpt(apiName: T, metadataLevel: 0 | 1 | 2 = 2) { + return { + apiName, + dataType: { + type: "integer", + }, + rid: `${apiName}Rid`, + displayName: metadataLevel >= 1 ? `${apiName} property dn` : apiName, + description: metadataLevel >= 2 ? `${apiName} property desc` : undefined, + } as const satisfies SharedPropertyType; +} + +function simpleInterface( + apiName: T, + spts: Q[], + parents: string[], + metadataLevel: 0 | 1 | 2 = 2, +) { + const properties = Object.fromEntries(spts.map(spt => [spt.apiName, spt])); + + return { + apiName, + rid: `${apiName}Rid`, + displayName: metadataLevel >= 1 ? `${apiName} interface dn` : apiName, + description: metadataLevel >= 2 ? `${apiName} interface desc` : undefined, + extendsInterfaces: parents, + links: {}, + properties, + } as const satisfies InterfaceType; +} + +function simpleOntology( + apiName: string, + interfaces: I[], +) { + const interfaceTypes: Record = Object + .fromEntries( + interfaces.map(i => [i.apiName, i]), + ) as Record; + + const sharedPropertyTypes: Record = Object + .fromEntries( + interfaces.flatMap(i => Object.entries(i.properties)), + ) as Record; + + return { + actionTypes: {}, + interfaceTypes, + objectTypes: {}, + ontology: { + apiName, + description: `${apiName} ontology desc`, + displayName: `${apiName} ontology dn`, + rid: `${apiName} ontology rid`, + }, + queryTypes: {}, + sharedPropertyTypes, + } as const satisfies WireOntologyDefinition; +} + +describe(__UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst, () => { + it("Does not say (inherited) on properties locally defined", async () => { + const ontology = simpleOntology("ontology", [ + simpleInterface("Bar", [simpleSpt("bar", 0)], [], 0), + ]); + + const formattedCode = await format( + __UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst( + ontology.interfaceTypes.Bar, + ontology, + true, + ), + { + parser: "typescript", + printWidth: 100, + }, + ); + expect(formattedCode).toMatchInlineSnapshot(` + "export interface Bar extends InterfaceDefinition<"Bar", Bar>, VersionBound<$ExpectedClientVersion> { + osdkMetadata: typeof $osdkMetadata; + displayName: "Bar"; + implements: []; + links: {}; + properties: { + /** + * (no ontology metadata) + */ + bar: PropertyDef<"integer", "nullable", "single">; + }; + } + + export const Bar: Bar = { + osdkMetadata: $osdkMetadata, + apiName: "Bar", + displayName: "Bar", + implements: [], + links: {}, + properties: { + bar: { + displayName: "bar", + multiplicity: false, + type: "integer", + nullable: true, + }, + }, + type: "interface", + }; + " + `); + }); + + it("Generates properties for inherited SPTs", async () => { + const fooSpt = simpleSpt("foo"); + const barSpt = simpleSpt("bar"); + + const ontology = simpleOntology("ontology", [ + simpleInterface("Foo", [fooSpt], ["Parent"]), + simpleInterface("Parent", [barSpt], []), + ]); + + const formattedCode = await format( + __UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst( + ontology.interfaceTypes.Foo, + ontology, + true, + ), + { + parser: "typescript", + }, + ); + expect(formattedCode).toMatchInlineSnapshot(` + "export interface Foo + extends InterfaceDefinition<"Foo", Foo>, + VersionBound<$ExpectedClientVersion> { + osdkMetadata: typeof $osdkMetadata; + description: "Foo interface desc"; + displayName: "Foo interface dn"; + implements: ["Parent"]; + links: {}; + properties: { + /** + * (inherited from parent) + * display name: 'bar property dn', + * description: bar property desc + */ + bar: PropertyDef<"integer", "nullable", "single">; + /** + * display name: 'foo property dn', + * description: foo property desc + */ + foo: PropertyDef<"integer", "nullable", "single">; + }; + } + + export const Foo: Foo = { + osdkMetadata: $osdkMetadata, + apiName: "Foo", + description: "Foo interface desc", + displayName: "Foo interface dn", + implements: ["Parent"], + links: {}, + properties: { + foo: { + displayName: "foo property dn", + multiplicity: false, + description: "foo property desc", + type: "integer", + nullable: true, + }, + bar: { + displayName: "bar property dn", + multiplicity: false, + description: "bar property desc", + type: "integer", + nullable: true, + }, + }, + type: "interface", + }; + " + `); + }); + + it("allows for SPT reuse if the same", async () => { + const fooSpt = simpleSpt("foo"); + const barSpt = simpleSpt("bar"); + + const ontology = simpleOntology("ontology", [ + simpleInterface("Foo", [fooSpt, barSpt], ["Parent"]), + simpleInterface("Parent", [barSpt], []), + ]); + + const formattedCode = await format( + __UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst( + ontology.interfaceTypes.Foo, + ontology, + true, + ), + { + parser: "typescript", + }, + ); + expect(formattedCode).toMatchInlineSnapshot(` + "export interface Foo + extends InterfaceDefinition<"Foo", Foo>, + VersionBound<$ExpectedClientVersion> { + osdkMetadata: typeof $osdkMetadata; + description: "Foo interface desc"; + displayName: "Foo interface dn"; + implements: ["Parent"]; + links: {}; + properties: { + /** + * display name: 'bar property dn', + * description: bar property desc + */ + bar: PropertyDef<"integer", "nullable", "single">; + /** + * display name: 'foo property dn', + * description: foo property desc + */ + foo: PropertyDef<"integer", "nullable", "single">; + }; + } + + export const Foo: Foo = { + osdkMetadata: $osdkMetadata, + apiName: "Foo", + description: "Foo interface desc", + displayName: "Foo interface dn", + implements: ["Parent"], + links: {}, + properties: { + foo: { + displayName: "foo property dn", + multiplicity: false, + description: "foo property desc", + type: "integer", + nullable: true, + }, + bar: { + displayName: "bar property dn", + multiplicity: false, + description: "bar property desc", + type: "integer", + nullable: true, + }, + }, + type: "interface", + }; + " + `); + }); +}); diff --git a/packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.ts b/packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.ts index d363cc688..94f908e04 100644 --- a/packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.ts +++ b/packages/generator/src/shared/UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst.ts @@ -15,15 +15,19 @@ */ import type { InterfaceType } from "@osdk/gateway/types"; - import { __UNSTABLE_wireInterfaceTypeV2ToSdkObjectDefinition } from "@osdk/generator-converters"; +import fastDeepEqual from "fast-deep-equal"; +import invariant from "tiny-invariant"; import { deleteUndefineds } from "../util/deleteUndefineds.js"; import { stringify } from "../util/stringify.js"; +import type { WireOntologyDefinition } from "../WireOntologyDefinition.js"; +import { propertyJsdoc } from "./propertyJsdoc.js"; import { getObjectDefIdentifier } from "./wireObjectTypeV2ToSdkObjectConst.js"; /** @internal */ export function __UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst( interfaceDef: InterfaceType, + ontology: WireOntologyDefinition, v2: boolean = false, ) { const definition = deleteUndefineds( @@ -38,6 +42,56 @@ export function __UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst( v2, ); + const parents = definition.implements?.map(p => { + invariant( + ontology.interfaceTypes[p] != null, + `Expected to find a parent interface named ${p} in the ontology and did not.`, + ); + + const it = deleteUndefineds( + __UNSTABLE_wireInterfaceTypeV2ToSdkObjectDefinition( + ontology.interfaceTypes[p], + v2, + ), + ); + + return it; + }) ?? []; + + const mergedProperties = { ...definition.properties }; + for (const parent of parents) { + for (const apiName of Object.keys(parent.properties)) { + if (definition.properties[apiName] != null) { + invariant( + fastDeepEqual( + definition.properties[apiName], + parent.properties[apiName], + ), + `Interface ${definition.apiName} redefines property '${apiName}' from parent '${parent.apiName}' but the properties do not match`, + ); + } else if (mergedProperties[apiName] != null) { + invariant( + fastDeepEqual( + mergedProperties[apiName], + parent.properties[apiName], + ), + `Some interface defines a conflicting property '${apiName}' that does not match property from parent '${parent.apiName}'`, + ); + } + mergedProperties[apiName] = parent.properties[apiName]; + } + } + + const ogProperties = definition.properties; + definition.properties = mergedProperties; + + function localPropertyJsdoc(apiName: string) { + const property = definition.properties[apiName]!; + const isInherited = ogProperties[apiName] == null; + + return propertyJsdoc(property, { isInherited, apiName }); + } + function getV2Types() { return ` export interface ${objectDefIdentifier} extends InterfaceDefinition<"${interfaceDef.apiName}", ${interfaceDef.apiName}>, VersionBound<$ExpectedClientVersion> { @@ -58,13 +112,19 @@ export function __UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst( }) } }`, - properties: (_value) => (`{ + properties: (properties) => (`{ ${ - stringify(definition.properties, { - "*": (propertyDefinition) => + stringify(properties, { + "*": ( + propertyDefinition, + _, + key, + ) => [ + `${localPropertyJsdoc(key)}${key}`, `PropertyDef<"${propertyDefinition.type}", "${ propertyDefinition.nullable ? "nullable" : "non-nullable" }", "${propertyDefinition.multiplicity ? "array" : "single"}">`, + ], }) } }`), diff --git a/packages/generator/src/shared/propertyJsdoc.ts b/packages/generator/src/shared/propertyJsdoc.ts new file mode 100644 index 000000000..a75a44776 --- /dev/null +++ b/packages/generator/src/shared/propertyJsdoc.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ObjectTypePropertyDefinition } from "@osdk/api"; + +export function propertyJsdoc( + property: ObjectTypePropertyDefinition, + { isInherited, apiName }: { isInherited?: boolean; apiName: string }, +) { + let ret = `/**\n`; + const renderDisplayName = property.displayName + && property.displayName !== apiName; + if (isInherited || renderDisplayName || property.description) { + if (isInherited) { + ret += ` * (inherited from parent)\n`; + } + + if (renderDisplayName) { + ret += ` * display name: '${property.displayName}'${ + property.description ? "," : "" + }\n`; + } + + if (property.description) { + ret += ` * description: ${property.description}\n`; + } + } else { + ret += ` * (no ontology metadata)\n`; + } + + ret += ` */\n`; + return ret; +} diff --git a/packages/generator/src/shared/wireObjectTypeV2ToSdkObjectConst.ts b/packages/generator/src/shared/wireObjectTypeV2ToSdkObjectConst.ts index e263d5daf..5da076eed 100644 --- a/packages/generator/src/shared/wireObjectTypeV2ToSdkObjectConst.ts +++ b/packages/generator/src/shared/wireObjectTypeV2ToSdkObjectConst.ts @@ -18,6 +18,7 @@ import type { ObjectTypeFullMetadata } from "@osdk/gateway/types"; import { wireObjectTypeFullMetadataToSdkObjectTypeDefinition } from "@osdk/generator-converters"; import { deleteUndefineds } from "../util/deleteUndefineds.js"; import { stringify } from "../util/stringify.js"; +import { propertyJsdoc } from "./propertyJsdoc.js"; export function getObjectDefIdentifier(name: string, v2: boolean) { return v2 ? name : `${name}Def`; @@ -91,10 +92,12 @@ export function wireObjectTypeV2ToSdkObjectConst( properties: (_value) => (`{ ${ stringify(definition.properties, { - "*": (propertyDefinition) => + "*": (propertyDefinition, _, apiName) => [ + `${propertyJsdoc(propertyDefinition, { apiName })}${apiName}`, `PropertyDef<"${propertyDefinition.type}", "${ propertyDefinition.nullable ? "nullable" : "non-nullable" }", "${propertyDefinition.multiplicity ? "array" : "single"}">`, + ], }) } }`), diff --git a/packages/generator/src/util/stringify.ts b/packages/generator/src/util/stringify.ts index cbc03b91a..a655ede03 100644 --- a/packages/generator/src/util/stringify.ts +++ b/packages/generator/src/util/stringify.ts @@ -28,10 +28,10 @@ * and the second string will be used as the value of the key/value pair. * If undefined is returned, the pair will be removed */ -type Customizer = ( +type Customizer = ( value: V, defaultValueFormatter: (value: any) => string, - key: K, + key: K extends "*" ? string : K, defaultKeyFormatter: (key: string) => string, ) => [string, string] | string | undefined; const defaultCustomizer: Customizer = ( diff --git a/packages/generator/src/v2.0/generateClientSdkVersionTwoPointZero.ts b/packages/generator/src/v2.0/generateClientSdkVersionTwoPointZero.ts index f607edf79..fd1a64aab 100644 --- a/packages/generator/src/v2.0/generateClientSdkVersionTwoPointZero.ts +++ b/packages/generator/src/v2.0/generateClientSdkVersionTwoPointZero.ts @@ -222,7 +222,7 @@ async function generateOntologyInterfaces( import { $osdkMetadata, $expectedClientVersion } from "../../OntologyMetadata${importExt}"; import type { $ExpectedClientVersion } from "../../OntologyMetadata${importExt}"; - ${__UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst(obj, true)} + ${__UNSTABLE_wireInterfaceTypeV2ToSdkObjectConst(obj, ontology, true)} `), ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc3f7ddd0..fb0027417 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1136,6 +1136,9 @@ importers: '@osdk/generator-converters': specifier: workspace:~ version: link:../generator-converters + fast-deep-equal: + specifier: ^3.1.3 + version: 3.1.3 fetch-retry: specifier: ^6.0.0 version: 6.0.0