@raketa-cloud/admin 0.0.1 → 0.0.3
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/dist/index.js +670 -0
- package/package.json +2 -1
- package/biome.json +0 -6
- package/jest.config.js +0 -15
- package/src/app.tsx +0 -19
- package/src/crud/login.tsx +0 -99
- package/src/crud/logout.tsx +0 -14
- package/src/crud/tags.tsx +0 -235
- package/src/index.ts +0 -1
- package/src/lib/dxp/private/tags.ts +0 -106
- package/src/lib/dxp/protocol.ts +0 -32
- package/src/lib/gateway/login.ts +0 -44
- package/src/lib/gateway/protocol.ts +0 -8
- package/src/lib/gqlEngine.ts +0 -17
- package/src/lib/logger.tsx +0 -149
- package/src/middleware/authentication.ts +0 -62
- package/src/middleware/http_logger.ts +0 -41
- package/src/middleware/logger.ts +0 -9
- package/src/shared/Layout.tsx +0 -50
- package/tests/integration/authentication/middleware.test.ts +0 -100
- package/tests/integration/login/create.test.ts +0 -117
- package/tests/integration/login/index.test.ts +0 -16
- package/tests/integration/logout/delete.test.ts +0 -17
- package/tests/integration/tags/create.test.ts +0 -66
- package/tests/integration/tags/delete.test.ts +0 -74
- package/tests/integration/tags/edit.test.ts +0 -43
- package/tests/integration/tags/index.test.ts +0 -66
- package/tests/integration/tags/new.test.ts +0 -16
- package/tests/integration/tags/update.test.ts +0 -87
- package/tests/support/helpers.ts +0 -53
- package/tests/support/setup.js +0 -9
- package/tsconfig.json +0 -15
- package/turbo/generators/config.ts +0 -30
- package/turbo/generators/templates/component.hbs +0 -14
- package/turbo.json +0 -9
package/src/lib/logger.tsx
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
enum Severity {
|
|
2
|
-
Info = 1,
|
|
3
|
-
Warn = 2,
|
|
4
|
-
Error = 3,
|
|
5
|
-
Debug = 4,
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
type Payload = {
|
|
9
|
-
date: Date;
|
|
10
|
-
identifier: string;
|
|
11
|
-
severity: Severity;
|
|
12
|
-
message: string;
|
|
13
|
-
meta?: object;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
class Fancy {
|
|
17
|
-
static SEVERITY = {
|
|
18
|
-
[Severity.Info]: "I",
|
|
19
|
-
[Severity.Warn]: "W",
|
|
20
|
-
[Severity.Error]: "E",
|
|
21
|
-
[Severity.Debug]: "D",
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
private static formatDate(date: Date) {
|
|
25
|
-
return date.toISOString().replace("T", " ").split(".")[0];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private static formatSeverity(severity: Severity) {
|
|
29
|
-
return Fancy.SEVERITY[severity];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
static formatStatus(status: number) {
|
|
33
|
-
const out: { [key: string]: string } = {
|
|
34
|
-
5: `\x1b[31m${status}\x1b[0m`,
|
|
35
|
-
4: `\x1b[31m${status}\x1b[0m`,
|
|
36
|
-
3: `\x1b[33m${status}\x1b[0m`,
|
|
37
|
-
2: `\x1b[32m${status}\x1b[0m`,
|
|
38
|
-
1: `\x1b[32m${status}\x1b[0m`,
|
|
39
|
-
0: `${status}`,
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const calculateStatus = (status / 100) | 0;
|
|
43
|
-
|
|
44
|
-
return out[calculateStatus];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
static formatMessage(message: string): string {
|
|
48
|
-
if (message.startsWith("-> ")) {
|
|
49
|
-
const { verb, path } = message.match(
|
|
50
|
-
/(?<verb>GET|POST|DELETE|PUT|PATCH|OPTIONS)\s(?<path>.*)$/i
|
|
51
|
-
).groups;
|
|
52
|
-
|
|
53
|
-
return `-> ${verb} \x1b[36m${path}\x1b[0m`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (message.startsWith("<- ")) {
|
|
57
|
-
const { status } = message.match(/<-\s(?<status>\d{3})\s/i).groups;
|
|
58
|
-
|
|
59
|
-
return `${message.replace(
|
|
60
|
-
status,
|
|
61
|
-
Fancy.formatStatus(parseInt(status))
|
|
62
|
-
)}\n`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return message;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
static format({ date, identifier, severity, message }: Payload): string {
|
|
69
|
-
return [
|
|
70
|
-
`[${Fancy.formatDate(date)}]`,
|
|
71
|
-
`(${identifier})`,
|
|
72
|
-
`${Fancy.formatSeverity(severity)}:`,
|
|
73
|
-
Fancy.formatMessage(message),
|
|
74
|
-
].join(" ");
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
class Structured {
|
|
79
|
-
static SEVERITY = {
|
|
80
|
-
[Severity.Info]: "info",
|
|
81
|
-
[Severity.Warn]: "warning",
|
|
82
|
-
[Severity.Error]: "error",
|
|
83
|
-
[Severity.Debug]: "debug",
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
private static formatDate(date: Date) {
|
|
87
|
-
return date.toISOString();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private static formatSeverity(severity: Severity) {
|
|
91
|
-
return Structured.SEVERITY[severity];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
static format({
|
|
95
|
-
date,
|
|
96
|
-
identifier,
|
|
97
|
-
severity,
|
|
98
|
-
message,
|
|
99
|
-
meta,
|
|
100
|
-
}: Payload): string {
|
|
101
|
-
return JSON.stringify({
|
|
102
|
-
date: Structured.formatDate(date),
|
|
103
|
-
identifier,
|
|
104
|
-
severity: Structured.formatSeverity(severity),
|
|
105
|
-
message,
|
|
106
|
-
meta,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export class Logger {
|
|
112
|
-
requestID: string;
|
|
113
|
-
formatter: typeof Fancy | typeof Structured;
|
|
114
|
-
|
|
115
|
-
constructor(requestID: string, formatter = Fancy) {
|
|
116
|
-
this.requestID = requestID;
|
|
117
|
-
this.formatter = formatter;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private log(severity: Severity, message: string, meta?: object) {
|
|
121
|
-
const date = new Date();
|
|
122
|
-
|
|
123
|
-
console.log(
|
|
124
|
-
this.formatter.format({
|
|
125
|
-
date,
|
|
126
|
-
identifier: this.requestID,
|
|
127
|
-
severity,
|
|
128
|
-
message,
|
|
129
|
-
meta,
|
|
130
|
-
})
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
info(message: string, meta?: object) {
|
|
135
|
-
this.log(Severity.Info, message, meta);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
warn(message: string, meta?: object) {
|
|
139
|
-
this.log(Severity.Warn, message, meta);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
error(message: string, meta?: object) {
|
|
143
|
-
this.log(Severity.Error, message, meta);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
debug(message: string, meta?: object) {
|
|
147
|
-
this.log(Severity.Debug, message, meta);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { Context, Next } from "hono";
|
|
2
|
-
import { getCookie } from "hono/cookie";
|
|
3
|
-
import { decode } from "hono/jwt";
|
|
4
|
-
import { env } from "hono/adapter";
|
|
5
|
-
|
|
6
|
-
const EXCLUDED_PATHS = ["/login", "/logout", "/serviceworker.js"];
|
|
7
|
-
|
|
8
|
-
function shouldAuthenticate(c: Context) {
|
|
9
|
-
return !EXCLUDED_PATHS.includes(c.req.path);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function redirect(location: string) {
|
|
13
|
-
return new Response("Not Authorized", {
|
|
14
|
-
status: 301,
|
|
15
|
-
headers: {
|
|
16
|
-
Location: location,
|
|
17
|
-
"Set-Cookie": "authToken=; Max-Age=0",
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function authenticate(c: Context, next: Next) {
|
|
23
|
-
const token = getCookie(c, "authToken");
|
|
24
|
-
|
|
25
|
-
if (!token) {
|
|
26
|
-
return redirect("/login");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const parsedToken = decode(token);
|
|
30
|
-
const payload = parsedToken.payload;
|
|
31
|
-
|
|
32
|
-
if (payload.exp < (new Date().getTime() + 1) / 1000) {
|
|
33
|
-
return redirect("/login");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!payload.info.active) {
|
|
37
|
-
return redirect("/login?reason=inactive");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const {
|
|
41
|
-
RAKETA_GATEWAY_PRIMARY_USER_POOL,
|
|
42
|
-
RAKETA_GATEWAY_SECONDARY_USER_POOL,
|
|
43
|
-
} = env(c);
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
![RAKETA_GATEWAY_PRIMARY_USER_POOL, RAKETA_GATEWAY_SECONDARY_USER_POOL]
|
|
47
|
-
.filter(Boolean)
|
|
48
|
-
.includes(payload.info.user_pool_id)
|
|
49
|
-
) {
|
|
50
|
-
return redirect("/login");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
await next();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default async (c: Context, next: Next) => {
|
|
57
|
-
if (shouldAuthenticate(c)) {
|
|
58
|
-
return authenticate(c, next);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
await next();
|
|
62
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Context, Next } from "hono";
|
|
2
|
-
|
|
3
|
-
const humanize = (times: string[]) => {
|
|
4
|
-
const [delimiter, separator] = [",", "."];
|
|
5
|
-
|
|
6
|
-
const orderTimes = times.map((v) =>
|
|
7
|
-
v.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, `$1${delimiter}`),
|
|
8
|
-
);
|
|
9
|
-
|
|
10
|
-
return orderTimes.join(separator);
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const time = (start: number) => {
|
|
14
|
-
const delta = Date.now() - start;
|
|
15
|
-
return humanize([
|
|
16
|
-
delta < 1000 ? `${delta}ms` : `${Math.round(delta / 1000)}s`,
|
|
17
|
-
]);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export default async (c: Context, next: Next) => {
|
|
21
|
-
const logger = c.get("logger");
|
|
22
|
-
const { method } = c.req;
|
|
23
|
-
|
|
24
|
-
const start = Date.now();
|
|
25
|
-
const url = new URL(c.req.url);
|
|
26
|
-
const path = url.href.replace(url.origin, "");
|
|
27
|
-
|
|
28
|
-
logger.info(`-> ${method} ${path}`, {
|
|
29
|
-
method,
|
|
30
|
-
path,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
await next();
|
|
34
|
-
|
|
35
|
-
const elapsed = time(start);
|
|
36
|
-
|
|
37
|
-
logger.info(`<- ${c.res.status} in ${elapsed}`, {
|
|
38
|
-
method,
|
|
39
|
-
elapsed,
|
|
40
|
-
});
|
|
41
|
-
};
|
package/src/middleware/logger.ts
DELETED
package/src/shared/Layout.tsx
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Context } from "hono";
|
|
2
|
-
import type { FC } from "hono/jsx";
|
|
3
|
-
import { getCookie } from "hono/cookie";
|
|
4
|
-
import { decode } from "hono/jwt";
|
|
5
|
-
|
|
6
|
-
function fetchUser(c: Context) {
|
|
7
|
-
const token = getCookie(c, "authToken");
|
|
8
|
-
|
|
9
|
-
if (!token) {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return decode(token).payload.info || null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const Layout: FC = ({ context, children }) => {
|
|
17
|
-
const user = fetchUser(context);
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<html lang="en">
|
|
21
|
-
<head>
|
|
22
|
-
<meta charset="UTF-8" />
|
|
23
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
24
|
-
|
|
25
|
-
<script src="https://cdn.tailwindcss.com" />
|
|
26
|
-
<script src="https://unpkg.com/htmx.org@1.9.6" />
|
|
27
|
-
|
|
28
|
-
<title>Administration</title>
|
|
29
|
-
</head>
|
|
30
|
-
|
|
31
|
-
<body>
|
|
32
|
-
{user && (
|
|
33
|
-
<div class="flex items-center justify-end gap-4 w-full h-[50px] p-4">
|
|
34
|
-
<img
|
|
35
|
-
src={user.avatar}
|
|
36
|
-
class="rounded-full h-[40px] w-[40px]"
|
|
37
|
-
alt="Admin avatar"
|
|
38
|
-
/>
|
|
39
|
-
<button type="button" hx-delete="/logout">
|
|
40
|
-
Logout
|
|
41
|
-
</button>
|
|
42
|
-
</div>
|
|
43
|
-
)}
|
|
44
|
-
{children}
|
|
45
|
-
</body>
|
|
46
|
-
</html>
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export default Layout;
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import { sign } from "hono/jwt";
|
|
3
|
-
import { describe, expect, test } from "@jest/globals";
|
|
4
|
-
import auth from "src/middleware/authentication";
|
|
5
|
-
|
|
6
|
-
const app = new Hono();
|
|
7
|
-
|
|
8
|
-
app.use("*", auth);
|
|
9
|
-
|
|
10
|
-
async function prepareToken(
|
|
11
|
-
userPool: string,
|
|
12
|
-
{ active = true, date }: { active?: boolean; date: Date },
|
|
13
|
-
) {
|
|
14
|
-
return await sign(
|
|
15
|
-
{
|
|
16
|
-
sub: "1",
|
|
17
|
-
exp: (date.getTime() / 1000).toFixed(0),
|
|
18
|
-
info: {
|
|
19
|
-
user_pool_id: userPool,
|
|
20
|
-
active: active,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
"secret",
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const USER_POOL = "primary-user-pool";
|
|
28
|
-
|
|
29
|
-
describe("Authentication middleware", () => {
|
|
30
|
-
test("Does not fire on excluded paths", async () => {
|
|
31
|
-
let res = await app.request("/login");
|
|
32
|
-
expect(res.status).toBe(404);
|
|
33
|
-
|
|
34
|
-
res = await app.request("/logout");
|
|
35
|
-
expect(res.status).toBe(404);
|
|
36
|
-
|
|
37
|
-
res = await app.request("/serviceworker.js");
|
|
38
|
-
expect(res.status).toBe(404);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("Allows access with ok token and logged in user", async () => {
|
|
42
|
-
const date = new Date(new Date().getTime() + 1000000);
|
|
43
|
-
const token = await prepareToken(USER_POOL, { date });
|
|
44
|
-
|
|
45
|
-
const req = new Request("https://example.com/tags", {
|
|
46
|
-
method: "GET",
|
|
47
|
-
headers: {
|
|
48
|
-
Cookie: `authToken=${token}`,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const res = await app.request(req);
|
|
53
|
-
|
|
54
|
-
expect(res.status).toBe(404);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("Redirects in case of missing token", async () => {
|
|
58
|
-
const res = await app.request("/tags");
|
|
59
|
-
|
|
60
|
-
expect(res.status).toBe(301);
|
|
61
|
-
expect(res.headers.get("Location")).toBe("/login");
|
|
62
|
-
expect(res.headers.get("Set-Cookie")).toBe("authToken=; Max-Age=0");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("Redirects in case of expired token", async () => {
|
|
66
|
-
const date = new Date(new Date().getTime() - 1000000);
|
|
67
|
-
const token = await prepareToken(USER_POOL, { date });
|
|
68
|
-
|
|
69
|
-
const req = new Request("https://example.com/tags", {
|
|
70
|
-
method: "GET",
|
|
71
|
-
headers: {
|
|
72
|
-
Cookie: `authToken=${token}`,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const res = await app.request(req);
|
|
77
|
-
|
|
78
|
-
expect(res.status).toBe(301);
|
|
79
|
-
expect(res.headers.get("Location")).toBe("/login");
|
|
80
|
-
expect(res.headers.get("Set-Cookie")).toBe("authToken=; Max-Age=0");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("Redirects in case of inactive user", async () => {
|
|
84
|
-
const date = new Date(new Date().getTime() + 1000000);
|
|
85
|
-
const token = await prepareToken(USER_POOL, { active: false, date });
|
|
86
|
-
|
|
87
|
-
const req = new Request("https://example.com/tags", {
|
|
88
|
-
method: "GET",
|
|
89
|
-
headers: {
|
|
90
|
-
Cookie: `authToken=${token}`,
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const res = await app.request(req);
|
|
95
|
-
|
|
96
|
-
expect(res.status).toBe(301);
|
|
97
|
-
expect(res.headers.get("Location")).toBe("/login?reason=inactive");
|
|
98
|
-
expect(res.headers.get("Set-Cookie")).toBe("authToken=; Max-Age=0");
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { sign } from "hono/jwt";
|
|
2
|
-
import { describe, expect, test } from "@jest/globals";
|
|
3
|
-
import app from "src/crud/login";
|
|
4
|
-
import {
|
|
5
|
-
mockGraphql,
|
|
6
|
-
assertAuthRequestCalled,
|
|
7
|
-
assertEmptyBody,
|
|
8
|
-
} from "tests/support/helpers";
|
|
9
|
-
|
|
10
|
-
const LOGIN_MUTATION = `
|
|
11
|
-
mutation Login($input: LoginInput!) {
|
|
12
|
-
login(input: $input) {
|
|
13
|
-
accessToken
|
|
14
|
-
errors {
|
|
15
|
-
key
|
|
16
|
-
message
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
`;
|
|
21
|
-
|
|
22
|
-
async function prepareToken(
|
|
23
|
-
userPool: string,
|
|
24
|
-
{ active = true, date }: { active?: boolean; date: Date },
|
|
25
|
-
) {
|
|
26
|
-
return await sign(
|
|
27
|
-
{
|
|
28
|
-
sub: "1",
|
|
29
|
-
exp: (date.getTime() / 1000).toFixed(0),
|
|
30
|
-
info: {
|
|
31
|
-
user_pool_id: userPool,
|
|
32
|
-
active: active,
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
"secret",
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const USER_POOL = "primary-user-pool";
|
|
40
|
-
|
|
41
|
-
describe("Login create endpoint", () => {
|
|
42
|
-
test("POST /login with valid credentials", async () => {
|
|
43
|
-
const date = new Date();
|
|
44
|
-
const token = await prepareToken(USER_POOL, { date });
|
|
45
|
-
|
|
46
|
-
const requestsMock = mockGraphql(
|
|
47
|
-
Promise.resolve({
|
|
48
|
-
login: {
|
|
49
|
-
accessToken: token,
|
|
50
|
-
errors: null,
|
|
51
|
-
},
|
|
52
|
-
}),
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const formData = new FormData();
|
|
56
|
-
formData.set("email", "john@example.com");
|
|
57
|
-
formData.set("password", "123");
|
|
58
|
-
|
|
59
|
-
const res = await app.request("/login", { method: "POST", body: formData });
|
|
60
|
-
|
|
61
|
-
assertAuthRequestCalled(requestsMock, LOGIN_MUTATION, {
|
|
62
|
-
input: {
|
|
63
|
-
email: "john@example.com",
|
|
64
|
-
password: "123",
|
|
65
|
-
userPools: [USER_POOL],
|
|
66
|
-
project: "1-raketa-gw-priject",
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
expect(res.status).toBe(301);
|
|
71
|
-
expect(res.headers.get("Set-Cookie")).toBe(
|
|
72
|
-
`authToken=${token}; Path=/; Expires=${date.toUTCString()}`,
|
|
73
|
-
);
|
|
74
|
-
expect(res.headers.get("Location")).toBe("/tags");
|
|
75
|
-
await assertEmptyBody(res);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe("Login create endpoint", () => {
|
|
80
|
-
test("POST /login with invalid credentials", async () => {
|
|
81
|
-
const requestsMock = mockGraphql(
|
|
82
|
-
Promise.resolve({
|
|
83
|
-
login: {
|
|
84
|
-
accessToken: null,
|
|
85
|
-
errors: [
|
|
86
|
-
{
|
|
87
|
-
key: "email",
|
|
88
|
-
message: "Invalid credentials",
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
},
|
|
92
|
-
}),
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
const formData = new FormData();
|
|
96
|
-
formData.set("email", "john@example.com");
|
|
97
|
-
formData.set("password", "123");
|
|
98
|
-
|
|
99
|
-
const res = await app.request("/login", { method: "POST", body: formData });
|
|
100
|
-
|
|
101
|
-
assertAuthRequestCalled(requestsMock, LOGIN_MUTATION, {
|
|
102
|
-
input: {
|
|
103
|
-
email: "john@example.com",
|
|
104
|
-
password: "123",
|
|
105
|
-
userPools: [USER_POOL],
|
|
106
|
-
project: "1-raketa-gw-priject",
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
expect(res.status).toBe(200);
|
|
111
|
-
expect(res.headers.get("Set-Cookie")).toBe("authToken=; Max-Age=0; Path=/");
|
|
112
|
-
|
|
113
|
-
const content = await res.text();
|
|
114
|
-
|
|
115
|
-
expect(content).toContain("Invalid credentials");
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "@jest/globals";
|
|
2
|
-
import app from "src/crud/login";
|
|
3
|
-
|
|
4
|
-
describe("Login index endpoint", () => {
|
|
5
|
-
test("GET /login", async () => {
|
|
6
|
-
const res = await app.request("/login");
|
|
7
|
-
|
|
8
|
-
expect(res.status).toBe(200);
|
|
9
|
-
|
|
10
|
-
const content = await res.text();
|
|
11
|
-
|
|
12
|
-
expect(content).toContain("Login");
|
|
13
|
-
expect(content).toContain("email");
|
|
14
|
-
expect(content).toContain("password");
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "@jest/globals";
|
|
2
|
-
import app from "src/crud/logout";
|
|
3
|
-
import { assertJSONContent } from "tests/support/helpers";
|
|
4
|
-
|
|
5
|
-
describe("Logout delete endpoint", () => {
|
|
6
|
-
test("DELETE /logout", async () => {
|
|
7
|
-
const res = await app.request("/logout", { method: "DELETE" });
|
|
8
|
-
|
|
9
|
-
expect(res.status).toBe(201);
|
|
10
|
-
expect(res.headers.get("HX-Redirect")).toBe("/login");
|
|
11
|
-
expect(res.headers.get("Set-Cookie")).toBe("authToken=; Max-Age=0; Path=/");
|
|
12
|
-
|
|
13
|
-
await assertJSONContent(res, {
|
|
14
|
-
message: "Bye",
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "@jest/globals";
|
|
2
|
-
import app from "src/crud/tags";
|
|
3
|
-
import {
|
|
4
|
-
mockGraphql,
|
|
5
|
-
assertPrivateRequestCalled,
|
|
6
|
-
assertEmptyBody,
|
|
7
|
-
} from "tests/support/helpers";
|
|
8
|
-
|
|
9
|
-
const TAG_MUTATION = `
|
|
10
|
-
mutation CreateTag($input: CreateTagInput!) {
|
|
11
|
-
createTag(input: $input) {
|
|
12
|
-
tag {
|
|
13
|
-
id
|
|
14
|
-
slug
|
|
15
|
-
name
|
|
16
|
-
}
|
|
17
|
-
errors {
|
|
18
|
-
key
|
|
19
|
-
message
|
|
20
|
-
codes {
|
|
21
|
-
key
|
|
22
|
-
placeholders
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
`;
|
|
28
|
-
|
|
29
|
-
describe("Tag create endpoint", () => {
|
|
30
|
-
test("GET /tags/:id/edit", async () => {
|
|
31
|
-
const tag = {
|
|
32
|
-
id: 1,
|
|
33
|
-
name: "Tag 1",
|
|
34
|
-
slug: "tag-1",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const requestsMock = mockGraphql(
|
|
38
|
-
Promise.resolve({
|
|
39
|
-
createTag: {
|
|
40
|
-
tag: tag,
|
|
41
|
-
errors: null,
|
|
42
|
-
},
|
|
43
|
-
}),
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const formData = new FormData();
|
|
47
|
-
formData.set("name", "Tag 1");
|
|
48
|
-
formData.set("slug", "tag-1");
|
|
49
|
-
|
|
50
|
-
const res = await app.request("/tags", { method: "POST", body: formData });
|
|
51
|
-
|
|
52
|
-
assertPrivateRequestCalled(requestsMock, TAG_MUTATION, {
|
|
53
|
-
input: {
|
|
54
|
-
locale: "en",
|
|
55
|
-
tag: {
|
|
56
|
-
name: "Tag 1",
|
|
57
|
-
slug: "tag-1",
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
expect(res.status).toBe(301);
|
|
63
|
-
expect(res.headers.get("Location")).toBe("/tags");
|
|
64
|
-
await assertEmptyBody(res);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "@jest/globals";
|
|
2
|
-
import app from "src/crud/tags";
|
|
3
|
-
import {
|
|
4
|
-
mockMultipleGraphql,
|
|
5
|
-
assertPrivateRequestCalled,
|
|
6
|
-
assertJSONContent,
|
|
7
|
-
} from "tests/support/helpers";
|
|
8
|
-
|
|
9
|
-
const TAG_QUERY = `
|
|
10
|
-
query TagByID($input: IdInput!) {
|
|
11
|
-
tag(input: $input) {
|
|
12
|
-
id
|
|
13
|
-
slug
|
|
14
|
-
name
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
`;
|
|
18
|
-
|
|
19
|
-
const DELETE_TAG_MUTATION = `
|
|
20
|
-
mutation DeleteTag($input: IdInput!) {
|
|
21
|
-
deleteTag(input: $input) {
|
|
22
|
-
tag {
|
|
23
|
-
id
|
|
24
|
-
slug
|
|
25
|
-
name
|
|
26
|
-
}
|
|
27
|
-
errors {
|
|
28
|
-
key
|
|
29
|
-
message
|
|
30
|
-
codes {
|
|
31
|
-
key
|
|
32
|
-
placeholders
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
describe("Tag delete endpoint", () => {
|
|
40
|
-
test("DELETE /tags/:id", async () => {
|
|
41
|
-
const tag = {
|
|
42
|
-
id: 1,
|
|
43
|
-
name: "Tag 1",
|
|
44
|
-
slug: "tag-1",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const requestsMock = mockMultipleGraphql([
|
|
48
|
-
Promise.resolve({
|
|
49
|
-
tag: tag,
|
|
50
|
-
}),
|
|
51
|
-
Promise.resolve({
|
|
52
|
-
deleteTag: {
|
|
53
|
-
tag: tag,
|
|
54
|
-
errors: null,
|
|
55
|
-
},
|
|
56
|
-
}),
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
const res = await app.request("/tags/1", { method: "DELETE" });
|
|
60
|
-
|
|
61
|
-
assertPrivateRequestCalled(requestsMock, TAG_QUERY, {
|
|
62
|
-
input: { locale: "en", id: 1 },
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
assertPrivateRequestCalled(requestsMock, DELETE_TAG_MUTATION, {
|
|
66
|
-
input: { locale: "en", id: 1 },
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
expect(res.status).toBe(201);
|
|
70
|
-
expect(res.headers.get("HX-Redirect")).toBe("/tags");
|
|
71
|
-
|
|
72
|
-
await assertJSONContent(res, tag);
|
|
73
|
-
});
|
|
74
|
-
});
|