Skip to content

Commit 01df060

Browse files
authored
Added script to generate Service Clients (#821)
1 parent 4cc3c34 commit 01df060

File tree

7 files changed

+206
-184
lines changed

7 files changed

+206
-184
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ node_modules
88
telemetryCache
99
quickStart.html
1010
.gitcommit
11+
12+
# Auto generated Service Clients
13+
src/shared/telemetry/clienttelemetry.d.ts
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*!
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as child_process from 'child_process'
7+
import * as del from 'del'
8+
import * as fs from 'fs'
9+
import * as os from 'os'
10+
import * as path from 'path'
11+
12+
/**
13+
* This script uses the AWS JS SDK to generate service clients where the client definition is contained within
14+
* this repo. Client definitions are added at the bottom of this script.
15+
*/
16+
17+
interface ServiceClientDefinition {
18+
serviceName: string
19+
serviceJsonPath: string
20+
}
21+
22+
async function generateServiceClients(serviceClientDefinitions: ServiceClientDefinition[]): Promise<void> {
23+
const tempJsSdkPath = fs.mkdtempSync(path.join(os.tmpdir(), 'vsctk-generate'))
24+
console.log(`Temp JS SDK Repo location: ${tempJsSdkPath}`)
25+
console.log('Serivce Clients to Generate: ', serviceClientDefinitions.map(x => x.serviceName).join(', '))
26+
27+
try {
28+
await cloneJsSdk(tempJsSdkPath)
29+
30+
await insertServiceClientsIntoJsSdk(tempJsSdkPath, serviceClientDefinitions)
31+
32+
await runTypingsGenerator(tempJsSdkPath)
33+
34+
await integrateServiceClients(tempJsSdkPath, serviceClientDefinitions)
35+
36+
console.log('Done generating service client(s)')
37+
} finally {
38+
// Clean up the temp path
39+
del.sync([tempJsSdkPath], { force: true })
40+
}
41+
}
42+
43+
async function cloneJsSdk(destinationPath: string): Promise<void> {
44+
console.log('Cloning AWS JS SDK...')
45+
46+
// Output stderr while it clones so it doesn't look frozen
47+
return new Promise<void>((resolve, reject) => {
48+
const exec = child_process.execFile(
49+
'git',
50+
['clone', '--depth', '1', 'https://github.com/aws/aws-sdk-js.git', destinationPath],
51+
{
52+
encoding: 'utf8'
53+
}
54+
)
55+
56+
exec.stderr.on('data', (data: any) => {
57+
console.log(data)
58+
})
59+
60+
exec.once('close', (code, signal) => {
61+
exec.stdout.removeAllListeners()
62+
resolve()
63+
})
64+
})
65+
}
66+
67+
async function insertServiceClientsIntoJsSdk(
68+
jsSdkPath: string,
69+
serviceClientDefinitions: ServiceClientDefinition[]
70+
): Promise<void> {
71+
serviceClientDefinitions.forEach(serviceClientDefinition => {
72+
const apiVersion = getApiVersion(serviceClientDefinition.serviceJsonPath)
73+
74+
// Copy the Service Json into the JS SDK for generation
75+
const jsSdkServiceJsonPath = path.join(
76+
jsSdkPath,
77+
'apis',
78+
`${serviceClientDefinition.serviceName.toLowerCase()}-${apiVersion}.normal.json`
79+
)
80+
fs.copyFileSync(serviceClientDefinition.serviceJsonPath, jsSdkServiceJsonPath)
81+
})
82+
83+
const apiMetadataPath = path.join(jsSdkPath, 'apis', 'metadata.json')
84+
await patchServicesIntoApiMetadata(apiMetadataPath, serviceClientDefinitions.map(x => x.serviceName))
85+
}
86+
87+
interface ServiceJsonSchema {
88+
metadata: {
89+
apiVersion: string
90+
}
91+
}
92+
93+
function getApiVersion(serviceJsonPath: string): string {
94+
const json = fs.readFileSync(serviceJsonPath).toString()
95+
const serviceJson = JSON.parse(json) as ServiceJsonSchema
96+
97+
return serviceJson.metadata.apiVersion
98+
}
99+
100+
interface ApiMetadata {
101+
[key: string]: { name: string }
102+
}
103+
104+
/**
105+
* Updates the JS SDK's api metadata to contain the provided services
106+
*/
107+
async function patchServicesIntoApiMetadata(apiMetadataPath: string, serviceNames: string[]): Promise<void> {
108+
console.log(`Patching services (${serviceNames.join(', ')}) into API Metadata...`)
109+
110+
const apiMetadataJson = fs.readFileSync(apiMetadataPath).toString()
111+
const apiMetadata = JSON.parse(apiMetadataJson) as ApiMetadata
112+
113+
serviceNames.forEach(serviceName => {
114+
apiMetadata[serviceName.toLowerCase()] = { name: serviceName }
115+
})
116+
117+
fs.writeFileSync(apiMetadataPath, JSON.stringify(apiMetadata, undefined, 4))
118+
}
119+
120+
/**
121+
* Generates service clients
122+
*/
123+
async function runTypingsGenerator(repoPath: string): Promise<void> {
124+
console.log('Generating service client typings...')
125+
126+
const stdout = child_process.execFileSync('node', ['scripts/typings-generator.js'], {
127+
encoding: 'utf8',
128+
cwd: repoPath
129+
})
130+
console.log(stdout)
131+
}
132+
133+
/**
134+
* Copies the generated service clients into the repo
135+
*/
136+
async function integrateServiceClients(
137+
repoPath: string,
138+
serviceClientDefinitions: ServiceClientDefinition[]
139+
): Promise<void> {
140+
for (const serviceClientDefinition of serviceClientDefinitions) {
141+
await integrateServiceClient(
142+
repoPath,
143+
serviceClientDefinition.serviceJsonPath,
144+
serviceClientDefinition.serviceName
145+
)
146+
}
147+
}
148+
149+
/**
150+
* Copies the generated service client into the repo
151+
*/
152+
async function integrateServiceClient(repoPath: string, serviceJsonPath: string, serviceName: string): Promise<void> {
153+
const typingsFilename = `${serviceName.toLowerCase()}.d.ts`
154+
const sourceClientPath = path.join(repoPath, 'clients', typingsFilename)
155+
const destinationClientPath = path.join(path.dirname(serviceJsonPath), typingsFilename)
156+
157+
console.log(`Integrating ${typingsFilename} ...`)
158+
159+
fs.copyFileSync(sourceClientPath, destinationClientPath)
160+
161+
await sanitizeServiceClient(destinationClientPath)
162+
}
163+
164+
/**
165+
* Patches the type file imports to be relative to the SDK module
166+
*/
167+
async function sanitizeServiceClient(generatedClientPath: string): Promise<void> {
168+
console.log('Altering Service Client to fit the codebase...')
169+
170+
let fileContents = fs.readFileSync(generatedClientPath).toString()
171+
172+
// Add a header stating the file is autogenerated
173+
fileContents = `
174+
/**
175+
* THIS FILE IS AUTOGENERATED BY 'generateServiceClient.ts'.
176+
* DO NOT EDIT BY HAND.
177+
*/
178+
179+
${fileContents}
180+
`
181+
182+
fileContents = fileContents.replace(/(import .* from.*)\.\.(.*)/g, '$1aws-sdk$2')
183+
184+
fs.writeFileSync(generatedClientPath, fileContents)
185+
}
186+
187+
// ---------------------------------------------------------------------------------------------------------------------
188+
189+
// tslint:disable-next-line:no-floating-promises
190+
;(async () => {
191+
const serviceClientDefinitions: ServiceClientDefinition[] = [
192+
{
193+
serviceJsonPath: 'src/shared/telemetry/service-2.json',
194+
serviceName: 'ClientTelemetry'
195+
}
196+
]
197+
await generateServiceClients(serviceClientDefinitions)
198+
})()

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@
349349
"compile": "tsc -p ./ && npm run lint && npm run buildScripts",
350350
"recompile": "npm run clean && npm run compile",
351351
"watch": "npm run buildScripts && tsc -watch -p ./",
352-
"postinstall": "node ./node_modules/vscode/bin/install",
352+
"postinstall": "node ./node_modules/vscode/bin/install && ts-node ./build-scripts/generateServiceClient.ts",
353353
"test": "npm run compile && node ./test-scripts/test.js",
354354
"integrationTest": "npm run compile && ts-node ./test-scripts/integrationTest.ts",
355355
"lint": "tslint --project .",

src/shared/telemetry/README

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,7 @@ This is the core of the telemetry service client for the AWS Toolkit for Visual
22

33
service-2.json is generated internally.
44

5-
clienttelemetry.d.ts is autogenerated from service-2.json using the AWS JS SDK
5+
clienttelemetry.d.ts is autogenerated from service-2.json using the AWS JS SDK. This is incorporated into the
6+
`postinstall` npm step.
67

7-
generate_types.sh automates the process of generating clienttelemetry.d.ts
8-
9-
10-
11-
Manual steps to generate a new type file:
12-
13-
* Get a fresh copy of the AWS JS SDK from https://github.com/aws/aws-sdk-js.git to $AWS_SDK_DIR
14-
* Change your working directory to $AWS_SDK_DIR
15-
* Copy the 'service-2.json' to '$AWS_SDK_DIR/apis/clienttelemetry-<date>.normal.json'
16-
* Add an entry similar to the blob below to '$AWS_SDK_DIR/apis/metadata.json'
17-
18-
clienttelemetry : {
19-
"name": "ClientTelemetry'
20-
}
21-
22-
* run 'node scripts/typings-generator.js'
23-
* copy the generated type file from '$AWS_SDK_DIR/clients/clienttelemetry.d.ts' to this directory
24-
* replace the imports in 'clienttelemetry.d.ts' to be relative to sdk module instead of the parent directory
25-
* i.e. import from 'aws-sdk/lib/request' instead of '../lib/request'
26-
* add a header to clienttelemetry.d.ts stating that it is autogenerated
27-
* cleanup by deleting $AWS_SDK_DIR
8+
\build-scripts\generateServiceClient.ts automates the process of generating clienttelemetry.d.ts

src/shared/telemetry/clienttelemetry.d.ts

Lines changed: 0 additions & 119 deletions
This file was deleted.

0 commit comments

Comments
 (0)