Skip to content

Commit

Permalink
Generate _opendistro endpoints through merger tool
Browse files Browse the repository at this point in the history
Signed-off-by: Theo Truong <[email protected]>
  • Loading branch information
nhtruong committed Apr 20, 2024
1 parent e02c076 commit 143e91a
Show file tree
Hide file tree
Showing 10 changed files with 698 additions and 6 deletions.
566 changes: 566 additions & 0 deletions spec/_replaced_operations.yaml

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions tools/merger/OpenApiMerger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fs from 'fs';
import _ from 'lodash';
import yaml from 'yaml';
import { write2file } from '../helpers';
import ReplacedOpsGenerator from "./ReplacedOpsGenerator";

// Create a single-file OpenAPI spec from multiple files for OpenAPI validation and programmatic consumption
export default class OpenApiMerger {
Expand Down Expand Up @@ -33,6 +34,7 @@ export default class OpenApiMerger {
this.#merge_namespaces();
this.#apply_global_params();
this.#sort_spec_keys();
this.#generate_replaced_ops();

if(output_path) write2file(output_path, this.spec);
return this.spec as OpenAPIV3.Document;
Expand Down Expand Up @@ -124,4 +126,9 @@ export default class OpenApiMerger {
this.spec.paths[path] = _.fromPairs(Object.entries(pathItem!).sort());
});
}

#generate_replaced_ops(): void {
const gen = new ReplacedOpsGenerator(this.root_folder);
gen.generate(this.spec);
}
}
25 changes: 25 additions & 0 deletions tools/merger/OpenDistro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from "fs";
import YAML from "yaml";
import { OperationPath, HttpVerb, ReplacedOperationMap } from "../types";
import { write2file } from "../helpers";

// One-time script to generate _replaced_operations.yaml file for OpenDistro
// Keeping this for now in case we need to update the file in the near future. Can be removed after a few months.
// TODO: Remove this file in 2025.
export default class OpenDistro {
input: Record<OperationPath, HttpVerb[]>;
output: ReplacedOperationMap = {};

constructor(file_path: string) {
this.input = YAML.parse(fs.readFileSync(file_path, 'utf8'));
this.build_output();
write2file(file_path, this.output);
}

build_output() {
for(const [path, operations] of Object.entries(this.input)) {
const replaced_by = path.replace('_opendistro', '_plugins');
this.output[path] = { replaced_by, operations };
}
}
}
50 changes: 50 additions & 0 deletions tools/merger/ReplacedOpsGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {OperationSpec, ReplacedOperationMap} from "../types";
import YAML from "yaml";
import fs from "fs";
import _ from "lodash";

export default class ReplacedOpsGenerator {
file_path: string;
replaced_ops: ReplacedOperationMap;

constructor(root_path: string) {
this.file_path = root_path + '/_replaced_operations.yaml';
this.replaced_ops = YAML.parse(fs.readFileSync(this.file_path, 'utf8'));
}

generate(spec: Record<string, any>): void {
for(const [path, { replaced_by, operations }] of _.entries(this.replaced_ops)) {
const regex = this.path_to_regex(replaced_by);
const operation_keys = operations.map(op => op.toLowerCase());
const replaced_path = this.copy_params(replaced_by, path);
const path_entry = _.entries(spec.paths).find(([path, _]) => regex.test(path));
if(!path_entry) console.log(`Path not found: ${replaced_by}`);
else spec.paths[replaced_path] = this.path_object(path_entry[1] as any, operation_keys);
}
}

path_object(obj: Record<string, any>, keys: string[]): Record<string, any> {
const cloned_obj = _.cloneDeep(_.pick(obj, keys));
for(const key in cloned_obj) {
const operation = cloned_obj[key] as OperationSpec;
operation.operationId = operation.operationId + '_replaced';
operation.deprecated = true;
operation['x-ignorable'] = true;
}
return cloned_obj;
}

path_to_regex(path: string): RegExp {
const source = '^' + path.replace(/\{.+?}/g, '\\{.+?\\}').replace(/\//g, '\\/') + '$';
return new RegExp(source, 'g');
}

copy_params(source: string, target: string): string {
const target_parts = target.split('/');
const target_params = target_parts.filter(part => part.startsWith('{'));
const source_params = source.split('/').filter(part => part.startsWith('{')).reverse();
if(target_params.length !== source_params.length)
throw new Error('Mismatched parameters in source and target paths: ' + source + ' -> ' + target);
return target_parts.map((part) => part.startsWith('{') ? source_params.pop()! : part).join('/');
}
}
2 changes: 1 addition & 1 deletion tools/merger/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ import OpenApiMerger from "./OpenApiMerger";
const root_path: string = process.argv[2] || '../spec/opensearch-openapi.yaml'
const output_path: string = process.argv[3] || '../opensearch-openapi.yaml'
const merger = new OpenApiMerger(root_path);
merger.merge(output_path);
merger.merge(output_path);
23 changes: 22 additions & 1 deletion tools/test/merger/fixtures/expected.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,39 @@ info:
description: OpenSearch API
version: 1.0.0
paths:
/adopt/{animal}:
/adopt/{animal}/dockets/{docket}:
get:
operationId: adopt.0
parameters:
- $ref: '#/components/parameters/adopt::path.animal'
- $ref: '#/components/parameters/adopt::path.docket'
- $ref: '#/components/parameters/_global::query.human'
responses:
'200':
$ref: '#/components/responses/adopt@200'
post:
operationId: adopt.1
parameters:
- $ref: '#/components/parameters/adopt::path.animal'
- $ref: '#/components/parameters/adopt::path.docket'
- $ref: '#/components/parameters/_global::query.human'
requestBody:
$ref: '#/components/requestBodies/adopt'
responses:
'200':
$ref: '#/components/responses/adopt@200'
/replaced/adopting/{animal}/something/{docket}:
get:
operationId: adopt.0_replaced
parameters:
- $ref: '#/components/parameters/adopt::path.animal'
- $ref: '#/components/parameters/adopt::path.docket'
- $ref: '#/components/parameters/_global::query.human'
responses:
'200':
$ref: '#/components/responses/adopt@200'
deprecated: true
x-ignorable: true
components:
parameters:
_global::query.human:
Expand All @@ -36,6 +52,11 @@ components:
in: path
schema:
$ref: '#/components/schemas/animals:Animal'
adopt::path.docket:
name: docket
in: path
schema:
type: number
indices.create::path.index:
name: index
in: path
Expand Down
10 changes: 10 additions & 0 deletions tools/test/merger/fixtures/spec/_replaced_operations.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/replaced/adopting/{a}/something/{b}:
replaced_by: /adopt/{animal}/dockets/{docket}
operations:
- GET
- DELETE
/something/else:
replaced_by: /not/here
operations:
- POST
- PUT
11 changes: 10 additions & 1 deletion tools/test/merger/fixtures/spec/namespaces/shelter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ info:
description: OpenSearch API
version: 1.0.0
paths:
'/adopt/{animal}':
'/adopt/{animal}/dockets/{docket}':
get:
operationId: adopt.0
parameters:
- $ref: '#/components/parameters/adopt::path.animal'
- $ref: '#/components/parameters/adopt::path.docket'
responses:
'200':
$ref: '#/components/responses/adopt@200'
post:
operationId: adopt.1
parameters:
- $ref: '#/components/parameters/adopt::path.animal'
- $ref: '#/components/parameters/adopt::path.docket'
requestBody:
$ref: '#/components/requestBodies/adopt'
responses:
Expand All @@ -28,6 +32,11 @@ components:
in: path
schema:
$ref: '../schemas/animals.yaml#/components/schemas/Animal'
adopt::path.docket:
name: docket
in: path
schema:
type: number
responses:
adopt@200:
description: ''
Expand Down
4 changes: 2 additions & 2 deletions tools/test/merger/fixtures/spec/opensearch-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ info:
description: OpenSearch API
version: 1.0.0
paths:
'/adopt/{animal}':
$ref: 'namespaces/shelter.yaml#/paths/~1adopt~1{animal}'
'/adopt/{animal}/dockets/{docket}':
$ref: 'namespaces/shelter.yaml#/paths/~1adopt~1{animal}~1dockets~1{docket}'
components:
parameters:
_global::query.human:
Expand Down
6 changes: 5 additions & 1 deletion tools/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ export interface ValidationError {
file: string;
location?: string;
message: string;
}
}

export type HttpVerb = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'TRACE'
export type OperationPath = string;
export type ReplacedOperationMap = Record<OperationPath, { replaced_by: OperationPath, operations: HttpVerb[] }>;

0 comments on commit 143e91a

Please sign in to comment.