Skip to content

Commit

Permalink
feat(api): codegen'd sdks now look a little bit nicer (#769)
Browse files Browse the repository at this point in the history
  • Loading branch information
erunion authored Oct 19, 2023
1 parent df9cadc commit 046b029
Show file tree
Hide file tree
Showing 513 changed files with 22,787 additions and 1,037 deletions.
27 changes: 14 additions & 13 deletions packages/api/src/codegen/languages/typescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,20 +230,19 @@ export default class TSGenerator extends CodeGenerator {
let filePath = sourceFile.getFilePath().toString();
filePath = filePath.substring(1);

/**
* It's not Prettier-level of nice but `ts-morph` offers a method of using the TS
* formatter for formatting code which we can use to make our generated SDK not look like
* total garbage.
*
* @see {@link https://ts-morph.com/manipulation/formatting}
*/
sourceFile.formatText();

return {
[filePath]: sourceFile.getFullText(),
};
}),

// Because we're returning the raw source files for TS generation we also need to separately
// emit out our declaration files so we can put those into a separate file in the installed
// SDK directory.
...this.project
.emitToMemory({ emitOnlyDtsFiles: true })
.getFiles()
.map(sourceFile => ({
[path.basename(sourceFile.filePath)]: sourceFile.text,
})),
].reduce((prev, next) => Object.assign(prev, next));
}

Expand Down Expand Up @@ -383,7 +382,7 @@ sdk.server('https://eu.api.example.com/v14');`),
name: 'createSDK',
initializer: writer => {
// `ts-morph` doesn't have any way to cleanly create an IIFE.
writer.writeLine('(() => { return new SDK(); })()');
writer.write('(() => { return new SDK(); })()');
return writer;
},
},
Expand Down Expand Up @@ -519,7 +518,9 @@ sdk.server('https://eu.api.example.com/v14');`),
moduleSpecifier: `./schemas/${schemaName}`,
});

let str = JSON.stringify(schema);
// Though we aren't using Prettier to make these generated SDKs look amazing we should at
// least make the schema files we generate not look like completely unreadable garbage.
let str = JSON.stringify(schema, null, 2);
let referencedSchemas = str.match(REF_PLACEHOLDER_REGEX)?.map(s => s.replace(REF_PLACEHOLDER_REGEX, '$1'));
if (referencedSchemas) {
referencedSchemas.sort();
Expand Down Expand Up @@ -549,7 +550,7 @@ sdk.server('https://eu.api.example.com/v14');`),
// need to clean them up.
str = str.replace(REF_PLACEHOLDER_REGEX, '$1');

writer.writeLine(`${str} as const`);
writer.write(`${str} as const`);
return writer;
},
},
Expand Down
12 changes: 10 additions & 2 deletions packages/api/test/codegen/languages/typescript/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ function assertSDKFixture(file: string, fixture: string) {
await Promise.all(
expectedFiles.map(filename => {
const actual = actualFiles[filename];
const expectedFilePath = path.join(dir, filename);

// We have to wrap in our current package version into the `<<useragent>>` placeholder so
// we don't need to worry about committing package versions into source control or trying
// to mock out our `packageInfo` library, potentially causing sideeffects in other tests.
return fs
.readFile(path.join(dir, filename), 'utf8')
.readFile(expectedFilePath, 'utf8')
.then(expected => expected.replace('<<package version>>', packageInfo.PACKAGE_VERSION))
.then(expected => {
.then(async expected => {
if (actual !== expected && process.env.UPDATE_FIXTURES) {
// eslint-disable-next-line no-console
console.info('[sdk fixture updated]', expectedFilePath);
await fs.writeFile(expectedFilePath, actual.replace(packageInfo.PACKAGE_VERSION, '<<package version>>'));
return;
}

expect(actual).toBe(expected);
});
}),
Expand Down
3 changes: 1 addition & 2 deletions packages/test-utils/sdks/alby/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,6 @@ class SDK {
}
}

const createSDK = (() => { return new SDK(); })()
;
const createSDK = (() => { return new SDK(); })();

export default createSDK;
91 changes: 89 additions & 2 deletions packages/test-utils/sdks/alby/src/schemas/AmqpExternalRulePatch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,92 @@
import RuleSource from './RuleSource';

const AmqpExternalRulePatch = {"additionalProperties":false,"properties":{"requestMode":{"description":"Single request mode sends each event separately to the endpoint specified by the rule. You can read more about single request mode events in the <a href=\"https://ably.com/documentation/general/events#batching\">Ably documentation</a>.","enum":["single"],"type":"string","examples":["single"]},"ruleType":{"description":"The type of rule. In this case AMQP external (using Firehose). See the <a href=\"https://ably.com/documentation/general/firehose\">Ably documentation</a> for further information.","enum":["amqp/external"],"type":"string"},"source":RuleSource,"status":{"description":"The status of the rule. Rules can be enabled or disabled.","enum":["enabled","disabled"],"type":"string","examples":["enabled"]},"target":{"additionalProperties":false,"properties":{"enveloped":{"description":"Messages delivered through Reactor are wrapped in an Ably envelope by default that contains metadata about the message and its payload. The form of the envelope depends on whether it is part of a Webhook/Function or a Queue/Firehose rule. For everything besides Webhooks, you can ensure you only get the raw payload by unchecking \"Enveloped\" when setting up the rule.","type":["boolean","null"]},"format":{"type":"string"},"headers":{"description":"If you have additional information to send, you'll need to include the relevant headers.","items":{"properties":{"name":{"description":"The name of the header.","type":"string"},"value":{"description":"The value of the header.","type":"string"}},"type":"object"},"type":"array"},"mandatoryRoute":{"description":"Reject delivery of the message if the route does not exist, otherwise fail silently.","type":"boolean"},"messageTtl":{"description":"You can optionally override the default TTL on a queue and specify a TTL in minutes for messages to be persisted. It is unusual to change the default TTL, so if this field is left empty, the default TTL for the queue will be used.","type":"integer"},"persistentMessages":{"description":"Marks the message as persistent, instructing the broker to write it to disk if it is in a durable queue.","type":"boolean"},"routingKey":{"description":"The AMQP routing key. See this <a href=\"https://knowledge.ably.com/what-is-the-format-of-the-routingkey-for-an-amqp-or-kinesis-reactor-rule\">Ably knowledge base article</a> for details.","type":"string"},"url":{"type":"string"}},"type":"object"}},"type":"object","title":"amqp_external_rule_patch","x-readme-ref-name":"amqp_external_rule_patch"} as const
;
const AmqpExternalRulePatch = {
"additionalProperties": false,
"properties": {
"requestMode": {
"description": "Single request mode sends each event separately to the endpoint specified by the rule. You can read more about single request mode events in the <a href=\"https://ably.com/documentation/general/events#batching\">Ably documentation</a>.",
"enum": [
"single"
],
"type": "string",
"examples": [
"single"
]
},
"ruleType": {
"description": "The type of rule. In this case AMQP external (using Firehose). See the <a href=\"https://ably.com/documentation/general/firehose\">Ably documentation</a> for further information.",
"enum": [
"amqp/external"
],
"type": "string"
},
"source": RuleSource,
"status": {
"description": "The status of the rule. Rules can be enabled or disabled.",
"enum": [
"enabled",
"disabled"
],
"type": "string",
"examples": [
"enabled"
]
},
"target": {
"additionalProperties": false,
"properties": {
"enveloped": {
"description": "Messages delivered through Reactor are wrapped in an Ably envelope by default that contains metadata about the message and its payload. The form of the envelope depends on whether it is part of a Webhook/Function or a Queue/Firehose rule. For everything besides Webhooks, you can ensure you only get the raw payload by unchecking \"Enveloped\" when setting up the rule.",
"type": [
"boolean",
"null"
]
},
"format": {
"type": "string"
},
"headers": {
"description": "If you have additional information to send, you'll need to include the relevant headers.",
"items": {
"properties": {
"name": {
"description": "The name of the header.",
"type": "string"
},
"value": {
"description": "The value of the header.",
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"mandatoryRoute": {
"description": "Reject delivery of the message if the route does not exist, otherwise fail silently.",
"type": "boolean"
},
"messageTtl": {
"description": "You can optionally override the default TTL on a queue and specify a TTL in minutes for messages to be persisted. It is unusual to change the default TTL, so if this field is left empty, the default TTL for the queue will be used.",
"type": "integer"
},
"persistentMessages": {
"description": "Marks the message as persistent, instructing the broker to write it to disk if it is in a durable queue.",
"type": "boolean"
},
"routingKey": {
"description": "The AMQP routing key. See this <a href=\"https://knowledge.ably.com/what-is-the-format-of-the-routingkey-for-an-amqp-or-kinesis-reactor-rule\">Ably knowledge base article</a> for details.",
"type": "string"
},
"url": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object",
"title": "amqp_external_rule_patch",
"x-readme-ref-name": "amqp_external_rule_patch"
} as const;
export default AmqpExternalRulePatch
92 changes: 90 additions & 2 deletions packages/test-utils/sdks/alby/src/schemas/AmqpExternalRulePost.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,93 @@
import RuleSource from './RuleSource';

const AmqpExternalRulePost = {"additionalProperties":false,"properties":{"requestMode":{"description":"Single request mode sends each event separately to the endpoint specified by the rule. You can read more about single request mode events in the <a href=\"https://ably.com/documentation/general/events#batching\">Ably documentation</a>.","enum":["single"],"type":"string","examples":["single"]},"ruleType":{"description":"The type of rule. In this case AMQP external (using Firehose). See the <a href=\"https://ably.com/documentation/general/firehose\">documentation</a> for further information.","enum":["amqp/external"],"type":"string"},"source":RuleSource,"target":{"additionalProperties":false,"properties":{"enveloped":{"description":"Messages delivered through Reactor are wrapped in an Ably envelope by default that contains metadata about the message and its payload. The form of the envelope depends on whether it is part of a Webhook/Function or a Queue/Firehose rule. For everything besides Webhooks, you can ensure you only get the raw payload by unchecking \"Enveloped\" when setting up the rule.","type":["boolean","null"]},"format":{"type":"string"},"headers":{"description":"If you have additional information to send, you'll need to include the relevant headers.","items":{"properties":{"name":{"description":"The name of the header.","type":"string"},"value":{"description":"The value of the header.","type":"string"}},"type":"object"},"type":"array"},"mandatoryRoute":{"description":"Reject delivery of the message if the route does not exist, otherwise fail silently.","type":"boolean"},"messageTtl":{"description":"You can optionally override the default TTL on a queue and specify a TTL in minutes for messages to be persisted. It is unusual to change the default TTL, so if this field is left empty, the default TTL for the queue will be used.","type":"integer"},"persistentMessages":{"description":"Marks the message as persistent, instructing the broker to write it to disk if it is in a durable queue.","type":"boolean"},"routingKey":{"description":"The AMQP routing key. See this <a href=\"https://knowledge.ably.com/what-is-the-format-of-the-routingkey-for-an-amqp-or-kinesis-reactor-rule\">Ably knowledge base article</a> for details.","type":"string"},"url":{"type":"string"}},"required":["url","routingKey","mandatoryRoute","persistentMessages"],"type":"object"}},"required":["ruleType","requestMode","source","target"],"type":"object","title":"amqp_external_rule_post","x-readme-ref-name":"amqp_external_rule_post"} as const
;
const AmqpExternalRulePost = {
"additionalProperties": false,
"properties": {
"requestMode": {
"description": "Single request mode sends each event separately to the endpoint specified by the rule. You can read more about single request mode events in the <a href=\"https://ably.com/documentation/general/events#batching\">Ably documentation</a>.",
"enum": [
"single"
],
"type": "string",
"examples": [
"single"
]
},
"ruleType": {
"description": "The type of rule. In this case AMQP external (using Firehose). See the <a href=\"https://ably.com/documentation/general/firehose\">documentation</a> for further information.",
"enum": [
"amqp/external"
],
"type": "string"
},
"source": RuleSource,
"target": {
"additionalProperties": false,
"properties": {
"enveloped": {
"description": "Messages delivered through Reactor are wrapped in an Ably envelope by default that contains metadata about the message and its payload. The form of the envelope depends on whether it is part of a Webhook/Function or a Queue/Firehose rule. For everything besides Webhooks, you can ensure you only get the raw payload by unchecking \"Enveloped\" when setting up the rule.",
"type": [
"boolean",
"null"
]
},
"format": {
"type": "string"
},
"headers": {
"description": "If you have additional information to send, you'll need to include the relevant headers.",
"items": {
"properties": {
"name": {
"description": "The name of the header.",
"type": "string"
},
"value": {
"description": "The value of the header.",
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"mandatoryRoute": {
"description": "Reject delivery of the message if the route does not exist, otherwise fail silently.",
"type": "boolean"
},
"messageTtl": {
"description": "You can optionally override the default TTL on a queue and specify a TTL in minutes for messages to be persisted. It is unusual to change the default TTL, so if this field is left empty, the default TTL for the queue will be used.",
"type": "integer"
},
"persistentMessages": {
"description": "Marks the message as persistent, instructing the broker to write it to disk if it is in a durable queue.",
"type": "boolean"
},
"routingKey": {
"description": "The AMQP routing key. See this <a href=\"https://knowledge.ably.com/what-is-the-format-of-the-routingkey-for-an-amqp-or-kinesis-reactor-rule\">Ably knowledge base article</a> for details.",
"type": "string"
},
"url": {
"type": "string"
}
},
"required": [
"url",
"routingKey",
"mandatoryRoute",
"persistentMessages"
],
"type": "object"
}
},
"required": [
"ruleType",
"requestMode",
"source",
"target"
],
"type": "object",
"title": "amqp_external_rule_post",
"x-readme-ref-name": "amqp_external_rule_post"
} as const;
export default AmqpExternalRulePost
Loading

0 comments on commit 046b029

Please sign in to comment.