Skip to content

docs: generate guides from snippets #3139

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 56 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
b2dee79
t
shortcuts Jun 5, 2024
e60daf1
docs: generate guides from snippets
shortcuts Jun 5, 2024
6268723
add imports and waitForTask
millotp Jun 5, 2024
691c455
feat: init
shortcuts Jun 5, 2024
5caf0ad
fix: consistent response var
shortcuts Jun 5, 2024
f8ab242
fix: imports and inits
shortcuts Jun 5, 2024
3a45555
fix: php snippets
shortcuts Jun 5, 2024
3dbe08b
fix: java
shortcuts Jun 5, 2024
541c8c6
fix: java (for real)
shortcuts Jun 5, 2024
dd706fd
chore: fix website build folder
shortcuts Jun 6, 2024
3b574a4
docs: breaking changes guide
shortcuts Jun 6, 2024
e445a41
fix: imports
shortcuts Jun 6, 2024
8975f03
docs: wait for task finish guide
shortcuts Jun 6, 2024
58e53ab
multiple snippets
millotp Jun 6, 2024
e5442e3
fix guides
millotp Jun 6, 2024
4617888
add for all languages
millotp Jun 6, 2024
c335fb5
default
millotp Jun 6, 2024
55be796
fix kotlin CTS
millotp Jun 6, 2024
8710064
chore: go templates
shortcuts Jun 6, 2024
4449840
chore: go templates (for real)
shortcuts Jun 6, 2024
9ed18e9
chore: wait for api key
shortcuts Jun 6, 2024
dbf5b4f
chore: operationIndex
shortcuts Jun 6, 2024
20064ef
fallback
millotp Jun 6, 2024
d64dbc1
chore: fallback
shortcuts Jun 6, 2024
87e5400
more explicit var
millotp Jun 6, 2024
ee42131
retriving-facets.mdx
millotp Jun 6, 2024
1b2210c
Merge branch 'main' into feat/generate-guides
shortcuts Jun 10, 2024
36c83a9
chore: wait for api key and ruby highlighting
shortcuts Jun 10, 2024
32fa6f8
Merge branch 'main' into feat/generate-guides
shortcuts Jun 10, 2024
1b7c013
chore: replace all rules n synonyms
shortcuts Jun 10, 2024
8c3133a
chore: copy index Algolia CLI
shortcuts Jun 10, 2024
44f9881
forwardToReplicas is not required
millotp Jun 10, 2024
42a8abb
more required: false
millotp Jun 10, 2024
dc8c32c
pass nil
millotp Jun 10, 2024
bbab1dc
chore: more guides
shortcuts Jun 10, 2024
eff99d7
feat(java): add replaceAllObjects helper (#3150)
millotp Jun 10, 2024
6c8b3d7
chore: generated code for commit 53ef92dadc676aeb11726dab6c4e46d326b6…
algolia-bot Jun 10, 2024
a8f2bf9
Merge branch 'main' into feat/generate-guides
millotp Jun 10, 2024
0b00a70
chore: send data to algolia
shortcuts Jun 10, 2024
a185458
fix: maybe not for real
shortcuts Jun 10, 2024
4223cbe
chore: bundled specs readme
shortcuts Jun 10, 2024
946cdb3
filtering-your-search
millotp Jun 10, 2024
ec00bcc
chore: cts
shortcuts Jun 10, 2024
dbdd121
allow nil
millotp Jun 10, 2024
78b8870
chore: best way to fix things
shortcuts Jun 10, 2024
42f8625
fix: type?
shortcuts Jun 10, 2024
1603f7b
chore: naming
shortcuts Jun 10, 2024
7fb865d
simplify logic
millotp Jun 10, 2024
c8a74b8
chore: slang
shortcuts Jun 10, 2024
a6db6ed
chore: thomas falcon eyes for real
shortcuts Jun 10, 2024
8a3ffa8
Revert "simplify logic"
shortcuts Jun 10, 2024
fa49c0d
add chevron
millotp Jun 10, 2024
2d6f9de
chore: wait api key guide
shortcuts Jun 10, 2024
7cce384
try to have less params
millotp Jun 10, 2024
ecffb93
final fixes
millotp Jun 10, 2024
a272c50
more elegant fix ?
millotp Jun 10, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ generators/bin

**/java/bin/
**/kotlin/bin/
.kotlin/

# Dart
.dart_tool/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void processOpts() {
if (mode.equals("tests")) {
ctsManager.addTestsSupportingFiles(supportingFiles);

testsGenerators.add(new TestsRequest(language, client));
testsGenerators.add(new TestsRequest(language, client, false));
testsGenerators.add(new TestsClient(language, client));
} else if (mode.equals("snippets")) {
ctsManager.addSnippetsSupportingFiles(supportingFiles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
public class SnippetsGenerator extends TestsRequest {

public SnippetsGenerator(String language, String client) {
super(language, client);
super(language, client, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
public class Request {

public String testName;
public boolean isSnippet;

public Map<String, Object> parameters;
public RequestOptions requestOptions;
Expand All @@ -23,6 +24,7 @@ public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class Request {\n");
sb.append(" testName: ").append(testName).append("\n");
sb.append(" isSnippet").append(isSnippet).append("\n");
sb.append(" parameters: ").append(parameters).append("\n");
sb.append(" requestOptions: ").append(requestOptions).append("\n");
sb.append(" request: ").append(request).append("\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@

public class TestsRequest extends TestsGenerator {

public TestsRequest(String language, String client) {
private final boolean withSnippets;

public TestsRequest(String language, String client, boolean withSnippets) {
super(language, client);
this.withSnippets = withSnippets;
}

protected Map<String, Request[]> loadRequestCTS() throws Exception {
Expand Down Expand Up @@ -84,13 +87,14 @@ public void run(Map<String, CodegenModel> models, Map<String, CodegenOperation>
}
Request[] op = cts.get(operationId);

List<Object> tests = new ArrayList<>();
List<Map<String, Object>> tests = new ArrayList<>();
for (int i = 0; i < op.length; i++) {
Map<String, Object> test = new HashMap<>();
Request req = op[i];
test.put("method", operationId);
test.put("testName", req.testName == null ? operationId + i : req.testName);
test.put("testIndex", i);
test.put("testName", req.testName == null ? operationId : req.testName);
test.put("testIndex", i == 0 ? "" : i);
test.put("isSnippet", req.isSnippet);
if (ope.returnType != null && ope.returnType.length() > 0) {
test.put("returnType", camelize(ope.returnType));
}
Expand Down Expand Up @@ -174,9 +178,20 @@ public void run(Map<String, CodegenModel> models, Map<String, CodegenOperation>
testObj.put("tests", tests);
testObj.put("operationId", operationId);

Map<String, Object> snippet = (Map<String, Object>) tests.get(0);
snippet.put("description", snippet.get("testName"));
testObj.put("snippet", snippet);
if (withSnippets) {
List<Map<String, Object>> snippets = tests.stream().filter(t -> (boolean) t.getOrDefault("isSnippet", false)).toList();
if (snippets.size() == 0) {
Map<String, Object> snippet = tests.get(0);
snippet.put("description", snippet.get("testName"));
snippet.put("testName", "default");
snippets = List.of(snippet);
} else {
for (Map<String, Object> snippet : snippets) {
snippet.put("description", snippet.get("testName"));
}
}
testObj.put("snippets", snippets);
}

blocks.add(testObj);
}
Expand Down
2 changes: 2 additions & 0 deletions scripts/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ module.exports = {
},
],
'@typescript-eslint/sort-type-union-intersection-members': 0,
'no-param-reassign': 0,
'@typescript-eslint/consistent-type-assertions': 0,
},
};
1 change: 0 additions & 1 deletion scripts/buildClients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export async function buildClients(generators: Generator[]): Promise<void> {
const generatorsMap = generators.reduce(
(map, gen) => {
if (!(gen.language in map)) {
// eslint-disable-next-line no-param-reassign
map[gen.language] = [];
}

Expand Down
81 changes: 69 additions & 12 deletions scripts/buildSpecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import yaml from 'js-yaml';
import { Cache } from './cache.js';
import { GENERATORS, capitalize, createClientName, exists, run, toAbsolutePath } from './common.js';
import { createSpinner } from './spinners.js';
import type { CodeSamples, Language, SnippetSamples, Spec } from './types.js';
import type { CodeSamples, Language, SnippetForMethod, SnippetSamples, Spec } from './types.js';

const ALGOLIASEARCH_LITE_OPERATIONS = ['search', 'customPost'];

Expand All @@ -22,6 +22,38 @@ function getCodeSampleLabel(language: Language): CodeSamples['label'] {
}
}

// Iterates over the snippet samples and sanitize the data to only keep the method part in order to use it in the guides.
function transformCodeSamplesToGuideMethods(snippetSamples: SnippetSamples): string {
for (const [language, operationWithSample] of Object.entries(snippetSamples)) {
for (const [operation, samples] of Object.entries(operationWithSample)) {
if (operation === 'import') {
continue;
}

for (const [sampleName, sample] of Object.entries(samples)) {
const sampleMatch = sample.match(
/.*Initialize the client\n(.*)((.|\n)*)(.*Call the API\n)((.|\n)*)/,
);
if (!sampleMatch) {
continue;
}

const initLine = sampleMatch[1];
const callLine = sampleMatch[5];

if (!('init' in snippetSamples[language])) {
snippetSamples[language].init = {
default: initLine.replace(/\n$/, ''),
};
}
snippetSamples[language][operation][sampleName] = callLine.replace(/\n$/, '');
}
}
}

return JSON.stringify(snippetSamples, null, 2);
}

// For a given `clientName`, reads the matching snippet file for every available clients and builds an hashmap of snippets per operationId per language.
async function transformSnippetsToCodeSamples(clientName: string): Promise<SnippetSamples> {
const snippetSamples = Object.values(GENERATORS).reduce(
Expand All @@ -42,26 +74,40 @@ async function transformSnippetsToCodeSamples(clientName: string): Promise<Snipp
'utf8',
);

const importMatch = snippetFileContent.match(/>IMPORT\n([\s\S]*?)\n.*IMPORT</);
if (importMatch) {
snippetSamples[gen.language].import = {
default: importMatch[1].replace(/\n$/, ''),
};
}

// iterate over every matches (operationId) and store it in the hashmap for later use
for (const match of snippetFileContent.matchAll(/>SEPARATOR (.+)\n([\s\S]*?)SEPARATOR</g)) {
for (const match of snippetFileContent.matchAll(
/>SEPARATOR (\w+) (.*)\n([\s\S]*?)SEPARATOR</g,
)) {
const lines: string[] = match[0].split('\n').slice(1, -1);
if (!lines.length) {
throw new Error(`No snippet found for ${gen.language} ${gen.client}`);
}

if (!snippetSamples[gen.language]) {
snippetSamples[gen.language] = {};
const operationId = match[1];
const testName = match[2] || 'default';

if (!snippetSamples[gen.language][operationId]) {
snippetSamples[gen.language][operationId] = {};
}

snippetSamples[gen.language][match[1]] = '';
const snippetForMethod: SnippetForMethod = snippetSamples[gen.language][operationId];

snippetForMethod[testName] = '';

const indent = lines[0].length - lines[0].trim().length;
// skip first and last lines because they contain the SEPARATOR or operationId
lines.forEach((line) => {
// best effort to determine how far the snippet is indented so we
// can have every snippets in the documentation on the far left
// without impacting the formatting
snippetSamples[gen.language][match[1]] += `${line.slice(indent).replaceAll(/\t/g, ' ')}\n`;
snippetForMethod[testName] += `${line.slice(indent).replaceAll(/\t/g, ' ')}\n`;
});
}
}
Expand Down Expand Up @@ -94,7 +140,16 @@ async function transformBundle({

const bundledSpec = yaml.load(await fsp.readFile(bundledPath, 'utf8')) as Spec;
const tagsDefinitions = bundledSpec.tags;
const snippetSamples = docs ? await transformSnippetsToCodeSamples(clientName) : {};
const snippetSamples = docs
? await transformSnippetsToCodeSamples(clientName)
: ({} as SnippetSamples);

if (docs) {
await fsp.writeFile(
toAbsolutePath(`website/src/generated/${clientName}-snippets.js`),
`export const snippets = ${transformCodeSamplesToGuideMethods(JSON.parse(JSON.stringify(snippetSamples)))}`,
);
}

for (const [pathKey, pathMethods] of Object.entries(bundledSpec.paths)) {
for (const [method, specMethod] of Object.entries(pathMethods)) {
Expand All @@ -120,11 +175,13 @@ async function transformBundle({
specMethod['x-codeSamples'] = [];
}

specMethod['x-codeSamples'].push({
lang: gen.language,
label: getCodeSampleLabel(gen.language),
source: snippetSamples[gen.language][specMethod.operationId],
});
if (snippetSamples[gen.language][specMethod.operationId]) {
specMethod['x-codeSamples'].push({
lang: gen.language,
label: getCodeSampleLabel(gen.language),
source: Object.values(snippetSamples[gen.language][specMethod.operationId])[0],
});
}
}

if (!bundledSpec.paths[pathKey][method].tags) {
Expand Down
1 change: 0 additions & 1 deletion scripts/ci/githubActions/setRunVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export const DEPENDENCIES = LANGUAGES.reduce(
const key = `${lang.toUpperCase()}_CLIENT_CHANGED`;
const langFolder = getLanguageFolder(lang);

// eslint-disable-next-line no-param-reassign
finalDependencies[key] = [
':!**node_modules',
`templates/${lang}`,
Expand Down
2 changes: 0 additions & 2 deletions scripts/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export const GENERATORS = Object.entries(clientsConfig).reduce(
clientName = client;
}

// eslint-disable-next-line no-param-reassign
current[key] = {
additionalProperties: {},
...gen,
Expand All @@ -59,7 +58,6 @@ export const GENERATORS = Object.entries(clientsConfig).reduce(

// guess the package name for js from the output folder variable
if (language === 'javascript') {
// eslint-disable-next-line no-param-reassign
current[key].additionalProperties.packageName = output.substring(
output.lastIndexOf('/') + 1,
);
Expand Down
4 changes: 3 additions & 1 deletion scripts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ export type CodeSamples = {
source: string;
};

export type SnippetSamples = Record<Language, Record<string, string>>;
export type SnippetForMethod = Record<string, string>;
export type SnippetForLanguage = Record<string, SnippetForMethod>;
export type SnippetSamples = Record<Language, SnippetForLanguage>;

/**
* Paths of a spec.
Expand Down
1 change: 0 additions & 1 deletion scripts/website/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
touch website/yarn.lock

# Generate the file with constants
mkdir -p website/src/generated
cat config/clients.config.json | jq -r '"export const versions = \(map_values(.packageVersion))"' > website/src/generated/variables.js

# install website deps and build
Expand Down
1 change: 1 addition & 0 deletions snippets/kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ configure<SpotlessExtension> {
"ktlint_standard_no-wildcard-imports" to "disabled",
"ktlint_standard_trailing-comma-on-declaration-site" to "disabled",
"ktlint_standard_filename" to "disabled",
"ktlint_standard_import-ordering" to "disabled",
),
)
}
Expand Down
13 changes: 8 additions & 5 deletions snippets/ruby/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ GEM
ast (2.4.2)
connection_pool (2.4.1)
dotenv (3.1.2)
faraday (2.9.0)
faraday (2.9.1)
faraday-net_http (>= 2.0, < 3.2)
faraday-net_http (3.1.0)
net-http
Expand All @@ -26,15 +26,16 @@ GEM
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
parallel (1.24.0)
parser (3.3.1.0)
parser (3.3.2.0)
ast (~> 2.4.1)
racc
racc (1.7.3)
racc (1.8.0)
rainbow (3.1.1)
rake (13.2.1)
regexp_parser (2.9.1)
regexp_parser (2.9.2)
rexml (3.2.8)
rubocop (1.63.5)
strscan (>= 3.0.9)
rubocop (1.64.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
Expand All @@ -48,10 +49,12 @@ GEM
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
ruby-progressbar (1.13.0)
strscan (3.1.0)
unicode-display_width (2.5.0)
uri (0.13.0)

PLATFORMS
aarch64-linux
x86_64-linux

DEPENDENCIES
Expand Down
5 changes: 5 additions & 0 deletions specs/common/parameters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ EndDateRequired:
ForwardToReplicas:
in: query
name: forwardToReplicas
required: false
description: Whether changes are applied to replica indices.
schema:
type: boolean
Expand All @@ -73,6 +74,7 @@ Page:
description: |
Requested page of the API response.
If `null`, the API response is not paginated.
required: false
schema:
oneOf:
- type: integer
Expand All @@ -97,6 +99,7 @@ HitsPerPage:
in: query
name: hitsPerPage
description: Number of hits per page.
required: false
schema:
type: integer
default: 100
Expand All @@ -105,6 +108,7 @@ Offset:
in: query
name: offset
description: Position of the first item to return.
required: false
schema:
type: integer
default: 0
Expand All @@ -114,6 +118,7 @@ Limit:
in: query
name: limit
description: Number of items to return.
required: false
schema:
type: integer
default: 10
Expand Down
Loading
Loading