diff --git a/src/DataAccessGenerator.ts b/src/DataAccessGenerator.ts new file mode 100644 index 0000000..43a7e29 --- /dev/null +++ b/src/DataAccessGenerator.ts @@ -0,0 +1,110 @@ +import { Collection, Entity, Property } from "./MySchema"; +import { FileWrite } from "./app"; + +export function generateDataAccess(collections: Collection[]): FileWrite[] { + return collections.map(generateDataAccessFile); +} + +function generateDataAccessFile(collection: Collection): FileWrite { + const definitions: string[] = [ + defineImports(collection), + defineSave(collection), + defineParseEntity(collection.entities), + defineParseCollection(collection), + defineLoad(collection), + defineGeneral(), + ]; + + return { + location: collection.name + "DataAccess.ts", + content: definitions.join("\n\n") + "\n", + }; +} + +function defineImports(collection: Collection): string { + const entity = collection.entities; + return `import { ${collection.name}, ${entity.name} } from "./${collection.name}Model"; +import * as fs from "fs";`; +} + +function defineSave(collection: Collection): string { + return `export function save(fileName: string, collection: ${collection.name}): void { + const content = JSON.stringify({ ...collection, $schema: "./${collection.name.toLowerCase()}.schema.json" }); + fs.writeFileSync(fileName, content); +}`; +} + +function defineGeneral(): string { + return `function parseArrayProperty(fieldName: keyof O, itemParser: (item: any) => T, data: any): T[] +{ + if(fieldName in data) + { + const valueList = data[fieldName]; + if(Array.isArray(valueList)) + { + return valueList.map(itemParser); + } + } + return []; +} + +function parsePrimitiveProperty(type: T extends string ? "string" : "int", fieldName: keyof O, defaultValue: T, obj: any): T +{ + if(fieldName in obj) + { + const fieldValue = obj[fieldName]; + if(typeof fieldValue === type) + { + return fieldValue; + } + } + return defaultValue; +} + +function canReadFile(path: string): boolean { + try { + fs.accessSync(path, fs.constants.R_OK); + return true; + } catch (error) { + return false; + } +}`; +} + +function defineParseEntity(entity: Entity): string { + const propParser = entity.properties.map(p => parseStringProperty(entity.name, p)).join("\n"); + return `function parse${entity.name}(data: any): ${entity.name} +{ + return { + id: parsePrimitiveProperty<${entity.name}, number>("int", "id", 0, data), +${propParser} + }; +}`; +} + +function parseStringProperty(entityName: string, property: Property): string { + return `\t\t${property.key}: parsePrimitiveProperty<${entityName}, string>("string", "${property.key}", "", data),`; +} + +function defineParseCollection(collection: Collection): string { + return `function parse${collection.name}(data: any): ${collection.name} { + return { + nextId: parsePrimitiveProperty<${collection.name}, number>("int", "nextId", 1, data), + items: parseArrayProperty<${collection.name}, ${collection.entities.name}>("items", parse${collection.entities.name}, data), + }; +}`; +} + +function defineLoad(collection: Collection): string { + return `export function load(fileName: string): ${collection.name} { + if (!canReadFile(fileName)) { + return { + items: [], + nextId: 1, + }; + } + const content = fs.readFileSync(fileName).toString(); + const data = JSON.parse(content); + return parse${collection.name}(data); +}`; +} diff --git a/src/DataAccessGenerator.ts b/src/DataAccessGenerator.ts new file mode 100644 index 0000000..43a7e29 --- /dev/null +++ b/src/DataAccessGenerator.ts @@ -0,0 +1,110 @@ +import { Collection, Entity, Property } from "./MySchema"; +import { FileWrite } from "./app"; + +export function generateDataAccess(collections: Collection[]): FileWrite[] { + return collections.map(generateDataAccessFile); +} + +function generateDataAccessFile(collection: Collection): FileWrite { + const definitions: string[] = [ + defineImports(collection), + defineSave(collection), + defineParseEntity(collection.entities), + defineParseCollection(collection), + defineLoad(collection), + defineGeneral(), + ]; + + return { + location: collection.name + "DataAccess.ts", + content: definitions.join("\n\n") + "\n", + }; +} + +function defineImports(collection: Collection): string { + const entity = collection.entities; + return `import { ${collection.name}, ${entity.name} } from "./${collection.name}Model"; +import * as fs from "fs";`; +} + +function defineSave(collection: Collection): string { + return `export function save(fileName: string, collection: ${collection.name}): void { + const content = JSON.stringify({ ...collection, $schema: "./${collection.name.toLowerCase()}.schema.json" }); + fs.writeFileSync(fileName, content); +}`; +} + +function defineGeneral(): string { + return `function parseArrayProperty(fieldName: keyof O, itemParser: (item: any) => T, data: any): T[] +{ + if(fieldName in data) + { + const valueList = data[fieldName]; + if(Array.isArray(valueList)) + { + return valueList.map(itemParser); + } + } + return []; +} + +function parsePrimitiveProperty(type: T extends string ? "string" : "int", fieldName: keyof O, defaultValue: T, obj: any): T +{ + if(fieldName in obj) + { + const fieldValue = obj[fieldName]; + if(typeof fieldValue === type) + { + return fieldValue; + } + } + return defaultValue; +} + +function canReadFile(path: string): boolean { + try { + fs.accessSync(path, fs.constants.R_OK); + return true; + } catch (error) { + return false; + } +}`; +} + +function defineParseEntity(entity: Entity): string { + const propParser = entity.properties.map(p => parseStringProperty(entity.name, p)).join("\n"); + return `function parse${entity.name}(data: any): ${entity.name} +{ + return { + id: parsePrimitiveProperty<${entity.name}, number>("int", "id", 0, data), +${propParser} + }; +}`; +} + +function parseStringProperty(entityName: string, property: Property): string { + return `\t\t${property.key}: parsePrimitiveProperty<${entityName}, string>("string", "${property.key}", "", data),`; +} + +function defineParseCollection(collection: Collection): string { + return `function parse${collection.name}(data: any): ${collection.name} { + return { + nextId: parsePrimitiveProperty<${collection.name}, number>("int", "nextId", 1, data), + items: parseArrayProperty<${collection.name}, ${collection.entities.name}>("items", parse${collection.entities.name}, data), + }; +}`; +} + +function defineLoad(collection: Collection): string { + return `export function load(fileName: string): ${collection.name} { + if (!canReadFile(fileName)) { + return { + items: [], + nextId: 1, + }; + } + const content = fs.readFileSync(fileName).toString(); + const data = JSON.parse(content); + return parse${collection.name}(data); +}`; +} diff --git a/src/ExampleOutput/DataAccess.ts b/src/ExampleOutput/DataAccess.ts index 279da34..f98ce06 100644 --- a/src/ExampleOutput/DataAccess.ts +++ b/src/ExampleOutput/DataAccess.ts @@ -76,8 +76,7 @@ if (!canReadFile(fileName)) { return emptyGraph(); } - // TODO check if file exists const content = fs.readFileSync(fileName).toString(); const data = JSON.parse(content); return parseGraph(data); -} \ No newline at end of file +} diff --git a/src/DataAccessGenerator.ts b/src/DataAccessGenerator.ts new file mode 100644 index 0000000..43a7e29 --- /dev/null +++ b/src/DataAccessGenerator.ts @@ -0,0 +1,110 @@ +import { Collection, Entity, Property } from "./MySchema"; +import { FileWrite } from "./app"; + +export function generateDataAccess(collections: Collection[]): FileWrite[] { + return collections.map(generateDataAccessFile); +} + +function generateDataAccessFile(collection: Collection): FileWrite { + const definitions: string[] = [ + defineImports(collection), + defineSave(collection), + defineParseEntity(collection.entities), + defineParseCollection(collection), + defineLoad(collection), + defineGeneral(), + ]; + + return { + location: collection.name + "DataAccess.ts", + content: definitions.join("\n\n") + "\n", + }; +} + +function defineImports(collection: Collection): string { + const entity = collection.entities; + return `import { ${collection.name}, ${entity.name} } from "./${collection.name}Model"; +import * as fs from "fs";`; +} + +function defineSave(collection: Collection): string { + return `export function save(fileName: string, collection: ${collection.name}): void { + const content = JSON.stringify({ ...collection, $schema: "./${collection.name.toLowerCase()}.schema.json" }); + fs.writeFileSync(fileName, content); +}`; +} + +function defineGeneral(): string { + return `function parseArrayProperty(fieldName: keyof O, itemParser: (item: any) => T, data: any): T[] +{ + if(fieldName in data) + { + const valueList = data[fieldName]; + if(Array.isArray(valueList)) + { + return valueList.map(itemParser); + } + } + return []; +} + +function parsePrimitiveProperty(type: T extends string ? "string" : "int", fieldName: keyof O, defaultValue: T, obj: any): T +{ + if(fieldName in obj) + { + const fieldValue = obj[fieldName]; + if(typeof fieldValue === type) + { + return fieldValue; + } + } + return defaultValue; +} + +function canReadFile(path: string): boolean { + try { + fs.accessSync(path, fs.constants.R_OK); + return true; + } catch (error) { + return false; + } +}`; +} + +function defineParseEntity(entity: Entity): string { + const propParser = entity.properties.map(p => parseStringProperty(entity.name, p)).join("\n"); + return `function parse${entity.name}(data: any): ${entity.name} +{ + return { + id: parsePrimitiveProperty<${entity.name}, number>("int", "id", 0, data), +${propParser} + }; +}`; +} + +function parseStringProperty(entityName: string, property: Property): string { + return `\t\t${property.key}: parsePrimitiveProperty<${entityName}, string>("string", "${property.key}", "", data),`; +} + +function defineParseCollection(collection: Collection): string { + return `function parse${collection.name}(data: any): ${collection.name} { + return { + nextId: parsePrimitiveProperty<${collection.name}, number>("int", "nextId", 1, data), + items: parseArrayProperty<${collection.name}, ${collection.entities.name}>("items", parse${collection.entities.name}, data), + }; +}`; +} + +function defineLoad(collection: Collection): string { + return `export function load(fileName: string): ${collection.name} { + if (!canReadFile(fileName)) { + return { + items: [], + nextId: 1, + }; + } + const content = fs.readFileSync(fileName).toString(); + const data = JSON.parse(content); + return parse${collection.name}(data); +}`; +} diff --git a/src/ExampleOutput/DataAccess.ts b/src/ExampleOutput/DataAccess.ts index 279da34..f98ce06 100644 --- a/src/ExampleOutput/DataAccess.ts +++ b/src/ExampleOutput/DataAccess.ts @@ -76,8 +76,7 @@ if (!canReadFile(fileName)) { return emptyGraph(); } - // TODO check if file exists const content = fs.readFileSync(fileName).toString(); const data = JSON.parse(content); return parseGraph(data); -} \ No newline at end of file +} diff --git a/src/ModelGenerator.ts b/src/ModelGenerator.ts index 29550ef..d471407 100644 --- a/src/ModelGenerator.ts +++ b/src/ModelGenerator.ts @@ -23,7 +23,7 @@ function defineCollection(collection: Collection): string { return `export interface ${collection.name} { - nodes: ${collection.entities.name}[]; + items: ${collection.entities.name}[]; nextId: number; }`; } @@ -43,16 +43,16 @@ ref: string; } -export function toPayload(node: ${entity.name}): ${entity.name}Payload { +export function toPayload(item: ${entity.name}): ${entity.name}Payload { return { - ...node, - ref: \`/${collection.name.toLowerCase()}/\${node.id}\`, + ...item, + ref: \`/${collection.name.toLowerCase()}/\${item.id}\`, } }`; } function defineFindFunction(collection: Collection): string { - return `export function findSingle(graph: ${collection.name}, id: number) { - return graph.nodes.find(n => n.id === id); + return `export function findSingle(collection: ${collection.name}, id: number) { + return collection.items.find(n => n.id === id); }`; } diff --git a/src/DataAccessGenerator.ts b/src/DataAccessGenerator.ts new file mode 100644 index 0000000..43a7e29 --- /dev/null +++ b/src/DataAccessGenerator.ts @@ -0,0 +1,110 @@ +import { Collection, Entity, Property } from "./MySchema"; +import { FileWrite } from "./app"; + +export function generateDataAccess(collections: Collection[]): FileWrite[] { + return collections.map(generateDataAccessFile); +} + +function generateDataAccessFile(collection: Collection): FileWrite { + const definitions: string[] = [ + defineImports(collection), + defineSave(collection), + defineParseEntity(collection.entities), + defineParseCollection(collection), + defineLoad(collection), + defineGeneral(), + ]; + + return { + location: collection.name + "DataAccess.ts", + content: definitions.join("\n\n") + "\n", + }; +} + +function defineImports(collection: Collection): string { + const entity = collection.entities; + return `import { ${collection.name}, ${entity.name} } from "./${collection.name}Model"; +import * as fs from "fs";`; +} + +function defineSave(collection: Collection): string { + return `export function save(fileName: string, collection: ${collection.name}): void { + const content = JSON.stringify({ ...collection, $schema: "./${collection.name.toLowerCase()}.schema.json" }); + fs.writeFileSync(fileName, content); +}`; +} + +function defineGeneral(): string { + return `function parseArrayProperty(fieldName: keyof O, itemParser: (item: any) => T, data: any): T[] +{ + if(fieldName in data) + { + const valueList = data[fieldName]; + if(Array.isArray(valueList)) + { + return valueList.map(itemParser); + } + } + return []; +} + +function parsePrimitiveProperty(type: T extends string ? "string" : "int", fieldName: keyof O, defaultValue: T, obj: any): T +{ + if(fieldName in obj) + { + const fieldValue = obj[fieldName]; + if(typeof fieldValue === type) + { + return fieldValue; + } + } + return defaultValue; +} + +function canReadFile(path: string): boolean { + try { + fs.accessSync(path, fs.constants.R_OK); + return true; + } catch (error) { + return false; + } +}`; +} + +function defineParseEntity(entity: Entity): string { + const propParser = entity.properties.map(p => parseStringProperty(entity.name, p)).join("\n"); + return `function parse${entity.name}(data: any): ${entity.name} +{ + return { + id: parsePrimitiveProperty<${entity.name}, number>("int", "id", 0, data), +${propParser} + }; +}`; +} + +function parseStringProperty(entityName: string, property: Property): string { + return `\t\t${property.key}: parsePrimitiveProperty<${entityName}, string>("string", "${property.key}", "", data),`; +} + +function defineParseCollection(collection: Collection): string { + return `function parse${collection.name}(data: any): ${collection.name} { + return { + nextId: parsePrimitiveProperty<${collection.name}, number>("int", "nextId", 1, data), + items: parseArrayProperty<${collection.name}, ${collection.entities.name}>("items", parse${collection.entities.name}, data), + }; +}`; +} + +function defineLoad(collection: Collection): string { + return `export function load(fileName: string): ${collection.name} { + if (!canReadFile(fileName)) { + return { + items: [], + nextId: 1, + }; + } + const content = fs.readFileSync(fileName).toString(); + const data = JSON.parse(content); + return parse${collection.name}(data); +}`; +} diff --git a/src/ExampleOutput/DataAccess.ts b/src/ExampleOutput/DataAccess.ts index 279da34..f98ce06 100644 --- a/src/ExampleOutput/DataAccess.ts +++ b/src/ExampleOutput/DataAccess.ts @@ -76,8 +76,7 @@ if (!canReadFile(fileName)) { return emptyGraph(); } - // TODO check if file exists const content = fs.readFileSync(fileName).toString(); const data = JSON.parse(content); return parseGraph(data); -} \ No newline at end of file +} diff --git a/src/ModelGenerator.ts b/src/ModelGenerator.ts index 29550ef..d471407 100644 --- a/src/ModelGenerator.ts +++ b/src/ModelGenerator.ts @@ -23,7 +23,7 @@ function defineCollection(collection: Collection): string { return `export interface ${collection.name} { - nodes: ${collection.entities.name}[]; + items: ${collection.entities.name}[]; nextId: number; }`; } @@ -43,16 +43,16 @@ ref: string; } -export function toPayload(node: ${entity.name}): ${entity.name}Payload { +export function toPayload(item: ${entity.name}): ${entity.name}Payload { return { - ...node, - ref: \`/${collection.name.toLowerCase()}/\${node.id}\`, + ...item, + ref: \`/${collection.name.toLowerCase()}/\${item.id}\`, } }`; } function defineFindFunction(collection: Collection): string { - return `export function findSingle(graph: ${collection.name}, id: number) { - return graph.nodes.find(n => n.id === id); + return `export function findSingle(collection: ${collection.name}, id: number) { + return collection.items.find(n => n.id === id); }`; } diff --git a/src/app.ts b/src/app.ts index 46ec548..dcdb3b4 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,6 +2,7 @@ import { join } from "path"; import * as fs from "fs"; import { generateModel } from "./ModelGenerator"; +import { generateDataAccess } from "./DataAccessGenerator"; interface Options { typeScriptOutputFolder: string; @@ -34,13 +35,18 @@ function generate(options: Options, description: Description): FileWrite[] { // generate model files + const modelWrites = generateModel(description.collections); + modelWrites.forEach(w => {w.location = join(options.typeScriptOutputFolder, w.location)}) + // generate data access files + const dataAccessWrites = generateDataAccess(description.collections); + dataAccessWrites.forEach(w => {w.location = join(options.typeScriptOutputFolder, w.location)}) + // generate middleware file // generate openapi file // -> see https://swagger.io/docs/specification/describing-responses/ - const writes = generateModel(description.collections); - writes.forEach(w => {w.location = join(options.typeScriptOutputFolder, w.location)}) - return writes; + + return [...modelWrites, ...dataAccessWrites]; } function execute() {