@workos/oagen-emitters 0.15.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +48 -1
- package/dist/index.d.mts +51 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +852 -2
- package/dist/index.mjs.map +1 -0
- package/dist/{plugin-C2Hp2Vs2.mjs → plugin-DuB1UozS.mjs} +62 -7
- package/dist/plugin-DuB1UozS.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/package.json +4 -4
- package/src/dotnet/naming.ts +1 -1
- package/src/go/naming.ts +1 -1
- package/src/index.ts +15 -0
- package/src/node/resources.ts +7 -3
- package/src/node/tests.ts +29 -3
- package/src/snippets/dotnet.ts +159 -0
- package/src/snippets/go.ts +148 -0
- package/src/snippets/index.ts +8 -0
- package/src/snippets/kotlin.ts +144 -0
- package/src/snippets/php.ts +149 -0
- package/src/snippets/plugin.ts +36 -0
- package/src/snippets/python.ts +135 -0
- package/src/snippets/ruby.ts +152 -0
- package/src/snippets/rust.ts +189 -0
- package/test/snippets/_helpers.ts +67 -0
- package/test/snippets/dotnet.test.ts +49 -0
- package/test/snippets/go.test.ts +94 -0
- package/test/snippets/kotlin.test.ts +53 -0
- package/test/snippets/php.test.ts +48 -0
- package/test/snippets/python.test.ts +73 -0
- package/test/snippets/ruby.test.ts +339 -0
- package/test/snippets/rust.test.ts +76 -0
- package/dist/plugin-C2Hp2Vs2.mjs.map +0 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ApiSpec, EmitterContext, Field, Model, Operation, ResolvedOperation, Service } from '@workos/oagen';
|
|
2
|
+
import { defaultSdkBehavior, toPascalCase, toSnakeCase } from '@workos/oagen';
|
|
3
|
+
|
|
4
|
+
export function makeSpec(services: Service[], models: Model[] = []): ApiSpec {
|
|
5
|
+
return {
|
|
6
|
+
name: 'Test',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
baseUrl: '',
|
|
9
|
+
services,
|
|
10
|
+
models,
|
|
11
|
+
enums: [],
|
|
12
|
+
sdk: defaultSdkBehavior(),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildResolvedOps(services: Service[]): ResolvedOperation[] {
|
|
17
|
+
const ops: ResolvedOperation[] = [];
|
|
18
|
+
for (const service of services) {
|
|
19
|
+
const mountOn = toPascalCase(service.name);
|
|
20
|
+
for (const op of service.operations) {
|
|
21
|
+
ops.push({
|
|
22
|
+
operation: op,
|
|
23
|
+
service,
|
|
24
|
+
methodName: toSnakeCase(op.name),
|
|
25
|
+
mountOn,
|
|
26
|
+
defaults: {},
|
|
27
|
+
inferFromClient: [],
|
|
28
|
+
urlBuilder: false,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return ops;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function makeCtx(spec: ApiSpec): EmitterContext {
|
|
36
|
+
return {
|
|
37
|
+
namespace: 'workos',
|
|
38
|
+
namespacePascal: 'WorkOS',
|
|
39
|
+
spec,
|
|
40
|
+
resolvedOperations: buildResolvedOps(spec.services),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function makeOp(overrides: Partial<Operation>): Operation {
|
|
45
|
+
return {
|
|
46
|
+
name: 'listOrganizations',
|
|
47
|
+
httpMethod: 'get',
|
|
48
|
+
path: '/organizations',
|
|
49
|
+
pathParams: [],
|
|
50
|
+
queryParams: [],
|
|
51
|
+
headerParams: [],
|
|
52
|
+
requestBody: undefined,
|
|
53
|
+
response: { kind: 'model', name: 'Organization' },
|
|
54
|
+
errors: [],
|
|
55
|
+
injectIdempotencyKey: false,
|
|
56
|
+
...overrides,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function makeStringField(name: string, example?: string, required = true): Field {
|
|
61
|
+
return {
|
|
62
|
+
name,
|
|
63
|
+
type: { kind: 'primitive', type: 'string' },
|
|
64
|
+
required,
|
|
65
|
+
...(example !== undefined ? { example } : {}),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Model, Service } from '@workos/oagen';
|
|
3
|
+
import { runSnippetEmitters } from '@workos/oagen';
|
|
4
|
+
import { dotnetSnippetEmitter } from '../../src/snippets/dotnet.js';
|
|
5
|
+
import { makeCtx, makeOp, makeSpec, makeStringField } from './_helpers.js';
|
|
6
|
+
|
|
7
|
+
function runDotnet(services: Service[], models: Model[] = []): string {
|
|
8
|
+
const results = runSnippetEmitters([dotnetSnippetEmitter], makeCtx(makeSpec(services, models)));
|
|
9
|
+
return results[0]!.content;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('snippets/dotnet', () => {
|
|
13
|
+
it('renders WorkOSClient with WorkOSOptions and an Async-suffixed call', () => {
|
|
14
|
+
const content = runDotnet([{ name: 'Organizations', operations: [makeOp({ name: 'list_organizations' })] }]);
|
|
15
|
+
expect(content).toContain('using WorkOS;');
|
|
16
|
+
expect(content).toContain('var client = new WorkOSClient(new WorkOSOptions');
|
|
17
|
+
expect(content).toContain('ApiKey = "sk_example_123456789"');
|
|
18
|
+
expect(content).toContain('ClientId = "client_123456789"');
|
|
19
|
+
// Mount target `Organizations`; method `list_organizations` → ListAsync.
|
|
20
|
+
expect(content).toContain('await client.Organizations.ListAsync();');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('builds the typed Options class for body args', () => {
|
|
24
|
+
const content = runDotnet(
|
|
25
|
+
[
|
|
26
|
+
{
|
|
27
|
+
name: 'Organizations',
|
|
28
|
+
operations: [
|
|
29
|
+
makeOp({
|
|
30
|
+
name: 'create_organization',
|
|
31
|
+
httpMethod: 'post',
|
|
32
|
+
path: '/organizations',
|
|
33
|
+
requestBody: { kind: 'model', name: 'CreateOrgReq' },
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
name: 'CreateOrgReq',
|
|
41
|
+
fields: [makeStringField('name', 'Foo Corp')],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
);
|
|
45
|
+
expect(content).toContain('await client.Organizations.CreateAsync(');
|
|
46
|
+
expect(content).toContain('new OrganizationsCreateOptions');
|
|
47
|
+
expect(content).toContain('Name = "Foo Corp",');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Model, Service } from '@workos/oagen';
|
|
3
|
+
import { runSnippetEmitters } from '@workos/oagen';
|
|
4
|
+
import { goSnippetEmitter } from '../../src/snippets/go.js';
|
|
5
|
+
import { makeCtx, makeOp, makeSpec, makeStringField } from './_helpers.js';
|
|
6
|
+
|
|
7
|
+
function runGo(services: Service[], models: Model[] = []): string {
|
|
8
|
+
const results = runSnippetEmitters([goSnippetEmitter], makeCtx(makeSpec(services, models)));
|
|
9
|
+
return results[0]!.content;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('snippets/go', () => {
|
|
13
|
+
it('renders a package main with context import and client init', () => {
|
|
14
|
+
const content = runGo([{ name: 'Organizations', operations: [makeOp({ name: 'list_organizations' })] }]);
|
|
15
|
+
expect(content).toContain('package main');
|
|
16
|
+
expect(content).toContain('"context"');
|
|
17
|
+
expect(content).toContain('"github.com/workos/workos-go/v9"');
|
|
18
|
+
expect(content).toContain('client := workos.NewClient("sk_example_123456789")');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('trims the mount-target resource from the method name', () => {
|
|
22
|
+
// Mount target is `Organizations`; the resolved method `create_organization`
|
|
23
|
+
// becomes `CreateOrganization` then trims to `Create`.
|
|
24
|
+
const content = runGo(
|
|
25
|
+
[
|
|
26
|
+
{
|
|
27
|
+
name: 'Organizations',
|
|
28
|
+
operations: [
|
|
29
|
+
makeOp({
|
|
30
|
+
name: 'create_organization',
|
|
31
|
+
httpMethod: 'post',
|
|
32
|
+
path: '/organizations',
|
|
33
|
+
requestBody: { kind: 'model', name: 'CreateOrgReq' },
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
[{ name: 'CreateOrgReq', fields: [makeStringField('name', 'Foo Corp')] }],
|
|
39
|
+
);
|
|
40
|
+
expect(content).toContain('client.Organizations().Create(');
|
|
41
|
+
expect(content).not.toContain('CreateOrganization(');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('renders required body fields inside a typed opts struct', () => {
|
|
45
|
+
const content = runGo(
|
|
46
|
+
[
|
|
47
|
+
{
|
|
48
|
+
name: 'Organizations',
|
|
49
|
+
operations: [
|
|
50
|
+
makeOp({
|
|
51
|
+
name: 'create_organization',
|
|
52
|
+
httpMethod: 'post',
|
|
53
|
+
path: '/organizations',
|
|
54
|
+
requestBody: { kind: 'model', name: 'CreateOrgReq' },
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
name: 'CreateOrgReq',
|
|
62
|
+
fields: [makeStringField('name', 'Foo Corp'), makeStringField('description', undefined, false)],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
);
|
|
66
|
+
expect(content).toContain('&workos.OrganizationsCreateParams{');
|
|
67
|
+
expect(content).toContain('Name: "Foo Corp",');
|
|
68
|
+
expect(content).not.toContain('Description:');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('passes required path params positionally before the opts struct', () => {
|
|
72
|
+
const content = runGo([
|
|
73
|
+
{
|
|
74
|
+
name: 'Organizations',
|
|
75
|
+
operations: [
|
|
76
|
+
makeOp({
|
|
77
|
+
name: 'get_organization',
|
|
78
|
+
httpMethod: 'get',
|
|
79
|
+
path: '/organizations/{id}',
|
|
80
|
+
pathParams: [
|
|
81
|
+
{
|
|
82
|
+
name: 'id',
|
|
83
|
+
type: { kind: 'primitive', type: 'string' },
|
|
84
|
+
required: true,
|
|
85
|
+
example: 'org_123',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
}),
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
expect(content).toContain('client.Organizations().Get(context.Background(), "org_123")');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Model, Service } from '@workos/oagen';
|
|
3
|
+
import { runSnippetEmitters } from '@workos/oagen';
|
|
4
|
+
import { kotlinSnippetEmitter } from '../../src/snippets/kotlin.js';
|
|
5
|
+
import { makeCtx, makeOp, makeSpec, makeStringField } from './_helpers.js';
|
|
6
|
+
|
|
7
|
+
function runKotlin(services: Service[], models: Model[] = []): string {
|
|
8
|
+
const results = runSnippetEmitters([kotlinSnippetEmitter], makeCtx(makeSpec(services, models)));
|
|
9
|
+
return results[0]!.content;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('snippets/kotlin (Java-syntax output)', () => {
|
|
13
|
+
it('emits the .java file extension and Java client init', () => {
|
|
14
|
+
const results = runSnippetEmitters(
|
|
15
|
+
[kotlinSnippetEmitter],
|
|
16
|
+
makeCtx(makeSpec([{ name: 'Organizations', operations: [makeOp({ name: 'list_organizations' })] }])),
|
|
17
|
+
);
|
|
18
|
+
expect(results[0]!.fileExtension).toBe('java');
|
|
19
|
+
expect(results[0]!.language).toBe('java');
|
|
20
|
+
expect(results[0]!.content).toContain('import com.workos.WorkOS;');
|
|
21
|
+
expect(results[0]!.content).toContain('WorkOS workos = new WorkOS("sk_example_123456789");');
|
|
22
|
+
expect(results[0]!.content).toContain('workos.organizations.listOrganizations();');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('builds the typed Options class via builder() for body args', () => {
|
|
26
|
+
const content = runKotlin(
|
|
27
|
+
[
|
|
28
|
+
{
|
|
29
|
+
name: 'Organizations',
|
|
30
|
+
operations: [
|
|
31
|
+
makeOp({
|
|
32
|
+
name: 'create_organization',
|
|
33
|
+
httpMethod: 'post',
|
|
34
|
+
path: '/organizations',
|
|
35
|
+
requestBody: { kind: 'model', name: 'CreateOrgReq' },
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
name: 'CreateOrgReq',
|
|
43
|
+
fields: [makeStringField('name', 'Foo Corp')],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
);
|
|
47
|
+
expect(content).toContain('import com.workos.organizations.OrganizationsApi.CreateOrganizationOptions;');
|
|
48
|
+
expect(content).toContain('CreateOrganizationOptions options = CreateOrganizationOptions.builder()');
|
|
49
|
+
expect(content).toContain('.name("Foo Corp")');
|
|
50
|
+
expect(content).toContain('.build();');
|
|
51
|
+
expect(content).toContain('workos.organizations.createOrganization(options);');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Model, Service } from '@workos/oagen';
|
|
3
|
+
import { runSnippetEmitters } from '@workos/oagen';
|
|
4
|
+
import { phpSnippetEmitter } from '../../src/snippets/php.js';
|
|
5
|
+
import { makeCtx, makeOp, makeSpec, makeStringField } from './_helpers.js';
|
|
6
|
+
|
|
7
|
+
function runPhp(services: Service[], models: Model[] = []): string {
|
|
8
|
+
const results = runSnippetEmitters([phpSnippetEmitter], makeCtx(makeSpec(services, models)));
|
|
9
|
+
return results[0]!.content;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('snippets/php', () => {
|
|
13
|
+
it('renders the modern WorkOS client constructor with named args', () => {
|
|
14
|
+
const content = runPhp([{ name: 'Organizations', operations: [makeOp({ name: 'list_organizations' })] }]);
|
|
15
|
+
expect(content).toContain('<?php');
|
|
16
|
+
expect(content).toContain('use WorkOS\\WorkOS;');
|
|
17
|
+
expect(content).toContain("apiKey: 'sk_example_123456789'");
|
|
18
|
+
expect(content).toContain("clientId: 'client_123456789'");
|
|
19
|
+
expect(content).toContain('$workos->organizations()->listOrganizations();');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('camelCases body field kwargs and uses PHP named args', () => {
|
|
23
|
+
const content = runPhp(
|
|
24
|
+
[
|
|
25
|
+
{
|
|
26
|
+
name: 'Organizations',
|
|
27
|
+
operations: [
|
|
28
|
+
makeOp({
|
|
29
|
+
name: 'create_organization',
|
|
30
|
+
httpMethod: 'post',
|
|
31
|
+
path: '/organizations',
|
|
32
|
+
requestBody: { kind: 'model', name: 'CreateOrgReq' },
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
{
|
|
39
|
+
name: 'CreateOrgReq',
|
|
40
|
+
fields: [makeStringField('name', 'Foo Corp'), makeStringField('external_id', 'ext_123')],
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
);
|
|
44
|
+
expect(content).toContain('$workos->organizations()->createOrganization(');
|
|
45
|
+
expect(content).toContain("name: 'Foo Corp'");
|
|
46
|
+
expect(content).toContain("externalId: 'ext_123'");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Model, Service } from '@workos/oagen';
|
|
3
|
+
import { runSnippetEmitters } from '@workos/oagen';
|
|
4
|
+
import { pythonSnippetEmitter } from '../../src/snippets/python.js';
|
|
5
|
+
import { makeCtx, makeOp, makeSpec, makeStringField } from './_helpers.js';
|
|
6
|
+
|
|
7
|
+
function runPython(services: Service[], models: Model[] = []): string {
|
|
8
|
+
const results = runSnippetEmitters([pythonSnippetEmitter], makeCtx(makeSpec(services, models)));
|
|
9
|
+
return results[0]!.content;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('snippets/python', () => {
|
|
13
|
+
it('renders a no-arg call with the WorkOSClient constructor', () => {
|
|
14
|
+
const content = runPython([{ name: 'Organizations', operations: [makeOp({ name: 'list_organizations' })] }]);
|
|
15
|
+
expect(content).toContain('from workos import WorkOSClient');
|
|
16
|
+
expect(content).toContain('client = WorkOSClient(api_key="sk_example_123456789", client_id="client_123456789")');
|
|
17
|
+
expect(content).toContain('client.organizations.list_organizations()');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renames Python builtin path params with a trailing underscore', () => {
|
|
21
|
+
const content = runPython([
|
|
22
|
+
{
|
|
23
|
+
name: 'Organizations',
|
|
24
|
+
operations: [
|
|
25
|
+
makeOp({
|
|
26
|
+
name: 'get_organization',
|
|
27
|
+
path: '/organizations/{id}',
|
|
28
|
+
pathParams: [
|
|
29
|
+
{
|
|
30
|
+
name: 'id',
|
|
31
|
+
type: { kind: 'primitive', type: 'string' },
|
|
32
|
+
required: true,
|
|
33
|
+
example: 'org_123',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
// Python builtins ('id', 'type') get an underscore via safeParamName,
|
|
41
|
+
// so the snippet exposes `id_=` instead of shadowing the builtin.
|
|
42
|
+
expect(content).toContain('client.organizations.get_organization(id_="org_123")');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('expands required body fields as kwargs', () => {
|
|
46
|
+
const content = runPython(
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
name: 'Organizations',
|
|
50
|
+
operations: [
|
|
51
|
+
makeOp({
|
|
52
|
+
name: 'create_organization',
|
|
53
|
+
httpMethod: 'post',
|
|
54
|
+
path: '/organizations',
|
|
55
|
+
requestBody: { kind: 'model', name: 'CreateOrgReq' },
|
|
56
|
+
}),
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
{
|
|
62
|
+
name: 'CreateOrgReq',
|
|
63
|
+
fields: [
|
|
64
|
+
makeStringField('name', 'Foo Corp'),
|
|
65
|
+
makeStringField('description', undefined, false), // optional
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
);
|
|
70
|
+
expect(content).toContain('name="Foo Corp"');
|
|
71
|
+
expect(content).not.toContain('description');
|
|
72
|
+
});
|
|
73
|
+
});
|