diff --git a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationService.java b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationService.java index ab93b554a6..a58c49ad50 100644 --- a/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationService.java +++ b/scheduler-server/src/main/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationService.java @@ -26,7 +26,7 @@ public static String generateTypescriptTypesFromMissionModel(final MerlinService result.add("/** Start Codegen */"); result.add("import type { ActivityTemplate } from './scheduler-edsl-fluent-api.js';"); result.add("import type { Windows } from './constraints-edsl-fluent-api.js';"); - result.add("import type * as ConstraintEDSL from './constraints-edsl-fluent-api.js'"); + result.add("import type { ActivityTypeParameterMap } from './mission-model-generated-code.js';"); for (final var activityTypeCode : activityTypeCodes) { result.add("interface %s extends ActivityTemplate {}".formatted(activityTypeCode.activityTypeName(), activityTypeCode.activityTypeName())); @@ -115,7 +115,19 @@ private static String generateActivityTemplateConstructors(final Iterable parameters.append(activityParameter.name+", "))); + if (parameters.length() > 0) { + parameters.setLength(parameters.length() - 2); // Remove the last comma and space + } + result.add(indent(""" + /** + * Creates a %s instance + * @param {Object} args - {%s}. + * @return A %s instance. + */ + """.formatted(activityTypeCode.activityTypeName,parameters.toString(),activityTypeCode.activityTypeName))); + result.add(indent("%s: function %sConstructor(args: ActivityTypeParameterMap[ActivityType.%s]".formatted(activityTypeCode.activityTypeName,activityTypeCode.activityTypeName,activityTypeCode.activityTypeName))); result.add(indent("): %s {".formatted(activityTypeCode.activityTypeName()))); result.add(indent("// @ts-ignore")); result.add(indent(indent("return { activityType: ActivityType.%s, args: makeArgumentsDiscreteProfiles(args) };".formatted(activityTypeCode.activityTypeName())))); diff --git a/scheduler-server/src/test/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationServiceTest.java b/scheduler-server/src/test/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationServiceTest.java index 480bb3ce5c..9ca8f23795 100644 --- a/scheduler-server/src/test/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationServiceTest.java +++ b/scheduler-server/src/test/java/gov/nasa/jpl/aerie/scheduler/server/services/TypescriptCodeGenerationServiceTest.java @@ -12,110 +12,125 @@ void testCodeGen() { final var expected = TypescriptCodeGenerationService.generateTypescriptTypesFromMissionModel(MISSION_MODEL_TYPES); assertEquals( """ -/** Start Codegen */ -import type { ActivityTemplate } from './scheduler-edsl-fluent-api.js'; -import type { Windows } from './constraints-edsl-fluent-api.js'; -import type * as ConstraintEDSL from './constraints-edsl-fluent-api.js' -interface SampleActivity1 extends ActivityTemplate {} -interface SampleActivity2 extends ActivityTemplate {} -interface SampleActivity3 extends ActivityTemplate {} -interface SampleActivityEmpty extends ActivityTemplate {} -export function makeAllDiscreteProfile (argument: any) : any{ - if (argument === undefined){ - return undefined - } - else if ((argument instanceof Discrete) || (argument instanceof Real)) { - return argument.__astNode - } else if ((argument instanceof Temporal.Duration) || (argument.kind === 'IntervalDuration')) { - return argument; - } else if(typeof(argument) === "number"){ - if(Number.isInteger(argument)){ - return Discrete.Value(argument).__astNode - } else{ - return Real.Value(argument).__astNode - } - } else if(typeof(argument) === "string" || argument instanceof Temporal.Duration || typeof(argument) === "boolean"){ - return Discrete.Value(argument).__astNode - } - else if(Array.isArray(argument)){ - const arr: any[] = []; - argument.forEach((element) => { arr.push(makeAllDiscreteProfile(element))}); - return Discrete.List(arr).__astNode; - } else if (typeof argument === "object" ){ - const obj: { [k: string]: any } = {}; - for(var key in (argument)){ - // @ts-ignore - obj[key] = makeAllDiscreteProfile((argument)[key]) - } - return Discrete.Map(obj).__astNode; - } else{ - throw new Error('[makeAllDiscreteProfile] Type not covered: ' + argument); - } -} + /** Start Codegen */ + import type { ActivityTemplate } from './scheduler-edsl-fluent-api.js'; + import type { Windows } from './constraints-edsl-fluent-api.js'; + import type { ActivityTypeParameterMap } from './mission-model-generated-code.js'; + interface SampleActivity1 extends ActivityTemplate {} + interface SampleActivity2 extends ActivityTemplate {} + interface SampleActivity3 extends ActivityTemplate {} + interface SampleActivityEmpty extends ActivityTemplate {} + export function makeAllDiscreteProfile (argument: any) : any{ + if (argument === undefined){ + return undefined + } + else if ((argument instanceof Discrete) || (argument instanceof Real)) { + return argument.__astNode + } else if ((argument instanceof Temporal.Duration) || (argument.kind === 'IntervalDuration')) { + return argument; + } else if(typeof(argument) === "number"){ + if(Number.isInteger(argument)){ + return Discrete.Value(argument).__astNode + } else{ + return Real.Value(argument).__astNode + } + } else if(typeof(argument) === "string" || argument instanceof Temporal.Duration || typeof(argument) === "boolean"){ + return Discrete.Value(argument).__astNode + } + else if(Array.isArray(argument)){ + const arr: any[] = []; + argument.forEach((element) => { arr.push(makeAllDiscreteProfile(element))}); + return Discrete.List(arr).__astNode; + } else if (typeof argument === "object" ){ + const obj: { [k: string]: any } = {}; + for(var key in (argument)){ + // @ts-ignore + obj[key] = makeAllDiscreteProfile((argument)[key]) + } + return Discrete.Map(obj).__astNode; + } else{ + throw new Error('[makeAllDiscreteProfile] Type not covered: ' + argument); + } + } -export function makeArgumentsDiscreteProfiles(args : T):T{ -// @ts-ignore -return (makeAllDiscreteProfile(args)) -} + export function makeArgumentsDiscreteProfiles(args : T):T{ + // @ts-ignore + return (makeAllDiscreteProfile(args)) + } -const ActivityTemplateConstructors = { - SampleActivity1: function SampleActivity1Constructor(args: ConstraintEDSL.Gen.ActivityTypeParameterMap[ActivityType.SampleActivity1] - ): SampleActivity1 { - // @ts-ignore - return { activityType: ActivityType.SampleActivity1, args: makeArgumentsDiscreteProfiles(args) }; - }, - SampleActivity2: function SampleActivity2Constructor(args: ConstraintEDSL.Gen.ActivityTypeParameterMap[ActivityType.SampleActivity2] - ): SampleActivity2 { - // @ts-ignore - return { activityType: ActivityType.SampleActivity2, args: makeArgumentsDiscreteProfiles(args) }; - }, - SampleActivity3: function SampleActivity3Constructor(args: ConstraintEDSL.Gen.ActivityTypeParameterMap[ActivityType.SampleActivity3] - ): SampleActivity3 { - // @ts-ignore - return { activityType: ActivityType.SampleActivity3, args: makeArgumentsDiscreteProfiles(args) }; - }, - SampleActivityEmpty: function SampleActivityEmptyConstructor(): SampleActivityEmpty { - return { activityType: ActivityType.SampleActivityEmpty, args: {} }; - }, -}; -const ActivityPresetMap = Object.freeze({ - SampleActivity1: Object.freeze({ - }), - SampleActivity2: Object.freeze({ - get "my preset"(): { quantity:(number | Real),} { - return { - "quantity": 5, - }; - }, - }), - SampleActivity3: Object.freeze({ - get "my preset"(): { variant:(("option1" | "option2") | Discrete<("option1" | "option2")>),} { - return { - "variant": "option1", - }; - }, - }), - SampleActivityEmpty: Object.freeze({ - }), -}); -export enum Resource { - "/sample/resource/1" = "/sample/resource/1", - "/sample/resource/3" = "/sample/resource/3", - "/sample/resource/2" = "/sample/resource/2", -}; -declare global { - var ActivityTemplates: typeof ActivityTemplateConstructors; - var ActivityPresets: typeof ActivityPresetMap; - var Resources: typeof Resource; -} -// Make ActivityTemplates and ActivityTypes available on the global object -Object.assign(globalThis, { - ActivityTemplates: ActivityTemplateConstructors, - ActivityPresets: ActivityPresetMap, - ActivityTypes: ActivityType, - Resources: Resource, -}); -/** End Codegen */""", + const ActivityTemplateConstructors = { + /** + * Creates a SampleActivity1 instance + * @param {Object} args - {duration, fancy, variant}. + * @return A SampleActivity1 instance. + */ + SampleActivity1: function SampleActivity1Constructor(args: ActivityTypeParameterMap[ActivityType.SampleActivity1] + ): SampleActivity1 { + // @ts-ignore + return { activityType: ActivityType.SampleActivity1, args: makeArgumentsDiscreteProfiles(args) }; + }, + /** + * Creates a SampleActivity2 instance + * @param {Object} args - {quantity}. + * @return A SampleActivity2 instance. + */ + SampleActivity2: function SampleActivity2Constructor(args: ActivityTypeParameterMap[ActivityType.SampleActivity2] + ): SampleActivity2 { + // @ts-ignore + return { activityType: ActivityType.SampleActivity2, args: makeArgumentsDiscreteProfiles(args) }; + }, + /** + * Creates a SampleActivity3 instance + * @param {Object} args - {variant}. + * @return A SampleActivity3 instance. + */ + SampleActivity3: function SampleActivity3Constructor(args: ActivityTypeParameterMap[ActivityType.SampleActivity3] + ): SampleActivity3 { + // @ts-ignore + return { activityType: ActivityType.SampleActivity3, args: makeArgumentsDiscreteProfiles(args) }; + }, + SampleActivityEmpty: function SampleActivityEmptyConstructor(): SampleActivityEmpty { + return { activityType: ActivityType.SampleActivityEmpty, args: {} }; + }, + }; + const ActivityPresetMap = Object.freeze({ + SampleActivity1: Object.freeze({ + }), + SampleActivity2: Object.freeze({ + get "my preset"(): { quantity:(number | Real),} { + return { + "quantity": 5, + }; + }, + }), + SampleActivity3: Object.freeze({ + get "my preset"(): { variant:(("option1" | "option2") | Discrete<("option1" | "option2")>),} { + return { + "variant": "option1", + }; + }, + }), + SampleActivityEmpty: Object.freeze({ + }), + }); + export enum Resource { + "/sample/resource/1" = "/sample/resource/1", + "/sample/resource/3" = "/sample/resource/3", + "/sample/resource/2" = "/sample/resource/2", + }; + declare global { + var ActivityTemplates: typeof ActivityTemplateConstructors; + var ActivityPresets: typeof ActivityPresetMap; + var Resources: typeof Resource; + } + // Make ActivityTemplates and ActivityTypes available on the global object + Object.assign(globalThis, { + ActivityTemplates: ActivityTemplateConstructors, + ActivityPresets: ActivityPresetMap, + ActivityTypes: ActivityType, + Resources: Resource, + }); + /** End Codegen */""", expected); } }