argoproj / argo-workflows

Workflow Engine for Kubernetes
https://argo-workflows.readthedocs.io/
Apache License 2.0
14.85k stars 3.17k forks source link

UI SDK: Publish Workflow Types Separately #3309

Open mtiller opened 4 years ago

mtiller commented 4 years ago

Summary

It would be very useful to publish the TypeScript definitions for Workflows in a separate package.

Motivation

This would allow any tool working with Argo Workflows to have a complete picture of the "shape" of the workflows. There are lots of fields. All of this is seemingly published in the swagger specification of the API. It would be very useful (to us) to have a completely TypeScript representation of these workflow types.

I cannot help but notice that even the definitions here in the ui folder are not consistent with the swagger files stored in this same repository. Now imagine all the other projects trying to maintain a synchronized version of these same type definitions.

Proposal

Well, at a minimum it would be good to publish an NPM package with all the type definitions in it. It wouldn't even have to have any code necessarily. Just the types would be very useful. Ideally, a script that could read the schemas from the swagger definitions and generate the TypeScript would be even better. This could, of course, also be published as a standalone NPM repository.


Message from the maintainers:

If you wish to see this enhancement implemented please add a 👍 reaction to this issue! We often sort issues this way to know what to prioritize.

alexec commented 4 years ago

Have you thought about generating types from the Swagger?

mtiller commented 4 years ago

Like when I said:

Ideally, a script that could read the schemas from the swagger definitions and generate the TypeScript would be even better.

😉

mtiller commented 4 years ago

I just ran this (using this tool) for fun to see what the results look like:

$ npx @manifoldco/swagger-to-ts  https://raw.githubusercontent.com/argoproj/argo/master/api/openapi-spec/swagger.json --output argo.ts

I'll report back.

alexec commented 4 years ago

Let us know how it goes please!

alexec commented 4 years ago

Maybe we do the same thing :)

mtiller commented 4 years ago

With the exception of this bug (which I found about 6 occurrences of and fixed manually and which is apparently fixed here), it generated code that compiled!

Not only that, for fun I ran it through the isCreate and createAssertType functions/macros of typescript-is. The result? Auto-generated functions (over 13,000 LOC) that do type narrowing and validation (e.g., of deserialized data).

Looks quite promising so far. The generated code from the Swagger stuff is organized by each "sub-type" (which is quite reasonable). This makes it is a little bit hard to see the complete picture. But the code generated by typescript-is flattens all in the .d.ts files which is quite nice because you can then see the complete picture, e.g.,

```typescript export declare const isArgoWorkflow: (object: any) => object is { apiVersion?: string | undefined; kind?: string | undefined; metadata: { annotations?: { [key: string]: string; } | undefined; clusterName?: string | undefined; creationTimestamp?: string | undefined; deletionGracePeriodSeconds?: number | undefined; deletionTimestamp?: string | undefined; finalizers?: string[] | undefined; generateName?: string | undefined; generation?: number | undefined; initializers?: { pending: { name: string; }[]; result?: { apiVersion?: string | undefined; code?: number | undefined; details?: { causes?: { field?: string | undefined; message?: string | undefined; reason?: string | undefined; }[] | undefined; group?: string | undefined; kind?: string | undefined; name?: string | undefined; retryAfterSeconds?: number | undefined; uid?: string | undefined; } | undefined; kind?: string | undefined; message?: string | undefined; metadata?: { continue?: string | undefined; remainingItemCount?: number | undefined; resourceVersion?: string | undefined; selfLink?: string | undefined; } | undefined; reason?: string | undefined; status?: string | undefined; } | undefined; } | undefined; labels?: { [key: string]: string; } | undefined; managedFields?: { apiVersion?: string | undefined; fields?: { [key: string]: any; } | undefined; manager?: string | undefined; operation?: string | undefined; time?: string | undefined; }[] | undefined; name?: string | undefined; namespace?: string | undefined; ownerReferences?: { apiVersion: string; blockOwnerDeletion?: boolean | undefined; controller?: boolean | undefined; kind: string; name: string; uid: string; }[] | undefined; resourceVersion?: string | undefined; selfLink?: string | undefined; uid?: string | undefined; }; spec: { activeDeadlineSeconds?: number | undefined; affinity?: { nodeAffinity?: { preferredDuringSchedulingIgnoredDuringExecution?: { preference: { matchExpressions?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; matchFields?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; }; weight: number; }[] | undefined; requiredDuringSchedulingIgnoredDuringExecution?: { nodeSelectorTerms: { matchExpressions?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; matchFields?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; }[]; } | undefined; } | undefined; podAffinity?: { preferredDuringSchedulingIgnoredDuringExecution?: { podAffinityTerm: { labelSelector?: { matchExpressions?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; matchLabels?: { [key: string]: string; } | undefined; } | undefined; namespaces?: string[] | undefined; topologyKey: string; }; weight: number; }[] | undefined; requiredDuringSchedulingIgnoredDuringExecution?: { labelSelector?: { matchExpressions?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; matchLabels?: { [key: string]: string; } | undefined; } | undefined; namespaces?: string[] | undefined; topologyKey: string; }[] | undefined; } | undefined; podAntiAffinity?: { preferredDuringSchedulingIgnoredDuringExecution?: { podAffinityTerm: { labelSelector?: { matchExpressions?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; matchLabels?: { [key: string]: string; } | undefined; } | undefined; namespaces?: string[] | undefined; topologyKey: string; }; weight: number; }[] | undefined; requiredDuringSchedulingIgnoredDuringExecution?: { labelSelector?: { matchExpressions?: { key: string; operator: string; values?: string[] | undefined; }[] | undefined; matchLabels?: { [key: string]: string; } | undefined; } | undefined; namespaces?: string[] | undefined; topologyKey: string; }[] | undefined; } | undefined; } | undefined; // Only 17,300+ more lines in the function definition 😉 ```
mtiller commented 4 years ago

Given the errors I quickly found in swagger.json (see #3318), I wonder if we wouldn't be better simply using something like this to construct the types directly from the Go code?

joebowbeer commented 1 year ago

There is @nevermined-io/argo-workflows-api

Typescript library to interact with Argo Workflows v3.4.1

Auto Generated with Swagger Code Gen https://swagger.io/docs/open-source-tools/swagger-codegen/

swagger-codegen generate -i \
https://raw.githubusercontent.com/argoproj/argo-workflows/master/api/openapi-spec/swagger.json \
-l typescript-axios
agilgur5 commented 1 month ago

Also stumbled upon https://www.npmjs.com/package/@kubernetes-models/argo-workflows (standard warning to double-check third-party modules)