Skip to content

Commit a60db70

Browse files
authored
add file schema renderer (#20)
* add file schema renderer * 2.14.1
1 parent 0f5c7db commit a60db70

File tree

8 files changed

+94
-15
lines changed

8 files changed

+94
-15
lines changed

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,4 +653,24 @@ or use query expressions for applying nested query options:
653653
}))
654654
).getItems();
655655

656-
> `/People?$expand=address($select=id,streetAddress,addressLocalilty;$expand=addressCountry)`
656+
> `/People?$expand=address($select=id,streetAddress,addressLocalilty;$expand=addressCountry)`
657+
658+
## Using CLI
659+
660+
`@themost/client` provides a command line interface for generating client-side type declarations from an OData metadata service.
661+
662+
Connect to an OData service and generate client-side type declarations:
663+
664+
```bash
665+
$ npx @themost/client http://localhost:3000/api/
666+
```
667+
or extract metadata from an OData metadata document:
668+
669+
```bash
670+
$ npx @themost/client ./metadata.xml
671+
```
672+
Use `--out-file` option for specifying the output file:
673+
674+
```bash
675+
$ npx @themost/client http://localhost:3000/api/ --out-file ./client.d.ts
676+
```

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@themost/client",
3-
"version": "2.14.0",
3+
"version": "2.14.1",
44
"description": "MOST Web Framework Codename Blueshift - Client Common",
55
"module": "dist/index.esm.js",
66
"main": "dist/index.js",

util/bin/cli.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
#!/usr/bin/env node
12
const minimist = require('minimist');
2-
const { TypeRenderer } = require('../dist');
3+
const { TypeRenderer, FileSchemaRenderer } = require('../dist');
34
const { writeFileSync } = require('fs');
45

56
async function main() {
@@ -9,17 +10,18 @@ async function main() {
910
},
1011
});
1112
if (args.help) {
12-
console.log('Usage: client-cli [options]');
13+
console.log('Usage: client-cli <source> [options]');
1314
console.log('Options:');
14-
console.log(' --host <host> The HTTP address of the host api server to connect to');
15-
console.log(' --out-file <file> The output file to write the rendered types to');
15+
console.log(' --out-file <output> The output file to write the rendered types to');
1616
return;
1717
}
18-
if (!args.host) {
19-
console.error('Error: Missing required argument --host');
18+
const source = args._[0];
19+
if (!source) {
20+
console.error('Missing argument: A source metadata file or a valid URL must be provided');
2021
return process.exit(-1);
2122
}
22-
const typeRenderer = new TypeRenderer(args.host);
23+
const isURL = source.startsWith('http://') || source.startsWith('https://');
24+
const typeRenderer = isURL ? new TypeRenderer(source) : new FileSchemaRenderer(source);
2325
const result = await typeRenderer.renderAny();
2426
if (args.outFile) {
2527
writeFileSync(args.outFile, result);

util/spec/FileSchemaRenderer.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {FileSchemaRenderer, TypeRenderer} from '@themost/client/util';
2+
import { resolve } from 'path';
3+
4+
describe("FileSchemaRenderer(", () => {
5+
6+
it("should render type", async () => {
7+
const renderer = new FileSchemaRenderer(resolve(__dirname, 'metadata.xml'));
8+
let typeDeclaration = await renderer.render('Thing');
9+
expect(typeDeclaration).toBeInstanceOf(String);
10+
typeDeclaration = await renderer.render('Workspace');
11+
expect(typeDeclaration).toBeInstanceOf(String);
12+
});
13+
14+
it("should render any type", async () => {
15+
const renderer = new FileSchemaRenderer(resolve(__dirname, 'metadata.xml'));
16+
const typeDeclarations = await renderer.renderAny();
17+
expect(typeDeclarations).toBeInstanceOf(String);
18+
});
19+
20+
21+
22+
});

util/spec/TypeRenderer.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {TypeRenderer} from '@themost/client/util';
22
import { serveApplication, getApplication } from '@themost/test';
33

4-
describe("BasicClientDataContext", () => {
4+
describe("TypeRenderer", () => {
55

66
beforeAll(async () => {
77
const app = await getApplication();

util/spec/metadata.xml

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

util/src/TypeRenderer.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {EdmEntityType, EdmNavigationProperty, EdmProperty, EdmSchema} from '@themost/client';
22
import {BasicDataContext} from '@themost/client/common';
3+
import { readFile } from 'fs';
34

45
const EdmTypeMap = new Map([
56
[
@@ -48,7 +49,7 @@ class TypeRenderer {
4849
/**
4950
* @param {string} host
5051
*/
51-
constructor(host: string) {
52+
constructor(host?: string) {
5253
this.context = new BasicDataContext(host);
5354
}
5455

@@ -116,13 +117,21 @@ ${properties.sort(
116117
}
117118

118119
async render(type: string) {
119-
this.schema = await this.context.getMetadata();
120+
if (this.schema == null) {
121+
this.schema = await this.getSchema();
122+
}
120123
const entityType = this.schema.EntityType.find((t) => t.Name === type);
121124
return this.renderType(entityType)
122125
}
123126

127+
protected getSchema(): Promise<EdmSchema> {
128+
return this.context.getMetadata();
129+
}
130+
124131
async renderAny() {
125-
this.schema = await this.context.getMetadata();
132+
if (this.schema == null) {
133+
this.schema = await this.getSchema();
134+
}
126135
const typeDeclarations = this.schema.EntityType.sort(
127136
(a, b) => {
128137
if (a.Name < b.Name) return -1;
@@ -132,7 +141,32 @@ ${properties.sort(
132141
).map((entityType) => this.renderType(entityType));
133142
return typeDeclarations.join('\n');
134143
}
144+
}
145+
146+
class FileSchemaRenderer extends TypeRenderer {
147+
constructor(private file: string) {
148+
super();
149+
}
150+
151+
protected getSchema(): Promise<EdmSchema> {
152+
return new Promise((resolve, reject) => {
153+
void readFile(this.file, 'utf8', (err, data) => {
154+
if (err) {
155+
return reject(err);
156+
}
157+
try {
158+
const schema = EdmSchema.loadXML(data);
159+
return resolve(schema);
160+
} catch (e) {
161+
return reject(e);
162+
}
163+
});
164+
});
165+
}
135166

136167
}
137168

138-
export { TypeRenderer }
169+
export {
170+
TypeRenderer,
171+
FileSchemaRenderer
172+
}

0 commit comments

Comments
 (0)