Skip to content

Commit 96f9e8b

Browse files
authored
build: resolve update-api workflow failure (#3766)
* fix update-api workflow failing * readability * add discovery docs filenames validation
1 parent 2b8be6d commit 96f9e8b

File tree

2 files changed

+93
-9
lines changed

2 files changed

+93
-9
lines changed

src/generator/download.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,40 @@ export async function downloadDiscoveryDocs(
113113
return changes;
114114
}
115115

116+
/**
117+
* Checks that a discovery doc file name is in the expected format of
118+
* [NAME(alphanumeric)]-[VERSION(alphanumeric, _, .)].json.
119+
* Throws an error if the file name is not in the expected format.
120+
* @param fileName The file name to validate
121+
*/
122+
export function validateDiscoveryDocFileName(fileName: string) {
123+
const regex = /^[a-zA-Z0-9]+-[a-zA-Z0-9_.]+\.json$/;
124+
if (!regex.test(fileName)) {
125+
throw new Error(
126+
`Discovery doc file name '${fileName}' is not in the expected format of '[NAME(alphanumeric)]-[VERSION(alphanumeric, _, .)].json'.`,
127+
);
128+
}
129+
}
130+
131+
export interface ApiData {
132+
name: string;
133+
version: string;
134+
}
135+
136+
/**
137+
* Parses a discovery doc file name and returns the API name and version.
138+
* @param fileName The file name to parse.
139+
* @returns The API data (name and version).
140+
*/
141+
export function getApiData(fileName: string): ApiData {
142+
validateDiscoveryDocFileName(fileName);
143+
const firstHyphenIndex = fileName.indexOf('-');
144+
const lastDotIndex = fileName.lastIndexOf('.');
145+
const name = fileName.substring(0, firstHyphenIndex);
146+
const version = fileName.substring(firstHyphenIndex + 1, lastDotIndex);
147+
return {name, version};
148+
}
149+
116150
// These are libraries we should no longer support because
117151
// they are not present in the index.json
118152
// example: b/148605368
@@ -123,22 +157,21 @@ function cleanupLibrariesNotInIndexJSON(
123157
const srcPath = path.join(__dirname, '../../../src', 'apis');
124158
const discoveryDirectory = fs.readdirSync(options.downloadPath);
125159
const apisReplaced = apis.map(
126-
x => x.id.toString().replace(':', '-') + '.json',
160+
api => api.id.toString().replace(':', '-') + '.json',
127161
);
128162
// So that we don't delete index.json
129163
apisReplaced.push('index.json');
130164
const discoveryDocsToDelete = discoveryDirectory.filter(
131-
x => !apisReplaced.includes(x),
165+
fileName => !apisReplaced.includes(fileName),
132166
);
133-
const clientFilesToDelete = discoveryDocsToDelete.map(x => {
134-
const apiName = x.split('-')[0];
135-
const versionName = apiName[1].split('.')[0];
136-
return path.join(srcPath, apiName, `${versionName}.ts`);
167+
const clientFilesToDelete = discoveryDocsToDelete.map(docFileName => {
168+
const api = getApiData(docFileName);
169+
return path.join(srcPath, api.name, `${api.version}.ts`);
137170
});
138-
discoveryDocsToDelete.forEach(x =>
139-
fs.unlinkSync(path.join(options.downloadPath, x)),
171+
discoveryDocsToDelete.forEach(docFileName =>
172+
fs.unlinkSync(path.join(options.downloadPath, docFileName)),
140173
);
141-
clientFilesToDelete.forEach(x => fs.unlinkSync(x));
174+
clientFilesToDelete.forEach(clientFile => fs.unlinkSync(clientFile));
142175
}
143176

144177
const ignoreLines = /^\s+"(?:etag|revision)": ".+"/;

test/test.download.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,35 @@ describe(__filename, () => {
153153
scopes.forEach(s => s.done());
154154
});
155155

156+
it('should clean up old files', async () => {
157+
const readdirSync = sandbox.stub(fs, 'readdirSync');
158+
const unlinkSync = sandbox.stub(fs, 'unlinkSync');
159+
const downloadPath = 'build/test/temp';
160+
readdirSync.returns(['a-v1.json', 'blogger-v2.json'] as any);
161+
const scopes = [
162+
nock(
163+
'https://raw.githubusercontent.com/googleapis/discovery-artifact-manager/master/discoveries',
164+
)
165+
.get('/index.json')
166+
.reply(200, JSON.stringify(fs.readFileSync(fakeIndexPath, 'utf8')), {
167+
'Content-Type': 'application/json',
168+
}),
169+
nock(
170+
'https://raw.githubusercontent.com/googleapis/discovery-artifact-manager/master/discoveries',
171+
)
172+
.get('/fake.v1.json')
173+
.reply(
174+
200,
175+
'{"id": "fake:v1","discoveryRestUrl": "http://localhost:3030/path","name": "fake","version": "v1"}',
176+
),
177+
];
178+
await dn.downloadDiscoveryDocs({discoveryUrl, downloadPath});
179+
assert(unlinkSync.calledWith(path.join(downloadPath, 'blogger-v2.json')));
180+
const expected = path.join(__dirname, '../../src/apis/blogger/v2.ts');
181+
assert(unlinkSync.calledWith(expected));
182+
scopes.forEach(s => s.done());
183+
});
184+
156185
it('should be invokable from the CLI', async () => {
157186
const port = 3030;
158187
const server = http
@@ -235,4 +264,26 @@ describe(__filename, () => {
235264
const result = dn.getDiffs(oldDoc, newDoc);
236265
assert.deepStrictEqual(result, expected);
237266
});
267+
268+
it('should validate discovery doc file names', () => {
269+
assert.doesNotThrow(() =>
270+
dn.validateDiscoveryDocFileName('apiname-v1.json'),
271+
);
272+
assert.doesNotThrow(() =>
273+
dn.validateDiscoveryDocFileName('apiname-v1.1.json'),
274+
);
275+
assert.doesNotThrow(() =>
276+
dn.validateDiscoveryDocFileName('apiname-v1_beta.json'),
277+
);
278+
assert.throws(() =>
279+
dn.validateDiscoveryDocFileName('apiname-v1-extra.json'),
280+
);
281+
assert.throws(() => dn.validateDiscoveryDocFileName('api_name-v1.json'));
282+
assert.throws(() => dn.validateDiscoveryDocFileName('apiname-v1.js'));
283+
});
284+
285+
it('should parse discovery doc file names', () => {
286+
const result = dn.getApiData('apiname-v1.2.json');
287+
assert.deepStrictEqual(result, {name: 'apiname', version: 'v1.2'});
288+
});
238289
});

0 commit comments

Comments
 (0)