Skip to content

Commit 6cc24af

Browse files
committed
Another approach: extending NodeJS server to support GCF.
This does not add a new language, but adding some client options to support Google Cloud Functions (GCF).
1 parent 8ccf982 commit 6cc24af

File tree

18 files changed

+1450
-23
lines changed

18 files changed

+1450
-23
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/sh
2+
3+
SCRIPT="$0"
4+
5+
while [ -h "$SCRIPT" ] ; do
6+
ls=`ls -ld "$SCRIPT"`
7+
link=`expr "$ls" : '.*-> \(.*\)$'`
8+
if expr "$link" : '/.*' > /dev/null; then
9+
SCRIPT="$link"
10+
else
11+
SCRIPT=`dirname "$SCRIPT"`/"$link"
12+
fi
13+
done
14+
15+
if [ ! -d "${APP_DIR}" ]; then
16+
APP_DIR=`dirname "$SCRIPT"`/..
17+
APP_DIR=`cd "${APP_DIR}"; pwd`
18+
fi
19+
20+
executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar"
21+
22+
if [ ! -f "$executable" ]
23+
then
24+
mvn clean package
25+
fi
26+
27+
# if you've executed sbt assembly previously it will use that instead.
28+
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
29+
ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l nodejs-server --additional-properties=googleCloudFunctions=true -o samples/server/petstore/nodejs-google-cloud-functions"
30+
31+
java $JAVA_OPTS -Dservice -jar $executable $ags

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NodeJSServerCodegen.java

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig
2424

2525
private static final Logger LOGGER = LoggerFactory.getLogger(NodeJSServerCodegen.class);
2626

27+
public static final String GOOGLE_CLOUD_FUNCTIONS = "googleCloudFunctions";
28+
public static final String EXPORTED_NAME = "exportedName";
29+
2730
protected String apiVersion = "1.0.0";
2831
protected int serverPort = 8080;
2932
protected String projectName = "swagger-server";
3033

34+
protected boolean googleCloudFunctions;
35+
protected String exportedName;
36+
3137
public NodeJSServerCodegen() {
3238
super();
3339

@@ -76,27 +82,13 @@ public NodeJSServerCodegen() {
7682
additionalProperties.put("apiVersion", apiVersion);
7783
additionalProperties.put("serverPort", serverPort);
7884

79-
/*
80-
* Supporting Files. You can write single files for the generator with the
81-
* entire object tree available. If the input file has a suffix of `.mustache
82-
* it will be processed by the template engine. Otherwise, it will be copied
83-
*/
84-
// supportingFiles.add(new SupportingFile("controller.mustache",
85-
// "controllers",
86-
// "controller.js")
87-
// );
88-
supportingFiles.add(new SupportingFile("swagger.mustache",
89-
"api",
90-
"swagger.yaml")
91-
);
92-
writeOptional(outputFolder, new SupportingFile("index.mustache", "", "index.js"));
93-
writeOptional(outputFolder, new SupportingFile("package.mustache", "", "package.json"));
94-
writeOptional(outputFolder, new SupportingFile("README.mustache", "", "README.md"));
95-
if (System.getProperty("noservice") == null) {
96-
apiTemplateFiles.put(
97-
"service.mustache", // the template to use
98-
"Service.js"); // the extension for each file to write
99-
}
85+
cliOptions.add(CliOption.newBoolean(GOOGLE_CLOUD_FUNCTIONS,
86+
"When specified, it will generate the code which runs within Google Cloud Functions "
87+
+ "instead of standalone Node.JS server."));
88+
cliOptions.add(new CliOption(EXPORTED_NAME,
89+
"When the generated code will be deployed to Google Cloud Functions, this option can be "
90+
+ "used to update the name of the exported function. By default, it refers to the "
91+
+ "basePath. This does not affect normal standalone nodejs server code."));
10092
}
10193

10294
@Override
@@ -171,6 +163,22 @@ public String apiFileFolder() {
171163
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
172164
}
173165

166+
public boolean getGoogleCloudFunctions() {
167+
return googleCloudFunctions;
168+
}
169+
170+
public void setGoogleCloudFunctions(boolean value) {
171+
googleCloudFunctions = value;
172+
}
173+
174+
public String getExportedName() {
175+
return exportedName;
176+
}
177+
178+
public void setExportedName(String name) {
179+
exportedName = name;
180+
}
181+
174182
@Override
175183
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
176184
@SuppressWarnings("unchecked")
@@ -240,6 +248,46 @@ private static List<Map<String, Object>> sortOperationsByPath(List<CodegenOperat
240248
return opsByPathList;
241249
}
242250

251+
@Override
252+
public void processOpts() {
253+
super.processOpts();
254+
255+
if (additionalProperties.containsKey(GOOGLE_CLOUD_FUNCTIONS)) {
256+
setGoogleCloudFunctions(
257+
Boolean.valueOf(additionalProperties.get(GOOGLE_CLOUD_FUNCTIONS).toString()));
258+
}
259+
260+
if (additionalProperties.containsKey(EXPORTED_NAME)) {
261+
setExportedName((String)additionalProperties.get(EXPORTED_NAME));
262+
}
263+
264+
/*
265+
* Supporting Files. You can write single files for the generator with the
266+
* entire object tree available. If the input file has a suffix of `.mustache
267+
* it will be processed by the template engine. Otherwise, it will be copied
268+
*/
269+
// supportingFiles.add(new SupportingFile("controller.mustache",
270+
// "controllers",
271+
// "controller.js")
272+
// );
273+
supportingFiles.add(new SupportingFile("swagger.mustache",
274+
"api",
275+
"swagger.yaml")
276+
);
277+
if (getGoogleCloudFunctions()) {
278+
writeOptional(outputFolder, new SupportingFile("index-gcf.mustache", "", "index.js"));
279+
} else {
280+
writeOptional(outputFolder, new SupportingFile("index.mustache", "", "index.js"));
281+
}
282+
writeOptional(outputFolder, new SupportingFile("package.mustache", "", "package.json"));
283+
writeOptional(outputFolder, new SupportingFile("README.mustache", "", "README.md"));
284+
if (System.getProperty("noservice") == null) {
285+
apiTemplateFiles.put(
286+
"service.mustache", // the template to use
287+
"Service.js"); // the extension for each file to write
288+
}
289+
}
290+
243291
@Override
244292
public void preprocessSwagger(Swagger swagger) {
245293
String host = swagger.getHost();
@@ -262,6 +310,22 @@ public void preprocessSwagger(Swagger swagger) {
262310
}
263311
}
264312

313+
if (getGoogleCloudFunctions()) {
314+
// Note that Cloud Functions don't allow customizing port name, simply checking host
315+
// is good enough.
316+
if (!host.endsWith(".cloudfunctions.net")) {
317+
LOGGER.warn("Host " + host + " seems not matching with cloudfunctions.net URL.");
318+
}
319+
if (!additionalProperties.containsKey(EXPORTED_NAME)) {
320+
String basePath = swagger.getBasePath();
321+
if (basePath == null || basePath.equals("/")) {
322+
LOGGER.warn("Cannot find the exported name properly. Using 'openapi' as the exported name");
323+
basePath = "/openapi";
324+
}
325+
additionalProperties.put(EXPORTED_NAME, basePath.substring(1));
326+
}
327+
}
328+
265329
// need vendor extensions for x-swagger-router-controller
266330
Map<String, Path> paths = swagger.getPaths();
267331
if(paths != null) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
var swaggerTools = require('swagger-tools');
4+
var jsyaml = require('js-yaml');
5+
var fs = require('fs');
6+
7+
// swaggerRouter configuration
8+
var options = {
9+
controllers: './controllers',
10+
useStubs: false
11+
};
12+
13+
// The Swagger document (require it, build it programmatically, fetch it from a URL, ...)
14+
var spec = fs.readFileSync('./api/swagger.yaml', 'utf8');
15+
var swaggerDoc = jsyaml.safeLoad(spec);
16+
17+
function toPromise(f, req, res) {
18+
return new Promise(function(resolve, reject) {
19+
f(req, res, function(err) {
20+
if (err) {
21+
reject(err);
22+
} else {
23+
resolve();
24+
}
25+
});
26+
});
27+
}
28+
29+
exports.{{exportedName}} = function(req, res) {
30+
swaggerTools.initializeMiddleware(swaggerDoc, function(middleware) {
31+
var metadata = middleware.swaggerMetadata();
32+
var validator = middleware.swaggerValidator();
33+
var router = middleware.swaggerRouter(options);
34+
req.url = swaggerDoc.basePath + req.url;
35+
toPromise(metadata, req, res).then(function() {
36+
return toPromise(validator, req, res);
37+
}).then(function() {
38+
return toPromise(router, req, res);
39+
}).catch(function(err) {
40+
console.error(err);
41+
res.status(res.statusCode || 400).send(err);
42+
});
43+
});
44+
};

modules/swagger-codegen/src/main/resources/nodejs/README.mustache

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Overview
44
This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub.
55

6+
{{^googleCloudFunctions}}
67
### Running the server
78
To run the server, run:
89

@@ -15,5 +16,6 @@ To view the Swagger UI interface:
1516
```
1617
open http://localhost:{{serverPort}}/docs
1718
```
19+
{{/googleCloudFunctions}}
1820

1921
This project leverages the mega-awesome [swagger-tools](https://github.com/apigee-127/swagger-tools) middleware which does most all the work.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
var swaggerTools = require('swagger-tools');
4+
var jsyaml = require('js-yaml');
5+
var fs = require('fs');
6+
7+
// swaggerRouter configuration
8+
var options = {
9+
controllers: './controllers',
10+
useStubs: false
11+
};
12+
13+
// The Swagger document (require it, build it programmatically, fetch it from a URL, ...)
14+
var spec = fs.readFileSync('./api/swagger.yaml', 'utf8');
15+
var swaggerDoc = jsyaml.safeLoad(spec);
16+
17+
function toPromise(f, req, res) {
18+
return new Promise(function(resolve, reject) {
19+
f(req, res, function(err) {
20+
if (err) {
21+
reject(err);
22+
} else {
23+
resolve();
24+
}
25+
});
26+
});
27+
}
28+
29+
exports.{{exportedName}} = function(req, res) {
30+
swaggerTools.initializeMiddleware(swaggerDoc, function(middleware) {
31+
var metadata = middleware.swaggerMetadata();
32+
var validator = middleware.swaggerValidator();
33+
var router = middleware.swaggerRouter(options);
34+
req.url = swaggerDoc.basePath + req.url;
35+
toPromise(metadata, req, res).then(function() {
36+
return toPromise(validator, req, res);
37+
}).then(function() {
38+
return toPromise(router, req, res);
39+
}).catch(function(err) {
40+
console.error(err);
41+
res.status(res.statusCode || 400).send(err);
42+
});
43+
});
44+
};

modules/swagger-codegen/src/main/resources/nodejs/package.mustache

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
"version": "{{appVersion}}",
44
"description": "{{{appDescription}}}",
55
"main": "index.js",
6+
{{^googleCloudFunctions}}
67
"scripts": {
78
"prestart": "npm install",
89
"start": "node index.js"
9-
},
10+
},
11+
{{/googleCloudFunctions}}
1012
"keywords": [
1113
"swagger"
1214
],
1315
"license": "Unlicense",
1416
"private": true,
1517
"dependencies": {
18+
{{^googleCloudFunctions}}
1619
"connect": "^3.2.0",
20+
{{/googleCloudFunctions}}
1721
"js-yaml": "^3.3.0",
1822
"swagger-tools": "0.10.1"
1923
}

modules/swagger-codegen/src/test/java/io/swagger/codegen/nodejs/NodeJSServerOptionsTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ protected CodegenConfig getCodegenConfig() {
2727
protected void setExpectations() {
2828
new Expectations(clientCodegen) {{
2929
clientCodegen.setSortParamsByRequiredFlag(Boolean.valueOf(NodeJSServerOptionsProvider.SORT_PARAMS_VALUE));
30+
clientCodegen.setGoogleCloudFunctions(Boolean.valueOf(NodeJSServerOptionsProvider.GOOGLE_CLOUD_FUNCTIONS));
31+
clientCodegen.setExportedName(NodeJSServerOptionsProvider.EXPORTED_NAME);
3032
times = 1;
3133
}};
3234
}

modules/swagger-codegen/src/test/java/io/swagger/codegen/options/NodeJSServerOptionsProvider.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package io.swagger.codegen.options;
22

33
import io.swagger.codegen.CodegenConstants;
4-
4+
import io.swagger.codegen.languages.NodeJSServerCodegen;
55
import com.google.common.collect.ImmutableMap;
66

77
import java.util.Map;
88

99
public class NodeJSServerOptionsProvider implements OptionsProvider {
1010
public static final String SORT_PARAMS_VALUE = "false";
1111
public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true";
12+
public static final String GOOGLE_CLOUD_FUNCTIONS = "false";
13+
public static final String EXPORTED_NAME = "exported";
1214

1315
@Override
1416
public String getLanguage() {
@@ -20,6 +22,8 @@ public Map<String, String> createOptions() {
2022
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
2123
return builder.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_VALUE)
2224
.put(CodegenConstants.ENSURE_UNIQUE_PARAMS, ENSURE_UNIQUE_PARAMS_VALUE)
25+
.put(NodeJSServerCodegen.GOOGLE_CLOUD_FUNCTIONS, GOOGLE_CLOUD_FUNCTIONS)
26+
.put(NodeJSServerCodegen.EXPORTED_NAME, EXPORTED_NAME)
2327
.build();
2428
}
2529

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Swagger generated server
2+
3+
## Overview
4+
This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub.
5+
6+
7+
This project leverages the mega-awesome [swagger-tools](https://github.com/apigee-127/swagger-tools) middleware which does most all the work.

0 commit comments

Comments
 (0)