-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@whook/example): add Google Cloud Functions build
- Loading branch information
Showing
6 changed files
with
428 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
import { extra, autoService } from 'knifecycle'; | ||
import { LogService } from 'common-services'; | ||
import { ENVService, identity } from '@whook/whook'; | ||
import { readArgs, WhookCommandArgs, WhookCommandDefinition } from '@whook/cli'; | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
import { getOpenAPIOperations } from '@whook/http-router'; | ||
import YError from 'yerror'; | ||
import { exec } from 'child_process'; | ||
import crypto from 'crypto'; | ||
import yaml from 'js-yaml'; | ||
|
||
export type WhookGoogleFunctionsBaseBuildConfiguration = { | ||
private?: boolean; | ||
memory?: number; | ||
timeout?: number; | ||
suffix?: string; | ||
sourceOperationId?: string; | ||
}; | ||
export type WhookGoogleFunctionsBuildConfiguration = { | ||
type: 'http'; | ||
} & WhookGoogleFunctionsBaseBuildConfiguration; | ||
|
||
export const definition: WhookCommandDefinition = { | ||
description: 'A command printing functions informations for Terraform', | ||
example: `whook terraformValues --type paths`, | ||
arguments: { | ||
type: 'object', | ||
additionalProperties: false, | ||
required: ['type'], | ||
properties: { | ||
type: { | ||
description: 'Type of values to return', | ||
type: 'string', | ||
enum: ['globals', 'paths', 'functions', 'function'], | ||
}, | ||
pretty: { | ||
description: 'Pretty print JSON values', | ||
type: 'boolean', | ||
}, | ||
functionName: { | ||
description: 'Name of the function', | ||
type: 'string', | ||
}, | ||
pathsIndex: { | ||
description: 'Index of the paths to retrieve', | ||
type: 'number', | ||
}, | ||
functionType: { | ||
description: 'Types of the functions to return', | ||
type: 'string', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
export default extra(definition, autoService(initTerraformValuesCommand)); | ||
|
||
async function initTerraformValuesCommand({ | ||
API, | ||
BASE_PATH, | ||
log, | ||
args, | ||
execAsync = _execAsync, | ||
}: { | ||
API: OpenAPIV3.Document; | ||
BASE_PATH: string; | ||
log: LogService; | ||
args: WhookCommandArgs; | ||
execAsync: typeof _execAsync; | ||
}) { | ||
return async () => { | ||
const { type, pretty, functionName, pathsIndex, functionType } = readArgs( | ||
definition.arguments, | ||
args, | ||
) as { | ||
type: string; | ||
pretty: boolean; | ||
functionName: string; | ||
pathsIndex: number; | ||
functionType: string; | ||
}; | ||
const operations = getOpenAPIOperations(API); | ||
const configurations = operations.map(operation => { | ||
const whookConfiguration = (operation['x-whook'] || { | ||
type: 'http', | ||
}) as WhookGoogleFunctionsBuildConfiguration; | ||
const configuration = { | ||
type: 'http', | ||
timeout: '10', | ||
memory: '128', | ||
description: operation.summary || '', | ||
enabled: 'true', | ||
sourceOperationId: operation.operationId, | ||
suffix: '', | ||
...Object.keys(whookConfiguration).reduce( | ||
(accConfigurations, key) => ({ | ||
...accConfigurations, | ||
[key]: whookConfiguration[key].toString(), | ||
}), | ||
{}, | ||
), | ||
}; | ||
const qualifiedOperationId = | ||
(configuration.sourceOperationId || operation.operationId) + | ||
(configuration.suffix || ''); | ||
|
||
return { | ||
qualifiedOperationId, | ||
method: operation.method.toUpperCase(), | ||
path: operation.path, | ||
...configuration, | ||
}; | ||
}); | ||
|
||
if (type === 'globals') { | ||
const commitHash = await execAsync(`git rev-parse HEAD`); | ||
const commitMessage = ( | ||
await execAsync(`git rev-list --format=%B --max-count=1 HEAD`) | ||
).split('\n')[1]; | ||
const openapi2 = yaml.safeDump({ | ||
swagger: '2.0', | ||
info: { | ||
title: API.info.title, | ||
description: API.info.description, | ||
version: API.info.version, | ||
}, | ||
host: '${infos_host}', | ||
basePath: BASE_PATH, | ||
schemes: ['https'], | ||
produces: ['application/json'], | ||
paths: configurations.reduce((accPaths, configuration) => { | ||
return { | ||
...accPaths, | ||
[configuration.path]: { | ||
...(accPaths[configuration.path] || {}), | ||
[configuration.method.toLowerCase()]: { | ||
summary: configuration.description || '', | ||
operationId: configuration.qualifiedOperationId, | ||
'x-google-backend': { | ||
address: `\${function_${configuration.qualifiedOperationId}}`, | ||
}, | ||
responses: { | ||
'200': { description: 'x', schema: { type: 'string' } }, | ||
}, | ||
}, | ||
}, | ||
}; | ||
}, {}), | ||
}); | ||
const openapiHash = crypto | ||
.createHash('md5') | ||
.update(JSON.stringify(API)) | ||
.digest('hex'); | ||
const infos = { | ||
commitHash, | ||
commitMessage, | ||
openapi2, | ||
openapiHash, | ||
}; | ||
log('info', JSON.stringify(infos)); | ||
return; | ||
} | ||
|
||
if (type === 'functions') { | ||
const functions = configurations | ||
.filter(configuration => | ||
functionType ? configuration.type === functionType : true, | ||
) | ||
.reduce( | ||
(accLambdas, configuration) => ({ | ||
...accLambdas, | ||
[configuration.qualifiedOperationId]: | ||
configuration.qualifiedOperationId, | ||
}), | ||
{}, | ||
); | ||
|
||
log('info', `${JSON.stringify(functions, null, pretty ? 2 : 0)}`); | ||
return; | ||
} | ||
|
||
if (!functionName) { | ||
throw new YError('E_FUNCTION_NAME_REQUIRED'); | ||
} | ||
|
||
const functionConfiguration = configurations.find( | ||
({ qualifiedOperationId }) => qualifiedOperationId === functionName, | ||
); | ||
|
||
log( | ||
'info', | ||
`${JSON.stringify(functionConfiguration, null, pretty ? 2 : 0)}`, | ||
); | ||
}; | ||
} | ||
|
||
function buildPartName(parts: string[]): string { | ||
return parts | ||
.map((aPart, anIndex) => { | ||
const realPartName = aPart.replace(/[{}]/g, ''); | ||
|
||
return anIndex | ||
? realPartName[0].toUpperCase() + realPartName.slice(1) | ||
: realPartName; | ||
}) | ||
.join(''); | ||
} | ||
|
||
function fixAWSSchedule(schedule: string): string { | ||
if (typeof schedule === 'undefined') { | ||
return ''; | ||
} | ||
|
||
// The last wildcard is for years. | ||
// This is a non-standard AWS addition... | ||
// Also, we have to put a `?` in either | ||
// day(month) or day(week) to fit AWS | ||
// way of building cron tabs... | ||
const fields = schedule.split(' '); | ||
|
||
if ('*' === fields[4]) { | ||
fields[4] = '?'; | ||
} else if ('*' === fields[2]) { | ||
fields[2] = '?'; | ||
} else { | ||
throw new YError('E_BAD_AWS_SCHEDULE', schedule); | ||
} | ||
return `cron(${fields.concat('*').join(' ')})`; | ||
} | ||
|
||
async function _execAsync(command: string): Promise<string> { | ||
return await new Promise((resolve, reject) => { | ||
exec(command, (err: Error, stdout: string, stderr: string) => { | ||
if (err) { | ||
reject(YError.wrap(err, 'E_EXEC_FAILURE', stderr)); | ||
return; | ||
} | ||
resolve(stdout.trim()); | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.