@raketa-cloud/admin 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel.config.js +6 -0
- package/bin/tailwind +2 -0
- package/package.json +22 -30
- package/src/Adapter.js +105 -0
- package/src/HonoAdapter.js +120 -0
- package/src/RaketaContext.js +33 -0
- package/src/createNestedResource.js +83 -0
- package/src/createResource.js +83 -0
- package/src/index.js +3 -0
- package/src/styles/application.css +17 -0
- package/src/tests/HonoAdapter.test.js +33 -0
- package/src/tests/createNestedResource.test.js +150 -0
- package/src/tests/createResource.test.js +115 -0
- package/src/views/layouts/application.ejs +22 -0
- package/src/views/redirects/index.ejs +1 -0
- package/tailwind.config.js +8 -0
- package/tsconfig.json +15 -0
- package/tsconfig.lint.json +8 -0
- package/dist/index.js +0 -670
package/babel.config.js
ADDED
package/bin/tailwind
ADDED
package/package.json
CHANGED
|
@@ -1,32 +1,24 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"typescript": "^5.3.3"
|
|
25
|
-
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
"@biomejs/biome": "1.5.3",
|
|
28
|
-
"graphql-request": "^6.1.0",
|
|
29
|
-
"hono": "^3.12.3",
|
|
30
|
-
"prettier": "^3.2.4"
|
|
31
|
-
}
|
|
2
|
+
"name": "@raketa-cloud/admin",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"main": "src/index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "bin/tailwind",
|
|
7
|
+
"test": "jest"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@babel/core": "^7.24.7",
|
|
11
|
+
"@babel/preset-env": "^7.24.7",
|
|
12
|
+
"@babel/preset-typescript": "^7.24.7",
|
|
13
|
+
"@repo/typescript-config": "*",
|
|
14
|
+
"@tailwindcss/forms": "^0.5.7",
|
|
15
|
+
"@tailwindcss/typography": "^0.5.13",
|
|
16
|
+
"@types/node": "^20.11.24",
|
|
17
|
+
"jest": "^29.7.0",
|
|
18
|
+
"tailwindcss": "^3.4.4",
|
|
19
|
+
"typescript": "^5.3.3"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ejs": "^3.1.10"
|
|
23
|
+
}
|
|
32
24
|
}
|
package/src/Adapter.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const nodePath = require("node:path");
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import ejs from "ejs";
|
|
4
|
+
|
|
5
|
+
function escapeHtml(string) {
|
|
6
|
+
var matchHtmlRegExp = /["'&<>]/;
|
|
7
|
+
|
|
8
|
+
var str = "" + string;
|
|
9
|
+
var match = matchHtmlRegExp.exec(str);
|
|
10
|
+
|
|
11
|
+
if (!match) {
|
|
12
|
+
return str;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
var escape;
|
|
16
|
+
var html = "";
|
|
17
|
+
var index = 0;
|
|
18
|
+
var lastIndex = 0;
|
|
19
|
+
|
|
20
|
+
for (index = match.index; index < str.length; index++) {
|
|
21
|
+
switch (str.charCodeAt(index)) {
|
|
22
|
+
case 34: // "
|
|
23
|
+
escape = """;
|
|
24
|
+
break;
|
|
25
|
+
case 38: // &
|
|
26
|
+
escape = "&";
|
|
27
|
+
break;
|
|
28
|
+
case 39: // '
|
|
29
|
+
escape = "'";
|
|
30
|
+
break;
|
|
31
|
+
case 60: // <
|
|
32
|
+
escape = "<";
|
|
33
|
+
break;
|
|
34
|
+
case 62: // >
|
|
35
|
+
escape = ">";
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (lastIndex !== index) {
|
|
42
|
+
html += str.substring(lastIndex, index);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
lastIndex = index + 1;
|
|
46
|
+
html += escape;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const renderError = (message, stack) => {
|
|
53
|
+
const errorCSS = `
|
|
54
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
55
|
+
html { font-family: sans-serif; font-size: 16px; padding: 2rem; background-color: pink; }
|
|
56
|
+
h1 { margin-bottom: 1rem; color: FireBrick; }
|
|
57
|
+
.fail-squid { font-size: 7rem; }
|
|
58
|
+
pre { padding: 1.5rem 2rem; border-radius: 0.5rem; background-color: black; color: white; line-height: 1.5; font-size: 1.25rem; }
|
|
59
|
+
`;
|
|
60
|
+
return `<style>${errorCSS}</style><div class="fail-squid">🦑</div><h1>${message}</h1><pre>${escapeHtml(stack)}</pre>`;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const Logger = {
|
|
64
|
+
error: (msg) => {
|
|
65
|
+
console.error(`\x1b[31m🦑 ${msg}\x1b[0m`);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default class Adapter {
|
|
70
|
+
constructor(router) {
|
|
71
|
+
this.router = router;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
createResource(_resource, _mapping) {
|
|
75
|
+
throw new Error("Implement in an adapter");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getActionPath(resource, action) {
|
|
79
|
+
const packagePath = `${nodePath.dirname(__filename)}/views/${resource}/${action}.ejs`;
|
|
80
|
+
const appPath = `${process.cwd()}/src/views/${resource}/${action}.ejs`;
|
|
81
|
+
|
|
82
|
+
return existsSync(appPath) ? appPath : packagePath;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async renderLayout(assigns, body) {
|
|
86
|
+
const layoutTemplate = `${nodePath.dirname(__filename)}/views/layouts/application.ejs`;
|
|
87
|
+
return await ejs.renderFile(layoutTemplate, { ...assigns, body });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async renderAction({ resource, action, assigns }) {
|
|
91
|
+
const fullTemplatePath = this.getActionPath(resource, action);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const rendered = await ejs.renderFile(fullTemplatePath, assigns);
|
|
95
|
+
const fullPage = await this.renderLayout(assigns, rendered);
|
|
96
|
+
|
|
97
|
+
return fullPage;
|
|
98
|
+
} catch (e) {
|
|
99
|
+
const error = `Error rendering:\n ${fullTemplatePath}`;
|
|
100
|
+
Logger.error(`${error}\n\n${e.stack}`);
|
|
101
|
+
|
|
102
|
+
return renderError(error, e.stack);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Adapter from "./Adapter";
|
|
2
|
+
import RaketaContext from "./RaketaContext";
|
|
3
|
+
|
|
4
|
+
export default class HonoAdapter extends Adapter {
|
|
5
|
+
constructor(router) {
|
|
6
|
+
super(router);
|
|
7
|
+
this.router = router;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async createContext(router, next, verb, resource, action) {
|
|
11
|
+
return new RaketaContext({
|
|
12
|
+
router,
|
|
13
|
+
res: next,
|
|
14
|
+
req: router.req,
|
|
15
|
+
method: verb,
|
|
16
|
+
headers: router.req.header(),
|
|
17
|
+
rawPath: router.req.routePath,
|
|
18
|
+
path: router.req.path,
|
|
19
|
+
url: router.req.url,
|
|
20
|
+
action,
|
|
21
|
+
params: router.req.param(),
|
|
22
|
+
body: await router.req.parseBody(),
|
|
23
|
+
query: router.req.query(),
|
|
24
|
+
resource,
|
|
25
|
+
assigns: {},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
createAction([
|
|
30
|
+
verb,
|
|
31
|
+
path,
|
|
32
|
+
resource,
|
|
33
|
+
action,
|
|
34
|
+
handler,
|
|
35
|
+
before_filter,
|
|
36
|
+
after_filter,
|
|
37
|
+
]) {
|
|
38
|
+
const method = this.getMethod(verb);
|
|
39
|
+
|
|
40
|
+
method(path, async (router, next) => {
|
|
41
|
+
const ctx = await this.createContext(
|
|
42
|
+
router,
|
|
43
|
+
next,
|
|
44
|
+
verb,
|
|
45
|
+
resource,
|
|
46
|
+
action
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
console.info(
|
|
50
|
+
`\x1b[35m[${ctx.method}] ${ctx.rawPath}\x1b[0m -> ${resource}/${action} | ${JSON.stringify(ctx.params)}`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const assigns = {
|
|
54
|
+
beforeFilterAssigns: {},
|
|
55
|
+
actionAssigns: {},
|
|
56
|
+
afterFilterAssigns: {},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Process before filters
|
|
60
|
+
if (before_filter) {
|
|
61
|
+
const { redirect, assigns: beforeFilterAssigns = {} } =
|
|
62
|
+
before_filter(ctx) || {};
|
|
63
|
+
|
|
64
|
+
if (redirect) return router.redirect(redirect);
|
|
65
|
+
|
|
66
|
+
assigns.beforeFilterAssigns = beforeFilterAssigns;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Process action
|
|
70
|
+
const { redirect, assigns: actionAssigns = {} } = handler(ctx) || {};
|
|
71
|
+
assigns.actionAssigns = actionAssigns;
|
|
72
|
+
|
|
73
|
+
if (redirect) return router.redirect(redirect);
|
|
74
|
+
|
|
75
|
+
// Process after filters
|
|
76
|
+
if (after_filter) {
|
|
77
|
+
const { redirect, assigns: afterFilterAssigns = {} } =
|
|
78
|
+
after_filter(ctx) || {};
|
|
79
|
+
|
|
80
|
+
if (redirect) return router.redirect(redirect);
|
|
81
|
+
|
|
82
|
+
assigns.afterFilterAssigns = afterFilterAssigns;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const fullAssigns = {
|
|
86
|
+
...ctx.assigns,
|
|
87
|
+
...assigns.beforeFilterAssigns,
|
|
88
|
+
...assigns.actionAssigns,
|
|
89
|
+
...assigns.afterFilterAssigns,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const rendered = this.renderAction({
|
|
93
|
+
resource,
|
|
94
|
+
action,
|
|
95
|
+
assigns: fullAssigns,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return router.html(rendered);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
createResource(mapping) {
|
|
103
|
+
mapping.forEach((handlerMap) => this.createAction(handlerMap));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
createNestedResource(mapping) {
|
|
107
|
+
mapping.forEach((handlerMap) => this.createAction(handlerMap));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getMethod(verb) {
|
|
111
|
+
return (
|
|
112
|
+
{
|
|
113
|
+
GET: this.router.get,
|
|
114
|
+
POST: this.router.post,
|
|
115
|
+
PUT: this.router.put,
|
|
116
|
+
DELETE: this.router.delete,
|
|
117
|
+
}[verb] || this.router.get
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default class RaketaContext {
|
|
2
|
+
constructor({
|
|
3
|
+
router,
|
|
4
|
+
res,
|
|
5
|
+
req,
|
|
6
|
+
method,
|
|
7
|
+
headers,
|
|
8
|
+
rawPath,
|
|
9
|
+
path,
|
|
10
|
+
url,
|
|
11
|
+
action,
|
|
12
|
+
params,
|
|
13
|
+
query,
|
|
14
|
+
body,
|
|
15
|
+
resource,
|
|
16
|
+
assigns,
|
|
17
|
+
}) {
|
|
18
|
+
this.router = router;
|
|
19
|
+
this.res = res;
|
|
20
|
+
this.req = req;
|
|
21
|
+
this.method = method;
|
|
22
|
+
this.headers = headers;
|
|
23
|
+
this.rawPath = rawPath;
|
|
24
|
+
this.path = path;
|
|
25
|
+
this.url = url;
|
|
26
|
+
this.action = action;
|
|
27
|
+
this.params = params;
|
|
28
|
+
this.query = query;
|
|
29
|
+
this.body = body;
|
|
30
|
+
this.resource = resource;
|
|
31
|
+
this.assigns = assigns;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const createNestedResource = (adapter, parent, resource, handler) => {
|
|
2
|
+
const {
|
|
3
|
+
before_filter,
|
|
4
|
+
after_filter,
|
|
5
|
+
index,
|
|
6
|
+
show,
|
|
7
|
+
create_form,
|
|
8
|
+
edit_form,
|
|
9
|
+
create,
|
|
10
|
+
update,
|
|
11
|
+
destroy,
|
|
12
|
+
} = handler;
|
|
13
|
+
|
|
14
|
+
const mapping = [
|
|
15
|
+
[
|
|
16
|
+
"GET",
|
|
17
|
+
`/${parent}/:parent_id/${resource}`,
|
|
18
|
+
resource,
|
|
19
|
+
"index",
|
|
20
|
+
index,
|
|
21
|
+
before_filter,
|
|
22
|
+
after_filter,
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"GET",
|
|
26
|
+
`/${parent}/:parent_id/${resource}/new`,
|
|
27
|
+
resource,
|
|
28
|
+
"create_form",
|
|
29
|
+
create_form,
|
|
30
|
+
before_filter,
|
|
31
|
+
after_filter,
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"GET",
|
|
35
|
+
`/${resource}/:id`,
|
|
36
|
+
resource,
|
|
37
|
+
"show",
|
|
38
|
+
show,
|
|
39
|
+
before_filter,
|
|
40
|
+
after_filter,
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
"GET",
|
|
44
|
+
`/${resource}/:id/edit`,
|
|
45
|
+
resource,
|
|
46
|
+
"edit_form",
|
|
47
|
+
edit_form,
|
|
48
|
+
before_filter,
|
|
49
|
+
after_filter,
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"POST",
|
|
53
|
+
`/orders/:parent_id/${resource}`,
|
|
54
|
+
resource,
|
|
55
|
+
"create",
|
|
56
|
+
create,
|
|
57
|
+
before_filter,
|
|
58
|
+
after_filter,
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"PUT",
|
|
62
|
+
`/${resource}/:id`,
|
|
63
|
+
resource,
|
|
64
|
+
"update",
|
|
65
|
+
update,
|
|
66
|
+
before_filter,
|
|
67
|
+
after_filter,
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"DELETE",
|
|
71
|
+
`/${resource}/:id`,
|
|
72
|
+
resource,
|
|
73
|
+
"destroy",
|
|
74
|
+
destroy,
|
|
75
|
+
before_filter,
|
|
76
|
+
after_filter,
|
|
77
|
+
],
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
adapter.createNestedResource(mapping);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default createNestedResource;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const createResource = (adapter, resource, handler) => {
|
|
2
|
+
const {
|
|
3
|
+
before_filter,
|
|
4
|
+
after_filter,
|
|
5
|
+
index,
|
|
6
|
+
show,
|
|
7
|
+
create_form,
|
|
8
|
+
edit_form,
|
|
9
|
+
create,
|
|
10
|
+
update,
|
|
11
|
+
destroy,
|
|
12
|
+
} = handler;
|
|
13
|
+
|
|
14
|
+
const mapping = [
|
|
15
|
+
[
|
|
16
|
+
"GET",
|
|
17
|
+
`/${resource}`,
|
|
18
|
+
resource,
|
|
19
|
+
"index",
|
|
20
|
+
index,
|
|
21
|
+
before_filter,
|
|
22
|
+
after_filter,
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"GET",
|
|
26
|
+
`/${resource}/new`,
|
|
27
|
+
resource,
|
|
28
|
+
"create_form",
|
|
29
|
+
create_form,
|
|
30
|
+
before_filter,
|
|
31
|
+
after_filter,
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"GET",
|
|
35
|
+
`/${resource}/:id`,
|
|
36
|
+
resource,
|
|
37
|
+
"show",
|
|
38
|
+
show,
|
|
39
|
+
before_filter,
|
|
40
|
+
after_filter,
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
"GET",
|
|
44
|
+
`/${resource}/:id/edit`,
|
|
45
|
+
resource,
|
|
46
|
+
"edit_form",
|
|
47
|
+
edit_form,
|
|
48
|
+
before_filter,
|
|
49
|
+
after_filter,
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"POST",
|
|
53
|
+
`/${resource}`,
|
|
54
|
+
resource,
|
|
55
|
+
"create",
|
|
56
|
+
create,
|
|
57
|
+
before_filter,
|
|
58
|
+
after_filter,
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"PUT",
|
|
62
|
+
`/${resource}/:id`,
|
|
63
|
+
resource,
|
|
64
|
+
"update",
|
|
65
|
+
update,
|
|
66
|
+
before_filter,
|
|
67
|
+
after_filter,
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"DELETE",
|
|
71
|
+
`/${resource}/:id`,
|
|
72
|
+
resource,
|
|
73
|
+
"destroy",
|
|
74
|
+
destroy,
|
|
75
|
+
before_filter,
|
|
76
|
+
after_filter,
|
|
77
|
+
],
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
adapter.createResource(mapping);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default createResource;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@layer components {
|
|
6
|
+
.btn-primary {
|
|
7
|
+
@apply bg-purple-500 text-white inline-block py-1 px-3 rounded;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.btn-secondary {
|
|
11
|
+
@apply bg-neutral-300 text-black inline-block py-1 px-3 rounded;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.btn-text {
|
|
15
|
+
@apply underline underline-offset-2;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import HonoAdapter from "../HonoAdapter";
|
|
2
|
+
|
|
3
|
+
class HonoMock {
|
|
4
|
+
get() {}
|
|
5
|
+
post() {}
|
|
6
|
+
put() {}
|
|
7
|
+
delete() {}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const app = new HonoMock();
|
|
11
|
+
const adapter = new HonoAdapter(app);
|
|
12
|
+
|
|
13
|
+
describe("HonoAdapter", () => {
|
|
14
|
+
test("getMethod returns Hono's get for invalid verbs", () => {
|
|
15
|
+
expect(adapter.getMethod("not existing")).toBe(app.get);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("getMethod for GET", () => {
|
|
19
|
+
expect(adapter.getMethod("GET")).toBe(app.get);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("getMethod for POST", () => {
|
|
23
|
+
expect(adapter.getMethod("POST")).toBe(app.post);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("getMethod for PUT", () => {
|
|
27
|
+
expect(adapter.getMethod("PUT")).toBe(app.put);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("getMethod for DELETE", () => {
|
|
31
|
+
expect(adapter.getMethod("DELETE")).toBe(app.delete);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import createNestedResource from "../createNestedResource";
|
|
2
|
+
|
|
3
|
+
const OrdersHandler = {
|
|
4
|
+
index: () => {},
|
|
5
|
+
show: () => {},
|
|
6
|
+
create_form: () => {},
|
|
7
|
+
edit_form: () => {},
|
|
8
|
+
create: () => {},
|
|
9
|
+
update: () => {},
|
|
10
|
+
destroy: () => {},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const OrdersHandlerWithFilters = {
|
|
14
|
+
before_filter: () => {},
|
|
15
|
+
after_filter: () => {},
|
|
16
|
+
index: () => {},
|
|
17
|
+
show: () => {},
|
|
18
|
+
create_form: () => {},
|
|
19
|
+
edit_form: () => {},
|
|
20
|
+
create: () => {},
|
|
21
|
+
update: () => {},
|
|
22
|
+
destroy: () => {},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const honoAdapter = {
|
|
26
|
+
createNestedResource: (_mapping) => {},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const adapterSpy = jest.spyOn(honoAdapter, "createNestedResource");
|
|
30
|
+
|
|
31
|
+
describe("createNestedResource", () => {
|
|
32
|
+
test("Valid handler mapping is passed to router", () => {
|
|
33
|
+
createNestedResource(honoAdapter, "orders", "line_items", OrdersHandler);
|
|
34
|
+
|
|
35
|
+
expect(adapterSpy).toHaveBeenCalledWith([
|
|
36
|
+
[
|
|
37
|
+
"GET",
|
|
38
|
+
"/orders/:parent_id/line_items",
|
|
39
|
+
"line_items",
|
|
40
|
+
"index",
|
|
41
|
+
OrdersHandler.index,
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
"GET",
|
|
45
|
+
"/orders/:parent_id/line_items/new",
|
|
46
|
+
"line_items",
|
|
47
|
+
"create_form",
|
|
48
|
+
OrdersHandler.create_form,
|
|
49
|
+
],
|
|
50
|
+
["GET", "/line_items/:id", "line_items", "show", OrdersHandler.show],
|
|
51
|
+
[
|
|
52
|
+
"GET",
|
|
53
|
+
"/line_items/:id/edit",
|
|
54
|
+
"line_items",
|
|
55
|
+
"edit_form",
|
|
56
|
+
OrdersHandler.edit_form,
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
"POST",
|
|
60
|
+
"/orders/:parent_id/line_items",
|
|
61
|
+
"line_items",
|
|
62
|
+
"create",
|
|
63
|
+
OrdersHandler.create,
|
|
64
|
+
],
|
|
65
|
+
["PUT", "/line_items/:id", "line_items", "update", OrdersHandler.update],
|
|
66
|
+
[
|
|
67
|
+
"DELETE",
|
|
68
|
+
"/line_items/:id",
|
|
69
|
+
"line_items",
|
|
70
|
+
"destroy",
|
|
71
|
+
OrdersHandler.destroy,
|
|
72
|
+
],
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("Valid handler mapping is passed to router with filters", () => {
|
|
77
|
+
createNestedResource(
|
|
78
|
+
honoAdapter,
|
|
79
|
+
"orders",
|
|
80
|
+
"line_items",
|
|
81
|
+
OrdersHandlerWithFilters
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(adapterSpy).toHaveBeenCalledWith([
|
|
85
|
+
[
|
|
86
|
+
"GET",
|
|
87
|
+
"/orders/:parent_id/line_items",
|
|
88
|
+
"line_items",
|
|
89
|
+
"index",
|
|
90
|
+
OrdersHandlerWithFilters.index,
|
|
91
|
+
OrdersHandlerWithFilters.before_filter,
|
|
92
|
+
OrdersHandlerWithFilters.after_filter,
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
"GET",
|
|
96
|
+
"/orders/:parent_id/line_items/new",
|
|
97
|
+
"line_items",
|
|
98
|
+
"create_form",
|
|
99
|
+
OrdersHandlerWithFilters.create_form,
|
|
100
|
+
OrdersHandlerWithFilters.before_filter,
|
|
101
|
+
OrdersHandlerWithFilters.after_filter,
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
"GET",
|
|
105
|
+
"/line_items/:id",
|
|
106
|
+
"line_items",
|
|
107
|
+
"show",
|
|
108
|
+
OrdersHandlerWithFilters.show,
|
|
109
|
+
OrdersHandlerWithFilters.before_filter,
|
|
110
|
+
OrdersHandlerWithFilters.after_filter,
|
|
111
|
+
],
|
|
112
|
+
[
|
|
113
|
+
"GET",
|
|
114
|
+
"/line_items/:id/edit",
|
|
115
|
+
"line_items",
|
|
116
|
+
"edit_form",
|
|
117
|
+
OrdersHandlerWithFilters.edit_form,
|
|
118
|
+
OrdersHandlerWithFilters.before_filter,
|
|
119
|
+
OrdersHandlerWithFilters.after_filter,
|
|
120
|
+
],
|
|
121
|
+
[
|
|
122
|
+
"POST",
|
|
123
|
+
"/orders/:parent_id/line_items",
|
|
124
|
+
"line_items",
|
|
125
|
+
"create",
|
|
126
|
+
OrdersHandlerWithFilters.create,
|
|
127
|
+
OrdersHandlerWithFilters.before_filter,
|
|
128
|
+
OrdersHandlerWithFilters.after_filter,
|
|
129
|
+
],
|
|
130
|
+
[
|
|
131
|
+
"PUT",
|
|
132
|
+
"/line_items/:id",
|
|
133
|
+
"line_items",
|
|
134
|
+
"update",
|
|
135
|
+
OrdersHandlerWithFilters.update,
|
|
136
|
+
OrdersHandlerWithFilters.before_filter,
|
|
137
|
+
OrdersHandlerWithFilters.after_filter,
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
"DELETE",
|
|
141
|
+
"/line_items/:id",
|
|
142
|
+
"line_items",
|
|
143
|
+
"destroy",
|
|
144
|
+
OrdersHandlerWithFilters.destroy,
|
|
145
|
+
OrdersHandlerWithFilters.before_filter,
|
|
146
|
+
OrdersHandlerWithFilters.after_filter,
|
|
147
|
+
],
|
|
148
|
+
]);
|
|
149
|
+
});
|
|
150
|
+
});
|