Skip to content

Commit b86ca33

Browse files
committed
refactor: modify a syntax for specifying queries in YAML
BREAKING CHANGE: Old syntax: - path: /v1/categories/count get: select count(*) as counter from categories New syntax: - path: /v1/categories/count get: query: select count(*) as counter from categories The new syntax is needed for upcoming support of DTO objects. This change also restructures internal representation in order to simplify code. Part of #9
1 parent aff164e commit b86ca33

File tree

5 files changed

+166
-123
lines changed

5 files changed

+166
-123
lines changed

README.md

+23-18
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,31 @@ Generates the endpoints (or a whole app) from a mapping (SQL query -> URL)
1515
```console
1616
$ vim endpoints.yaml
1717
- path: /v1/categories
18-
get_list: >-
19-
SELECT id, name, name_ru, slug
20-
FROM categories
21-
post: >-
22-
INSERT INTO categories(name, slug, created_at, created_by, updated_at, updated_by)
23-
VALUES (:b.name, :b.slug, NOW(), :b.user_id, NOW(), :b.user_id)
18+
get_list:
19+
query: >-
20+
SELECT id, name, name_ru, slug
21+
FROM categories
22+
post:
23+
query: >-
24+
INSERT INTO categories(name, slug, created_at, created_by, updated_at, updated_by)
25+
VALUES (:b.name, :b.slug, NOW(), :b.user_id, NOW(), :b.user_id)
2426

2527
- path: /v1/categories/:categoryId
26-
get: >-
27-
SELECT id, name, name_ru, slug
28-
FROM categories
29-
WHERE id = :p.categoryId
30-
put: >-
31-
UPDATE categories
32-
SET name = :b.name, name_ru = :b.name_ru, slug = :b.slug, updated_at = NOW(), updated_by = :b.user_id
33-
WHERE id = :p.categoryId
34-
delete: >-
35-
DELETE
36-
FROM categories
37-
WHERE id = :p.categoryId
28+
get:
29+
query: >-
30+
SELECT id, name, name_ru, slug
31+
FROM categories
32+
WHERE id = :p.categoryId
33+
put:
34+
query: >-
35+
UPDATE categories
36+
SET name = :b.name, name_ru = :b.name_ru, slug = :b.slug, updated_at = NOW(), updated_by = :b.user_id
37+
WHERE id = :p.categoryId
38+
delete:
39+
query: >-
40+
DELETE
41+
FROM categories
42+
WHERE id = :p.categoryId
3843
```
3944
Note that the queries use a little unusual named parameters: `:b.name`, `:p.categoryId`, etc The prefixes `b` (body) and `p` (path) are used here in order to bind to parameters from the appropriate sources. The prefixes are needed only during code generation and they will absent from the resulted code.
4045

examples/js/endpoints.yaml

+59-52
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,66 @@
11
- path: /v1/categories/count
2-
get: SELECT COUNT(*) AS counter FROM categories
2+
get:
3+
query: SELECT COUNT(*) AS counter FROM categories
34

45
- path: /v1/collections/:collectionId/categories/count
5-
get: >-
6-
SELECT COUNT(DISTINCT s.category_id) AS counter
7-
FROM collections_series cs
8-
JOIN series s
9-
ON s.id = cs.series_id
10-
WHERE cs.collection_id = :p.collectionId
6+
get:
7+
query: >-
8+
SELECT COUNT(DISTINCT s.category_id) AS counter
9+
FROM collections_series cs
10+
JOIN series s
11+
ON s.id = cs.series_id
12+
WHERE cs.collection_id = :p.collectionId
1113
1214
- path: /v1/categories
13-
get_list: >-
14-
SELECT id
15-
, name
16-
, name_ru
17-
, slug
18-
FROM categories
19-
post: >-
20-
INSERT
21-
INTO categories
22-
( name
23-
, name_ru
24-
, slug
25-
, created_at
26-
, created_by
27-
, updated_at
28-
, updated_by
29-
)
30-
VALUES
31-
( :b.name
32-
, :b.name_ru
33-
, :b.slug
34-
, NOW()
35-
, :b.user_id
36-
, NOW()
37-
, :b.user_id
38-
)
15+
get_list:
16+
query: >-
17+
SELECT id
18+
, name
19+
, name_ru
20+
, slug
21+
FROM categories
22+
post:
23+
query: >-
24+
INSERT
25+
INTO categories
26+
( name
27+
, name_ru
28+
, slug
29+
, created_at
30+
, created_by
31+
, updated_at
32+
, updated_by
33+
)
34+
VALUES
35+
( :b.name
36+
, :b.name_ru
37+
, :b.slug
38+
, NOW()
39+
, :b.user_id
40+
, NOW()
41+
, :b.user_id
42+
)
3943
4044
- path: /v1/categories/:categoryId
41-
get: >-
42-
SELECT id
43-
, name
44-
, name_ru
45-
, slug
46-
FROM categories
47-
WHERE id = :p.categoryId
48-
put: >-
49-
UPDATE categories
50-
SET name = :b.name
51-
, name_ru = :b.name_ru
52-
, slug = :b.slug
53-
, updated_at = NOW()
54-
, updated_by = :b.user_id
55-
WHERE id = :p.categoryId
56-
delete: >-
57-
DELETE
58-
FROM categories
59-
WHERE id = :p.categoryId
45+
get:
46+
query: >-
47+
SELECT id
48+
, name
49+
, name_ru
50+
, slug
51+
FROM categories
52+
WHERE id = :p.categoryId
53+
put:
54+
query: >-
55+
UPDATE categories
56+
SET name = :b.name
57+
, name_ru = :b.name_ru
58+
, slug = :b.slug
59+
, updated_at = NOW()
60+
, updated_by = :b.user_id
61+
WHERE id = :p.categoryId
62+
delete:
63+
query: >-
64+
DELETE
65+
FROM categories
66+
WHERE id = :p.categoryId

src/cli.js

+38-14
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,43 @@ const parseCommandLineArgs = (args) => {
2323
return argv;
2424
}
2525

26+
// Restructure YAML configuration to simplify downstream code.
27+
//
28+
// Converts
29+
// {
30+
// get_list: { query: <sql> },
31+
// put: { query: <sql> }
32+
// }
33+
// into
34+
// {
35+
// methods: [
36+
// { name: get_list, verb: get, query: <sql> },
37+
// { name: put, verb: put, query: <sql> }
38+
// ]
39+
// }
40+
const restructureConfiguration = (config) => {
41+
for (const endpoint of config) {
42+
endpoint.methods = [];
43+
[ 'get', 'get_list', 'post', 'put', 'delete' ].forEach(method => {
44+
if (!endpoint.hasOwnProperty(method)) {
45+
return;
46+
}
47+
endpoint.methods.push({
48+
'name': method,
49+
'verb': method !== 'get_list' ? method : 'get',
50+
...endpoint[method],
51+
});
52+
delete endpoint[method];
53+
});
54+
}
55+
};
56+
2657
const loadConfig = (endpointsFile) => {
2758
console.log('Read', endpointsFile);
2859
try {
2960
const content = fs.readFileSync(endpointsFile, 'utf8');
3061
const config = yaml.safeLoad(content);
62+
restructureConfiguration(config);
3163
//console.debug(config);
3264
return config;
3365
} catch (ex) {
@@ -64,20 +96,12 @@ const createEndpoints = async (destDir, lang, config) => {
6496
if (lang === 'go') {
6597
path = convertPathPlaceholders(path)
6698
}
67-
if (endpoint.hasOwnProperty('get')) {
68-
console.log('GET', path, '=>', removePlaceholders(flattenQuery(endpoint.get)));
69-
} else if (endpoint.hasOwnProperty('get_list')) {
70-
console.log('GET', path, '=>', removePlaceholders(flattenQuery(endpoint.get_list)));
71-
}
72-
if (endpoint.hasOwnProperty('post')) {
73-
console.log('POST', path, '=>', removePlaceholders(flattenQuery(endpoint.post)));
74-
}
75-
if (endpoint.hasOwnProperty('put')) {
76-
console.log('PUT', path, '=>', removePlaceholders(flattenQuery(endpoint.put)));
77-
}
78-
if (endpoint.hasOwnProperty('delete')) {
79-
console.log('DELETE', path, '=>', removePlaceholders(flattenQuery(endpoint.delete)));
80-
}
99+
endpoint.methods.forEach(method => {
100+
const sql = removePlaceholders(flattenQuery(method.query));
101+
const verb = method.verb.toUpperCase();
102+
103+
console.log(`${verb} ${path} => ${sql}`);
104+
});
81105
}
82106

83107
const placeholdersMap = {

src/templates/routes.go.ejs

+21-18
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,11 @@ function dtoInCache(dto) {
149149
return dtoCache.hasOwnProperty(dto.signature);
150150
}
151151
152-
const verbs_with_dto = [ 'get', 'get_list', 'post', 'put' ]
152+
const verbs_with_dto = [ 'get', 'post', 'put' ]
153153
endpoints.forEach(function(endpoint) {
154-
const dtos = Object.keys(endpoint)
155-
.filter(propName => verbs_with_dto.includes(propName))
156-
.map(propName => endpoint[propName])
154+
const dtos = endpoint.methods
155+
.filter(method => verbs_with_dto.includes(method.verb))
156+
.map(method => method.query)
157157
.map(query => query2dto(sqlParser, removePlaceholders(query)))
158158
.filter(elem => elem) // filter out nulls
159159
.filter(dto => !dtoInCache(dto))
@@ -171,24 +171,26 @@ func registerRoutes(r chi.Router) {
171171
<%
172172
endpoints.forEach(function(endpoint) {
173173
const path = convertPathPlaceholders(endpoint.path);
174-
const hasGetOne = endpoint.hasOwnProperty('get');
175-
const hasGetMany = endpoint.hasOwnProperty('get_list');
176-
if (hasGetOne || hasGetMany) {
174+
175+
endpoint.methods.forEach(function(method) {
176+
const hasGetOne = method.name === 'get';
177+
const hasGetMany = method.name === 'get_list';
178+
if (hasGetOne || hasGetMany) {
177179
%>
178180
r.Get("<%- path %>", func(w http.ResponseWriter, r *http.Request) {
179181
<%
180-
if (path === '/v1/categories/count') {
182+
if (path === '/v1/categories/count') {
181183
-%>
182184
w.Header().Set("Content-Type", "application/json; charset=utf-8")
183185
fmt.Fprintf(w, `{"counter": %d}`, len(categories))
184186
<%
185-
} else if (hasGetMany) {
187+
} else if (hasGetMany) {
186188
-%>
187189
w.Header().Set("Content-Type", "application/json; charset=utf-8")
188190
list := []Category{categories[1]}
189191
json.NewEncoder(w).Encode(&list)
190192
<%
191-
} else {
193+
} else {
192194
-%>
193195
id, _ := strconv.Atoi(chi.URLParam(r, "categoryId"))
194196
category, exist := categories[id]
@@ -199,12 +201,12 @@ endpoints.forEach(function(endpoint) {
199201
w.Header().Set("Content-Type", "application/json; charset=utf-8")
200202
json.NewEncoder(w).Encode(&category)
201203
<%
202-
}
204+
}
203205
%>
204206
})
205207
<%
206-
}
207-
if (endpoint.hasOwnProperty('post')) {
208+
}
209+
if (method.name === 'post') {
208210
%>
209211
r.Post("<%- path %>", func(w http.ResponseWriter, r *http.Request) {
210212
var category Category
@@ -215,8 +217,8 @@ endpoints.forEach(function(endpoint) {
215217
w.WriteHeader(http.StatusNoContent)
216218
})
217219
<%
218-
}
219-
if (endpoint.hasOwnProperty('put')) {
220+
}
221+
if (method.name === 'put') {
220222
%>
221223
r.Put("<%- path %>", func(w http.ResponseWriter, r *http.Request) {
222224
id, _ := strconv.Atoi(chi.URLParam(r, "categoryId"))
@@ -226,16 +228,17 @@ endpoints.forEach(function(endpoint) {
226228
w.WriteHeader(http.StatusNoContent)
227229
})
228230
<%
229-
}
230-
if (endpoint.hasOwnProperty('delete')) {
231+
}
232+
if (method.name === 'delete') {
231233
%>
232234
r.Delete("<%- path %>", func(w http.ResponseWriter, r *http.Request) {
233235
id, _ := strconv.Atoi(chi.URLParam(r, "categoryId"))
234236
delete(categories, id)
235237
w.WriteHeader(http.StatusNoContent)
236238
})
237239
<%
238-
}
240+
}
241+
});
239242
})
240243
%>
241244
}

0 commit comments

Comments
 (0)