Skip to content

Commit ee7f211

Browse files
authored
Allow to publish an OpenAPI spec from URL (#785)
1 parent 30dad9a commit ee7f211

File tree

4 files changed

+77
-14
lines changed

4 files changed

+77
-14
lines changed

.changeset/good-penguins-clap.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/cli': patch
3+
---
4+
5+
Allow to publish an OpenAPI spec from URL

packages/cli/src/cli.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import checkNodeVersion from 'check-node-version';
44
import { program } from 'commander';
55
import * as path from 'path';
6+
import { URL } from 'url';
67
import prompts from 'prompts';
78

89
import { GITBOOK_DEFAULT_ENDPOINT } from '@gitbook/api';
@@ -15,7 +16,11 @@ import { publishIntegration, unpublishIntegration } from './publish';
1516
import { authenticate, whoami } from './remote';
1617
import { tailLogs } from './tail';
1718
import { checkIntegrationBuild } from './check';
18-
import { publishOpenAPISpecification } from 'openapi/publish';
19+
import {
20+
publishOpenAPISpecificationFromFilepath,
21+
publishOpenAPISpecificationFromURL,
22+
} from 'openapi/publish';
23+
import { checkIsHTTPURL } from './util';
1924

2025
program
2126
.name(Object.keys(packageJSON.bin)[0])
@@ -120,16 +125,22 @@ const openAPIProgram = program.command('openapi').description('manage OpenAPI sp
120125

121126
openAPIProgram
122127
.command('publish')
123-
.description('publish an OpenAPI specification')
124-
.argument('<file>', 'OpenAPI specification file path')
128+
.description('publish an OpenAPI specification from a file or URL')
129+
.argument('<file-or-url>', 'OpenAPI specification file path or URL')
125130
.requiredOption('-s, --spec <spec>', 'name of the OpenAPI specification')
126131
.requiredOption('-o, --organization <organization>', 'organization to publish to')
127-
.action(async (filepath, options) => {
128-
const spec = await publishOpenAPISpecification({
129-
specSlug: options.spec,
130-
specFilepath: path.resolve(process.cwd(), filepath),
131-
organizationId: options.organization,
132-
});
132+
.action(async (filepathOrURL, options) => {
133+
const spec = checkIsHTTPURL(filepathOrURL)
134+
? await publishOpenAPISpecificationFromURL({
135+
specSlug: options.spec,
136+
organizationId: options.organization,
137+
url: filepathOrURL,
138+
})
139+
: await publishOpenAPISpecificationFromFilepath({
140+
specSlug: options.spec,
141+
organizationId: options.organization,
142+
filepath: path.resolve(process.cwd(), filepathOrURL),
143+
});
133144
console.log(`OpenAPI specification "${options.spec}" published to ${spec.urls.app}`);
134145
});
135146

packages/cli/src/openapi/publish.ts

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,56 @@ import * as api from '@gitbook/api';
55
import { getAPIClient } from '../remote';
66

77
/**
8-
* Publish an OpenAPI specification to GitBook.
8+
* Publish an OpenAPI specification to GitBook from a URL.
99
* If it already exists, it'll update it.
1010
*/
11-
export async function publishOpenAPISpecification(args: {
11+
export async function publishOpenAPISpecificationFromURL(args: {
1212
/**
1313
* The slug of the OpenAPI specification.
1414
*/
1515
specSlug: string;
1616
/**
17-
* The path to the OpenAPI specification file.
17+
* The organization to publish to.
18+
*/
19+
organizationId: string;
20+
/**
21+
* The URL pointing to the OpenAPI specification.
1822
*/
19-
specFilepath: string;
23+
url: string;
24+
}): Promise<api.OpenAPISpec> {
25+
const api = await getAPIClient(true);
26+
const spec = await api.orgs.createOrUpdateOpenApiSpecBySlug(
27+
args.organizationId,
28+
args.specSlug,
29+
{
30+
source: {
31+
url: args.url,
32+
},
33+
},
34+
);
35+
return spec.data;
36+
}
2037

38+
/**
39+
* Publish an OpenAPI specification to GitBook from a file.
40+
* If it already exists, it'll update it.
41+
*/
42+
export async function publishOpenAPISpecificationFromFilepath(args: {
43+
/**
44+
* The slug of the OpenAPI specification.
45+
*/
46+
specSlug: string;
2147
/**
2248
* The organization to publish to.
2349
*/
2450
organizationId: string;
51+
/**
52+
* The path to the OpenAPI specification file.
53+
*/
54+
filepath: string;
2555
}): Promise<api.OpenAPISpec> {
2656
const api = await getAPIClient(true);
27-
const fileContent = await readOpenAPIFile(args.specFilepath);
57+
const fileContent = await readOpenAPIFile(args.filepath);
2858
const spec = await api.orgs.createOrUpdateOpenApiSpecBySlug(
2959
args.organizationId,
3060
args.specSlug,
@@ -37,6 +67,9 @@ export async function publishOpenAPISpecification(args: {
3767
return spec.data;
3868
}
3969

70+
/**
71+
* Read the OpenAPI specification file.
72+
*/
4073
async function readOpenAPIFile(filePath: string): Promise<string> {
4174
try {
4275
const fileContent = await fs.promises.readFile(filePath, 'utf8');

packages/cli/src/util.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Check if a string is a valid HTTP URL
3+
*/
4+
export function checkIsHTTPURL(str: string): boolean {
5+
if (!str.startsWith('http')) {
6+
return false;
7+
}
8+
try {
9+
new URL(str);
10+
return true;
11+
} catch (e) {
12+
return false;
13+
}
14+
}

0 commit comments

Comments
 (0)