UNPKG

7.97 kBJavaScriptView Raw
1/* @flow */
2'use strict'
3
4/* ::
5import type {
6 BlinkMRCServer, CorsConfiguration, RouteConfiguration, AnalyticsConfig
7} from '../types.js'
8*/
9
10const path = require('path')
11
12const execa = require('execa')
13const loadJsonFile = require('load-json-file')
14const semver = require('semver')
15const writeJsonFile = require('write-json-file')
16
17const nonAlphaNumericToDashes = require('./utils/non-alpha-numeric-to-dashes.js')
18const readCors = require('./cors/read.js')
19const readRoutes = require('./routes/read.js')
20const scope = require('./scope.js')
21const variables = require('./variables.js')
22const network = require('./network.js')
23const updateYamlFile = require('./utils/yaml.js').updateYamlFile
24const copyRecursive = require('./utils/copy-recursive.js')
25const validateCors = require('./cors/validate.js')
26const values = require('./values.js')
27
28const HANDLER = 'handler'
29const WRAPPER = path.join(__dirname, '..', 'dist', 'wrapper.js')
30
31function applyTemplate(cwd /* : string */) /* : Promise<void> */ {
32 return executeSLSCommand(['create', '--template', 'aws-nodejs'], { cwd })
33}
34
35function copyConfiguration(
36 target /* : string */,
37 projectPath /* : string */,
38 env /* : string */
39) /* : Promise<void> */ {
40 const configPath = path.join(target, 'bm-server.json')
41 return Promise.all([
42 readCors(projectPath).then(cors => (cors ? validateCors(cors) : false)),
43 readRoutes(projectPath),
44 scope.read(projectPath)
45 ]).then(results =>
46 writeJsonFile(configPath, {
47 cors: results[0],
48 routes: results[1].map((routeConfig, index) => {
49 routeConfig.module = path.posix.join('project', routeConfig.module)
50 return routeConfig
51 }),
52 scope: results[2].project,
53 env
54 })
55 )
56}
57
58function copyProject(
59 source /* : string */,
60 target /* : string */
61) /* : Promise<string> */ {
62 const projectPath = path.join(target, 'project')
63 return copyRecursive(source, projectPath).then(() => projectPath)
64}
65
66function copyWrapper(target /* : string */) /* : Promise<void> */ {
67 const wrapperPath = path.join(target, `${HANDLER}.js`)
68 return copyRecursive(WRAPPER, wrapperPath)
69}
70
71function executeSLSCommand(
72 args /* : Array<string> */,
73 options /* : { [id:string]: any } */
74) /* : Promise<void> */ {
75 return execa(
76 'serverless',
77 args,
78 Object.assign({}, options, {
79 preferLocal: true,
80 localDir: path.join(__dirname, '..')
81 })
82 )
83}
84
85function getFunctionName(
86 cfg /* : BlinkMRCServer */,
87 env /* : string */
88) /* : string */ {
89 // Lambdas do not allow for any characters other than alpha-numeric and dashes in function names
90 const arr = [cfg.project || '', env]
91 const mapped = arr.map(nonAlphaNumericToDashes)
92 return mapped.join('-')
93}
94
95function registerFunctions(
96 target /* : string */,
97 projectPath /* : string */,
98 env /* : string */
99) /* : Promise<void> */ {
100 return scope.read(projectPath).then(cfg =>
101 updateServerlessYamlFile(target, config => {
102 config.service = nonAlphaNumericToDashes(cfg.project || '')
103 config.provider = config.provider || {}
104 config.provider.region = cfg.region
105 config.provider.stage = env
106
107 const functionName = getFunctionName(cfg, env)
108 config.functions = {
109 [functionName]: {
110 events: [
111 {
112 http: 'ANY {path+}'
113 }
114 ],
115 handler: `${HANDLER}.handler`,
116 name: functionName,
117 description: `Server CLI Lambda function for project: ${cfg.project ||
118 ''}`,
119 timeout: cfg.timeout || values.DEFAULT_TIMEOUT_SECONDS
120 }
121 }
122
123 return config
124 })
125 )
126}
127
128function registerDeploymentBucket(
129 target /* : string */,
130 deploymentBucket /* : string | void */
131) /* : Promise<void> */ {
132 if (!deploymentBucket) {
133 return Promise.resolve()
134 }
135 return updateServerlessYamlFile(target, config => {
136 config.provider = config.provider || {}
137 config.provider.deploymentBucket = deploymentBucket
138 return config
139 })
140}
141
142function registerExecutionRole(
143 target /* : string */,
144 executionRole /* : string | void */
145) /* : Promise<void> */ {
146 if (!executionRole) {
147 return Promise.resolve()
148 }
149 return updateServerlessYamlFile(target, config => {
150 config.provider = config.provider || {}
151 config.provider.role = executionRole
152 return config
153 })
154}
155
156function registerRootProxy(
157 target /* : string */,
158 env /* : string */
159) /* : Promise<void> */ {
160 // TODO: detect root route Lambda function (if any) and skip this proxy
161
162 // https://serverless.com/framework/docs/providers/aws/events/apigateway#setting-an-http-proxy-on-api-gateway
163
164 return loadJsonFile(path.join(__dirname, 'root-route-proxy.json')).then(
165 resources =>
166 updateServerlessYamlFile(target, config => {
167 config.resources = resources
168 return config
169 })
170 )
171}
172
173function registerVariables(
174 target /* : string */,
175 projectPath /* : string */,
176 env /* : string */,
177 analyticsConfig /* : AnalyticsConfig */
178) /* : Promise<void> */ {
179 return variables.read(projectPath, env).then(envVars => {
180 if (analyticsConfig.origin && analyticsConfig.collectorToken) {
181 envVars.ONEBLINK_ANALYTICS_ORIGIN = analyticsConfig.origin
182 envVars.ONEBLINK_ANALYTICS_COLLECTOR_TOKEN =
183 analyticsConfig.collectorToken
184 }
185
186 if (!Object.keys(envVars).length) {
187 return
188 }
189
190 return updateServerlessYamlFile(target, config => {
191 config.provider = config.provider || {}
192 config.provider.environment = envVars
193 return config
194 })
195 })
196}
197
198async function registerVpc(
199 target /* : string */,
200 projectPath /* : string */,
201 env /* : string */,
202 vpcSecurityGroups /* : string | void */,
203 vpcSubnets /* : string | void */,
204 separator /* : string */
205) /* : Promise<void> */ {
206 const securityGroupIds /* : string[] */ = []
207 const subnetIds /* : string[] */ = []
208
209 // Configuration in .blinkmrc file takes precedence over the CLI fags
210 const networkConfiguration = await network.readNetwork(projectPath, env)
211 if (networkConfiguration) {
212 securityGroupIds.push(...networkConfiguration.vpcSecurityGroups)
213 subnetIds.push(...networkConfiguration.vpcSubnets)
214 } else {
215 if (typeof vpcSecurityGroups === 'string') {
216 securityGroupIds.push(...vpcSecurityGroups.split(separator))
217 }
218 if (typeof vpcSubnets === 'string') {
219 subnetIds.push(...vpcSubnets.split(separator))
220 }
221 }
222
223 // https://serverless.com/framework/docs/providers/aws/guide/functions#vpc-configuration
224 return updateServerlessYamlFile(target, config => {
225 config.provider = config.provider || {}
226 config.provider.vpc = {
227 securityGroupIds: securityGroupIds
228 .map(securityGroup => securityGroup.trim())
229 .filter(securityGroup => !!securityGroup),
230 subnetIds: subnetIds
231 .map(subnet => subnet.trim())
232 .filter(subnet => !!subnet)
233 }
234 return config
235 })
236}
237
238function registerNodeVersion(
239 target /* : string */,
240 bmServerVersion /* : string */
241) /* : Promise<void> */ {
242 return updateServerlessYamlFile(target, config => {
243 config.provider = config.provider || {}
244 switch (semver.major(bmServerVersion)) {
245 case 0:
246 case 1: {
247 config.provider.runtime = 'nodejs4.3'
248 break
249 }
250 case 2: {
251 config.provider.runtime = 'nodejs6.10'
252 break
253 }
254 case 3: {
255 config.provider.runtime = 'nodejs8.10'
256 break
257 }
258 }
259 return config
260 })
261}
262
263function updateServerlessYamlFile(
264 target /* : string */,
265 updater /* : (data: Object) => Object */
266) /* : Promise<void> */ {
267 const configPath = path.join(target, 'serverless.yml')
268 return updateYamlFile(configPath, updater)
269}
270
271module.exports = {
272 applyTemplate,
273 copyConfiguration,
274 copyProject,
275 copyWrapper,
276 executeSLSCommand,
277 getFunctionName,
278 registerDeploymentBucket,
279 registerExecutionRole,
280 registerFunctions,
281 registerNodeVersion,
282 registerRootProxy,
283 registerVariables,
284 registerVpc
285}