Skip to content

feat(generators): generate algoliasearch JS client #760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,6 @@ jobs:
if: ${{ steps.cache.outputs.cache-hit != 'true' && matrix.client.language != 'php' }}
run: yarn cli build clients ${{ matrix.client.language }} ${{ matrix.client.toBuild }}

- name: Build JavaScript 'algoliasearch' client
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is beautiful

if: ${{ steps.cache.outputs.cache-hit != 'true' && matrix.client.language == 'javascript' }}
run: yarn cli build clients javascript algoliasearch

- name: Run JavaScript 'algoliasearch' client tests
if: ${{ steps.cache.outputs.cache-hit != 'true' && matrix.client.language == 'javascript' }}
run: cd ${{ matrix.client.path }} && yarn workspace @experimental-api-clients-automation/algoliasearch test
Expand Down
2 changes: 0 additions & 2 deletions config/generation.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ module.exports = {
'!clients/algoliasearch-client-javascript/.github/**',
'!clients/algoliasearch-client-javascript/.yarn/**',
'!clients/algoliasearch-client-javascript/scripts/**',
'!clients/algoliasearch-client-javascript/packages/algoliasearch/**',
'!clients/algoliasearch-client-javascript/packages/requester-*/**',
'!clients/algoliasearch-client-javascript/packages/client-common/**',
'clients/algoliasearch-client-javascript/packages/algoliasearch/lite/**',

// PHP
'!clients/algoliasearch-client-php/*',
Expand Down
8 changes: 4 additions & 4 deletions config/openapitools.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"generator-cli": {
"version": "6.0.0",
"generators": {
"javascript-search": {
"output": "#{cwd}/clients/algoliasearch-client-javascript/packages/client-search",
"javascript-algoliasearch": {
"output": "#{cwd}/clients/algoliasearch-client-javascript/packages/algoliasearch",
"reservedWordsMappings": "queryParameters=queryParameters,requestOptions=requestOptions,delete=delete",
"additionalProperties": {
"packageVersion": "0.6.1"
}
},
"javascript-lite": {
"output": "#{cwd}/clients/algoliasearch-client-javascript/packages/algoliasearch/lite",
"javascript-search": {
"output": "#{cwd}/clients/algoliasearch-client-javascript/packages/client-search",
"reservedWordsMappings": "queryParameters=queryParameters,requestOptions=requestOptions,delete=delete",
"additionalProperties": {
"packageVersion": "0.6.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
public class AlgoliaJavaScriptGenerator extends TypeScriptNodeClientCodegen {

private String CLIENT;
private boolean isAlgoliasearchClient;

@Override
public String getName() {
Expand All @@ -24,6 +25,7 @@ public void processOpts() {
super.processOpts();

CLIENT = Utils.camelize((String) additionalProperties.get("client"));
isAlgoliasearchClient = CLIENT.equals("algoliasearch");

// generator specific options
setSupportsES6(true);
Expand All @@ -35,23 +37,67 @@ public void processOpts() {
// clear all supported files to avoid unwanted ones
supportingFiles.clear();

supportingFiles.add(new SupportingFile("clientMethodProps.mustache", "model", "clientMethodProps.ts"));
supportingFiles.add(new SupportingFile("modelBarrel.mustache", "model", "index.ts"));
supportingFiles.add(new SupportingFile("browser.mustache", "builds", "browser.ts"));
supportingFiles.add(new SupportingFile("node.mustache", "builds", "node.ts"));
// Files common to both generations
supportingFiles.add(new SupportingFile("package.mustache", "", "package.json"));
supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json"));

// root
// root export files
supportingFiles.add(new SupportingFile("index.mustache", "", "index.js"));
supportingFiles.add(new SupportingFile("index.d.mustache", "", "index.d.ts"));

// the `lite` package is a subfolder of `algoliasearch`, which does not require
// those package-related files, as they are present at an higher level.
if (!CLIENT.equals("lite")) {
supportingFiles.add(new SupportingFile("package.mustache", "", "package.json"));
supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json"));
// `client` related files, `algoliasearch` have it's own logic below
if (!isAlgoliasearchClient) {
// models
supportingFiles.add(new SupportingFile("client/model/clientMethodProps.mustache", "model", "clientMethodProps.ts"));
supportingFiles.add(new SupportingFile("client/model/modelBarrel.mustache", "model", "index.ts"));

// builds
supportingFiles.add(new SupportingFile("client/builds/browser.mustache", "builds", "browser.ts"));
supportingFiles.add(new SupportingFile("client/builds/node.mustache", "builds", "node.ts"));
}
// `algoliasearch` related files
else {
// `algoliasearch` builds
supportingFiles.add(new SupportingFile("algoliasearch/builds/browser.mustache", "builds", "browser.ts"));
supportingFiles.add(new SupportingFile("algoliasearch/builds/node.mustache", "builds", "node.ts"));
supportingFiles.add(new SupportingFile("algoliasearch/builds/models.mustache", "builds", "models.ts"));

// `lite` builds
supportingFiles.add(new SupportingFile("client/builds/browser.mustache", "lite/builds", "browser.ts"));
supportingFiles.add(new SupportingFile("client/builds/node.mustache", "lite/builds", "node.ts"));

// `lite` models
supportingFiles.add(new SupportingFile("client/model/clientMethodProps.mustache", "lite/model", "clientMethodProps.ts"));
supportingFiles.add(new SupportingFile("client/model/modelBarrel.mustache", "lite/model", "index.ts"));

// `lite root export files
supportingFiles.add(new SupportingFile("algoliasearch/lite.mustache", "", "lite.js"));
supportingFiles.add(new SupportingFile("algoliasearch/lite.d.mustache", "", "lite.d.ts"));
}
}

@Override
public String apiFileFolder() {
String fileFolder = super.apiFileFolder();

if (!isAlgoliasearchClient) {
return fileFolder;
}

return fileFolder.replace("src", "lite/src");
}

@Override
public String modelFileFolder() {
String fileFolder = super.modelFileFolder();

if (!isAlgoliasearchClient) {
return fileFolder;
}

return fileFolder.replace("model", "lite/model");
}

@Override
public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List<Server> servers) {
return Utils.specifyCustomRequest(super.fromOperation(path, httpMethod, operation, servers));
Expand All @@ -66,7 +112,20 @@ private void setDefaultGeneratorOptions() {
additionalProperties.put("algoliaAgent", Utils.capitalize(CLIENT));
additionalProperties.put("gitRepoId", "algoliasearch-client-javascript");
additionalProperties.put("isSearchClient", CLIENT.equals("search"));
additionalProperties.put("isLiteClient", CLIENT.equals("lite"));
additionalProperties.put("isAlgoliasearchClient", isAlgoliasearchClient);

if (isAlgoliasearchClient) {
// Files used to create the package.json of the algoliasearch package
additionalProperties.put("analyticsVersion", Utils.getOpenApiToolsField("javascript", "analytics", "packageVersion"));
additionalProperties.put("personalizationVersion", Utils.getOpenApiToolsField("javascript", "personalization", "packageVersion"));
additionalProperties.put("searchVersion", Utils.getOpenApiToolsField("javascript", "search", "packageVersion"));

// Files used to generate the `lite` client
apiName = "lite" + Utils.API_SUFFIX;
additionalProperties.put("apiName", apiName);
additionalProperties.put("capitalizedApiName", Utils.capitalize(apiName));
additionalProperties.put("algoliaAgent", "Lite");
}
}

/** Provides an opportunity to inspect and modify operation data before the code is generated. */
Expand Down Expand Up @@ -128,7 +187,9 @@ public String toApiName(String name) {
return "Default" + Utils.API_SUFFIX;
}

return Utils.capitalize(CLIENT + Utils.API_SUFFIX);
String endClient = isAlgoliasearchClient ? "lite" : CLIENT;

return Utils.capitalize(endClient + Utils.API_SUFFIX);
}

/** The `apiFileName` is in camelCase. */
Expand All @@ -138,7 +199,9 @@ public String toApiFilename(String name) {
return "default" + Utils.API_SUFFIX;
}

return CLIENT + Utils.API_SUFFIX;
String endClient = isAlgoliasearchClient ? "lite" : CLIENT;

return endClient + Utils.API_SUFFIX;
}

/** The `apiFileName` is in camelCase. */
Expand Down
23 changes: 23 additions & 0 deletions generators/src/main/java/com/algolia/codegen/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class Utils {
public static final Set<String> CUSTOM_METHOD = Sets.newHashSet("del", "get", "post", "put");

private static JsonNode cacheConfig;
private static JsonNode cacheOpenApiToolsConfig;

private Utils() {}

Expand Down Expand Up @@ -140,6 +141,28 @@ public static String getClientConfigField(String language, String... fields) thr
return value.asText();
}

/** Get the `field` value in the `config/openapitools.json` file for the given language */
public static String getOpenApiToolsField(String language, String client, String... fields) throws ConfigException {
if (fields.length == 0) {
throw new ConfigException("getOpenApiToolsField requires at least one field");
}
if (cacheOpenApiToolsConfig == null) {
cacheOpenApiToolsConfig = readJsonFile("config/openapitools.json");
}
JsonNode value = cacheOpenApiToolsConfig
.get("generator-cli")
.get("generators")
.get(language + "-" + client)
.get("additionalProperties");
for (String field : fields) {
value = value.get(field);
}
if (!value.isTextual()) {
throw new ConfigException(fields[fields.length - 1] + " is not a string");
}
return value.asText();
}

public static JsonNode readJsonFile(String filePath) throws ConfigException {
try {
return Json.mapper().readTree(new File(filePath));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,16 @@ public Map<String, Object> postProcessSupportingFileData(Map<String, Object> obj
Map<String, Object> bundle = objs;
bundle.clear();

// This only exists for the `javascript-algoliasearch` combo, because the `lite` client is
// nested inside `algoliasearch`.
String importClientName = client;
if (language.equals("javascript") && client.equals("algoliasearch")) {
importClientName = "lite";
}

// We can put whatever we want in the bundle, and it will be accessible in the template
bundle.put("client", Utils.createClientName(client, language) + "Client");
bundle.put("clientPrefix", Utils.createClientName(client, language));
bundle.put("client", Utils.createClientName(importClientName, language) + "Client");
bundle.put("clientPrefix", Utils.createClientName(importClientName, language));
bundle.put("hasRegionalHost", hasRegionalHost);
bundle.put("defaultRegion", client.equals("predict") ? "ew" : "us");
bundle.put("lambda", lambda);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void addDataToBundle(Map<String, Object> bundle) throws GeneratorExceptio
String clientName = output.substring(output.lastIndexOf('/') + 1);
String npmNamespace = Utils.getClientConfigField("javascript", "npmNamespace");

if (clientName.equals("lite")) {
if (clientName.equals("algoliasearch")) {
bundle.put("import", npmNamespace + "/" + "algoliasearch/lite");
} else {
bundle.put("import", npmNamespace + "/" + clientName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public TestsClient(String language, String client) {
@Override
public boolean available() {
// no `lite` client test for now
if (language.equals("javascript") && client.equals("lite")) {
if (language.equals("javascript") && client.equals("algoliasearch")) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ protected Map<String, Request[]> loadRequestCTS() throws Exception {
String clientName = client;
// This special case allow us to read the `search` CTS to generated the tests for the
// `lite` client, which is only available in JavaScript
if (language.equals("javascript") && client.equals("lite")) {
if (language.equals("javascript") && client.equals("algoliasearch")) {
clientName = "search";
}
return super.loadCTS("methods/requests", clientName, Request[].class);
Expand Down
7 changes: 7 additions & 0 deletions scripts/buildClients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ export async function buildClients(
): Promise<void> {
const langs = [...new Set(generators.map((gen) => gen.language))];

if (langs.includes('javascript')) {
await run('yarn install', {
verbose,
cwd: getLanguageFolder('javascript'),
});
}

if (!CI && !skipUtils && langs.includes('javascript')) {
const spinner = createSpinner(
"building 'JavaScript' utils",
Expand Down
6 changes: 3 additions & 3 deletions scripts/buildSpecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ async function buildSpec(
verbose: boolean,
useCache: boolean
): Promise<void> {
const isLite = spec === 'lite';
const isAlgoliasearch = spec === 'algoliasearch';
// In case of lite we use a the `search` spec as a base because only its bundled form exists.
const specBase = isLite ? 'search' : spec;
const specBase = isAlgoliasearch ? 'search' : spec;
const cacheFile = toAbsolutePath(`specs/dist/${spec}.cache`);
let hash = '';

Expand Down Expand Up @@ -233,7 +233,7 @@ async function buildSpec(
);

// Add the correct tags to be able to generate the proper client
if (!isLite) {
if (!isAlgoliasearch) {
await transformBundle({
bundledPath: toAbsolutePath(bundledPath),
clientName: spec,
Expand Down
15 changes: 2 additions & 13 deletions scripts/ci/githubActions/createMatrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ async function getClientMatrix(baseBranch: string): Promise<void> {
);

for (const { language, client, output } of Object.values(GENERATORS)) {
// `algoliasearch` is an aggregation of generated clients.
// `lite` is a subfolder of `algoliasearch`
if (client === 'algoliasearch') {
continue;
}

const languageDependencies = Object.entries(DEPENDENCIES).reduce(
(finalDeps, [key, deps]) => {
if (key.startsWith(`${language.toUpperCase()}_`)) {
Expand All @@ -51,7 +45,7 @@ async function getClientMatrix(baseBranch: string): Promise<void> {
{} as Record<string, string[]>
);

const bundledSpec = client === 'lite' ? 'search' : client;
const bundledSpec = client === 'algoliasearch' ? 'search' : client;
const specChanges = await getNbGitDiff({
branch: baseBranch,
path: `specs/${bundledSpec}`,
Expand Down Expand Up @@ -90,12 +84,8 @@ async function getClientMatrix(baseBranch: string): Promise<void> {
)}`;
const testsToDelete = `${testsOutputBase}/client ${testsOutputBase}/methods`;
let testsToStore = testsToDelete;
let toBuild = matrix[language].toRun;

switch (language) {
case 'javascript':
toBuild = toBuild.filter((client) => client !== 'lite');
break;
case 'java':
testsToStore = `${testsToDelete} ${testsRootFolder}/build.gradle`;
break;
Expand All @@ -107,7 +97,6 @@ async function getClientMatrix(baseBranch: string): Promise<void> {
language,
path: matrix[language].path,
toRun: matrix[language].toRun.join(' '),
toBuild: toBuild.join(' '),
cacheKey: await computeCacheKey(`clients-${language}`, [
...matrix[language].cacheToCompute,
'specs/common',
Expand Down Expand Up @@ -145,7 +134,7 @@ async function getSpecMatrix(): Promise<void> {
}

// The `lite` spec is created by the `search` spec
const bundledSpecName = client === 'lite' ? 'search' : client;
const bundledSpecName = client === 'algoliasearch' ? 'search' : client;

matrix.toRun.push(client);
matrix.cacheToCompute.push(`specs/${bundledSpecName}`);
Expand Down
4 changes: 0 additions & 4 deletions scripts/ci/githubActions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ type BaseMatrix = {
* The list of clients to run in the CI.
*/
toRun: string;
/**
* The list of clients to build in the CI, defaults to `toRun`.
*/
toBuild: string;
};

export type ClientMatrix = BaseMatrix & {
Expand Down
Loading