Skip to content

Commit d3e1315

Browse files
committed
simpler example + intro + outline
1 parent df9ce0f commit d3e1315

File tree

2 files changed

+68
-233
lines changed

2 files changed

+68
-233
lines changed

client-generator/custom.md

Lines changed: 66 additions & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Custom Generator
22

3-
You will probably want to extend or, at least, take a look at [BaseGenerator.js](https://github.com/api-platform/client-generator/blob/main/src/generators/BaseGenerator.js), since the library expects some methods to be available, as well as one of the included generator to make your own.
3+
Client Generator provides support for many of the popular JS frameworks, but you may be using another framework or language and may need a solution adapted to your specific needs. For this cenario, you can write your own generator and pass it to the CLI using a path as the `-g` argument.
4+
5+
You will probably want to extend or, at least, take a look at [BaseGenerator.js](https://github.com/api-platform/client-generator/blob/main/src/generators/BaseGenerator.js), since the library expects some methods to be available, as well as one of the [included generators](https://github.com/api-platform/client-generator/blob/main/src/generators/BaseGenerator.j) to make your own.
46

57
## Usage
68

@@ -12,7 +14,7 @@ The `-g` argument can point to any resolvable node module which means it can be
1214

1315
## Example
1416

15-
Let's create a basic react generator with Create form as an example:
17+
Client Generator makes use of the [Handlebars](https://handlebarsjs.com/) template engine. You can use any programming language or file type. Your generator can also pass data to your templates in any shape you want.
1618

1719
### Generator
1820

@@ -24,258 +26,90 @@ export default class extends BaseGenerator {
2426
constructor(params) {
2527
super(params);
2628

27-
this.registerTemplates("", [
28-
"utils/dataAccess.js",
29-
"components/foo/Create.js",
30-
"routes/foo.js",
31-
]);
29+
this.registerTemplates("", ["main.rs"]);
3230
}
3331

34-
help(resource) {
35-
const titleLc = resource.title.toLowerCase();
36-
37-
console.log(
38-
'Code for the "%s" resource type has been generated!',
39-
resource.title
40-
);
41-
console.log(`
42-
//import routes
43-
import ${titleLc}Routes from './routes/${titleLc}';
44-
45-
// Add routes to <Switch>
46-
{ ${titleLc}Routes }
47-
`);
48-
}
32+
help() {}
4933

5034
generate(api, resource, dir) {
51-
const lc = resource.title.toLowerCase();
52-
const titleUcFirst =
53-
resource.title.charAt(0).toUpperCase() + resource.title.slice(1);
54-
5535
const context = {
56-
title: resource.title,
57-
name: resource.name,
58-
lc,
59-
uc: resource.title.toUpperCase(),
60-
fields: resource.readableFields,
61-
formFields: this.buildFields(resource.writableFields),
62-
hydraPrefix: this.hydraPrefix,
63-
titleUcFirst,
36+
type: "Tilia",
37+
structure: [
38+
{ name: "name", type: "String" },
39+
{ name: "min_size", type: "u8" },
40+
{ name: "max_size", type: "u8" },
41+
],
42+
list: [
43+
{
44+
name: "Tilia cordata",
45+
minSize: 50,
46+
maxSize: 80,
47+
},
48+
{
49+
name: "Tilia platyphyllos",
50+
minSize: 50,
51+
maxSize: 70,
52+
},
53+
{
54+
name: "Tilia tomentosa",
55+
minSize: 50,
56+
maxSize: 70,
57+
},
58+
{
59+
name: "Tilia intermedia",
60+
minSize: 50,
61+
maxSize: 165,
62+
},
63+
],
6464
};
6565

66-
// Create directories
67-
// These directories may already exist
68-
[`${dir}/utils`, `${dir}/config`, `${dir}/routes`].forEach((dir) =>
69-
this.createDir(dir, false)
70-
);
71-
72-
[`${dir}/components/${lc}`].forEach((dir) => this.createDir(dir));
66+
this.createDir(dir);
7367

74-
["components/%s/Create.js", "routes/%s.js"].forEach((pattern) =>
75-
this.createFileFromPattern(pattern, dir, lc, context)
76-
);
77-
78-
// utils
79-
this.createFile(
80-
"utils/dataAccess.js",
81-
`${dir}/utils/dataAccess.js`,
82-
context,
83-
false
84-
);
85-
86-
this.createEntrypoint(api.entrypoint, `${dir}/config/entrypoint.js`);
68+
this.createFile("main.rs", `${dir}/main.rs`, context, false);
8769
}
8870
}
8971
```
9072

91-
### `Create` component
92-
93-
```js
94-
// template/components/Create.js
95-
import React from 'react';
96-
import { Redirect } from 'react-router-dom';
97-
import fetch from '../utils/dataAccess';
98-
99-
export default function Create() {
100-
const [isLoading, setLoading] = useState(false);
101-
const [error, setError] = useState(null);
102-
const [created, setCreated] = useState(null);
103-
104-
const create = useCallback(async (e) => {
105-
setLoading(true)
106-
try {
107-
const values = Array.from(e.target.elements).reduce((vals, e) => {
108-
vals[e.id] = e.value;
109-
return vals
110-
}, {})
111-
const response = await fetch('{{{name}}}', { method: 'POST', body: JSON.stringify(values) });
112-
const retrieved = await response.json();
113-
setCreated(retrieved);
114-
} catch (err) {
115-
setError(err);
116-
} finally {
117-
setLoading(false);
118-
}
119-
}, [setLoading, setError])
120-
121-
if (created) {
122-
return <Redirect to={`edit/${encodeURIComponent(created['@id'])}`} />;
123-
}
124-
125-
return (
126-
<div>
127-
<h1>New {{{title}}}</h1>
73+
### Template
12874

129-
{isLoading && (
130-
<div className="alert alert-info" role="status">
131-
Loading...
132-
</div>
133-
)}
134-
{error && (
135-
<div className="alert alert-danger" role="alert">
136-
<span className="fa fa-exclamation-triangle" aria-hidden="true" />{' '}
137-
{error}
138-
</div>
139-
)}
140-
141-
<form onSubmit={create}>
142-
{{#each formFields}}
143-
<div className={`form-group`}>
144-
<label
145-
htmlFor={`{{{lc}}}_{{{name}}}`}
146-
className="form-control-label"
147-
>
148-
{data.input.name}
149-
</label>
150-
<input
151-
name="{{{name}}}"
152-
type="{{{type}}}"{{#if step}}
153-
step="{{{step}}}"{{/if}}
154-
placeholder="{{{description}}}"{{#if required}}
155-
required={true}{{/if}}
156-
id={`{{{lc}}}_{{{name}}}`}
157-
/>
158-
</div>
159-
160-
<button type="submit" className="btn btn-success">
161-
Submit
162-
</button>
163-
</form>
164-
</div>
165-
);
75+
```rs
76+
// template/main.rs
77+
struct {{{type}}} {
78+
{{#each structure}}
79+
{{{name}}}: {{{type}}}
80+
{{/each}}
16681
}
167-
```
16882

169-
### Utilities
170-
171-
```js
172-
// template/entrypoint.js
173-
export const ENTRYPOINT = "{{{entrypoint}}}";
83+
fn main() {
84+
let tilias = [
85+
{{#each list}}
86+
Tilia { name: "{{{name}}}", min_size: {{{minSize}}}, max_size: {{{maxSize}}}, },
87+
{{/each}}
88+
];
89+
}
17490
```
17591

176-
```js
177-
// template/routes/foo.js
178-
import React from "react";
179-
import { Route } from "react-router-dom";
180-
import { Create } from "../components/{{{lc}}}/";
92+
Then we can use our generator:
18193

182-
export default [
183-
<Route path="/{{{name}}}/create" component={Create} exact key="create" />,
184-
];
94+
```shell
95+
generate-api-platform-client https://demo.api-platform.com out/ -g "$(pwd)/Generator.js" -t "$(pwd)/template"
18596
```
18697

187-
```js
188-
// template/utils/dataAccess.js
189-
import { ENTRYPOINT } from "../config/entrypoint";
190-
import { SubmissionError } from "redux-form";
191-
import get from "lodash/get";
192-
import has from "lodash/has";
193-
import mapValues from "lodash/mapValues";
194-
195-
const MIME_TYPE = "application/ld+json";
196-
197-
export function fetch(id, options = {}) {
198-
if ("undefined" === typeof options.headers) options.headers = new Headers();
199-
if (null === options.headers.get("Accept"))
200-
options.headers.set("Accept", MIME_TYPE);
201-
202-
if (
203-
"undefined" !== options.body &&
204-
!(options.body instanceof FormData) &&
205-
null === options.headers.get("Content-Type")
206-
)
207-
options.headers.set("Content-Type", MIME_TYPE);
208-
209-
return global.fetch(new URL(id, ENTRYPOINT), options).then((response) => {
210-
if (response.ok) return response;
211-
212-
return response.json().then(
213-
(json) => {
214-
const error =
215-
json["hydra:description"] ||
216-
json["hydra:title"] ||
217-
"An error occurred.";
218-
if (!json.violations) throw Error(error);
219-
220-
let errors = { _error: error };
221-
json.violations.forEach((violation) =>
222-
errors[violation.propertyPath]
223-
? (errors[violation.propertyPath] +=
224-
"\n" + errors[violation.propertyPath])
225-
: (errors[violation.propertyPath] = violation.message)
226-
);
227-
228-
throw new SubmissionError(errors);
229-
},
230-
() => {
231-
throw new Error(response.statusText || "An error occurred.");
232-
}
233-
);
234-
});
235-
}
236-
237-
export function mercureSubscribe(url, topics) {
238-
topics.forEach((topic) =>
239-
url.searchParams.append("topic", new URL(topic, ENTRYPOINT))
240-
);
241-
242-
return new EventSource(url.toString());
243-
}
244-
245-
export function normalize(data) {
246-
if (has(data, "hydra:member")) {
247-
// Normalize items in collections
248-
data["hydra:member"] = data["hydra:member"].map((item) =>
249-
normalize(item)
250-
);
251-
252-
return data;
253-
}
98+
which will produces:
25499

255-
// Flatten nested documents
256-
return mapValues(data, (value) =>
257-
Array.isArray(value)
258-
? value.map((v) => normalize(v))
259-
: value instanceof Object
260-
? normalize(value)
261-
: get(value, "@id", value)
262-
);
100+
```ts
101+
struct Tilia {
102+
name: String
103+
min_size: u8
104+
max_size: u8
263105
}
264106

265-
export function extractHubURL(response) {
266-
const linkHeader = response.headers.get("Link");
267-
if (!linkHeader) return null;
268-
269-
const matches = linkHeader.match(
270-
/<([^>]+)>;\s+rel=(?:mercure|"[^"]*mercure[^"]*")/
271-
);
272-
273-
return matches && matches[1] ? new URL(matches[1], ENTRYPOINT) : null;
107+
fn main() {
108+
let tilias = [
109+
Tilia { name: "Tilia cordata", min_size: 50, max_size: 80, },
110+
Tilia { name: "Tilia platyphyllos", min_size: 50, max_size: 70, },
111+
Tilia { name: "Tilia tomentosa", min_size: 50, max_size: 70, },
112+
Tilia { name: "Tilia intermedia", min_size: 50, max_size: 165, },
113+
];
274114
}
275115
```
276-
277-
Then we can use our generator:
278-
279-
```shell
280-
generate-api-platform-client https://demo.api-platform.com out/ -g "$(pwd)/Generator.js" -t "$(pwd)/template"
281-
```

outline.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
chapters:
2-
- title: 'The Distribution: Create Powerful APIs with Ease'
2+
- title: "The Distribution: Create Powerful APIs with Ease"
33
path: distribution
44
items:
55
- index
@@ -83,6 +83,7 @@ chapters:
8383
- react-native
8484
- vuejs
8585
- typescript
86+
- custom
8687
- troubleshooting
8788
- title: Deployment
8889
path: deployment

0 commit comments

Comments
 (0)