Skip to content

Commit dbb1be7

Browse files
authored
fix(java): solve oneOf using a custom generator APIC-300 (#125)
1 parent beb5c1a commit dbb1be7

File tree

90 files changed

+1784
-1378
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+1784
-1378
lines changed

.github/actions/cache/action.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ runs:
110110
if: ${{ inputs.job == 'cts' }}
111111
uses: actions/cache@v2
112112
with:
113-
path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-java-2/target
113+
path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-java-2
114114
key: ${{ runner.os }}-${{ env.CACHE_VERSION }}-java-client-${{ hashFiles('clients/algoliasearch-client-java-2/**') }}-${{ hashFiles('specs/bundled/search.yml') }}
115115

116116
# setup yarn
@@ -134,3 +134,8 @@ runs:
134134
if: ${{ inputs.language == 'java' || inputs.job == 'cts' }}
135135
shell: bash
136136
run: curl -L "https://github.com/google/google-java-format/releases/download/v1.13.0/google-java-format-1.13.0-all-deps.jar" > /tmp/java-formatter.jar
137+
138+
- name: Download openapi generator jar for java (TODO REMOVE)
139+
if: ${{ inputs.language == 'java' || inputs.job == 'cts' }}
140+
shell: bash
141+
run: curl -L "https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.4.0/openapi-generator-cli-5.4.0.jar" > /tmp/openapi-generator-cli.jar

.github/workflows/check.yml

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,6 @@ jobs:
142142
if: steps.cache.outputs.cache-hit != 'true' && matrix.client.name != 'algoliasearch'
143143
run: yarn cli generate javascript ${{ matrix.client.name }}
144144

145-
- name: Check diff with pushed client
146-
if: steps.cache.outputs.cache-hit != 'true'
147-
run: |
148-
git status
149-
exit $(git status --porcelain ${{ matrix.client.folder }} | wc -l)
150-
151145
- name: Build ${{ matrix.client.name }} client
152146
if: steps.cache.outputs.cache-hit != 'true'
153147
run: yarn cli build clients javascript ${{ matrix.client.name }}
@@ -178,19 +172,13 @@ jobs:
178172
id: cache
179173
uses: actions/cache@v2
180174
with:
181-
path: '/home/runner/work/api-clients-automation/api-clients-automation/${{ matrix.client.folder }}/target'
182-
key: ${{ runner.os }}-${{ env.CACHE_VERSION }}-java-client-${{ matrix.client.name }}-${{ hashFiles(format('{0}/**', matrix.client.folder)) }}-${{ hashFiles(format('specs/bundled/{0}.yml', matrix.client.name)) }}
175+
path: '/home/runner/work/api-clients-automation/api-clients-automation/${{ matrix.client.folder }}'
176+
key: ${{ runner.os }}-${{ env.CACHE_VERSION }}-java-client-${{ hashFiles(format('{0}/**', matrix.client.folder)) }}-${{ hashFiles(format('specs/bundled/{0}.yml', matrix.client.name)) }}
183177

184178
- name: Generate ${{ matrix.client.name }} client
185179
if: steps.cache.outputs.cache-hit != 'true'
186180
run: yarn cli generate java ${{ matrix.client.name }}
187181

188-
- name: Check diff with pushed client
189-
if: steps.cache.outputs.cache-hit != 'true'
190-
run: |
191-
git status
192-
exit $(git status --porcelain ${{ matrix.client.folder }} | wc -l)
193-
194182
- name: Build ${{ matrix.client.name }} client
195183
if: steps.cache.outputs.cache-hit != 'true'
196184
run: yarn cli build clients java ${{ matrix.client.name }}
@@ -221,12 +209,6 @@ jobs:
221209
if: steps.cache.outputs.cache-hit != 'true'
222210
run: yarn cli generate php ${{ matrix.client.name }}
223211

224-
- name: Check diff with pushed client
225-
if: steps.cache.outputs.cache-hit != 'true'
226-
run: |
227-
git status
228-
exit $(git status --porcelain ${{ matrix.client.folder }} | wc -l)
229-
230212
- name: Build ${{ matrix.client.name }} client
231213
if: steps.cache.outputs.cache-hit != 'true'
232214
run: yarn cli build clients php ${{ matrix.client.name }}

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
dist
2+
node_modules

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ ENV JAVA_HOME=/usr/lib/jvm/default-jvm
1010
# Java formatter
1111
ADD https://github.com/google/google-java-format/releases/download/v1.13.0/google-java-format-1.13.0-all-deps.jar /tmp/java-formatter.jar
1212

13+
# openapi generator jar (TODO: REMOVE)
14+
ADD https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.4.0/openapi-generator-cli-5.4.0.jar /tmp/openapi-generator-cli.jar
15+
1316
# PHP dependencies
1417
RUN apk add -U composer php8 php8-tokenizer php8-dom php8-xml php8-xmlwriter
1518

File renamed without changes.
File renamed without changes.

config/openapitools-java-cts.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"generatorName": "algolia-cts",
3+
"templateDir": "tests/CTS/methods/requests/templates/java",
4+
"outputDir": "tests/output/java",
5+
"artifactId": "java-tests",
6+
"groupId": "com.algolia",
7+
"invokerPackage": "com.algolia",
8+
"inputSpec": "specs/bundled/search.yml"
9+
}

config/openapitools-java.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"generatorName": "algolia-java",
3+
"templateDir": "templates/java/",
4+
"outputDir": "clients/algoliasearch-client-java-2",
5+
"artifactId": "algoliasearch-client-java-2",
6+
"groupId": "com.algolia",
7+
"apiPackage": "com.algolia.search",
8+
"invokerPackage": "com.algolia",
9+
"modelPackage": "com.algolia.model.search",
10+
"library": "okhttp-gson",
11+
"inputSpec": "specs/bundled/search.yml",
12+
"gitHost": "algolia",
13+
"gitUserId": "algolia",
14+
"gitRepoId": "algoliasearch-client-java-2",
15+
"additionalProperties": {
16+
"sourceFolder": "algoliasearch-core",
17+
"java8": true,
18+
"dateLibrary": "java8",
19+
"packageName": "algoliasearch-client-java-2"
20+
}
21+
}
File renamed without changes.

generators/build.gradle

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
plugins {
2+
id 'java'
3+
}
4+
5+
group = 'org.openapitools'
6+
version = '1.0.0'
7+
description = 'algolia-java-openapi-generator'
8+
java.sourceCompatibility = JavaVersion.VERSION_1_8
9+
10+
repositories {
11+
mavenCentral()
12+
}
13+
14+
dependencies {
15+
compileOnly 'org.openapitools:openapi-generator:5.4.0'
16+
compileOnly 'org.yaml:snakeyaml:1.19'
17+
}
18+
19+
tasks.withType(JavaCompile) {
20+
options.encoding = 'UTF-8'
21+
}

generators/settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = 'algolia-java-openapi-generator'
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package com.algolia.codegen;
2+
3+
import org.openapitools.codegen.*;
4+
import org.openapitools.codegen.languages.JavaClientCodegen;
5+
import org.openapitools.codegen.utils.ModelUtils;
6+
import org.yaml.snakeyaml.Yaml;
7+
8+
import java.util.*;
9+
import java.util.Map.Entry;
10+
import java.io.FileInputStream;
11+
import java.net.URL;
12+
13+
import io.swagger.v3.oas.models.media.Schema;
14+
15+
@SuppressWarnings("unchecked")
16+
public class AlgoliaJavaGenerator extends JavaClientCodegen {
17+
/**
18+
* Configures a friendly name for the generator. This will be used by the
19+
* generator
20+
* to select the library with the -g flag.
21+
*
22+
* @return the friendly name for the generator
23+
*/
24+
@Override
25+
public String getName() {
26+
return "algolia-java";
27+
}
28+
29+
/**
30+
* Inject server info into the client to generate the right URL
31+
*/
32+
private void generateServer(Map<String, Object> client) {
33+
String clientName = (String) client.get("pathPrefix");
34+
Yaml yaml = new Yaml();
35+
try {
36+
Map<String, Object> spec = yaml.load(new FileInputStream("specs/" + clientName + "/spec.yml"));
37+
List<Map<String, Object>> servers = (List<Map<String, Object>>) spec.get("servers");
38+
39+
boolean hasRegionalHost = false;
40+
boolean fallbackToAliasHost = false;
41+
42+
boolean isEuHost = false;
43+
boolean isDeHost = false;
44+
String host = "";
45+
String topLevelDomain = "";
46+
47+
for (Map<String, Object> server : servers) {
48+
if (!server.containsKey("url")) {
49+
throw new GenerationException("Invalid server, does not contains 'url'");
50+
}
51+
52+
if (!server.containsKey("variables")) {
53+
continue;
54+
}
55+
56+
Map<String, Map<String, Object>> variables = (Map<String, Map<String, Object>>) server.get("variables");
57+
58+
if (!variables.containsKey("region") || !variables.get("region").containsKey("enum")) {
59+
continue;
60+
}
61+
ArrayList<String> enums = (ArrayList<String>) variables.get("region").get("enum");
62+
hasRegionalHost = true;
63+
64+
URL url = new URL((String) server.get("url"));
65+
66+
if (!fallbackToAliasHost) {
67+
// Determine if the current URL with `region` also have an alias without
68+
// variables.
69+
fallbackToAliasHost = true;
70+
}
71+
72+
if (enums.contains("eu")) {
73+
isEuHost = true;
74+
}
75+
76+
if (enums.contains("de")) {
77+
isDeHost = true;
78+
}
79+
80+
// This is used for hosts like `insights` that uses `.io`
81+
String[] hostParts = url.getHost().split("\\.");
82+
host = hostParts[0];
83+
topLevelDomain = hostParts[hostParts.length - 1];
84+
}
85+
client.put("hasRegionalHost", hasRegionalHost);
86+
client.put("fallbackToAliasHost", fallbackToAliasHost);
87+
client.put("isEuHost", isEuHost);
88+
client.put("isDeHost", isDeHost);
89+
client.put("host", host);
90+
client.put("topLevelDomain", topLevelDomain);
91+
} catch (Exception e) {
92+
e.printStackTrace();
93+
}
94+
}
95+
96+
/**
97+
* Provides an opportunity to inspect and modify operation data before the code
98+
* is generated.
99+
*/
100+
@Override
101+
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
102+
Map<String, Object> results = super.postProcessOperationsWithModels(objs, allModels);
103+
Map<String, Object> client = (Map<String, Object>) results.get("operations");
104+
105+
generateServer(client);
106+
107+
return results;
108+
}
109+
110+
@Override
111+
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
112+
Map<String, Object> models = super.postProcessAllModels(objs);
113+
114+
for (Object modelContainer : models.values()) {
115+
CodegenModel model = ((Map<String, List<Map<String, CodegenModel>>>) modelContainer).get("models").get(0)
116+
.get("model");
117+
if (!model.oneOf.isEmpty()) {
118+
model.vendorExtensions.put("x-is-one-of-interface", true);
119+
}
120+
}
121+
122+
return models;
123+
}
124+
125+
@Override
126+
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
127+
Map<String, Object> bundle = super.postProcessSupportingFileData(objs);
128+
List<Map<String, Object>> apis = ((Map<String, List<Map<String, Object>>>) bundle.get("apiInfo")).get("apis");
129+
for (Map<String, Object> api : apis) {
130+
List<CodegenOperation> operations = ((Map<String, List<CodegenOperation>>) api.get("operations"))
131+
.get("operation");
132+
133+
for (CodegenOperation ope : operations) {
134+
ope.returnType = ope.returnType.replace("Map<", "HashMap<").replace("List<", "ArrayList<");
135+
}
136+
}
137+
return bundle;
138+
}
139+
140+
/**
141+
* Returns human-friendly help for the generator. Provide the consumer with help
142+
* tips, parameters here
143+
*
144+
* @return A string value for the help message
145+
*/
146+
@Override
147+
public String getHelp() {
148+
return "Generates an algolia-java client library.";
149+
}
150+
151+
public AlgoliaJavaGenerator() {
152+
super();
153+
154+
supportingFiles.add(new SupportingFile("EchoResponse.mustache",
155+
"algoliasearch-core/com/algolia/utils/echo",
156+
"EchoResponse.java"));
157+
158+
// Prevent all useless file to generate
159+
apiTestTemplateFiles.clear();
160+
modelTestTemplateFiles.clear();
161+
apiDocTemplateFiles.clear();
162+
modelDocTemplateFiles.clear();
163+
}
164+
165+
@Override
166+
public String toDefaultValue(Schema schema) {
167+
// Replace the {} from openapi with new Object()
168+
if (ModelUtils.isObjectSchema(schema) && schema.getDefault() != null) {
169+
return "new Object()";
170+
}
171+
return super.toDefaultValue(schema);
172+
}
173+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.algolia.codegen;
2+
3+
public class GenerationException extends Exception {
4+
public GenerationException(String message) {
5+
super(message);
6+
}
7+
}

0 commit comments

Comments
 (0)