Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds option to disable client auto creation in SDK #1526

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pink-seahorses-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hey-api/openapi-ts': minor
---

Add SDK option to disable the auto-creation of the client
1 change: 1 addition & 0 deletions packages/openapi-ts/src/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const compiler = {
propertyAccessExpression: types.createPropertyAccessExpression,
propertyAccessExpressions: transform.createPropertyAccessExpressions,
propertyAssignment: types.createPropertyAssignment,
propertyDeclaration: types.createPropertyDeclaration,
regularExpressionLiteral: types.createRegularExpressionLiteral,
returnFunctionCall: _return.createReturnFunctionCall,
returnStatement: _return.createReturnStatement,
Expand Down
36 changes: 36 additions & 0 deletions packages/openapi-ts/src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,42 @@
name: string | ts.PropertyName;
}) => ts.factory.createPropertyAssignment(name, initializer);

export const createPropertyDeclaration = ({
accessLevel,
comment,
initializer,
isReadonly,
name,
type,
}: {
accessLevel?: AccessLevel;
comment?: Comments;
initializer?: ts.Expression;
isReadonly?: boolean;
name: string;
type: ts.TypeNode;
}) => {
const modifiers = toAccessLevelModifiers(accessLevel);
if (isReadonly) {
modifiers.push(ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword));
}

const node = ts.factory.createPropertyDeclaration(
modifiers,
createIdentifier({ text: name }),
undefined,
type,
initializer,
);

addLeadingComments({
comments: comment,
node,
});

return node;
};

Check warning on line 935 in packages/openapi-ts/src/compiler/types.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/compiler/types.ts#L902-L935

Added lines #L902 - L935 were not covered by tests

export const createRegularExpressionLiteral = ({
flags = [],
text,
Expand Down
4 changes: 4 additions & 0 deletions packages/openapi-ts/src/generate/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export const clientModulePath = ({
};

export const clientApi = {
Client: {
asType: true,
name: 'Client',
},
Options: {
asType: true,
name: 'Options',
Expand Down
1 change: 1 addition & 0 deletions packages/openapi-ts/src/plugins/@hey-api/sdk/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const defaultConfig: Plugin.Config<Config> = {
},
asClass: false,
auth: true,
autoCreateClient: true,
exportFromIndex: true,
name: '@hey-api/sdk',
operationId: true,
Expand Down
156 changes: 113 additions & 43 deletions packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
import type { Config } from './types';

export const operationOptionsType = ({
clientOption,
identifierData,
throwOnError,
}: {
clientOption?: 'required' | 'omitted';
context: IR.Context;
identifierData?: ReturnType<TypeScriptFile['identifier']>;
// TODO: refactor this so we don't need to import error type unless it's used here
Expand All @@ -39,13 +41,24 @@
}) => {
const optionsName = clientApi.Options.name;

let optionsType = identifierData
? `${optionsName}<${identifierData.name}>`
: optionsName;

Check warning on line 46 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L46

Added line #L46 was not covered by tests

// TODO: refactor this to be more generic, works for now
if (throwOnError) {
return `${optionsName}<${identifierData?.name || 'unknown'}, ${throwOnError}>`;
optionsType = `${optionsName}<${identifierData?.name || 'unknown'}, ${throwOnError}>`;
}
return identifierData
? `${optionsName}<${identifierData.name}>`
: optionsName;

if (clientOption === 'required') {
optionsType += ` & { client: Client }`;
}

Check warning on line 55 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L54-L55

Added lines #L54 - L55 were not covered by tests

if (clientOption === 'omitted') {
optionsType = `Omit<${optionsType}, 'client'>`;
}

Check warning on line 59 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L58-L59

Added lines #L58 - L59 were not covered by tests

return optionsType;
};

const sdkId = 'sdk';
Expand Down Expand Up @@ -377,6 +390,12 @@
value: operation.path,
});

let clientCall = '(options?.client ?? client)';

if (!plugin.autoCreateClient) {
clientCall = plugin.asClass ? 'this._client' : 'options.client';
}

Check warning on line 397 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L396-L397

Added lines #L396 - L397 were not covered by tests

return [
compiler.returnFunctionCall({
args: [
Expand All @@ -385,7 +404,7 @@
obj: requestOptions,
}),
],
name: `(options?.client ?? client).${operation.method}`,
name: `${clientCall}.${operation.method}`,
types: [
identifierResponse.name || 'unknown',
identifierError.name || 'unknown',
Expand Down Expand Up @@ -417,7 +436,7 @@
operation.summary && escapeComment(operation.summary),
operation.description && escapeComment(operation.description),
],
isStatic: true,
isStatic: !!plugin.autoCreateClient, // if client is required, methods are not static

Check warning on line 439 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L439

Added line #L439 was not covered by tests
name: serviceFunctionIdentifier({
config: context.config,
handleIllegal: false,
Expand All @@ -429,6 +448,7 @@
isRequired: hasOperationDataRequired(operation),
name: 'options',
type: operationOptionsType({
clientOption: !plugin.autoCreateClient ? 'omitted' : undefined,

Check warning on line 451 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L451

Added line #L451 was not covered by tests
context,
identifierData,
// identifierError,
Expand Down Expand Up @@ -466,9 +486,45 @@

context.subscribe('after', () => {
for (const [name, nodes] of sdks) {
const extraMembers: ts.ClassElement[] = [];

// Add client property and constructor if autoCreateClient is false
if (!plugin.autoCreateClient) {
const clientType = 'Client';

const clientProperty = compiler.propertyDeclaration({
accessLevel: 'private',
comment: ['Client Instance'],
name: '_client',
type: compiler.typeReferenceNode({ typeName: clientType }),
});

const constructor = compiler.constructorDeclaration({
comment: ['@param client - Client Instance'],
parameters: [
{
isRequired: true,
name: 'client',
type: clientType,
},
],
statements: [
compiler.expressionToStatement({
expression: compiler.binaryExpression({
left: compiler.identifier({ text: 'this._client ' }),
operator: '=',
right: compiler.identifier({ text: 'client' }),
}),
}),
],
});

extraMembers.push(clientProperty, constructor);
}

Check warning on line 524 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L489-L524

Added lines #L489 - L524 were not covered by tests
const node = compiler.classDeclaration({
decorator: undefined,
members: nodes,
members: [...extraMembers, ...nodes],

Check warning on line 527 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L527

Added line #L527 was not covered by tests
name: transformServiceName({
config: context.config,
name,
Expand Down Expand Up @@ -503,9 +559,11 @@
expression: compiler.arrowFunction({
parameters: [
{
isRequired: hasOperationDataRequired(operation),
isRequired:
hasOperationDataRequired(operation) || !plugin.autoCreateClient,
name: 'options',
type: operationOptionsType({
clientOption: !plugin.autoCreateClient ? 'required' : undefined,
context,
identifierData,
// identifierError,
Expand Down Expand Up @@ -550,53 +608,65 @@
id: sdkId,
path: plugin.output,
});

const sdkOutput = file.nameWithoutExtension();

// import required packages and core files
const clientModule = clientModulePath({
config: context.config,
sourceOutput: sdkOutput,
});
file.import({
module: clientModule,
name: 'createClient',
});
file.import({
module: clientModule,
name: 'createConfig',
});

if (plugin.autoCreateClient) {
file.import({
module: clientModule,
name: 'createClient',
});
file.import({
module: clientModule,
name: 'createConfig',
});

// define client first
const statement = compiler.constVariable({
exportConst: true,
expression: compiler.callExpression({
functionName: 'createClient',
parameters: [
compiler.callExpression({
functionName: 'createConfig',
parameters: [
plugin.throwOnError
? compiler.objectExpression({
obj: [
{
key: 'throwOnError',
value: plugin.throwOnError,
},
],
})

Check warning on line 647 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L640-L647

Added lines #L640 - L647 were not covered by tests
: undefined,
],
}),
],
}),
name: 'client',
});

file.add(statement);
} else {
// Bring in the client type
file.import({
...clientApi.Client,
module: clientModule,
});
}

Check warning on line 663 in packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts#L658-L663

Added lines #L658 - L663 were not covered by tests

file.import({
...clientApi.Options,
module: clientModule,
});

// define client first
const statement = compiler.constVariable({
exportConst: true,
expression: compiler.callExpression({
functionName: 'createClient',
parameters: [
compiler.callExpression({
functionName: 'createConfig',
parameters: [
plugin.throwOnError
? compiler.objectExpression({
obj: [
{
key: 'throwOnError',
value: plugin.throwOnError,
},
],
})
: undefined,
],
}),
],
}),
name: 'client',
});
file.add(statement);

if (plugin.asClass) {
generateClassSdk({ context, plugin });
} else {
Expand Down
12 changes: 12 additions & 0 deletions packages/openapi-ts/src/plugins/@hey-api/sdk/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ export interface Config extends Plugin.Name<'@hey-api/sdk'> {
* @default true
*/
auth?: boolean;
/**
* **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)**
*
* Should the generated SDK do a createClient call automatically? If this is
* set to false, the generated SDK will expect a client to be passed in during:
*
* - instantiation if asClass is set to true (and the client will be passed to the constructor. All methods will not be static either)
* - each method call if asClass is set to false
*
* @default true
*/
autoCreateClient?: boolean;
/**
* **This feature works only with the legacy parser**
*
Expand Down
Loading