Skip to content

Commit

Permalink
file changes
Browse files Browse the repository at this point in the history
  • Loading branch information
sambhavgupta0705 committed Mar 24, 2024
1 parent 0fa88f3 commit eaa800c
Showing 1 changed file with 64 additions and 51 deletions.
115 changes: 64 additions & 51 deletions tools/bundler/index.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
const path = require('path');
const fs = require('fs');
const traverse = require('json-schema-traverse');
const { url } = require('inspector');
const definitionsDirectory = path.resolve(__dirname, '../../definitions');
const commonSchemasDirectory = path.resolve(__dirname, '../../common');
const bindingsDirectory = path.resolve(__dirname, '../../bindings');
const extensionsDirectory = path.resolve(__dirname, '../../extensions');
const outputDirectory = path.resolve(__dirname, '../../schemas');
const JSON_SCHEMA_PROP_NAME = 'json-schema-draft-07-schema';
console.log(`Looking for separate definitions in the following directory: ${definitionsDirectory}`);
console.log(`Looking for binding version schemas in the following directory: ${bindingsDirectory}`);
console.log(`Looking for extension version schemas in the following directory: ${extensionsDirectory}`);
console.log(`Using the following output directory: ${outputDirectory}`);

// definitionsRegex is used to transform the name of a definition into a valid one to be used in the -without-$id.json files.
const definitionsRegex = /http:\/\/asyncapi\.com\/definitions\/[^/]*\/(.+)\.json#?(.*)/i;
const definitionsRegex = /http:\/\/asyncapi\.com\/definitions\/[^/]*\/(.+)\.json#?(.*)/i

// definitionsRegex is used to transform the name of a binding into a valid one to be used in the -without-$id.json files.
const bindingsRegex = /http:\/\/asyncapi\.com\/(bindings\/[^/]+)\/([^/]+)\/(.+)\.json(.*)/i;
const bindingsRegex = /http:\/\/asyncapi\.com\/(bindings\/[^/]+)\/([^/]+)\/(.+)\.json(.*)/i

// definitionsRegex is used to transform the name of a binding into a valid one to be used in the -without-$id.json files.
const extensionsRegex = /http:\/\/asyncapi\.com\/(extensions\/[^/]+)\/([^/]+)\/(.+)\.json(.*)/i

/**
* Function to load all the core AsyncAPI spec definition (except the root asyncapi schema, as that will be loaded later) into the bundler.
*/
async function loadDefinitions(bundler, versionDir) {
const definitions = await fs.promises.readdir(versionDir);
const definitionFiles = definitions.filter((value) => {return !value.includes('asyncapi');}).map((file) => fs.readFileSync(path.resolve(versionDir, file)));
const definitionFiles = definitions.filter((value) => {return !value.includes('asyncapi')}).map((file) => fs.readFileSync(path.resolve(versionDir, file)));
const definitionJson = definitionFiles.map((file) => JSON.parse(file));

for (const jsonFile of definitionJson) {
Expand All @@ -40,35 +45,39 @@ async function loadDefinitions(bundler, versionDir) {
}
}


/**
* Function to load all the binding version schemas into the bundler
* Function to load all schemas into bundler, by "type" you specify if these are "bindings" or "extensions"
*/
async function loadBindings(bundler) {
const bindingDirectories = await fs.promises.readdir(bindingsDirectory);
for (const bindingDirectory of bindingDirectories) {
const bindingVersionDirectories = await fs.promises.readdir(path.resolve(bindingsDirectory, bindingDirectory));
const bindingVersionDirectoriesFiltered = bindingVersionDirectories.filter((file) => fs.lstatSync(path.resolve(bindingsDirectory, bindingDirectory, file)).isDirectory());
for (const bindingVersionDirectory of bindingVersionDirectoriesFiltered) {
const bindingFiles = await fs.promises.readdir(path.resolve(bindingsDirectory, bindingDirectory, bindingVersionDirectory));
const bindingFilesFiltered = bindingFiles.filter((bindingFile) => path.extname(bindingFile) === '.json').map((bindingFile) => path.resolve(bindingsDirectory, bindingDirectory, bindingVersionDirectory, bindingFile));
for (const bindingFile of bindingFilesFiltered) {
const bindingFileContent = require(bindingFile);
bundler.add(bindingFileContent);
}
}
async function loadSchemas(bundler, type) {

let directory;

switch (type) {
case "bindings":
directory = bindingsDirectory;
break;
case "extensions":
directory = extensionsDirectory;
break;
default:
console.error("Invalid input. I'm not going to assume if you want bindings or extensions - these are different beasts.");
}
}

async function loadCommonSchemas(bundler) {
// Add common schemas to all versions
const commonSchemas = await fs.promises.readdir(commonSchemasDirectory);
const commonSchemaFiles = commonSchemas.map((file) => path.resolve(commonSchemasDirectory, file));
for (const commonSchemaFile of commonSchemaFiles) {
const commonSchemaFileContent = require(commonSchemaFile);
bundler.add(commonSchemaFileContent);
const directories = await fs.promises.readdir(directory);
for (const nestedDir of directories) {
const versionDirectories = await fs.promises.readdir(path.resolve(directory, nestedDir));
const versionDirectoriesFiltered = versionDirectories.filter((file) => fs.lstatSync(path.resolve(directory, nestedDir, file)).isDirectory());
for (const versionDir of versionDirectoriesFiltered) {
const files = await fs.promises.readdir(path.resolve(directory, nestedDir, versionDir));
const filesFiltered = files.filter((file) => path.extname(file) === '.json').map((file) => path.resolve(directory, nestedDir, versionDir, file));
for (const filteredFile of filesFiltered) {
const fileContent = require(filteredFile);
bundler.add(fileContent);
}
}
}
}

/**
* When run, go through all versions that have split definitions and bundles them together.
*/
Expand All @@ -80,15 +89,15 @@ async function loadCommonSchemas(bundler) {
}
console.log(`The following versions have separate definitions: ${versions.join(',')}`);
for (const version of versions) {
const Bundler = require('@hyperjump/json-schema-bundle');
try {
const Bundler = require("@hyperjump/json-schema-bundle");
try{
console.log(`Bundling the following version together: ${version}`);
const outputFileWithId = path.resolve(outputDirectory, `${version}.json`);
const outputFileWithoutId = path.resolve(outputDirectory, `${version}-without-$id.json`);
const versionDir = path.resolve(definitionsDirectory, version);
await loadDefinitions(Bundler, versionDir);
await loadCommonSchemas(Bundler);
await loadBindings(Bundler);
await loadSchemas(Bundler, 'bindings');
await loadSchemas(Bundler, 'extensions');

const filePathToBundle = `file://${versionDir}/asyncapi.json`;
const fileToBundle = await Bundler.get(filePathToBundle);
Expand All @@ -97,7 +106,7 @@ async function loadCommonSchemas(bundler) {
* bundling schemas into one file with $id
*/
const bundledSchemaWithId = await Bundler.bundle(fileToBundle);
bundledSchemaWithId.description = `!!Auto generated!! \n Do not manually edit. ${bundledSchemaWithId.description !== undefined && bundledSchemaWithId.description !== null ? bundledSchemaWithId.description : ''}`;
bundledSchemaWithId.description = `!!Auto generated!! \n Do not manually edit. ${bundledSchemaWithId.description ?? ''}`;
console.log(`Writing the bundled file WITH $ids to: ${outputFileWithId}`);
await fs.promises.writeFile(outputFileWithId, JSON.stringify(bundledSchemaWithId, null, 4));

Expand All @@ -108,7 +117,7 @@ async function loadCommonSchemas(bundler) {
const bundledSchemaWithoutIds = modifyRefsAndDefinitions(bundledSchemaWithId);
console.log(`Writing the bundled file WITHOUT $ids to: ${outputFileWithoutId}`);
await fs.promises.writeFile(outputFileWithoutId, JSON.stringify(bundledSchemaWithoutIds, null, 4));
} catch (e) {
}catch(e) {
throw new Error(e);
}
}
Expand All @@ -118,6 +127,7 @@ async function loadCommonSchemas(bundler) {
/**
* Extract file data from reference file path
*/

async function loadRefProperties(filePath) {
const schemaPath = filePath.$ref;
// first we need to turn the path to an absolute file path instead of a generic url
Expand All @@ -126,31 +136,32 @@ async function loadRefProperties(filePath) {
try {
const data = await fs.promises.readFile(`../../examples${versionPath}`);
return JSON.parse(data);
} catch (e) {
throw new Error(e);
}catch(e) {
throw new Error(e);
}
}
}

/**
* we first update definitions from URL to normal names
* than update refs to point to new definitions, always inline never remote
*/
function modifyRefsAndDefinitions(bundledSchema) {

//first we need to improve names of the definitions from URL to their names
for (const def of Object.keys(bundledSchema.definitions)) {
const newDefName = getDefinitionName(def);

//creating copy of definition under new name so later definition stored under URL name can be removed
bundledSchema.definitions[newDefName] = bundledSchema.definitions[def];
delete bundledSchema.definitions[def];
delete bundledSchema.definitions[def]
}

traverse(bundledSchema, replaceRef);
traverse(bundledSchema.definitions.avroSchema_v1, updateAvro);
traverse(bundledSchema.definitions.openapiSchema_3_0, updateOpenApi);
traverse(bundledSchema.definitions['json-schema-draft-07-schema'], updateJsonSchema);

return bundledSchema;
return bundledSchema
}

/**
Expand All @@ -167,8 +178,12 @@ function getDefinitionName(def) {
const result = bindingsRegex.exec(def);
if (result) return `${result[1].replace('/', '-')}-${result[2]}-${result[3]}`;
}
if (def.startsWith('http://asyncapi.com/extensions')) {
const result = extensionsRegex.exec(def);
if (result) return `${result[1].replace('/', '-')}-${result[2]}-${result[3]}`;
}

return path.basename(def, '.json');
return path.basename(def, '.json')
}

/**
Expand All @@ -177,11 +192,10 @@ function getDefinitionName(def) {
*/
function replaceRef(schema) {
//new refs will only work if we remove $id that all point to asyncapi.com
delete schema.$id;

//traversing should take place only in case of schemas with refs
if (schema.$ref === undefined) return;

delete schema.$id

//traversing shoudl take place only in case of schemas with refs
if (schema.$ref === undefined ) return;
// updating refs that are related to remote URL refs that need to be update and point to inlined versions
if (!schema.$ref.startsWith('#')) schema.$ref = `#/definitions/${getDefinitionName(schema.$ref)}`;
}
Expand All @@ -190,12 +204,11 @@ function replaceRef(schema) {
* this is a callback used when traversing through json schema
* to fix avro schema definitions to point to right direction
*/
function updateAvro(schema) {
//traversing should take place only in case of schemas with refs
function updateAvro(schema){
//traversing shoudl take place only in case of schemas with refs
if (schema.$ref === undefined) return;

schema.$ref = schema.$ref.replace(
/* eslint-disable sonarjs/no-duplicate-string */
'#/definitions/',
'#/definitions/avroSchema_v1/definitions/'
);
Expand All @@ -205,8 +218,8 @@ function updateAvro(schema) {
* this is a callback used when traversing through json schema
* to fix open api schema definitions to point to right direction
*/
function updateOpenApi(schema) {
//traversing should take place only in case of schemas with refs
function updateOpenApi(schema){
//traversing shoudl take place only in case of schemas with refs
if (schema.$ref === undefined) return;
const openApiPropName = 'openapiSchema_3_0';

Expand All @@ -224,8 +237,8 @@ function updateOpenApi(schema) {
* this is a callback used when traversing through json schema
* to fix open api schema definitions to point to right direction
*/
function updateJsonSchema(schema) {
//traversing should take place only in case of schemas with refs
function updateJsonSchema(schema){
//traversing shoudl take place only in case of schemas with refs
if (schema.$ref === undefined) return;

schema.$ref = schema.$ref.replace(
Expand Down

0 comments on commit eaa800c

Please sign in to comment.