Skip to content

Commit

Permalink
Externalize scim endpoints parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
berhalak committed Jan 9, 2025
1 parent fa49e97 commit 45e5427
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 22 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ Once you're done, add the results to the main documentation with:
./api/build.sh
```

Before editing the API take a look at `./api-parameters.js` script. Parameters for endpoints need
to be externalized for Swagger UI (and Grist API console) to work correctly. This script explains
it in more details and offer some help with the process.

## Updating function reference

To update `help/en/docs/functions.md` from the documentation comments in Grist, run:
Expand Down
85 changes: 85 additions & 0 deletions api/parameters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env node

// This script is an attempt to automate work started in https://github.com/gristlabs/grist-help/pull/293
// TLDR: Swagger UI (and Grist API) console doesn't support inline parameters well, so they need to be
// converted to shared parameters. This script does that conversion, but it's not perfect and may need
// manual tweaking afterwards if the parameters are complex or have shared names.

const fs = require('fs');
const yaml = require('js-yaml');

// Get command-line arguments
const inputFilePath = process.argv[2];
const outputFilePath = process.argv[3];

if (!inputFilePath) {
console.error('Usage: node api-parameters.js <inputFilePath> [outputFilePath]');
process.exit(1);
}

try {
const fileContents = fs.readFileSync(inputFilePath, 'utf8');
const apiSpec = yaml.load(fileContents);

// Initialize shared parameters in the components section if not already present
apiSpec.components = apiSpec.components || {};
apiSpec.components.parameters = apiSpec.components.parameters || {};

// Function to generate a unique parameter name based on its contents
const generateParameterName = (param) => {
const baseName = param.name;
return `${baseName}Param`;
};

// Function to create shared parameter references
const createSharedParameter = (param) => {
const paramName = generateParameterName(param);
if (!apiSpec.components.parameters[paramName]) {
apiSpec.components.parameters[paramName] = {
in: param.in,
name: param.name,
schema: param.schema,
description: param.description || `${param.name} parameter`,
required: param.required
};
}
return { $ref: `#/components/parameters/${paramName}` };
};

// Function to process each endpoint's parameters
const processParameters = (parameters) => {
return parameters.map((param) => {
// Skip parameters that are already references
if (param.$ref) {
return param;
}
return createSharedParameter(param);
});
};

// Update paths to use shared parameters
Object.keys(apiSpec.paths).forEach((path) => {
const methods = apiSpec.paths[path];
Object.keys(methods).forEach((method) => {
const operation = methods[method];
if (operation.parameters) {
operation.parameters = processParameters(operation.parameters);
}
});
});

// Write the updated YAML
const updatedYaml = yaml.dump(apiSpec);

if (outputFilePath === '-') {
fs.writeFileSync(inputFilePath, updatedYaml, 'utf8');
console.log(`Overwritten API spec at ${inputFilePath}`);
} else if (outputFilePath) {
fs.writeFileSync(outputFilePath, updatedYaml, 'utf8');
console.log(`Updated API spec written to ${outputFilePath}`);
} else {
process.stdout.write(updatedYaml);
}
} catch (e) {
console.error(e);
}
49 changes: 28 additions & 21 deletions api/scim/users.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,9 @@ paths:
tags:
- scim
parameters:
- name: startIndex
in: query
description: The starting index for pagination.
required: false
schema:
type: integer
example: 1
- name: count
in: query
description: The number of users to retrieve.
required: false
schema:
type: integer
example: 10
- name: filter
in: query
description: Filter users based on specific criteria.
required: false
schema:
type: string
example: "userName pr"
- $ref: '#/components/parameters/startIndexParam'
- $ref: '#/components/parameters/countParam'
- $ref: '#/components/parameters/filterParam'
responses:
'200':
description: Successfully retrieved list of users.
Expand Down Expand Up @@ -417,3 +399,28 @@ components:
description: The unique identifier of the user.
example: "1"
- $ref: '#/components/schemas/UserInRequest'
parameters:
startIndexParam:
in: query
name: startIndex
schema:
type: integer
example: 1
description: The starting index for pagination.
required: false
countParam:
in: query
name: count
schema:
type: integer
example: 10
description: The number of users to retrieve.
required: false
filterParam:
in: query
name: filter
schema:
type: string
example: userName pr
description: Filter users based on specific criteria.
required: false
2 changes: 1 addition & 1 deletion help/en/docs/api.md

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"redoc-cli": "^0.13.20"
},
"devDependencies": {
"js-yaml": "^4.1.0",
"typedoc": "^0.23.9",
"typedoc-plugin-markdown": "3.13.4",
"typescript": "^4.7.4"
Expand Down

0 comments on commit 45e5427

Please sign in to comment.