@raketa-cloud/admin 1.6.1 → 1.6.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/package.json +7 -7
- package/src/HonoAdapter.js +84 -39
- package/src/cloud/Articles.js +20 -2
- package/src/cloud/Events.js +21 -3
- package/src/cloud/Pages.js +15 -0
- package/src/cloud/documents/articlesDocuments.js +29 -0
- package/src/cloud/documents/eventsDocuments.js +36 -0
- package/src/cloud/documents/pagesDocuments.js +28 -0
- package/src/handlers/TagsAPIHandler.js +11 -0
- package/src/handlers/articles/ArticlesAPIHandler.js +15 -0
- package/src/handlers/articles/ChangeHandler.js +22 -0
- package/src/handlers/events/ChangeHandler.js +22 -0
- package/src/handlers/events/EventsAPIHandler.js +15 -0
- package/src/handlers/pages/ChangeHandler.js +22 -0
- package/src/handlers/pages/PagesAPIHandler.js +15 -0
- package/src/helpers/AuthHelper.js +7 -1
- package/src/index.js +1 -0
- package/src/routeArticlesModule.js +11 -0
- package/src/routeEventsModule.js +11 -0
- package/src/routePagesModule.js +11 -0
- package/src/routeStandard.js +8 -6
- package/src/tests/HonoAdapter.test.js +90 -20
- package/src/tests/handlers/TagsHandlerAPI.test/index.test.js +38 -0
- package/src/tests/handlers/articles/ArticlesAPIHandler.test/index.test.js +43 -0
- package/src/tests/handlers/articles/ChangeHandler.test/show.test.js +59 -0
- package/src/tests/handlers/events/ChangeHandler.test/show.test.js +59 -0
- package/src/tests/handlers/events/EventsAPIHandler.test/index.test.js +39 -0
- package/src/tests/handlers/pages/ChangeHandler.test/show.test.js +59 -0
- package/src/tests/handlers/pages/PagesAPIHandler.test/index.test.js +39 -0
- package/src/tests/helpers/AuthHelper.test.js +12 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raketa-cloud/admin",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"src/",
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
"prepare": "npx tailwindcss -i ./src/styles/application.css -o ./dist/application.css"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@babel/core": "7.
|
|
16
|
-
"@babel/preset-env": "7.
|
|
17
|
-
"@babel/preset-typescript": "7.
|
|
15
|
+
"@babel/core": "7.27.1",
|
|
16
|
+
"@babel/preset-env": "7.27.2",
|
|
17
|
+
"@babel/preset-typescript": "7.27.1",
|
|
18
18
|
"@repo/typescript-config": "*",
|
|
19
19
|
"@tailwindcss/forms": "0.5.7",
|
|
20
20
|
"@tailwindcss/typography": "0.5.13",
|
|
21
|
-
"@types/node": "22.
|
|
21
|
+
"@types/node": "22.15.21",
|
|
22
22
|
"jest": "29.7.0",
|
|
23
23
|
"tailwindcss": "3.4.4",
|
|
24
|
-
"typescript": "5.8.
|
|
24
|
+
"typescript": "5.8.3"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"ejs": "3.1.10",
|
|
28
|
-
"graphql-request": "7.
|
|
28
|
+
"graphql-request": "7.2.0"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/src/HonoAdapter.js
CHANGED
|
@@ -16,14 +16,14 @@ import utils from "./helpers/utils";
|
|
|
16
16
|
|
|
17
17
|
function escapeHtml(string) {
|
|
18
18
|
const escape = {
|
|
19
|
-
'"':
|
|
20
|
-
|
|
21
|
-
"'":
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
'"': """,
|
|
20
|
+
"&": "&",
|
|
21
|
+
"'": "'",
|
|
22
|
+
"<": "<",
|
|
23
|
+
">": ">",
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
return String(string).replace(/["&'<>]/g, char => escape[char]);
|
|
26
|
+
return String(string).replace(/["&'<>]/g, (char) => escape[char]);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const renderError = (message, stack) => {
|
|
@@ -38,7 +38,8 @@ const renderError = (message, stack) => {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
// Convert to array if needed
|
|
41
|
-
const toArray =
|
|
41
|
+
const toArray = (value) =>
|
|
42
|
+
Array.isArray(value) ? value : [value].filter(Boolean);
|
|
42
43
|
|
|
43
44
|
// Handle nested form values
|
|
44
45
|
function putIn(result, key, value) {
|
|
@@ -50,9 +51,8 @@ function putIn(result, key, value) {
|
|
|
50
51
|
|
|
51
52
|
if (isLastSegment) {
|
|
52
53
|
// Set value at final segment
|
|
53
|
-
current[segment] =
|
|
54
|
-
? toArray(value)
|
|
55
|
-
: value;
|
|
54
|
+
current[segment] =
|
|
55
|
+
key.endsWith("[]") || Array.isArray(value) ? toArray(value) : value;
|
|
56
56
|
} else {
|
|
57
57
|
// Create path to next segment
|
|
58
58
|
current[segment] ??= {};
|
|
@@ -62,22 +62,26 @@ function putIn(result, key, value) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Normalize paths for consistent handling
|
|
65
|
-
const sanitizePath = filePath =>
|
|
65
|
+
const sanitizePath = (filePath) =>
|
|
66
|
+
path.normalize(path.join("/", ...filePath.split("/")));
|
|
66
67
|
|
|
67
68
|
// Find template path with fallbacks
|
|
68
69
|
function getPartialPath(partialPath, views, viewsFallback) {
|
|
69
70
|
const paths = [
|
|
70
71
|
sanitizePath(`${process.cwd()}/src/views/${views}/${partialPath}`),
|
|
71
72
|
sanitizePath(`${currentPath}/views/${views}/${partialPath}`),
|
|
72
|
-
viewsFallback &&
|
|
73
|
+
viewsFallback &&
|
|
74
|
+
sanitizePath(`${currentPath}/views/${viewsFallback}/${partialPath}`),
|
|
73
75
|
].filter(Boolean);
|
|
74
76
|
|
|
75
77
|
// Return first existing path
|
|
76
|
-
const existingPath = paths.find(path => existsSync(path));
|
|
78
|
+
const existingPath = paths.find((path) => existsSync(path));
|
|
77
79
|
|
|
78
80
|
if (existingPath) return existingPath;
|
|
79
81
|
|
|
80
|
-
throw new Error(
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Template not found: "${partialPath}"\nSearched in:\n${paths.join("\n")}`
|
|
84
|
+
);
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
// Parse request body based on content type
|
|
@@ -109,6 +113,7 @@ export default class HonoAdapter {
|
|
|
109
113
|
this.globalConfig = config;
|
|
110
114
|
this.globalBeforeFilters = [];
|
|
111
115
|
this.globalAfterFilters = [];
|
|
116
|
+
this.localRouter = [];
|
|
112
117
|
|
|
113
118
|
// TODO: Pass in a path to redirect to after login?
|
|
114
119
|
if (config) {
|
|
@@ -261,21 +266,48 @@ export default class HonoAdapter {
|
|
|
261
266
|
});
|
|
262
267
|
}
|
|
263
268
|
|
|
264
|
-
|
|
265
|
-
|
|
269
|
+
addRoute(verb, path, handler) {
|
|
270
|
+
this.localRouter = this.localRouter.filter((routeItem) => {
|
|
271
|
+
return !(routeItem.verb === verb && routeItem.path === path);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
this.localRouter.push({
|
|
275
|
+
verb,
|
|
276
|
+
path,
|
|
277
|
+
handler,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
link() {
|
|
282
|
+
// Clean up all of hono's routes before linking to the adapter locally build routes
|
|
283
|
+
// this.router.routes = [];
|
|
284
|
+
// this.router.routes.length = 0;
|
|
285
|
+
|
|
286
|
+
this.localRouter.forEach((routeItem) => {
|
|
287
|
+
const method = this.getMethod(routeItem.verb);
|
|
288
|
+
|
|
289
|
+
method(routeItem.path, routeItem.handler);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async createAction(
|
|
294
|
+
[verb, path, resource, views, action, handler],
|
|
295
|
+
resourceConfig
|
|
296
|
+
) {
|
|
266
297
|
const { i18n } = this.globalConfig;
|
|
267
298
|
|
|
268
299
|
Object.keys(i18n.locales).forEach((currentLocale) => {
|
|
269
300
|
const completePath = `/${currentLocale}${path}`;
|
|
270
301
|
|
|
271
|
-
|
|
272
|
-
this.router.routes = this.router.routes.filter(route =>
|
|
273
|
-
route[0] !== verb || route[1] !== completePath
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
method(completePath, async (routerCtx, next) => {
|
|
302
|
+
this.addRoute(verb, completePath, async (routerCtx, next) => {
|
|
277
303
|
// Create context with request data
|
|
278
|
-
const ctx = await this.createContext(
|
|
304
|
+
const ctx = await this.createContext(
|
|
305
|
+
routerCtx,
|
|
306
|
+
next,
|
|
307
|
+
verb,
|
|
308
|
+
resource,
|
|
309
|
+
action
|
|
310
|
+
);
|
|
279
311
|
|
|
280
312
|
// Set context properties
|
|
281
313
|
Object.assign(ctx, {
|
|
@@ -285,7 +317,7 @@ export default class HonoAdapter {
|
|
|
285
317
|
maxOpenRevisions: this.globalConfig?.maxOpenRevisions || 3,
|
|
286
318
|
locale: currentLocale,
|
|
287
319
|
remoteConfig: { ...this.globalConfig.remoteConfig },
|
|
288
|
-
logger: Logger
|
|
320
|
+
logger: Logger,
|
|
289
321
|
});
|
|
290
322
|
|
|
291
323
|
// Build context object
|
|
@@ -301,15 +333,17 @@ export default class HonoAdapter {
|
|
|
301
333
|
resourceConfig,
|
|
302
334
|
adminJwt: ctx.authentication?.token,
|
|
303
335
|
remoteConfig: { ...this.globalConfig.remoteConfig },
|
|
304
|
-
globalConfig: this.globalConfig
|
|
336
|
+
globalConfig: this.globalConfig,
|
|
305
337
|
};
|
|
306
338
|
|
|
307
|
-
ctx.logger.info(
|
|
339
|
+
ctx.logger.info(
|
|
340
|
+
`${ctx.method} "${ctx.rawPath}" -> ${resource}/${action} | ${JSON.stringify(ctx.params)}`
|
|
341
|
+
);
|
|
308
342
|
|
|
309
343
|
const assigns = {
|
|
310
344
|
beforeFilterAssigns: {},
|
|
311
345
|
actionAssigns: {},
|
|
312
|
-
afterFilterAssigns: {}
|
|
346
|
+
afterFilterAssigns: {},
|
|
313
347
|
};
|
|
314
348
|
|
|
315
349
|
// Initialize global context
|
|
@@ -328,7 +362,7 @@ export default class HonoAdapter {
|
|
|
328
362
|
}
|
|
329
363
|
|
|
330
364
|
// Process action handler
|
|
331
|
-
const result = await handler(ctx, resourceConfig) || {};
|
|
365
|
+
const result = (await handler(ctx, resourceConfig)) || {};
|
|
332
366
|
assigns.actionAssigns = result.assigns || {};
|
|
333
367
|
|
|
334
368
|
if (result.headers) this.applyHeaders(result.headers, routerCtx);
|
|
@@ -340,10 +374,14 @@ export default class HonoAdapter {
|
|
|
340
374
|
if (pattern.test(ctx.path)) {
|
|
341
375
|
const afterResult = filter(ctx) || {};
|
|
342
376
|
|
|
343
|
-
if (afterResult.redirect)
|
|
377
|
+
if (afterResult.redirect)
|
|
378
|
+
return routerCtx.redirect(afterResult.redirect);
|
|
344
379
|
if (afterResult.json) return routerCtx.json(afterResult.json);
|
|
345
380
|
|
|
346
|
-
Object.assign(
|
|
381
|
+
Object.assign(
|
|
382
|
+
assigns.afterFilterAssigns,
|
|
383
|
+
afterResult.assigns || {}
|
|
384
|
+
);
|
|
347
385
|
}
|
|
348
386
|
}
|
|
349
387
|
|
|
@@ -353,16 +391,19 @@ export default class HonoAdapter {
|
|
|
353
391
|
...assigns.beforeFilterAssigns,
|
|
354
392
|
...assigns.actionAssigns,
|
|
355
393
|
...assigns.afterFilterAssigns,
|
|
356
|
-
_context
|
|
394
|
+
_context,
|
|
357
395
|
};
|
|
358
396
|
|
|
359
397
|
// Render the view
|
|
360
|
-
const rendered = this.renderAction(
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
398
|
+
const rendered = this.renderAction(
|
|
399
|
+
{
|
|
400
|
+
views,
|
|
401
|
+
viewsFallback: resourceConfig.viewsFallback,
|
|
402
|
+
action,
|
|
403
|
+
assigns: fullAssigns,
|
|
404
|
+
},
|
|
405
|
+
this.globalConfig?.helpers || {}
|
|
406
|
+
);
|
|
366
407
|
|
|
367
408
|
return routerCtx.html(rendered);
|
|
368
409
|
});
|
|
@@ -371,7 +412,9 @@ export default class HonoAdapter {
|
|
|
371
412
|
|
|
372
413
|
// Single method to handle all route types
|
|
373
414
|
routeCollection(mapping, resourceConfig) {
|
|
374
|
-
mapping.forEach(handlerMap =>
|
|
415
|
+
mapping.forEach((handlerMap) =>
|
|
416
|
+
this.createAction(handlerMap, resourceConfig)
|
|
417
|
+
);
|
|
375
418
|
}
|
|
376
419
|
|
|
377
420
|
// Use the same implementation for all route types
|
|
@@ -391,7 +434,9 @@ export default class HonoAdapter {
|
|
|
391
434
|
|
|
392
435
|
applyHeaders(headers, router) {
|
|
393
436
|
if (!headers) return;
|
|
394
|
-
Object.entries(headers).forEach(([key, value]) =>
|
|
437
|
+
Object.entries(headers).forEach(([key, value]) =>
|
|
438
|
+
router.header(key, value)
|
|
439
|
+
);
|
|
395
440
|
}
|
|
396
441
|
|
|
397
442
|
formatQuery(queries) {
|
package/src/cloud/Articles.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dateToUTCISO8601String, toDateInput } from "../utils";
|
|
2
2
|
import RaketaClient from "./RaketaClient";
|
|
3
3
|
import {
|
|
4
4
|
ArticleByIdDocument,
|
|
5
|
+
ArticleChangePreviewDocument,
|
|
5
6
|
ArticlesDocument,
|
|
6
7
|
ChangeArticleSlugDocument,
|
|
7
8
|
CloneArticleDocument,
|
|
8
9
|
CreateArticleDocument,
|
|
9
|
-
buildBatchTranslateArticleDocument,
|
|
10
10
|
DeleteArticleDocument,
|
|
11
11
|
MiniArticlesDocument,
|
|
12
12
|
TranslateArticleDocument,
|
|
13
|
+
buildBatchTranslateArticleDocument,
|
|
13
14
|
} from "./documents/articlesDocuments";
|
|
14
15
|
|
|
15
16
|
export default class Articles extends RaketaClient {
|
|
@@ -191,4 +192,21 @@ export default class Articles extends RaketaClient {
|
|
|
191
192
|
|
|
192
193
|
return { article, errors };
|
|
193
194
|
}
|
|
195
|
+
|
|
196
|
+
async getChangePreview(articleId, version, locale) {
|
|
197
|
+
const input = {
|
|
198
|
+
articleId,
|
|
199
|
+
version: Number.parseInt(version, 10),
|
|
200
|
+
locale,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const { articleChangePreview } = await this.query(
|
|
204
|
+
ArticleChangePreviewDocument,
|
|
205
|
+
{
|
|
206
|
+
input,
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return articleChangePreview;
|
|
211
|
+
}
|
|
194
212
|
}
|
package/src/cloud/Events.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
+
dateToUTCISO8601String,
|
|
3
|
+
toDateInput,
|
|
2
4
|
toDateTimeInput,
|
|
3
5
|
toUTCISO8601String,
|
|
4
|
-
toDateInput,
|
|
5
|
-
dateToUTCISO8601String,
|
|
6
6
|
} from "../utils";
|
|
7
7
|
import RaketaClient from "./RaketaClient";
|
|
8
8
|
import {
|
|
9
9
|
ChangeEventSlugDocument,
|
|
10
10
|
CloneEventDocument,
|
|
11
|
-
buildBatchTranslateEventDocument,
|
|
12
11
|
CreateEventDocument,
|
|
13
12
|
DeleteEventDocument,
|
|
14
13
|
EventByIdDocument,
|
|
14
|
+
EventChangePreviewDocument,
|
|
15
15
|
EventsDocument,
|
|
16
16
|
MiniEventsDocument,
|
|
17
17
|
TranslateEventDocument,
|
|
18
|
+
buildBatchTranslateEventDocument,
|
|
18
19
|
} from "./documents/eventsDocuments";
|
|
19
20
|
|
|
20
21
|
export default class Events extends RaketaClient {
|
|
@@ -206,4 +207,21 @@ export default class Events extends RaketaClient {
|
|
|
206
207
|
|
|
207
208
|
return { event, errors };
|
|
208
209
|
}
|
|
210
|
+
|
|
211
|
+
async getChangePreview(eventId, version, locale) {
|
|
212
|
+
const input = {
|
|
213
|
+
eventId,
|
|
214
|
+
version: Number.parseInt(version, 10),
|
|
215
|
+
locale,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const { eventChangePreview } = await this.query(
|
|
219
|
+
EventChangePreviewDocument,
|
|
220
|
+
{
|
|
221
|
+
input,
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
return eventChangePreview;
|
|
226
|
+
}
|
|
209
227
|
}
|
package/src/cloud/Pages.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
DeletePageDocument,
|
|
7
7
|
MiniPagesDocument,
|
|
8
8
|
PageByIdDocument,
|
|
9
|
+
PageChangePreviewDocument,
|
|
9
10
|
PagesDocument,
|
|
10
11
|
TranslatePageDocument,
|
|
11
12
|
buildBatchTranslatePageDocument,
|
|
@@ -182,4 +183,18 @@ export default class Pages extends RaketaClient {
|
|
|
182
183
|
|
|
183
184
|
return { page, errors };
|
|
184
185
|
}
|
|
186
|
+
|
|
187
|
+
async getChangePreview(pageId, version, locale) {
|
|
188
|
+
const input = {
|
|
189
|
+
pageId,
|
|
190
|
+
version: Number.parseInt(version, 10),
|
|
191
|
+
locale,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const { pageChangePreview } = await this.query(PageChangePreviewDocument, {
|
|
195
|
+
input,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return pageChangePreview;
|
|
199
|
+
}
|
|
185
200
|
}
|
|
@@ -315,6 +315,34 @@ const UnlockArticleRevisionDocument = gql`
|
|
|
315
315
|
}
|
|
316
316
|
`;
|
|
317
317
|
|
|
318
|
+
const ArticleChangePreviewDocument = gql`
|
|
319
|
+
query ($input: ArticleChangePreviewInput!) {
|
|
320
|
+
articleChangePreview(input: $input) {
|
|
321
|
+
article {
|
|
322
|
+
id
|
|
323
|
+
slug
|
|
324
|
+
title
|
|
325
|
+
widgets
|
|
326
|
+
settings
|
|
327
|
+
visibility
|
|
328
|
+
publishAt
|
|
329
|
+
translations
|
|
330
|
+
tags ${tagFragment()}
|
|
331
|
+
websiteSettings
|
|
332
|
+
}
|
|
333
|
+
articleChange {
|
|
334
|
+
version
|
|
335
|
+
title
|
|
336
|
+
description
|
|
337
|
+
operator
|
|
338
|
+
operatorMeta
|
|
339
|
+
source
|
|
340
|
+
insertedAt
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
`;
|
|
345
|
+
|
|
318
346
|
export {
|
|
319
347
|
ArticleByIdDocument,
|
|
320
348
|
ArticlesDocument,
|
|
@@ -340,4 +368,5 @@ export {
|
|
|
340
368
|
buildBatchTranslateArticleDocument,
|
|
341
369
|
buildBatchStartArticleRevisionDocument,
|
|
342
370
|
ArticleEditorsDocument,
|
|
371
|
+
ArticleChangePreviewDocument,
|
|
343
372
|
};
|
|
@@ -323,6 +323,41 @@ const UnlockEventRevisionDocument = gql`
|
|
|
323
323
|
}
|
|
324
324
|
`;
|
|
325
325
|
|
|
326
|
+
const EventChangePreviewDocument = gql`
|
|
327
|
+
query ($input: EventChangePreviewInput!) {
|
|
328
|
+
eventChangePreview(input: $input) {
|
|
329
|
+
event {
|
|
330
|
+
id
|
|
331
|
+
slug
|
|
332
|
+
title
|
|
333
|
+
widgets
|
|
334
|
+
settings
|
|
335
|
+
visibility
|
|
336
|
+
publishAt
|
|
337
|
+
startsAt
|
|
338
|
+
endsAt
|
|
339
|
+
eventType {
|
|
340
|
+
id
|
|
341
|
+
slug
|
|
342
|
+
title
|
|
343
|
+
}
|
|
344
|
+
translations
|
|
345
|
+
tags ${tagFragment()}
|
|
346
|
+
websiteSettings
|
|
347
|
+
}
|
|
348
|
+
eventChange {
|
|
349
|
+
version
|
|
350
|
+
title
|
|
351
|
+
description
|
|
352
|
+
operator
|
|
353
|
+
operatorMeta
|
|
354
|
+
source
|
|
355
|
+
insertedAt
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
`;
|
|
360
|
+
|
|
326
361
|
export {
|
|
327
362
|
EventByIdDocument,
|
|
328
363
|
EventsDocument,
|
|
@@ -348,4 +383,5 @@ export {
|
|
|
348
383
|
buildBatchTranslateEventDocument,
|
|
349
384
|
EventHistoryDocument,
|
|
350
385
|
EventEditorsDocument,
|
|
386
|
+
EventChangePreviewDocument,
|
|
351
387
|
};
|
|
@@ -311,6 +311,33 @@ const UnlockPageRevisionDocument = gql`
|
|
|
311
311
|
}
|
|
312
312
|
`;
|
|
313
313
|
|
|
314
|
+
const PageChangePreviewDocument = gql`
|
|
315
|
+
query ($input: PageChangePreviewInput!) {
|
|
316
|
+
pageChangePreview(input: $input) {
|
|
317
|
+
page {
|
|
318
|
+
id
|
|
319
|
+
slug
|
|
320
|
+
title
|
|
321
|
+
widgets
|
|
322
|
+
settings
|
|
323
|
+
visibility
|
|
324
|
+
translations
|
|
325
|
+
tags ${tagFragment()}
|
|
326
|
+
websiteSettings
|
|
327
|
+
}
|
|
328
|
+
pageChange {
|
|
329
|
+
version
|
|
330
|
+
title
|
|
331
|
+
description
|
|
332
|
+
operator
|
|
333
|
+
operatorMeta
|
|
334
|
+
source
|
|
335
|
+
insertedAt
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
`;
|
|
340
|
+
|
|
314
341
|
export {
|
|
315
342
|
PageByIdDocument,
|
|
316
343
|
PagesDocument,
|
|
@@ -336,4 +363,5 @@ export {
|
|
|
336
363
|
PageHistoryDocument,
|
|
337
364
|
buildBatchStartPageRevisionDocument,
|
|
338
365
|
PageEditorsDocument,
|
|
366
|
+
PageChangePreviewDocument,
|
|
339
367
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const show = async (ctx) => {
|
|
2
|
+
const articleId = ctx.params.parent_id;
|
|
3
|
+
const version = ctx.query.version;
|
|
4
|
+
|
|
5
|
+
const item = await ctx.client.articles.getChangePreview(
|
|
6
|
+
articleId,
|
|
7
|
+
version,
|
|
8
|
+
ctx.locale
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
if (!item) {
|
|
12
|
+
throw new Error(`Missing ${ctx.resourceConfig.articleType} #${articleId}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
json: {
|
|
17
|
+
...item,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default { show };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const show = async (ctx) => {
|
|
2
|
+
const eventId = ctx.params.parent_id;
|
|
3
|
+
const version = ctx.query.version;
|
|
4
|
+
|
|
5
|
+
const item = await ctx.client.events.getChangePreview(
|
|
6
|
+
eventId,
|
|
7
|
+
version,
|
|
8
|
+
ctx.locale
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
if (!item) {
|
|
12
|
+
throw new Error(`Missing ${ctx.resourceConfig.eventType} #${eventId}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
json: {
|
|
17
|
+
...item,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default { show };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const show = async (ctx) => {
|
|
2
|
+
const pageId = ctx.params.parent_id;
|
|
3
|
+
const version = ctx.query.version;
|
|
4
|
+
|
|
5
|
+
const item = await ctx.client.pages.getChangePreview(
|
|
6
|
+
pageId,
|
|
7
|
+
version,
|
|
8
|
+
ctx.locale
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
if (!item) {
|
|
12
|
+
throw new Error(`Missing ${ctx.resourceConfig.pageType} #${pageId}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
json: {
|
|
17
|
+
...item,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default { show };
|
|
@@ -2,7 +2,13 @@ const AuthHelper = {
|
|
|
2
2
|
cookie_key: "auth_cookie",
|
|
3
3
|
|
|
4
4
|
parseJWT: (token) => {
|
|
5
|
-
|
|
5
|
+
const tokenPayload = token?.split(".")?.[1];
|
|
6
|
+
|
|
7
|
+
if(!tokenPayload){
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return JSON.parse(Buffer.from(tokenPayload, "base64").toString());
|
|
6
12
|
},
|
|
7
13
|
|
|
8
14
|
verifyJWT: (token) => {
|
package/src/index.js
CHANGED
|
@@ -46,6 +46,7 @@ export { default as RedirectsHandler } from "./handlers/RedirectsHandler";
|
|
|
46
46
|
export { default as TagsHandler } from "./handlers/TagsHandler";
|
|
47
47
|
|
|
48
48
|
export { default as PageClonesHandler } from "./handlers/pages/ClonesHandler";
|
|
49
|
+
export { default as ChangeHandler } from "./handlers/pages/ChangeHandler";
|
|
49
50
|
export { default as PageDeletesHandler } from "./handlers/pages/DeletesHandler";
|
|
50
51
|
export { default as PageTranslationsHandler } from "./handlers/pages/TranslationsHandler";
|
|
51
52
|
export { default as PageURLsHandler } from "./handlers/pages/URLsHandler";
|
|
@@ -10,7 +10,9 @@ import TagHandler from "./handlers/article_revisions/TagHandler";
|
|
|
10
10
|
import UnlocksHandler from "./handlers/article_revisions/UnlocksHandler";
|
|
11
11
|
import VisualHandler from "./handlers/article_revisions/VisualHandler";
|
|
12
12
|
import WidgetHandler from "./handlers/article_revisions/WidgetHandler";
|
|
13
|
+
import ArticlesAPIHandler from "./handlers/articles/ArticlesAPIHandler";
|
|
13
14
|
import ArticlesHandler from "./handlers/articles/ArticlesHandler";
|
|
15
|
+
import ChangeHandler from "./handlers/articles/ChangeHandler";
|
|
14
16
|
import ClonesHandler from "./handlers/articles/ClonesHandler";
|
|
15
17
|
import DeletesHandler from "./handlers/articles/DeletesHandler";
|
|
16
18
|
import HistoryHandler from "./handlers/articles/HistoryHandler";
|
|
@@ -146,6 +148,15 @@ function routeArticlesModule(
|
|
|
146
148
|
WidgetHandler,
|
|
147
149
|
resourceConfig
|
|
148
150
|
);
|
|
151
|
+
|
|
152
|
+
routeCollection(adapter, "api/articles", ArticlesAPIHandler, resourceConfig);
|
|
153
|
+
routeNestedMember(
|
|
154
|
+
adapter,
|
|
155
|
+
"api/article",
|
|
156
|
+
"change",
|
|
157
|
+
ChangeHandler,
|
|
158
|
+
resourceConfig
|
|
159
|
+
);
|
|
149
160
|
}
|
|
150
161
|
|
|
151
162
|
export default routeArticlesModule;
|
package/src/routeEventsModule.js
CHANGED
|
@@ -10,8 +10,10 @@ import TagHandler from "./handlers/event_revisions/TagHandler";
|
|
|
10
10
|
import UnlocksHandler from "./handlers/event_revisions/UnlocksHandler";
|
|
11
11
|
import VisualHandler from "./handlers/event_revisions/VisualHandler";
|
|
12
12
|
import WidgetHandler from "./handlers/event_revisions/WidgetHandler";
|
|
13
|
+
import ChangeHandler from "./handlers/events/ChangeHandler";
|
|
13
14
|
import ClonesHandler from "./handlers/events/ClonesHandler";
|
|
14
15
|
import DeletesHandler from "./handlers/events/DeletesHandler";
|
|
16
|
+
import EventsAPIHandler from "./handlers/events/EventsAPIHandler";
|
|
15
17
|
import EventsHandler from "./handlers/events/EventsHandler";
|
|
16
18
|
import HistoryHandler from "./handlers/events/HistoryHandler";
|
|
17
19
|
import TranslationsHandler from "./handlers/events/TranslationsHandler";
|
|
@@ -161,6 +163,15 @@ function routeEventsModule(
|
|
|
161
163
|
WidgetHandler,
|
|
162
164
|
resourceConfig
|
|
163
165
|
);
|
|
166
|
+
|
|
167
|
+
routeCollection(adapter, "api/events", EventsAPIHandler, resourceConfig);
|
|
168
|
+
routeNestedMember(
|
|
169
|
+
adapter,
|
|
170
|
+
"api/event",
|
|
171
|
+
"change",
|
|
172
|
+
ChangeHandler,
|
|
173
|
+
resourceConfig
|
|
174
|
+
);
|
|
164
175
|
}
|
|
165
176
|
|
|
166
177
|
export default routeEventsModule;
|
package/src/routePagesModule.js
CHANGED
|
@@ -10,9 +10,11 @@ import TagHandler from "./handlers/page_revisions/TagHandler";
|
|
|
10
10
|
import UnlocksHandler from "./handlers/page_revisions/UnlocksHandler";
|
|
11
11
|
import VisualHandler from "./handlers/page_revisions/VisualHandler";
|
|
12
12
|
import WidgetHandler from "./handlers/page_revisions/WidgetHandler";
|
|
13
|
+
import ChangeHandler from "./handlers/pages/ChangeHandler";
|
|
13
14
|
import ClonesHandler from "./handlers/pages/ClonesHandler";
|
|
14
15
|
import DeletesHandler from "./handlers/pages/DeletesHandler";
|
|
15
16
|
import HistoryHandler from "./handlers/pages/HistoryHandler";
|
|
17
|
+
import PagesAPIHandler from "./handlers/pages/PagesAPIHandler";
|
|
16
18
|
import PagesHandler from "./handlers/pages/PagesHandler";
|
|
17
19
|
import TranslationsHandler from "./handlers/pages/TranslationsHandler";
|
|
18
20
|
import URLsHandler from "./handlers/pages/URLsHandler";
|
|
@@ -158,6 +160,15 @@ function routePagesModule(
|
|
|
158
160
|
WidgetHandler,
|
|
159
161
|
resourceConfig
|
|
160
162
|
);
|
|
163
|
+
|
|
164
|
+
routeCollection(adapter, "api/pages", PagesAPIHandler, resourceConfig);
|
|
165
|
+
routeNestedMember(
|
|
166
|
+
adapter,
|
|
167
|
+
"api/page",
|
|
168
|
+
"change",
|
|
169
|
+
ChangeHandler,
|
|
170
|
+
resourceConfig
|
|
171
|
+
);
|
|
161
172
|
}
|
|
162
173
|
|
|
163
174
|
export default routePagesModule;
|
package/src/routeStandard.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import routeSettings from "./routeSettings";
|
|
3
|
-
import routePagesModule from "./routePagesModule";
|
|
4
|
-
import routeArticlesModule from "./routeArticlesModule";
|
|
5
|
-
import routeEventsModule from "./routeEventsModule";
|
|
1
|
+
import DashboardsHandler from "./handlers/DashboardsHandler";
|
|
6
2
|
import RedirectsHandler from "./handlers/RedirectsHandler";
|
|
3
|
+
import TagsAPIHandler from "./handlers/TagsAPIHandler";
|
|
7
4
|
import TagsHandler from "./handlers/TagsHandler";
|
|
8
|
-
import
|
|
5
|
+
import routeArticlesModule from "./routeArticlesModule";
|
|
6
|
+
import routeCollection from "./routeCollection";
|
|
7
|
+
import routeEventsModule from "./routeEventsModule";
|
|
8
|
+
import routePagesModule from "./routePagesModule";
|
|
9
|
+
import routeSettings from "./routeSettings";
|
|
9
10
|
|
|
10
11
|
function routeStandard(adapter) {
|
|
11
12
|
adapter.routeCollection(
|
|
@@ -26,6 +27,7 @@ function routeStandard(adapter) {
|
|
|
26
27
|
|
|
27
28
|
routeCollection(adapter, "redirects", RedirectsHandler);
|
|
28
29
|
routeCollection(adapter, "tags", TagsHandler);
|
|
30
|
+
routeCollection(adapter, "api/tags", TagsAPIHandler);
|
|
29
31
|
routeSettings(adapter);
|
|
30
32
|
routePagesModule(adapter);
|
|
31
33
|
routeArticlesModule(adapter);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import HonoAdapter from "../HonoAdapter";
|
|
2
|
+
import RaketaContext from "../RaketaContext";
|
|
2
3
|
|
|
3
4
|
class HonoMock {
|
|
4
5
|
constructor() {
|
|
5
6
|
this.router = {
|
|
6
|
-
routes: []
|
|
7
|
+
routes: [],
|
|
7
8
|
};
|
|
8
9
|
this.get = jest.fn();
|
|
9
10
|
this.post = jest.fn();
|
|
@@ -11,18 +12,19 @@ class HonoMock {
|
|
|
11
12
|
this.delete = jest.fn();
|
|
12
13
|
this.use = jest.fn();
|
|
13
14
|
this.header = jest.fn();
|
|
15
|
+
// this.routes = [];
|
|
14
16
|
}
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
describe("HonoAdapter", () => {
|
|
18
20
|
let app;
|
|
19
21
|
let adapter;
|
|
20
|
-
|
|
22
|
+
|
|
21
23
|
beforeEach(() => {
|
|
22
24
|
app = new HonoMock();
|
|
23
25
|
adapter = new HonoAdapter(app);
|
|
24
26
|
});
|
|
25
|
-
|
|
27
|
+
|
|
26
28
|
// Method tests
|
|
27
29
|
test("getMethod returns Hono's get for invalid verbs", () => {
|
|
28
30
|
expect(adapter.getMethod("not existing")).toBe(app.get);
|
|
@@ -78,58 +80,126 @@ describe("HonoAdapter", () => {
|
|
|
78
80
|
},
|
|
79
81
|
});
|
|
80
82
|
});
|
|
81
|
-
|
|
83
|
+
|
|
82
84
|
test("setGlobalContext sets the global context function", () => {
|
|
83
85
|
const newCtx = () => ({ test: true });
|
|
84
86
|
adapter.setGlobalContext(newCtx);
|
|
85
87
|
expect(adapter.globalCtx).toBe(newCtx);
|
|
86
88
|
});
|
|
87
|
-
|
|
89
|
+
|
|
88
90
|
test("applyHeaders applies headers to router context", () => {
|
|
89
91
|
const headers = {
|
|
90
92
|
"Content-Type": "application/json",
|
|
91
|
-
"X-Custom-Header": "test-value"
|
|
93
|
+
"X-Custom-Header": "test-value",
|
|
92
94
|
};
|
|
93
|
-
|
|
95
|
+
|
|
94
96
|
adapter.applyHeaders(headers, app);
|
|
95
|
-
|
|
97
|
+
|
|
96
98
|
expect(app.header).toHaveBeenCalledTimes(2);
|
|
97
99
|
expect(app.header).toHaveBeenCalledWith("Content-Type", "application/json");
|
|
98
100
|
expect(app.header).toHaveBeenCalledWith("X-Custom-Header", "test-value");
|
|
99
101
|
});
|
|
100
|
-
|
|
102
|
+
|
|
101
103
|
test("applyHeaders does nothing when headers is undefined", () => {
|
|
102
104
|
adapter.applyHeaders(undefined, app);
|
|
103
105
|
expect(app.header).not.toHaveBeenCalled();
|
|
104
106
|
});
|
|
105
|
-
|
|
107
|
+
|
|
106
108
|
test("formatQuery formats query parameters correctly", () => {
|
|
107
109
|
const queries = {
|
|
108
110
|
single: ["value"],
|
|
109
|
-
multiple: ["one", "two"]
|
|
111
|
+
multiple: ["one", "two"],
|
|
110
112
|
};
|
|
111
|
-
|
|
113
|
+
|
|
112
114
|
const result = adapter.formatQuery(queries);
|
|
113
|
-
|
|
115
|
+
|
|
114
116
|
expect(result).toEqual({
|
|
115
117
|
single: "value",
|
|
116
|
-
multiple: ["one", "two"]
|
|
118
|
+
multiple: ["one", "two"],
|
|
117
119
|
});
|
|
118
120
|
});
|
|
119
121
|
|
|
120
122
|
test("parseFormBody handles array values", () => {
|
|
121
123
|
expect(
|
|
122
124
|
adapter.parseFormBody({
|
|
123
|
-
"tags[]": ["javascript", "react", "node"]
|
|
125
|
+
"tags[]": ["javascript", "react", "node"],
|
|
124
126
|
})
|
|
125
127
|
).toEqual({
|
|
126
|
-
tags: ["javascript", "react", "node"]
|
|
128
|
+
tags: ["javascript", "react", "node"],
|
|
127
129
|
});
|
|
128
130
|
});
|
|
129
131
|
|
|
130
132
|
test("parseFormBody handles empty values", () => {
|
|
131
|
-
expect(
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
expect(adapter.parseFormBody({})).toEqual({});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("renderLayout renders an existing layout template", async () => {
|
|
137
|
+
const result = await adapter.renderLayout(
|
|
138
|
+
{
|
|
139
|
+
_context: {
|
|
140
|
+
layout: {
|
|
141
|
+
sidebar: [],
|
|
142
|
+
},
|
|
143
|
+
path: "/en/pages",
|
|
144
|
+
locale: "en",
|
|
145
|
+
locales: {
|
|
146
|
+
en: {
|
|
147
|
+
flag: "",
|
|
148
|
+
name: "English",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
globalConfig: {
|
|
152
|
+
cloud: {
|
|
153
|
+
images_api_key: "api-key",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
"<h1>Hi</h1>"
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
expect(typeof result).toBe("string");
|
|
162
|
+
expect(result.length).toBeGreaterThan(0);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("addRouote - adds a route to the local router", () => {
|
|
166
|
+
expect(adapter.localRouter).toEqual([]);
|
|
167
|
+
|
|
168
|
+
const handler = () => {};
|
|
169
|
+
|
|
170
|
+
adapter.addRoute("GET", "/en/pages", handler);
|
|
171
|
+
|
|
172
|
+
expect(adapter.localRouter).toEqual([
|
|
173
|
+
{ verb: "GET", path: "/en/pages", handler },
|
|
174
|
+
]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("addRouote - overrides routes if they already exist in the local router", () => {
|
|
178
|
+
expect(adapter.localRouter).toEqual([]);
|
|
179
|
+
|
|
180
|
+
const handler = () => {};
|
|
181
|
+
const handler2 = () => {};
|
|
182
|
+
|
|
183
|
+
adapter.addRoute("GET", "/en/pages", handler);
|
|
184
|
+
|
|
185
|
+
expect(adapter.localRouter).toEqual([
|
|
186
|
+
{ verb: "GET", path: "/en/pages", handler },
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
adapter.addRoute("GET", "/en/pages", handler2);
|
|
190
|
+
|
|
191
|
+
expect(adapter.localRouter).toEqual([
|
|
192
|
+
{ verb: "GET", path: "/en/pages", handler: handler2 },
|
|
193
|
+
]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("link links local routes with the app", () => {
|
|
197
|
+
const handler = () => {};
|
|
198
|
+
adapter.addRoute("GET", "/en/pages", handler);
|
|
199
|
+
|
|
200
|
+
adapter.link();
|
|
201
|
+
|
|
202
|
+
expect(app.get).toHaveBeenCalledTimes(1);
|
|
203
|
+
expect(app.get).toHaveBeenCalledWith("/en/pages", handler);
|
|
134
204
|
});
|
|
135
|
-
});
|
|
205
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Tags from "../../../cloud/Tags";
|
|
2
|
+
import TagsAPIHandler from "../../../handlers/TagsAPIHandler";
|
|
3
|
+
|
|
4
|
+
import Factory from "../../support/factory";
|
|
5
|
+
|
|
6
|
+
describe("Pages API Handler", () => {
|
|
7
|
+
function buildCtx() {
|
|
8
|
+
return {
|
|
9
|
+
client: {
|
|
10
|
+
tags: new Tags(() => ({}), "token"),
|
|
11
|
+
},
|
|
12
|
+
query: { foo: "bar" },
|
|
13
|
+
locale: "en",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test("index", async () => {
|
|
18
|
+
const tags = [
|
|
19
|
+
Factory.build("tag", { id: "1", name: "First Tag" }),
|
|
20
|
+
Factory.build("tag", { id: "2", name: "Second Tag" }),
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const ctx = buildCtx();
|
|
24
|
+
|
|
25
|
+
const mockedList = jest
|
|
26
|
+
.spyOn(ctx.client.tags, "list")
|
|
27
|
+
.mockImplementation(() => ({ tags: tags }));
|
|
28
|
+
|
|
29
|
+
const result = await TagsAPIHandler.index(ctx);
|
|
30
|
+
|
|
31
|
+
expect(mockedList).toHaveBeenCalledWith({ foo: "bar" }, "en");
|
|
32
|
+
expect(result).toEqual({
|
|
33
|
+
json: {
|
|
34
|
+
tags: tags,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Articles from "../../../../cloud/Articles";
|
|
2
|
+
import ArticlesAPIHandler from "../../../../handlers/articles/ArticlesAPIHandler";
|
|
3
|
+
|
|
4
|
+
import Factory from "../../../support/factory";
|
|
5
|
+
|
|
6
|
+
describe("Articles API Handler", () => {
|
|
7
|
+
function buildCtx() {
|
|
8
|
+
return {
|
|
9
|
+
client: {
|
|
10
|
+
articles: new Articles(() => ({}), "token"),
|
|
11
|
+
},
|
|
12
|
+
query: { foo: "bar" },
|
|
13
|
+
locale: "en",
|
|
14
|
+
resourceConfig: {
|
|
15
|
+
articleType: "article",
|
|
16
|
+
articleTypePlural: "articles",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("index", async () => {
|
|
22
|
+
const article = Factory.build("article");
|
|
23
|
+
const ctx = buildCtx();
|
|
24
|
+
|
|
25
|
+
const mockedArticleList = jest
|
|
26
|
+
.spyOn(ctx.client.articles, "listAll")
|
|
27
|
+
.mockImplementation(() => ({ list: [article] }));
|
|
28
|
+
|
|
29
|
+
const result = await ArticlesAPIHandler.index(ctx);
|
|
30
|
+
|
|
31
|
+
expect(mockedArticleList).toHaveBeenCalledWith(
|
|
32
|
+
{ foo: "bar" },
|
|
33
|
+
"en",
|
|
34
|
+
"article"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(result).toEqual({
|
|
38
|
+
json: {
|
|
39
|
+
list: [article],
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import ArticleChanges from "../../../../cloud/ArticleChanges";
|
|
2
|
+
import ArticleRevisions from "../../../../cloud/ArticleRevisions";
|
|
3
|
+
import Articles from "../../../../cloud/Articles";
|
|
4
|
+
import ChangeHandler from "../../../../handlers/articles/ChangeHandler";
|
|
5
|
+
|
|
6
|
+
import Factory from "../../../support/factory";
|
|
7
|
+
|
|
8
|
+
describe("ChangeHandler", () => {
|
|
9
|
+
function buildCtx(article) {
|
|
10
|
+
return {
|
|
11
|
+
client: {
|
|
12
|
+
articles: new Articles(() => ({}), "token"),
|
|
13
|
+
articleChanges: new ArticleChanges(() => ({}), "token"),
|
|
14
|
+
articleRevisions: new ArticleRevisions(() => ({}), "token"),
|
|
15
|
+
},
|
|
16
|
+
params: { parent_id: article.id },
|
|
17
|
+
query: { version: 49 },
|
|
18
|
+
locale: "en",
|
|
19
|
+
resourceConfig: {
|
|
20
|
+
articleType: "article",
|
|
21
|
+
articleTypePlural: "articles",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test("show for existing article", async () => {
|
|
27
|
+
const article = Factory.build("article");
|
|
28
|
+
|
|
29
|
+
const ctx = buildCtx(article);
|
|
30
|
+
|
|
31
|
+
const mockedArticleFind = jest
|
|
32
|
+
.spyOn(ctx.client.articles, "getChangePreview")
|
|
33
|
+
.mockImplementation(() => [article]);
|
|
34
|
+
|
|
35
|
+
const result = await ChangeHandler.show(ctx);
|
|
36
|
+
|
|
37
|
+
expect(mockedArticleFind).toHaveBeenCalledWith(article.id, 49, "en");
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual({
|
|
40
|
+
json: {
|
|
41
|
+
...[article],
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("show for non existing article", async () => {
|
|
47
|
+
const ctx = buildCtx({ id: "42" });
|
|
48
|
+
|
|
49
|
+
const mockedArticleFind = jest
|
|
50
|
+
.spyOn(ctx.client.articles, "getChangePreview")
|
|
51
|
+
.mockImplementation(() => null);
|
|
52
|
+
|
|
53
|
+
expect(async () => {
|
|
54
|
+
await ChangeHandler.show(ctx);
|
|
55
|
+
}).rejects.toThrow("Missing article #42");
|
|
56
|
+
|
|
57
|
+
expect(mockedArticleFind).toHaveBeenCalledWith("42", 49, "en");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import EventChanges from "../../../../cloud/EventChanges";
|
|
2
|
+
import EventRevisions from "../../../../cloud/EventRevisions";
|
|
3
|
+
import Events from "../../../../cloud/Events";
|
|
4
|
+
import ChangeHandler from "../../../../handlers/events/ChangeHandler";
|
|
5
|
+
|
|
6
|
+
import Factory from "../../../support/factory";
|
|
7
|
+
|
|
8
|
+
describe("ChangeHandler", () => {
|
|
9
|
+
function buildCtx(event) {
|
|
10
|
+
return {
|
|
11
|
+
client: {
|
|
12
|
+
events: new Events(() => ({}), "token"),
|
|
13
|
+
eventChanges: new EventChanges(() => ({}), "token"),
|
|
14
|
+
eventRevisions: new EventRevisions(() => ({}), "token"),
|
|
15
|
+
},
|
|
16
|
+
params: { parent_id: event.id },
|
|
17
|
+
query: { version: 49 },
|
|
18
|
+
locale: "en",
|
|
19
|
+
resourceConfig: {
|
|
20
|
+
eventType: "event",
|
|
21
|
+
eventTypePlural: "events",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test("show for existing event", async () => {
|
|
27
|
+
const event = Factory.build("event");
|
|
28
|
+
|
|
29
|
+
const ctx = buildCtx(event);
|
|
30
|
+
|
|
31
|
+
const mockedEventFind = jest
|
|
32
|
+
.spyOn(ctx.client.events, "getChangePreview")
|
|
33
|
+
.mockImplementation(() => [event]);
|
|
34
|
+
|
|
35
|
+
const result = await ChangeHandler.show(ctx);
|
|
36
|
+
|
|
37
|
+
expect(mockedEventFind).toHaveBeenCalledWith(event.id, 49, "en");
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual({
|
|
40
|
+
json: {
|
|
41
|
+
...[event],
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("show for non existing event", async () => {
|
|
47
|
+
const ctx = buildCtx({ id: "42" });
|
|
48
|
+
|
|
49
|
+
const mockedEventFind = jest
|
|
50
|
+
.spyOn(ctx.client.events, "getChangePreview")
|
|
51
|
+
.mockImplementation(() => null);
|
|
52
|
+
|
|
53
|
+
expect(async () => {
|
|
54
|
+
await ChangeHandler.show(ctx);
|
|
55
|
+
}).rejects.toThrow("Missing event #42");
|
|
56
|
+
|
|
57
|
+
expect(mockedEventFind).toHaveBeenCalledWith("42", 49, "en");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Events from "../../../../cloud/Events";
|
|
2
|
+
import EventsAPIHandler from "../../../../handlers/events/EventsAPIHandler";
|
|
3
|
+
|
|
4
|
+
import Factory from "../../../support/factory";
|
|
5
|
+
|
|
6
|
+
describe("Events API Handler", () => {
|
|
7
|
+
function buildCtx() {
|
|
8
|
+
return {
|
|
9
|
+
client: {
|
|
10
|
+
events: new Events(() => ({}), "token"),
|
|
11
|
+
},
|
|
12
|
+
query: { foo: "bar" },
|
|
13
|
+
locale: "en",
|
|
14
|
+
resourceConfig: {
|
|
15
|
+
eventType: "event",
|
|
16
|
+
eventTypePlural: "events",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("index", async () => {
|
|
22
|
+
const event = Factory.build("event");
|
|
23
|
+
const ctx = buildCtx();
|
|
24
|
+
|
|
25
|
+
const mockedEventList = jest
|
|
26
|
+
.spyOn(ctx.client.events, "listAll")
|
|
27
|
+
.mockImplementation(() => ({ list: [event] }));
|
|
28
|
+
|
|
29
|
+
const result = await EventsAPIHandler.index(ctx);
|
|
30
|
+
|
|
31
|
+
expect(mockedEventList).toHaveBeenCalledWith({ foo: "bar" }, "en", "event");
|
|
32
|
+
|
|
33
|
+
expect(result).toEqual({
|
|
34
|
+
json: {
|
|
35
|
+
list: [event],
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import PageChanges from "../../../../cloud/PageChanges";
|
|
2
|
+
import PageRevisions from "../../../../cloud/PageRevisions";
|
|
3
|
+
import Pages from "../../../../cloud/Pages";
|
|
4
|
+
import ChangeHandler from "../../../../handlers/pages/ChangeHandler";
|
|
5
|
+
|
|
6
|
+
import Factory from "../../../support/factory";
|
|
7
|
+
|
|
8
|
+
describe("ChangeHandler", () => {
|
|
9
|
+
function buildCtx(page) {
|
|
10
|
+
return {
|
|
11
|
+
client: {
|
|
12
|
+
pages: new Pages(() => ({}), "token"),
|
|
13
|
+
pageChanges: new PageChanges(() => ({}), "token"),
|
|
14
|
+
pageRevisions: new PageRevisions(() => ({}), "token"),
|
|
15
|
+
},
|
|
16
|
+
params: { parent_id: page.id },
|
|
17
|
+
query: { version: 49 },
|
|
18
|
+
locale: "en",
|
|
19
|
+
resourceConfig: {
|
|
20
|
+
pageType: "page",
|
|
21
|
+
pageTypePlural: "pages",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test("show for existing page", async () => {
|
|
27
|
+
const page = Factory.build("page");
|
|
28
|
+
|
|
29
|
+
const ctx = buildCtx(page);
|
|
30
|
+
|
|
31
|
+
const mockedPageFind = jest
|
|
32
|
+
.spyOn(ctx.client.pages, "getChangePreview")
|
|
33
|
+
.mockImplementation(() => [page]);
|
|
34
|
+
|
|
35
|
+
const result = await ChangeHandler.show(ctx);
|
|
36
|
+
|
|
37
|
+
expect(mockedPageFind).toHaveBeenCalledWith(page.id, 49, "en");
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual({
|
|
40
|
+
json: {
|
|
41
|
+
...[page],
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("show for non existing page", async () => {
|
|
47
|
+
const ctx = buildCtx({ id: "42" });
|
|
48
|
+
|
|
49
|
+
const mockedPageFind = jest
|
|
50
|
+
.spyOn(ctx.client.pages, "getChangePreview")
|
|
51
|
+
.mockImplementation(() => null);
|
|
52
|
+
|
|
53
|
+
expect(async () => {
|
|
54
|
+
await ChangeHandler.show(ctx);
|
|
55
|
+
}).rejects.toThrow("Missing page #42");
|
|
56
|
+
|
|
57
|
+
expect(mockedPageFind).toHaveBeenCalledWith("42", 49, "en");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Pages from "../../../../cloud/Pages";
|
|
2
|
+
import PagesAPIHandler from "../../../../handlers/pages/PagesAPIHandler";
|
|
3
|
+
|
|
4
|
+
import Factory from "../../../support/factory";
|
|
5
|
+
|
|
6
|
+
describe("Pages API Handler", () => {
|
|
7
|
+
function buildCtx() {
|
|
8
|
+
return {
|
|
9
|
+
client: {
|
|
10
|
+
pages: new Pages(() => ({}), "token"),
|
|
11
|
+
},
|
|
12
|
+
query: { foo: "bar" },
|
|
13
|
+
locale: "en",
|
|
14
|
+
resourceConfig: {
|
|
15
|
+
pageType: "page",
|
|
16
|
+
pageTypePlural: "pages",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("index", async () => {
|
|
22
|
+
const page = Factory.build("page");
|
|
23
|
+
const ctx = buildCtx();
|
|
24
|
+
|
|
25
|
+
const mockedPageList = jest
|
|
26
|
+
.spyOn(ctx.client.pages, "listAll")
|
|
27
|
+
.mockImplementation(() => ({ list: [page] }));
|
|
28
|
+
|
|
29
|
+
const result = await PagesAPIHandler.index(ctx);
|
|
30
|
+
|
|
31
|
+
expect(mockedPageList).toHaveBeenCalledWith({ foo: "bar" }, "en", "page");
|
|
32
|
+
|
|
33
|
+
expect(result).toEqual({
|
|
34
|
+
json: {
|
|
35
|
+
list: [page],
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -49,6 +49,18 @@ describe("AuthHelper", () => {
|
|
|
49
49
|
const result = AuthHelper.verifyJWT(mockToken);
|
|
50
50
|
expect(result).toBe(false);
|
|
51
51
|
});
|
|
52
|
+
|
|
53
|
+
test("returns false for invalid token", () => {
|
|
54
|
+
const invalidToken = "header";
|
|
55
|
+
const invalidToken2 = "header.";
|
|
56
|
+
|
|
57
|
+
const result = AuthHelper.verifyJWT(invalidToken);
|
|
58
|
+
expect(result).toBe(false);
|
|
59
|
+
|
|
60
|
+
const result2 = AuthHelper.verifyJWT(invalidToken2);
|
|
61
|
+
expect(result2).toBe(false);
|
|
62
|
+
})
|
|
63
|
+
|
|
52
64
|
});
|
|
53
65
|
|
|
54
66
|
describe("isAuthenticated", () => {
|