Skip to content

Commit b9bc818

Browse files
committed
Adding terraform file generation
1 parent 0bd5dc8 commit b9bc818

File tree

4 files changed

+78
-0
lines changed

4 files changed

+78
-0
lines changed

release/v2.0.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ rx test # --load --chaos
4545
- [x] I should be able to browse and use an automatically generated Swagger UI for my API
4646
- [ ] I should be able to automatically generate an SDK for calling my API
4747
- [x] I should be able to automatically generate a Postman collection file
48+
- [x] I should be able to automatically generate a Terraform file for interpolating and implementing the API
4849
- [ ] I should be able to automatically generate a UML diagram of my resource relationships
4950
- [ ] I should be able to issue API keys for my API
5051

src/library/cli/files.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ export const swaggerFile = (directory: string, file: string): string => path.joi
5050
*/
5151
export const postmanFile = (directory: string, file: string): string => path.join(rxSubdirectory(directory, file), 'postman.json')
5252

53+
/**
54+
* Returns the resolved path to the Terraform file
55+
* @param directory the absolute path of the directory to use
56+
* @param file the path of the document file to use
57+
*/
58+
export const terraformFile = (directory: string, file: string): string => path.join(rxSubdirectory(directory, file), 'main.tf')
59+
5360
/**
5461
* Returns the resolved path to the Swagger with mocks file
5562
* @param directory the absolute path of the directory to use
@@ -100,6 +107,14 @@ export const writeSwaggerFile = (directory: string, file: string, specification:
100107
*/
101108
export const writePostmanFile = (directory: string, file: string, object: object): Promise<void> => fse.writeFile(postmanFile(directory, file), JSON.stringify(object, null, 2))
102109

110+
/**
111+
* Writes the Terraform file
112+
* @param directory the absolute path of the directory to use
113+
* @param file the path of the document file to use
114+
* @param string the Terraform string
115+
*/
116+
export const writeTerraformFile = (directory: string, file: string, string: string): Promise<void> => fse.writeFile(terraformFile(directory, file), string)
117+
103118
/**
104119
* Reads the Swagger with mocks file
105120
* @param directory the absolute path of the directory to use

src/library/cli/generate.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import * as rx from '../index'
1515
* Currently, it generates:
1616
* - a Swagger API specification file
1717
* - a Postman Collection file
18+
* - a Terraform file
1819
* @param directory the absolute path of the directory to use
1920
* @param file the path of the document file to use
2021
*/
@@ -48,6 +49,13 @@ export const generate = async (directory: string, file: string): Promise<void> =
4849
await files.writePostmanFile(directory, file, collection)
4950
log('success', `Generated the ${basename(files.postmanFile(directory, file))} file successfully.`)
5051
log('info', `path: ${files.postmanFile(directory, file)}`)
52+
// Check if the corresponding terraform file for the provided document file exists
53+
await proceed(await files.exists(files.terraformFile(directory, file)), basename(files.terraformFile(directory, file)))
54+
let terraform = await rx.terraform(specification)
55+
// Write the Terraform string to the file-specific directory
56+
await files.writeTerraformFile(directory, file, terraform)
57+
log('success', `Generated the ${basename(files.terraformFile(directory, file))} file successfully.`)
58+
log('info', `path: ${files.terraformFile(directory, file)}`)
5159
}
5260

5361
const proceed = async (exists: boolean, name: string): Promise<void> => {

src/library/index.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,60 @@ export const mocks = async (specification: OpenAPIV2.Document & { [key: string]:
308308
}
309309
}
310310

311+
/**
312+
* Generates a Terraform string based on the provided Swagger API specification
313+
* @param specification the Swagger API specification object
314+
*/
315+
export const terraform = async (spec: OpenAPIV2.Document & { [key: string]: any }): Promise<string> => {
316+
try {
317+
// validate and dereference the specification, and generate mock integrations
318+
const specification = await mocks(cloneDeep(spec))
319+
// initialize the terraform string
320+
let terraformString = ''
321+
terraformString += `variable "title" {` + '\n'
322+
terraformString += ' type = string' + '\n'
323+
terraformString += ` description = "The title of the API"` + '\n'
324+
terraformString += ` default = "${specification.info.title}"` + '\n'
325+
terraformString += '}' + '\n' + '\n'
326+
terraformString += `variable "version" {` + '\n'
327+
terraformString += ' type = string' + '\n'
328+
terraformString += ` description = "The version of the API"` + '\n'
329+
terraformString += ` default = "${specification.info.version}"` + '\n'
330+
terraformString += '}' + '\n' + '\n'
331+
// add terraform interpolation support to specification fields
332+
specification.info.title = '${var.title}'
333+
specification.info.version = '${var.version}'
334+
// generate terraform variables for providing AWS API Gateway integrations for each operation
335+
for (let pathKey of Object.keys(specification.paths)) {
336+
for (let operationKey of Object.keys(specification.paths[pathKey])) {
337+
const operationId = specification.paths[pathKey][operationKey].operationId
338+
const integration = specification.paths[pathKey][operationKey]['x-amazon-apigateway-integration']
339+
const description = `Provide the AWS API Gateway integration configuration for the ${operationId} operation`
340+
terraformString += `variable "${operationId}" {` + '\n'
341+
terraformString += ' type = string' + '\n'
342+
terraformString += ` description = "${description}"` + '\n'
343+
terraformString += ' default = <<EOF' + '\n'
344+
terraformString += `${JSON.stringify(integration, null, 2)}` + '\n'
345+
terraformString += 'EOF' + '\n'
346+
terraformString += '}' + '\n' + '\n'
347+
specification.paths[pathKey][operationKey]['x-amazon-apigateway-integration'] = '${var.' + operationId + '}'
348+
}
349+
}
350+
terraformString += `output "swagger_specification" {` + '\n'
351+
terraformString += ` description = "The interpolated Swagger Specification string"` + '\n'
352+
terraformString += ` value = local.swagger_specification` + '\n'
353+
terraformString += '}' + '\n' + '\n'
354+
terraformString += 'locals {' + '\n'
355+
terraformString += ' swagger_specification = <<EOF' + '\n'
356+
terraformString += `${JSON.stringify(specification, null, 2)}` + '\n'
357+
terraformString += 'EOF' + '\n'
358+
terraformString += '}' + '\n'
359+
return terraformString
360+
} catch (error) {
361+
throw new Error(`Error while generating the terraform for the swagger specification: ${error.message}`)
362+
}
363+
}
364+
311365
export interface Deploy {
312366
id: string
313367
url: string

0 commit comments

Comments
 (0)