@shopware-ag/app-server-sdk 1.0.0 → 1.1.0
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/LICENSE +21 -0
- package/README.md +23 -4
- package/dist/commonjs/context-resolver.test.d.ts +2 -0
- package/dist/commonjs/context-resolver.test.d.ts.map +1 -0
- package/dist/commonjs/context-resolver.test.js +81 -0
- package/dist/commonjs/context-resolver.test.js.map +1 -0
- package/dist/commonjs/helper/app-actions.test.d.ts +2 -0
- package/dist/commonjs/helper/app-actions.test.d.ts.map +1 -0
- package/dist/commonjs/helper/app-actions.test.js +31 -0
- package/dist/commonjs/helper/app-actions.test.js.map +1 -0
- package/dist/commonjs/http-client.test.d.ts +2 -0
- package/dist/commonjs/http-client.test.d.ts.map +1 -0
- package/dist/commonjs/http-client.test.js +71 -0
- package/dist/commonjs/http-client.test.js.map +1 -0
- package/dist/commonjs/integration/bun-sqlite.d.ts +11 -0
- package/dist/commonjs/integration/bun-sqlite.d.ts.map +1 -0
- package/dist/commonjs/integration/bun-sqlite.js +60 -0
- package/dist/commonjs/integration/bun-sqlite.js.map +1 -0
- package/dist/commonjs/integration/bun-sqlite.test.d.ts +2 -0
- package/dist/commonjs/integration/bun-sqlite.test.d.ts.map +1 -0
- package/dist/commonjs/integration/bun-sqlite.test.js +24 -0
- package/dist/commonjs/integration/bun-sqlite.test.js.map +1 -0
- package/dist/{deno/service/cloudflare.d.ts → commonjs/integration/cloudflare-kv.d.ts} +1 -24
- package/dist/commonjs/integration/cloudflare-kv.d.ts.map +1 -0
- package/dist/commonjs/{service/cloudflare.js → integration/cloudflare-kv.js} +5 -1
- package/dist/commonjs/integration/cloudflare-kv.js.map +1 -0
- package/dist/commonjs/integration/cloudflare.test.d.ts +2 -0
- package/dist/commonjs/integration/cloudflare.test.d.ts.map +1 -0
- package/dist/commonjs/integration/cloudflare.test.js +39 -0
- package/dist/commonjs/integration/cloudflare.test.js.map +1 -0
- package/dist/{esm/service/deno.d.ts → commonjs/integration/deno-kv.d.ts} +1 -1
- package/dist/commonjs/integration/deno-kv.d.ts.map +1 -0
- package/dist/commonjs/{service/deno.js → integration/deno-kv.js} +5 -1
- package/dist/commonjs/integration/deno-kv.js.map +1 -0
- package/dist/commonjs/integration/deno.test.d.ts +2 -0
- package/dist/commonjs/integration/deno.test.d.ts.map +1 -0
- package/dist/commonjs/integration/deno.test.js +48 -0
- package/dist/commonjs/integration/deno.test.js.map +1 -0
- package/dist/commonjs/integration/dynamodb.d.ts +13 -0
- package/dist/commonjs/integration/dynamodb.d.ts.map +1 -0
- package/dist/commonjs/integration/dynamodb.js +73 -0
- package/dist/commonjs/integration/dynamodb.js.map +1 -0
- package/dist/commonjs/integration/dynamodb.test.d.ts +2 -0
- package/dist/commonjs/integration/dynamodb.test.d.ts.map +1 -0
- package/dist/commonjs/integration/dynamodb.test.js +132 -0
- package/dist/commonjs/integration/dynamodb.test.js.map +1 -0
- package/dist/commonjs/integration/hono.d.ts +29 -0
- package/dist/commonjs/integration/hono.d.ts.map +1 -0
- package/dist/commonjs/{framework → integration}/hono.js +25 -4
- package/dist/commonjs/integration/hono.js.map +1 -0
- package/dist/commonjs/integration/hono.test.d.ts +2 -0
- package/dist/commonjs/integration/hono.test.d.ts.map +1 -0
- package/dist/commonjs/integration/hono.test.js +86 -0
- package/dist/commonjs/integration/hono.test.js.map +1 -0
- package/dist/commonjs/registration.d.ts +24 -0
- package/dist/commonjs/registration.d.ts.map +1 -1
- package/dist/commonjs/registration.js +42 -3
- package/dist/commonjs/registration.js.map +1 -1
- package/dist/commonjs/registration.test.d.ts +2 -0
- package/dist/commonjs/registration.test.d.ts.map +1 -0
- package/dist/commonjs/registration.test.js +59 -0
- package/dist/commonjs/registration.test.js.map +1 -0
- package/dist/commonjs/repository.d.ts +5 -1
- package/dist/commonjs/repository.d.ts.map +1 -1
- package/dist/commonjs/repository.js +7 -1
- package/dist/commonjs/repository.js.map +1 -1
- package/dist/commonjs/repository.test.d.ts +2 -0
- package/dist/commonjs/repository.test.d.ts.map +1 -0
- package/dist/commonjs/repository.test.js +32 -0
- package/dist/commonjs/repository.test.js.map +1 -0
- package/dist/commonjs/signer.test.d.ts +2 -0
- package/dist/commonjs/signer.test.d.ts.map +1 -0
- package/dist/commonjs/signer.test.js +25 -0
- package/dist/commonjs/signer.test.js.map +1 -0
- package/dist/esm/context-resolver.test.d.ts +2 -0
- package/dist/esm/context-resolver.test.d.ts.map +1 -0
- package/dist/esm/context-resolver.test.js +79 -0
- package/dist/esm/context-resolver.test.js.map +1 -0
- package/dist/esm/helper/app-actions.test.d.ts +2 -0
- package/dist/esm/helper/app-actions.test.d.ts.map +1 -0
- package/dist/esm/helper/app-actions.test.js +29 -0
- package/dist/esm/helper/app-actions.test.js.map +1 -0
- package/dist/esm/http-client.test.d.ts +2 -0
- package/dist/esm/http-client.test.d.ts.map +1 -0
- package/dist/esm/http-client.test.js +69 -0
- package/dist/esm/http-client.test.js.map +1 -0
- package/dist/esm/integration/bun-sqlite.d.ts +11 -0
- package/dist/esm/integration/bun-sqlite.d.ts.map +1 -0
- package/dist/esm/integration/bun-sqlite.js +56 -0
- package/dist/esm/integration/bun-sqlite.js.map +1 -0
- package/dist/esm/integration/bun-sqlite.test.d.ts +2 -0
- package/dist/esm/integration/bun-sqlite.test.d.ts.map +1 -0
- package/dist/esm/integration/bun-sqlite.test.js +22 -0
- package/dist/esm/integration/bun-sqlite.test.js.map +1 -0
- package/dist/{commonjs/service/cloudflare.d.ts → esm/integration/cloudflare-kv.d.ts} +1 -24
- package/dist/esm/integration/cloudflare-kv.d.ts.map +1 -0
- package/dist/esm/{service/cloudflare.js → integration/cloudflare-kv.js} +5 -1
- package/dist/esm/integration/cloudflare-kv.js.map +1 -0
- package/dist/esm/integration/cloudflare.test.d.ts +2 -0
- package/dist/esm/integration/cloudflare.test.d.ts.map +1 -0
- package/dist/esm/integration/cloudflare.test.js +37 -0
- package/dist/esm/integration/cloudflare.test.js.map +1 -0
- package/dist/{commonjs/service/deno.d.ts → esm/integration/deno-kv.d.ts} +1 -1
- package/dist/esm/integration/deno-kv.d.ts.map +1 -0
- package/dist/{deno/service/deno.js → esm/integration/deno-kv.js} +5 -1
- package/dist/esm/integration/deno-kv.js.map +1 -0
- package/dist/esm/integration/deno.test.d.ts +2 -0
- package/dist/esm/integration/deno.test.d.ts.map +1 -0
- package/dist/esm/integration/deno.test.js +46 -0
- package/dist/esm/integration/deno.test.js.map +1 -0
- package/dist/esm/integration/dynamodb.d.ts +13 -0
- package/dist/esm/integration/dynamodb.d.ts.map +1 -0
- package/dist/esm/integration/dynamodb.js +69 -0
- package/dist/esm/integration/dynamodb.js.map +1 -0
- package/dist/esm/integration/dynamodb.test.d.ts +2 -0
- package/dist/esm/integration/dynamodb.test.d.ts.map +1 -0
- package/dist/esm/integration/dynamodb.test.js +130 -0
- package/dist/esm/integration/dynamodb.test.js.map +1 -0
- package/dist/esm/integration/hono.d.ts +29 -0
- package/dist/esm/integration/hono.d.ts.map +1 -0
- package/dist/{deno/framework → esm/integration}/hono.js +25 -4
- package/dist/esm/integration/hono.js.map +1 -0
- package/dist/esm/integration/hono.test.d.ts +2 -0
- package/dist/esm/integration/hono.test.d.ts.map +1 -0
- package/dist/esm/integration/hono.test.js +84 -0
- package/dist/esm/integration/hono.test.js.map +1 -0
- package/dist/esm/registration.d.ts +24 -0
- package/dist/esm/registration.d.ts.map +1 -1
- package/dist/esm/registration.js +42 -3
- package/dist/esm/registration.js.map +1 -1
- package/dist/esm/registration.test.d.ts +2 -0
- package/dist/esm/registration.test.d.ts.map +1 -0
- package/dist/esm/registration.test.js +57 -0
- package/dist/esm/registration.test.js.map +1 -0
- package/dist/esm/repository.d.ts +5 -1
- package/dist/esm/repository.d.ts.map +1 -1
- package/dist/esm/repository.js +7 -1
- package/dist/esm/repository.js.map +1 -1
- package/dist/esm/repository.test.d.ts +2 -0
- package/dist/esm/repository.test.d.ts.map +1 -0
- package/dist/esm/repository.test.js +30 -0
- package/dist/esm/repository.test.js.map +1 -0
- package/dist/esm/signer.test.d.ts +2 -0
- package/dist/esm/signer.test.d.ts.map +1 -0
- package/dist/esm/signer.test.js +23 -0
- package/dist/esm/signer.test.js.map +1 -0
- package/package.json +87 -16
- package/dist/commonjs/framework/hono.d.ts +0 -36
- package/dist/commonjs/framework/hono.d.ts.map +0 -1
- package/dist/commonjs/framework/hono.js.map +0 -1
- package/dist/commonjs/service/cloudflare.d.ts.map +0 -1
- package/dist/commonjs/service/cloudflare.js.map +0 -1
- package/dist/commonjs/service/deno.d.ts.map +0 -1
- package/dist/commonjs/service/deno.js.map +0 -1
- package/dist/deno/app.d.ts +0 -31
- package/dist/deno/app.d.ts.map +0 -1
- package/dist/deno/app.js +0 -21
- package/dist/deno/app.js.map +0 -1
- package/dist/deno/context-resolver.d.ts +0 -30
- package/dist/deno/context-resolver.d.ts.map +0 -1
- package/dist/deno/context-resolver.js +0 -65
- package/dist/deno/context-resolver.js.map +0 -1
- package/dist/deno/framework/hono.d.ts +0 -36
- package/dist/deno/framework/hono.d.ts.map +0 -1
- package/dist/deno/framework/hono.js.map +0 -1
- package/dist/deno/helper/app-actions.d.ts +0 -13
- package/dist/deno/helper/app-actions.d.ts.map +0 -1
- package/dist/deno/helper/app-actions.js +0 -49
- package/dist/deno/helper/app-actions.js.map +0 -1
- package/dist/deno/http-client.d.ts +0 -67
- package/dist/deno/http-client.d.ts.map +0 -1
- package/dist/deno/http-client.js +0 -148
- package/dist/deno/http-client.js.map +0 -1
- package/dist/deno/mod.d.ts +0 -6
- package/dist/deno/mod.d.ts.map +0 -1
- package/dist/deno/mod.js +0 -5
- package/dist/deno/mod.js.map +0 -1
- package/dist/deno/package.json +0 -3
- package/dist/deno/registration.d.ts +0 -18
- package/dist/deno/registration.d.ts.map +0 -1
- package/dist/deno/registration.js +0 -84
- package/dist/deno/registration.js.map +0 -1
- package/dist/deno/repository.d.ts +0 -51
- package/dist/deno/repository.d.ts.map +0 -1
- package/dist/deno/repository.js +0 -63
- package/dist/deno/repository.js.map +0 -1
- package/dist/deno/service/cloudflare.d.ts.map +0 -1
- package/dist/deno/service/cloudflare.js +0 -41
- package/dist/deno/service/cloudflare.js.map +0 -1
- package/dist/deno/service/deno.d.ts +0 -18
- package/dist/deno/service/deno.d.ts.map +0 -1
- package/dist/deno/service/deno.js.map +0 -1
- package/dist/deno/signer.d.ts +0 -12
- package/dist/deno/signer.d.ts.map +0 -1
- package/dist/deno/signer.js +0 -54
- package/dist/deno/signer.js.map +0 -1
- package/dist/deno/types.d.ts +0 -28
- package/dist/deno/types.d.ts.map +0 -1
- package/dist/deno/types.js +0 -2
- package/dist/deno/types.js.map +0 -1
- package/dist/esm/framework/hono.d.ts +0 -36
- package/dist/esm/framework/hono.d.ts.map +0 -1
- package/dist/esm/framework/hono.js +0 -81
- package/dist/esm/framework/hono.js.map +0 -1
- package/dist/esm/service/cloudflare.d.ts +0 -43
- package/dist/esm/service/cloudflare.d.ts.map +0 -1
- package/dist/esm/service/cloudflare.js.map +0 -1
- package/dist/esm/service/deno.d.ts.map +0 -1
- package/dist/esm/service/deno.js +0 -44
- package/dist/esm/service/deno.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AppServer } from "../app.js";
|
|
2
|
+
import type { Context } from "../context-resolver.js";
|
|
3
|
+
import type { ShopInterface, ShopRepositoryInterface } from "../repository.js";
|
|
4
|
+
import type { Hono, Context as HonoContext } from "hono";
|
|
5
|
+
declare module "hono" {
|
|
6
|
+
interface ContextVariableMap {
|
|
7
|
+
app: AppServer<ShopInterface>;
|
|
8
|
+
shop: ShopInterface;
|
|
9
|
+
context: Context<ShopInterface, unknown>;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
interface MiddlewareConfig {
|
|
13
|
+
appName: string | ((c: HonoContext) => string);
|
|
14
|
+
appSecret: string | ((c: HonoContext) => string);
|
|
15
|
+
appUrl?: string | null;
|
|
16
|
+
registrationUrl?: string | null;
|
|
17
|
+
registerConfirmationUrl?: string | null;
|
|
18
|
+
appActivateUrl?: string | null;
|
|
19
|
+
appDeactivateUrl?: string | null;
|
|
20
|
+
appDeleteUrl?: string | null;
|
|
21
|
+
appPath?: string | null;
|
|
22
|
+
shopRepository: ShopRepositoryInterface | ((c: HonoContext) => ShopRepositoryInterface);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Configure the Hono server to handle the app registration and context resolution
|
|
26
|
+
*/
|
|
27
|
+
export declare function configureAppServer(hono: Hono, cfg: MiddlewareConfig): void;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AAEzD,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;AAED,UAAU,gBAAgB;IACzB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC/C,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,uBAAuB,CAAC,CAAC;CACjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,QAqHnE"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { AppServer } from "../app.js";
|
|
2
|
-
let app = null;
|
|
3
2
|
/**
|
|
4
3
|
* Configure the Hono server to handle the app registration and context resolution
|
|
5
4
|
*/
|
|
6
|
-
export function configureAppServer(
|
|
7
|
-
|
|
5
|
+
export function configureAppServer(hono, cfg) {
|
|
6
|
+
let app = null;
|
|
8
7
|
cfg.registrationUrl = cfg.registrationUrl || "/app/register";
|
|
9
8
|
cfg.registerConfirmationUrl =
|
|
10
9
|
cfg.registerConfirmationUrl || "/app/register/confirm";
|
|
10
|
+
cfg.appActivateUrl = cfg.appActivateUrl || "/app/activate";
|
|
11
|
+
cfg.appDeactivateUrl = cfg.appDeactivateUrl || "/app/deactivate";
|
|
12
|
+
cfg.appDeleteUrl = cfg.appDeleteUrl || "/app/delete";
|
|
11
13
|
cfg.appPath = cfg.appPath || "/app/*";
|
|
12
14
|
hono.use("*", async (ctx, next) => {
|
|
13
15
|
if (app === null) {
|
|
@@ -27,14 +29,19 @@ export function configureAppServer(honoExternal, cfg) {
|
|
|
27
29
|
authorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,
|
|
28
30
|
}, cfg.shopRepository);
|
|
29
31
|
}
|
|
32
|
+
// @ts-ignore
|
|
30
33
|
ctx.set("app", app);
|
|
31
34
|
await next();
|
|
32
35
|
});
|
|
33
36
|
hono.use(cfg.appPath, async (ctx, next) => {
|
|
37
|
+
// @ts-ignore
|
|
34
38
|
const app = ctx.get("app");
|
|
35
39
|
// Don't validate signature for registration
|
|
36
40
|
if (ctx.req.path === cfg.registrationUrl ||
|
|
37
|
-
ctx.req.path === cfg.registerConfirmationUrl
|
|
41
|
+
ctx.req.path === cfg.registerConfirmationUrl ||
|
|
42
|
+
ctx.req.path === cfg.appActivateUrl ||
|
|
43
|
+
ctx.req.path === cfg.appDeactivateUrl ||
|
|
44
|
+
ctx.req.path === cfg.appDeleteUrl) {
|
|
38
45
|
await next();
|
|
39
46
|
return;
|
|
40
47
|
}
|
|
@@ -48,7 +55,9 @@ export function configureAppServer(honoExternal, cfg) {
|
|
|
48
55
|
catch (_e) {
|
|
49
56
|
return jsonResponse({ message: "Invalid request" }, 400);
|
|
50
57
|
}
|
|
58
|
+
// @ts-ignore
|
|
51
59
|
ctx.set("shop", context.shop);
|
|
60
|
+
// @ts-ignore
|
|
52
61
|
ctx.set("context", context);
|
|
53
62
|
await next();
|
|
54
63
|
const cloned = ctx.res.clone();
|
|
@@ -65,6 +74,18 @@ export function configureAppServer(honoExternal, cfg) {
|
|
|
65
74
|
const app = ctx.get("app");
|
|
66
75
|
return await app.registration.authorizeCallback(ctx.req.raw);
|
|
67
76
|
});
|
|
77
|
+
hono.post(cfg.appActivateUrl, async (ctx) => {
|
|
78
|
+
const app = ctx.get("app");
|
|
79
|
+
return await app.registration.activate(ctx.req.raw);
|
|
80
|
+
});
|
|
81
|
+
hono.post(cfg.appDeactivateUrl, async (ctx) => {
|
|
82
|
+
const app = ctx.get("app");
|
|
83
|
+
return await app.registration.deactivate(ctx.req.raw);
|
|
84
|
+
});
|
|
85
|
+
hono.post(cfg.appDeleteUrl, async (ctx) => {
|
|
86
|
+
const app = ctx.get("app");
|
|
87
|
+
return await app.registration.delete(ctx.req.raw);
|
|
88
|
+
});
|
|
68
89
|
}
|
|
69
90
|
function jsonResponse(body, status = 200) {
|
|
70
91
|
return new Response(JSON.stringify(body), {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppServer } from \"../app.js\";\nimport type { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\ninterface MiddlewareConfig {\n\tappName: string | ((c: HonoContext) => string);\n\tappSecret: string | ((c: HonoContext) => string);\n\tappUrl?: string | null;\n\tregistrationUrl?: string | null;\n\tregisterConfirmationUrl?: string | null;\n\tappActivateUrl?: string | null;\n\tappDeactivateUrl?: string | null;\n\tappDeleteUrl?: string | null;\n\tappPath?: string | null;\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n}\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t}\n\n\t\t\tapp = new AppServer(\n\t\t\t\t{\n\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t},\n\t\t\t\tcfg.shopRepository,\n\t\t\t);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\treturn `${u.protocol}//${u.host}`;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.test.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { InMemoryShopRepository } from "../repository.js";
|
|
4
|
+
import { configureAppServer } from "./hono.js";
|
|
5
|
+
describe("Hono", async () => {
|
|
6
|
+
const repo = new InMemoryShopRepository();
|
|
7
|
+
await repo.createShop("a", "a", "a");
|
|
8
|
+
test("configre by functions", async () => {
|
|
9
|
+
const hono = new Hono();
|
|
10
|
+
configureAppServer(hono, {
|
|
11
|
+
appName: () => "test",
|
|
12
|
+
appSecret: () => "test",
|
|
13
|
+
shopRepository: () => repo,
|
|
14
|
+
});
|
|
15
|
+
const resp = await hono.fetch(new Request("http://localhost/app/register"));
|
|
16
|
+
expect(resp.status).toBe(400);
|
|
17
|
+
});
|
|
18
|
+
test("register", async () => {
|
|
19
|
+
const hono = new Hono();
|
|
20
|
+
configureAppServer(hono, {
|
|
21
|
+
appName: "test",
|
|
22
|
+
appSecret: "test",
|
|
23
|
+
shopRepository: repo,
|
|
24
|
+
});
|
|
25
|
+
const resp = await hono.fetch(new Request("http://localhost/app/register"));
|
|
26
|
+
expect(resp.status).toBe(400);
|
|
27
|
+
});
|
|
28
|
+
test("register: success", async () => {
|
|
29
|
+
const hono = new Hono();
|
|
30
|
+
configureAppServer(hono, {
|
|
31
|
+
appName: "My App",
|
|
32
|
+
appSecret: "my-secret",
|
|
33
|
+
shopRepository: repo,
|
|
34
|
+
});
|
|
35
|
+
const resp = await hono.fetch(new Request("http://localhost/app/register?shop-id=123&shop-url=https://my-shop.com×tamp=1234567890", {
|
|
36
|
+
headers: new Headers({
|
|
37
|
+
"shopware-app-signature": "96c91f86c822e11444b7a57b54ef125ed86b1a639c5360d45c5397daa8c3f70b",
|
|
38
|
+
}),
|
|
39
|
+
}));
|
|
40
|
+
expect(resp.status).toBe(200);
|
|
41
|
+
const body = (await resp.json());
|
|
42
|
+
expect(body).toBeObject();
|
|
43
|
+
expect(body.proof).not.toBeNull();
|
|
44
|
+
});
|
|
45
|
+
test("registerConfirm", async () => {
|
|
46
|
+
const hono = new Hono();
|
|
47
|
+
configureAppServer(hono, {
|
|
48
|
+
appName: "test",
|
|
49
|
+
appSecret: "test",
|
|
50
|
+
shopRepository: repo,
|
|
51
|
+
});
|
|
52
|
+
const resp = await hono.fetch(new Request("http://localhost/app/register/confirm", {
|
|
53
|
+
method: "POST",
|
|
54
|
+
body: "{}",
|
|
55
|
+
}));
|
|
56
|
+
expect(resp.status).toBe(400);
|
|
57
|
+
});
|
|
58
|
+
test("signature failure", async () => {
|
|
59
|
+
const hono = new Hono();
|
|
60
|
+
configureAppServer(hono, {
|
|
61
|
+
appName: "test",
|
|
62
|
+
appSecret: "test",
|
|
63
|
+
shopRepository: repo,
|
|
64
|
+
});
|
|
65
|
+
hono.get("/app/test", (c) => c.text("ok"));
|
|
66
|
+
const resp = await hono.fetch(new Request("http://localhost/app/test?shopware-shop-signature=invalid&shop-id=a"));
|
|
67
|
+
expect(resp.status).toBe(400);
|
|
68
|
+
expect(await resp.text()).toBe('{"message":"Invalid request"}');
|
|
69
|
+
});
|
|
70
|
+
test("signature success", async () => {
|
|
71
|
+
const hono = new Hono();
|
|
72
|
+
configureAppServer(hono, {
|
|
73
|
+
appName: "test",
|
|
74
|
+
appSecret: "test",
|
|
75
|
+
shopRepository: repo,
|
|
76
|
+
});
|
|
77
|
+
hono.get("/app/test", (c) => c.text("ok"));
|
|
78
|
+
const resp = await hono.fetch(new Request("http://localhost/app/test?shopware-shop-signature=8b523d99aeef5b456288fcec48236c3367a914c6b2c9fded63d81257ac019b25&shop-id=a"));
|
|
79
|
+
expect(resp.status).toBe(200);
|
|
80
|
+
expect(await resp.text()).toBe("ok");
|
|
81
|
+
expect(resp.headers.get("shopware-app-signature")).toBe("4a43a105ccce57e8e38d4a1f7b3565d743b1e15fb1fec36f41fdf20164fa1c8b");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=hono.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.test.js","sourceRoot":"","sources":["../../../src/integration/hono.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, test } from \"bun:test\";\nimport { Hono } from \"hono\";\nimport { InMemoryShopRepository } from \"../repository.js\";\nimport { configureAppServer } from \"./hono.js\";\n\ndescribe(\"Hono\", async () => {\n\tconst repo = new InMemoryShopRepository();\n\n\tawait repo.createShop(\"a\", \"a\", \"a\");\n\n\ttest(\"configre by functions\", async () => {\n\t\tconst hono = new Hono();\n\n\t\tconfigureAppServer(hono, {\n\t\t\tappName: () => \"test\",\n\t\t\tappSecret: () => \"test\",\n\t\t\tshopRepository: () => repo,\n\t\t});\n\n\t\tconst resp = await hono.fetch(new Request(\"http://localhost/app/register\"));\n\n\t\texpect(resp.status).toBe(400);\n\t});\n\n\ttest(\"register\", async () => {\n\t\tconst hono = new Hono();\n\n\t\tconfigureAppServer(hono, {\n\t\t\tappName: \"test\",\n\t\t\tappSecret: \"test\",\n\t\t\tshopRepository: repo,\n\t\t});\n\n\t\tconst resp = await hono.fetch(new Request(\"http://localhost/app/register\"));\n\n\t\texpect(resp.status).toBe(400);\n\t});\n\n\ttest(\"register: success\", async () => {\n\t\tconst hono = new Hono();\n\n\t\tconfigureAppServer(hono, {\n\t\t\tappName: \"My App\",\n\t\t\tappSecret: \"my-secret\",\n\t\t\tshopRepository: repo,\n\t\t});\n\n\t\tconst resp = await hono.fetch(\n\t\t\tnew Request(\n\t\t\t\t\"http://localhost/app/register?shop-id=123&shop-url=https://my-shop.com×tamp=1234567890\",\n\t\t\t\t{\n\t\t\t\t\theaders: new Headers({\n\t\t\t\t\t\t\"shopware-app-signature\":\n\t\t\t\t\t\t\t\"96c91f86c822e11444b7a57b54ef125ed86b1a639c5360d45c5397daa8c3f70b\",\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\n\t\texpect(resp.status).toBe(200);\n\n\t\tconst body = (await resp.json()) as { proof: string };\n\n\t\texpect(body).toBeObject();\n\n\t\texpect(body.proof).not.toBeNull();\n\t});\n\n\ttest(\"registerConfirm\", async () => {\n\t\tconst hono = new Hono();\n\n\t\tconfigureAppServer(hono, {\n\t\t\tappName: \"test\",\n\t\t\tappSecret: \"test\",\n\t\t\tshopRepository: repo,\n\t\t});\n\n\t\tconst resp = await hono.fetch(\n\t\t\tnew Request(\"http://localhost/app/register/confirm\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: \"{}\",\n\t\t\t}),\n\t\t);\n\n\t\texpect(resp.status).toBe(400);\n\t});\n\n\ttest(\"signature failure\", async () => {\n\t\tconst hono = new Hono();\n\n\t\tconfigureAppServer(hono, {\n\t\t\tappName: \"test\",\n\t\t\tappSecret: \"test\",\n\t\t\tshopRepository: repo,\n\t\t});\n\n\t\thono.get(\"/app/test\", (c) => c.text(\"ok\"));\n\n\t\tconst resp = await hono.fetch(\n\t\t\tnew Request(\n\t\t\t\t\"http://localhost/app/test?shopware-shop-signature=invalid&shop-id=a\",\n\t\t\t),\n\t\t);\n\n\t\texpect(resp.status).toBe(400);\n\t\texpect(await resp.text()).toBe('{\"message\":\"Invalid request\"}');\n\t});\n\n\ttest(\"signature success\", async () => {\n\t\tconst hono = new Hono();\n\n\t\tconfigureAppServer(hono, {\n\t\t\tappName: \"test\",\n\t\t\tappSecret: \"test\",\n\t\t\tshopRepository: repo,\n\t\t});\n\n\t\thono.get(\"/app/test\", (c) => c.text(\"ok\"));\n\n\t\tconst resp = await hono.fetch(\n\t\t\tnew Request(\n\t\t\t\t\"http://localhost/app/test?shopware-shop-signature=8b523d99aeef5b456288fcec48236c3367a914c6b2c9fded63d81257ac019b25&shop-id=a\",\n\t\t\t),\n\t\t);\n\n\t\texpect(resp.status).toBe(200);\n\t\texpect(await resp.text()).toBe(\"ok\");\n\t\texpect(resp.headers.get(\"shopware-app-signature\")).toBe(\n\t\t\t\"4a43a105ccce57e8e38d4a1f7b3565d743b1e15fb1fec36f41fdf20164fa1c8b\",\n\t\t);\n\t});\n});\n"]}
|
|
@@ -13,6 +13,30 @@ export declare class Registration {
|
|
|
13
13
|
* It will update the shop with the given oauth2 credentials.
|
|
14
14
|
*/
|
|
15
15
|
authorizeCallback(req: Request): Promise<Response>;
|
|
16
|
+
/**
|
|
17
|
+
* This method should be called by Shopware Shop to set the shop active.
|
|
18
|
+
*
|
|
19
|
+
* <webhooks>
|
|
20
|
+
* <webhook name="appActivate" url="http://localhost:3000/app/delete" event="app.activated"/>
|
|
21
|
+
* </webhooks>
|
|
22
|
+
*/
|
|
23
|
+
activate(req: Request): Promise<Response>;
|
|
24
|
+
/**
|
|
25
|
+
* This method should be called by Shopware Shop to set the shop in-active.
|
|
26
|
+
*
|
|
27
|
+
* <webhooks>
|
|
28
|
+
* <webhook name="appDeactivated" url="http://localhost:3000/app/deactivated" event="app.deactivated"/>
|
|
29
|
+
* </webhooks>
|
|
30
|
+
*/
|
|
31
|
+
deactivate(req: Request): Promise<Response>;
|
|
32
|
+
/**
|
|
33
|
+
* This method should be called by Shopware Shop to delete the app.
|
|
34
|
+
*
|
|
35
|
+
* <webhooks>
|
|
36
|
+
* <webhook name="appDelete" url="http://localhost:3000/app/delete" event="app.deleted"/>
|
|
37
|
+
* </webhooks>
|
|
38
|
+
*/
|
|
39
|
+
delete(req: Request): Promise<Response>;
|
|
16
40
|
}
|
|
17
41
|
export declare function randomString(length?: number): string;
|
|
18
42
|
//# sourceMappingURL=registration.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/registration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,qBAAa,YAAY;IACZ,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;;;OAIG;IACU,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/registration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,qBAAa,YAAY;IACZ,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;;;OAIG;IACU,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IA+CvD;;;OAGG;IACU,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAuC/D;;;;;;OAMG;IACU,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAUtD;;;;;;OAMG;IACU,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAUxD;;;;;;OAMG;IACU,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAOpD;AAED,wBAAgB,YAAY,CAAC,MAAM,SAAM,UAUxC"}
|
package/dist/esm/registration.js
CHANGED
|
@@ -16,12 +16,13 @@ export class Registration {
|
|
|
16
16
|
!url.searchParams.has("timestamp")) {
|
|
17
17
|
return new InvalidRequestResponse("Invalid Request", 400);
|
|
18
18
|
}
|
|
19
|
-
const
|
|
19
|
+
const shopId = url.searchParams.get("shop-id");
|
|
20
|
+
const shopUrl = url.searchParams.get("shop-url");
|
|
21
|
+
const timestamp = url.searchParams.get("timestamp");
|
|
22
|
+
const v = await this.app.signer.verify(req.headers.get("shopware-app-signature"), `shop-id=${shopId}&shop-url=${shopUrl}×tamp=${timestamp}`, this.app.cfg.appSecret);
|
|
20
23
|
if (!v) {
|
|
21
24
|
return new InvalidRequestResponse("Cannot validate app signature");
|
|
22
25
|
}
|
|
23
|
-
const shopId = url.searchParams.get("shop-id");
|
|
24
|
-
const shopUrl = url.searchParams.get("shop-url");
|
|
25
26
|
const shopSecret = randomString();
|
|
26
27
|
await this.app.repository.createShop(shopId, shopUrl, shopSecret);
|
|
27
28
|
return new Response(JSON.stringify({
|
|
@@ -61,6 +62,44 @@ export class Registration {
|
|
|
61
62
|
await this.app.repository.updateShop(shop);
|
|
62
63
|
return new Response(null, { status: 204 });
|
|
63
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* This method should be called by Shopware Shop to set the shop active.
|
|
67
|
+
*
|
|
68
|
+
* <webhooks>
|
|
69
|
+
* <webhook name="appActivate" url="http://localhost:3000/app/delete" event="app.activated"/>
|
|
70
|
+
* </webhooks>
|
|
71
|
+
*/
|
|
72
|
+
async activate(req) {
|
|
73
|
+
const ctx = await this.app.contextResolver.fromAPI(req);
|
|
74
|
+
ctx.shop.setShopActive(true);
|
|
75
|
+
await this.app.repository.updateShop(ctx.shop);
|
|
76
|
+
return new Response(null, { status: 204 });
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* This method should be called by Shopware Shop to set the shop in-active.
|
|
80
|
+
*
|
|
81
|
+
* <webhooks>
|
|
82
|
+
* <webhook name="appDeactivated" url="http://localhost:3000/app/deactivated" event="app.deactivated"/>
|
|
83
|
+
* </webhooks>
|
|
84
|
+
*/
|
|
85
|
+
async deactivate(req) {
|
|
86
|
+
const ctx = await this.app.contextResolver.fromAPI(req);
|
|
87
|
+
ctx.shop.setShopActive(false);
|
|
88
|
+
await this.app.repository.updateShop(ctx.shop);
|
|
89
|
+
return new Response(null, { status: 204 });
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* This method should be called by Shopware Shop to delete the app.
|
|
93
|
+
*
|
|
94
|
+
* <webhooks>
|
|
95
|
+
* <webhook name="appDelete" url="http://localhost:3000/app/delete" event="app.deleted"/>
|
|
96
|
+
* </webhooks>
|
|
97
|
+
*/
|
|
98
|
+
async delete(req) {
|
|
99
|
+
const ctx = await this.app.contextResolver.fromAPI(req);
|
|
100
|
+
await this.app.repository.deleteShop(ctx.shop.getShopId());
|
|
101
|
+
return new Response(null, { status: 204 });
|
|
102
|
+
}
|
|
64
103
|
}
|
|
65
104
|
export function randomString(length = 120) {
|
|
66
105
|
let result = "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/registration.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACJ;IAApB,YAAoB,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAEtC;;;;OAIG;IACI,KAAK,CAAC,SAAS,CAAC,GAAY;QAClC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE7B,IACC,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC;YACjC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;YAC1C,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC;YAChC,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EACjC,CAAC;YACF,OAAO,IAAI,sBAAsB,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,
|
|
1
|
+
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/registration.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACJ;IAApB,YAAoB,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAEtC;;;;OAIG;IACI,KAAK,CAAC,SAAS,CAAC,GAAY;QAClC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE7B,IACC,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC;YACjC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;YAC1C,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC;YAChC,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EACjC,CAAC;YACF,OAAO,IAAI,sBAAsB,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAW,CAAC;QACzD,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAW,CAAC;QAC3D,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAW,CAAC;QAE9D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CACrC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAW,EACnD,WAAW,MAAM,aAAa,OAAO,cAAc,SAAS,EAAE,EAC9D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CACtB,CAAC;QAEF,IAAI,CAAC,CAAC,EAAE,CAAC;YACR,OAAO,IAAI,sBAAsB,CAAC,+BAA+B,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,UAAU,GAAG,YAAY,EAAE,CAAC;QAElC,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAElE,OAAO,IAAI,QAAQ,CAClB,IAAI,CAAC,SAAS,CAAC;YACd,KAAK,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAChC,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EACvC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CACtB;YACD,MAAM,EAAE,UAAU;YAClB,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,oBAAoB;SACnD,CAAC,EACF;YACC,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;aAClC;SACD,CACD,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,iBAAiB,CAAC,GAAY;QAC1C,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAErC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAErC,IACC,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;YAC/B,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;YAC/B,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;YAClC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,EAC1C,CAAC;YACF,OAAO,IAAI,sBAAsB,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,MAAgB,CAAC,CAAC;QAE1E,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,OAAO,IAAI,sBAAsB,CAAC,oBAAoB,CAAC,CAAC;QACzD,CAAC;QAED,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CACrC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAW,EACpD,WAAW,EACX,IAAI,CAAC,aAAa,EAAE,CACpB,CAAC;QACF,IAAI,CAAC,CAAC,EAAE,CAAC;YACR,oDAAoD;YACpD,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;YAEvD,OAAO,IAAI,sBAAsB,CAAC,+BAA+B,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAErD,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAE3C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,QAAQ,CAAC,GAAY;QACjC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAExD,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAE7B,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE/C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,UAAU,CAAC,GAAY;QACnC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAExD,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAE9B,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE/C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,MAAM,CAAC,GAAY;QAC/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAExD,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAE3D,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;CACD;AAED,MAAM,UAAU,YAAY,CAAC,MAAM,GAAG,GAAG;IACxC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,MAAM,UAAU,GACf,gEAAgE,CAAC;IAClE,MAAM,gBAAgB,GAAG,UAAU,CAAC,MAAM,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,sBAAuB,SAAQ,QAAQ;IAC5C,YAAY,OAAe,EAAE,MAAM,GAAG,GAAG;QACxC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;YAClC,MAAM;YACN,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;aAClC;SACD,CAAC,CAAC;IACJ,CAAC;CACD","sourcesContent":["import type { AppServer } from \"./app.js\";\n\nexport class Registration {\n\tconstructor(private app: AppServer) {}\n\n\t/**\n\t * This method checks the request for the handshake with the Shopware Shop.\n\t * if it's valid a Shop will be created, and a proof will be responded with a confirmation url.\n\t * then the Shop will call the confirmation url, and this should be handled by the authorizeCallback method to finish the handshake.\n\t */\n\tpublic async authorize(req: Request): Promise<Response> {\n\t\tconst url = new URL(req.url);\n\n\t\tif (\n\t\t\t!url.searchParams.has(\"shop-url\") ||\n\t\t\t!req.headers.has(\"shopware-app-signature\") ||\n\t\t\t!url.searchParams.has(\"shop-id\") ||\n\t\t\t!url.searchParams.has(\"timestamp\")\n\t\t) {\n\t\t\treturn new InvalidRequestResponse(\"Invalid Request\", 400);\n\t\t}\n\n\t\tconst shopId = url.searchParams.get(\"shop-id\") as string;\n\t\tconst shopUrl = url.searchParams.get(\"shop-url\") as string;\n\t\tconst timestamp = url.searchParams.get(\"timestamp\") as string;\n\n\t\tconst v = await this.app.signer.verify(\n\t\t\treq.headers.get(\"shopware-app-signature\") as string,\n\t\t\t`shop-id=${shopId}&shop-url=${shopUrl}×tamp=${timestamp}`,\n\t\t\tthis.app.cfg.appSecret,\n\t\t);\n\n\t\tif (!v) {\n\t\t\treturn new InvalidRequestResponse(\"Cannot validate app signature\");\n\t\t}\n\n\t\tconst shopSecret = randomString();\n\n\t\tawait this.app.repository.createShop(shopId, shopUrl, shopSecret);\n\n\t\treturn new Response(\n\t\t\tJSON.stringify({\n\t\t\t\tproof: await this.app.signer.sign(\n\t\t\t\t\tshopId + shopUrl + this.app.cfg.appName,\n\t\t\t\t\tthis.app.cfg.appSecret,\n\t\t\t\t),\n\t\t\t\tsecret: shopSecret,\n\t\t\t\tconfirmation_url: this.app.cfg.authorizeCallbackUrl,\n\t\t\t}),\n\t\t\t{\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * This method is called by the Shopware Shop to confirm the handshake.\n\t * It will update the shop with the given oauth2 credentials.\n\t */\n\tpublic async authorizeCallback(req: Request): Promise<Response> {\n\t\tconst bodyContent = await req.text();\n\n\t\tconst body = JSON.parse(bodyContent);\n\n\t\tif (\n\t\t\ttypeof body.shopId !== \"string\" ||\n\t\t\ttypeof body.apiKey !== \"string\" ||\n\t\t\ttypeof body.secretKey !== \"string\" ||\n\t\t\t!req.headers.has(\"shopware-shop-signature\")\n\t\t) {\n\t\t\treturn new InvalidRequestResponse(\"Invalid Request\", 400);\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(body.shopId as string);\n\n\t\tif (shop === null) {\n\t\t\treturn new InvalidRequestResponse(\"Invalid shop given\");\n\t\t}\n\n\t\tconst v = await this.app.signer.verify(\n\t\t\treq.headers.get(\"shopware-shop-signature\") as string,\n\t\t\tbodyContent,\n\t\t\tshop.getShopSecret(),\n\t\t);\n\t\tif (!v) {\n\t\t\t// Shop has failed the verify. Delete it from our DB\n\t\t\tawait this.app.repository.deleteShop(shop.getShopId());\n\n\t\t\treturn new InvalidRequestResponse(\"Cannot validate app signature\");\n\t\t}\n\n\t\tshop.setShopCredentials(body.apiKey, body.secretKey);\n\n\t\tawait this.app.repository.updateShop(shop);\n\n\t\treturn new Response(null, { status: 204 });\n\t}\n\n\t/**\n\t * This method should be called by Shopware Shop to set the shop active.\n\t *\n\t * <webhooks>\n\t * <webhook name=\"appActivate\" url=\"http://localhost:3000/app/delete\" event=\"app.activated\"/>\n\t * </webhooks>\n\t */\n\tpublic async activate(req: Request): Promise<Response> {\n\t\tconst ctx = await this.app.contextResolver.fromAPI(req);\n\n\t\tctx.shop.setShopActive(true);\n\n\t\tawait this.app.repository.updateShop(ctx.shop);\n\n\t\treturn new Response(null, { status: 204 });\n\t}\n\n\t/**\n\t * This method should be called by Shopware Shop to set the shop in-active.\n\t *\n\t * <webhooks>\n\t * <webhook name=\"appDeactivated\" url=\"http://localhost:3000/app/deactivated\" event=\"app.deactivated\"/>\n\t * </webhooks>\n\t */\n\tpublic async deactivate(req: Request): Promise<Response> {\n\t\tconst ctx = await this.app.contextResolver.fromAPI(req);\n\n\t\tctx.shop.setShopActive(false);\n\n\t\tawait this.app.repository.updateShop(ctx.shop);\n\n\t\treturn new Response(null, { status: 204 });\n\t}\n\n\t/**\n\t * This method should be called by Shopware Shop to delete the app.\n\t *\n\t * <webhooks>\n\t * <webhook name=\"appDelete\" url=\"http://localhost:3000/app/delete\" event=\"app.deleted\"/>\n\t * </webhooks>\n\t */\n\tpublic async delete(req: Request): Promise<Response> {\n\t\tconst ctx = await this.app.contextResolver.fromAPI(req);\n\n\t\tawait this.app.repository.deleteShop(ctx.shop.getShopId());\n\n\t\treturn new Response(null, { status: 204 });\n\t}\n}\n\nexport function randomString(length = 120) {\n\tlet result = \"\";\n\tconst characters =\n\t\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\tconst charactersLength = characters.length;\n\tfor (let i = 0; i < length; i++) {\n\t\tresult += characters.charAt(Math.floor(Math.random() * charactersLength));\n\t}\n\n\treturn result;\n}\n\nclass InvalidRequestResponse extends Response {\n\tconstructor(message: string, status = 401) {\n\t\tsuper(JSON.stringify({ message }), {\n\t\t\tstatus,\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t},\n\t\t});\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.test.d.ts","sourceRoot":"","sources":["../../src/registration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, jest, test } from "bun:test";
|
|
2
|
+
import { AppServer } from "../src/app.js";
|
|
3
|
+
import { InMemoryShopRepository } from "../src/repository.js";
|
|
4
|
+
describe("Registration", async () => {
|
|
5
|
+
const app = new AppServer({ appName: "test", appSecret: "test", authorizeCallbackUrl: "test" }, new InMemoryShopRepository());
|
|
6
|
+
test("authorize: invalid request", async () => {
|
|
7
|
+
const resp = await app.registration.authorize(new Request("http://localhost"));
|
|
8
|
+
expect(resp.status).toBe(400);
|
|
9
|
+
});
|
|
10
|
+
test("authorize: invalid signature", async () => {
|
|
11
|
+
const resp = await app.registration.authorize(new Request("http://localhost?shop-url=test&shop-id=test×tamp=test", { headers: new Headers({ "shopware-app-signature": "test" }) }));
|
|
12
|
+
expect(resp.status).toBe(401);
|
|
13
|
+
});
|
|
14
|
+
test("authorize: valid request", async () => {
|
|
15
|
+
app.signer.verify = jest.fn().mockResolvedValue(true);
|
|
16
|
+
const resp = await app.registration.authorize(new Request("http://localhost?shop-url=test&shop-id=test×tamp=test", { headers: new Headers({ "shopware-app-signature": "test" }) }));
|
|
17
|
+
expect(resp.status).toBe(200);
|
|
18
|
+
});
|
|
19
|
+
test("authorizeCallback: invalid request", async () => {
|
|
20
|
+
const resp = await app.registration.authorizeCallback(new Request("http://localhost", { body: "{}" }));
|
|
21
|
+
expect(resp.status).toBe(400);
|
|
22
|
+
});
|
|
23
|
+
test("authorizeCallback: shop does not exist", async () => {
|
|
24
|
+
const resp = await app.registration.authorizeCallback(new Request("http://localhost", {
|
|
25
|
+
body: '{"shopId": "test", "apiKey": "test", "secretKey": "test"}',
|
|
26
|
+
}));
|
|
27
|
+
expect(resp.status).toBe(400);
|
|
28
|
+
});
|
|
29
|
+
test("activateShop", async () => {
|
|
30
|
+
await app.repository.createShop("1", "http://localhost", "test");
|
|
31
|
+
const shop = await app.repository.getShopById("1");
|
|
32
|
+
shop?.setShopActive(false);
|
|
33
|
+
app.contextResolver.fromAPI = jest.fn().mockResolvedValue({ shop });
|
|
34
|
+
const resp = await app.registration.activate(new Request("http://localhost", { body: '{"source": {"shopId": "1"}}' }));
|
|
35
|
+
expect(resp.status).toBe(204);
|
|
36
|
+
expect(shop?.getShopActive()).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
test("deactivateShop", async () => {
|
|
39
|
+
await app.repository.createShop("1", "http://localhost", "test");
|
|
40
|
+
const shop = await app.repository.getShopById("1");
|
|
41
|
+
shop?.setShopActive(true);
|
|
42
|
+
app.contextResolver.fromAPI = jest.fn().mockResolvedValue({ shop });
|
|
43
|
+
const resp = await app.registration.deactivate(new Request("http://localhost", { body: '{"source": {"shopId": "1"}}' }));
|
|
44
|
+
expect(resp.status).toBe(204);
|
|
45
|
+
expect(shop?.getShopActive()).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
test("deletedShop", async () => {
|
|
48
|
+
await app.repository.createShop("1", "http://localhost", "test");
|
|
49
|
+
const shop = await app.repository.getShopById("1");
|
|
50
|
+
expect(shop).not.toBeNull();
|
|
51
|
+
app.contextResolver.fromAPI = jest.fn().mockResolvedValue({ shop });
|
|
52
|
+
const resp = await app.registration.delete(new Request("http://localhost", { body: '{"source": {"shopId": "1"}}' }));
|
|
53
|
+
expect(resp.status).toBe(204);
|
|
54
|
+
expect(app.repository.getShopById("1")).resolves.toBeNull();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=registration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.test.js","sourceRoot":"","sources":["../../src/registration.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, jest, test } from \"bun:test\";\nimport { AppServer } from \"../src/app.js\";\nimport { InMemoryShopRepository, SimpleShop } from \"../src/repository.js\";\n\ndescribe(\"Registration\", async () => {\n\tconst app = new AppServer(\n\t\t{ appName: \"test\", appSecret: \"test\", authorizeCallbackUrl: \"test\" },\n\t\tnew InMemoryShopRepository(),\n\t);\n\n\ttest(\"authorize: invalid request\", async () => {\n\t\tconst resp = await app.registration.authorize(\n\t\t\tnew Request(\"http://localhost\"),\n\t\t);\n\n\t\texpect(resp.status).toBe(400);\n\t});\n\n\ttest(\"authorize: invalid signature\", async () => {\n\t\tconst resp = await app.registration.authorize(\n\t\t\tnew Request(\n\t\t\t\t\"http://localhost?shop-url=test&shop-id=test×tamp=test\",\n\t\t\t\t{ headers: new Headers({ \"shopware-app-signature\": \"test\" }) },\n\t\t\t),\n\t\t);\n\n\t\texpect(resp.status).toBe(401);\n\t});\n\n\ttest(\"authorize: valid request\", async () => {\n\t\tapp.signer.verify = jest.fn().mockResolvedValue(true);\n\n\t\tconst resp = await app.registration.authorize(\n\t\t\tnew Request(\n\t\t\t\t\"http://localhost?shop-url=test&shop-id=test×tamp=test\",\n\t\t\t\t{ headers: new Headers({ \"shopware-app-signature\": \"test\" }) },\n\t\t\t),\n\t\t);\n\n\t\texpect(resp.status).toBe(200);\n\t});\n\n\ttest(\"authorizeCallback: invalid request\", async () => {\n\t\tconst resp = await app.registration.authorizeCallback(\n\t\t\tnew Request(\"http://localhost\", { body: \"{}\" }),\n\t\t);\n\n\t\texpect(resp.status).toBe(400);\n\t});\n\n\ttest(\"authorizeCallback: shop does not exist\", async () => {\n\t\tconst resp = await app.registration.authorizeCallback(\n\t\t\tnew Request(\"http://localhost\", {\n\t\t\t\tbody: '{\"shopId\": \"test\", \"apiKey\": \"test\", \"secretKey\": \"test\"}',\n\t\t\t}),\n\t\t);\n\n\t\texpect(resp.status).toBe(400);\n\t});\n\n\ttest(\"activateShop\", async () => {\n\t\tawait app.repository.createShop(\"1\", \"http://localhost\", \"test\");\n\n\t\tconst shop = await app.repository.getShopById(\"1\");\n\n\t\tshop?.setShopActive(false);\n\n\t\tapp.contextResolver.fromAPI = jest.fn().mockResolvedValue({ shop });\n\n\t\tconst resp = await app.registration.activate(\n\t\t\tnew Request(\"http://localhost\", { body: '{\"source\": {\"shopId\": \"1\"}}' }),\n\t\t);\n\n\t\texpect(resp.status).toBe(204);\n\n\t\texpect(shop?.getShopActive()).toBe(true);\n\t});\n\n\ttest(\"deactivateShop\", async () => {\n\t\tawait app.repository.createShop(\"1\", \"http://localhost\", \"test\");\n\n\t\tconst shop = await app.repository.getShopById(\"1\");\n\n\t\tshop?.setShopActive(true);\n\n\t\tapp.contextResolver.fromAPI = jest.fn().mockResolvedValue({ shop });\n\n\t\tconst resp = await app.registration.deactivate(\n\t\t\tnew Request(\"http://localhost\", { body: '{\"source\": {\"shopId\": \"1\"}}' }),\n\t\t);\n\n\t\texpect(resp.status).toBe(204);\n\n\t\texpect(shop?.getShopActive()).toBe(false);\n\t});\n\n\ttest(\"deletedShop\", async () => {\n\t\tawait app.repository.createShop(\"1\", \"http://localhost\", \"test\");\n\n\t\tconst shop = await app.repository.getShopById(\"1\");\n\n\t\texpect(shop).not.toBeNull();\n\n\t\tapp.contextResolver.fromAPI = jest.fn().mockResolvedValue({ shop });\n\n\t\tconst resp = await app.registration.delete(\n\t\t\tnew Request(\"http://localhost\", { body: '{\"source\": {\"shopId\": \"1\"}}' }),\n\t\t);\n\n\t\texpect(resp.status).toBe(204);\n\n\t\texpect(app.repository.getShopById(\"1\")).resolves.toBeNull();\n\t});\n});\n"]}
|
package/dist/esm/repository.d.ts
CHANGED
|
@@ -7,7 +7,9 @@ export interface ShopInterface {
|
|
|
7
7
|
getShopSecret(): string;
|
|
8
8
|
getShopClientId(): string | null;
|
|
9
9
|
getShopClientSecret(): string | null;
|
|
10
|
+
getShopActive(): boolean;
|
|
10
11
|
setShopCredentials(clientId: string, clientSecret: string): void;
|
|
12
|
+
setShopActive(active: boolean): void;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* ShopRepositoryInterface is the storage interface for the shops, you should implement this to save the shop data to your database
|
|
@@ -28,8 +30,10 @@ export declare class SimpleShop implements ShopInterface {
|
|
|
28
30
|
private shopSecret;
|
|
29
31
|
private shopClientId;
|
|
30
32
|
private shopClientSecret;
|
|
31
|
-
|
|
33
|
+
private shopActive;
|
|
32
34
|
constructor(shopId: string, shopUrl: string, shopSecret: string);
|
|
35
|
+
getShopActive(): boolean;
|
|
36
|
+
setShopActive(active: boolean): void;
|
|
33
37
|
getShopId(): string;
|
|
34
38
|
getShopUrl(): string;
|
|
35
39
|
getShopSecret(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,IAAI,MAAM,CAAC;IACpB,UAAU,IAAI,MAAM,CAAC;IACrB,aAAa,IAAI,MAAM,CAAC;IACxB,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAAC;IACrC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,IAAI,MAAM,CAAC;IACpB,UAAU,IAAI,MAAM,CAAC;IACrB,aAAa,IAAI,MAAM,CAAC;IACxB,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAAC;IACrC,aAAa,IAAI,OAAO,CAAC;IACzB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACjE,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB,CAAC,IAAI,GAAG,aAAa;IAC5D,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE9C,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,qBAAa,UAAW,YAAW,aAAa;IAC/C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,UAAU,CAAQ;gBAEd,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAQ/D,aAAa,IAAI,OAAO;IAGxB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAIpC,SAAS,IAAI,MAAM;IAGnB,UAAU,IAAI,MAAM;IAGpB,aAAa,IAAI,MAAM;IAGvB,eAAe,IAAI,MAAM,GAAG,IAAI;IAGhC,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAGpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;CAIhE;AAED;;GAEG;AACH,qBAAa,sBACZ,YAAW,uBAAuB,CAAC,UAAU,CAAC;IAE9C,OAAO,CAAC,OAAO,CAA0B;;IAMnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAIlD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAUnD,UAAU,CAAC,IAAI,EAAE,UAAU;IAI3B,UAAU,CAAC,EAAE,EAAE,MAAM;CAG3B"}
|
package/dist/esm/repository.js
CHANGED
|
@@ -7,7 +7,7 @@ export class SimpleShop {
|
|
|
7
7
|
shopSecret;
|
|
8
8
|
shopClientId;
|
|
9
9
|
shopClientSecret;
|
|
10
|
-
|
|
10
|
+
shopActive = true;
|
|
11
11
|
constructor(shopId, shopUrl, shopSecret) {
|
|
12
12
|
this.shopId = shopId;
|
|
13
13
|
this.shopUrl = shopUrl;
|
|
@@ -15,6 +15,12 @@ export class SimpleShop {
|
|
|
15
15
|
this.shopClientId = null;
|
|
16
16
|
this.shopClientSecret = null;
|
|
17
17
|
}
|
|
18
|
+
getShopActive() {
|
|
19
|
+
return this.shopActive;
|
|
20
|
+
}
|
|
21
|
+
setShopActive(active) {
|
|
22
|
+
this.shopActive = active;
|
|
23
|
+
}
|
|
18
24
|
getShopId() {
|
|
19
25
|
return this.shopId;
|
|
20
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"AA4BA;;GAEG;AACH,MAAM,OAAO,UAAU;IACd,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,UAAU,CAAS;IACnB,YAAY,CAAgB;IAC5B,gBAAgB,CAAgB;IAChC,UAAU,GAAG,IAAI,CAAC;IAE1B,YAAY,MAAc,EAAE,OAAe,EAAE,UAAkB;QAC9D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED,aAAa;QACZ,OAAO,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IACD,aAAa,CAAC,MAAe;QAC5B,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;IAC1B,CAAC;IAED,SAAS;QACR,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IACD,UAAU;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IACD,aAAa;QACZ,OAAO,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IACD,eAAe;QACd,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IACD,mBAAmB;QAClB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC9B,CAAC;IACD,kBAAkB,CAAC,QAAgB,EAAE,YAAoB;QACxD,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;QAC7B,IAAI,CAAC,gBAAgB,GAAG,YAAY,CAAC;IACtC,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAG1B,OAAO,CAA0B;IAEzC;QACC,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,MAAc,EAAE,GAAW;QACvD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAElC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAgB;QAChC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU;QAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;CACD","sourcesContent":["/**\n * ShopInterface defines the object that given back from the ShopRepository, it should methods to get the shop data and set them\n */\nexport interface ShopInterface {\n\tgetShopId(): string;\n\tgetShopUrl(): string;\n\tgetShopSecret(): string;\n\tgetShopClientId(): string | null;\n\tgetShopClientSecret(): string | null;\n\tgetShopActive(): boolean;\n\tsetShopCredentials(clientId: string, clientSecret: string): void;\n\tsetShopActive(active: boolean): void;\n}\n\n/**\n * ShopRepositoryInterface is the storage interface for the shops, you should implement this to save the shop data to your database\n * For testing cases the InMemoryShopRepository can be used\n */\nexport interface ShopRepositoryInterface<Shop = ShopInterface> {\n\tcreateShop(id: string, url: string, secret: string): Promise<void>;\n\n\tgetShopById(id: string): Promise<Shop | null>;\n\n\tupdateShop(shop: Shop): Promise<void>;\n\n\tdeleteShop(id: string): Promise<void>;\n}\n\n/**\n * SimpleShop is a simple implementation of the ShopInterface, it stores the shop data in memory\n */\nexport class SimpleShop implements ShopInterface {\n\tprivate shopId: string;\n\tprivate shopUrl: string;\n\tprivate shopSecret: string;\n\tprivate shopClientId: string | null;\n\tprivate shopClientSecret: string | null;\n\tprivate shopActive = true;\n\n\tconstructor(shopId: string, shopUrl: string, shopSecret: string) {\n\t\tthis.shopId = shopId;\n\t\tthis.shopUrl = shopUrl;\n\t\tthis.shopSecret = shopSecret;\n\t\tthis.shopClientId = null;\n\t\tthis.shopClientSecret = null;\n\t}\n\n\tgetShopActive(): boolean {\n\t\treturn this.shopActive;\n\t}\n\tsetShopActive(active: boolean): void {\n\t\tthis.shopActive = active;\n\t}\n\n\tgetShopId(): string {\n\t\treturn this.shopId;\n\t}\n\tgetShopUrl(): string {\n\t\treturn this.shopUrl;\n\t}\n\tgetShopSecret(): string {\n\t\treturn this.shopSecret;\n\t}\n\tgetShopClientId(): string | null {\n\t\treturn this.shopClientId;\n\t}\n\tgetShopClientSecret(): string | null {\n\t\treturn this.shopClientSecret;\n\t}\n\tsetShopCredentials(clientId: string, clientSecret: string): void {\n\t\tthis.shopClientId = clientId;\n\t\tthis.shopClientSecret = clientSecret;\n\t}\n}\n\n/**\n * InMemoryShopRepository is a simple implementation of the ShopRepositoryInterface, it stores the shop data in memory\n */\nexport class InMemoryShopRepository\n\timplements ShopRepositoryInterface<SimpleShop>\n{\n\tprivate storage: Map<string, SimpleShop>;\n\n\tconstructor() {\n\t\tthis.storage = new Map<string, SimpleShop>();\n\t}\n\n\tasync createShop(id: string, secret: string, url: string) {\n\t\tthis.storage.set(id, new SimpleShop(id, url, secret));\n\t}\n\n\tasync getShopById(id: string): Promise<SimpleShop | null> {\n\t\tconst shop = this.storage.get(id);\n\n\t\tif (shop === undefined) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn shop;\n\t}\n\n\tasync updateShop(shop: SimpleShop) {\n\t\tthis.storage.set(shop.getShopId(), shop);\n\t}\n\n\tasync deleteShop(id: string) {\n\t\tthis.storage.delete(id);\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.test.d.ts","sourceRoot":"","sources":["../../src/repository.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { InMemoryShopRepository, SimpleShop } from "../src/repository.js";
|
|
3
|
+
describe("Repository", async () => {
|
|
4
|
+
test("SimpleShop", async () => {
|
|
5
|
+
const shop = new SimpleShop("test", "test", "test");
|
|
6
|
+
expect(shop.getShopId()).toBe("test");
|
|
7
|
+
expect(shop.getShopUrl()).toBe("test");
|
|
8
|
+
expect(shop.getShopSecret()).toBe("test");
|
|
9
|
+
expect(shop.getShopClientId()).toBeNull();
|
|
10
|
+
expect(shop.getShopClientSecret()).toBeNull();
|
|
11
|
+
expect(shop.getShopActive()).toBe(true);
|
|
12
|
+
shop.setShopActive(false);
|
|
13
|
+
expect(shop.getShopActive()).toBe(false);
|
|
14
|
+
shop.setShopCredentials("test", "test");
|
|
15
|
+
expect(shop.getShopClientId()).toBe("test");
|
|
16
|
+
expect(shop.getShopClientSecret()).toBe("test");
|
|
17
|
+
});
|
|
18
|
+
test("InMemoryShopRepository", async () => {
|
|
19
|
+
const shop = new SimpleShop("test", "test", "test");
|
|
20
|
+
const repository = new InMemoryShopRepository();
|
|
21
|
+
expect(repository.getShopById("test")).resolves.toBeNull();
|
|
22
|
+
await repository.createShop("test", "test", "test");
|
|
23
|
+
expect(repository.getShopById("test")).resolves.toBeInstanceOf(SimpleShop);
|
|
24
|
+
await repository.deleteShop("test");
|
|
25
|
+
expect(repository.getShopById("test")).resolves.toBeNull();
|
|
26
|
+
await repository.updateShop(shop);
|
|
27
|
+
expect(repository.getShopById("test")).resolves.toBe(shop);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=repository.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.test.js","sourceRoot":"","sources":["../../src/repository.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE1E,QAAQ,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;IACjC,IAAI,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;QAC7B,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAEpD,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAE1B,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEzC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAExC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAEpD,MAAM,UAAU,GAAG,IAAI,sBAAsB,EAAE,CAAC;QAEhD,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAE3D,MAAM,UAAU,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAEpD,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAE3E,MAAM,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAEpC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAE3D,MAAM,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAElC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, test } from \"bun:test\";\nimport { InMemoryShopRepository, SimpleShop } from \"../src/repository.js\";\n\ndescribe(\"Repository\", async () => {\n\ttest(\"SimpleShop\", async () => {\n\t\tconst shop = new SimpleShop(\"test\", \"test\", \"test\");\n\n\t\texpect(shop.getShopId()).toBe(\"test\");\n\t\texpect(shop.getShopUrl()).toBe(\"test\");\n\t\texpect(shop.getShopSecret()).toBe(\"test\");\n\t\texpect(shop.getShopClientId()).toBeNull();\n\t\texpect(shop.getShopClientSecret()).toBeNull();\n\t\texpect(shop.getShopActive()).toBe(true);\n\n\t\tshop.setShopActive(false);\n\n\t\texpect(shop.getShopActive()).toBe(false);\n\n\t\tshop.setShopCredentials(\"test\", \"test\");\n\n\t\texpect(shop.getShopClientId()).toBe(\"test\");\n\t\texpect(shop.getShopClientSecret()).toBe(\"test\");\n\t});\n\n\ttest(\"InMemoryShopRepository\", async () => {\n\t\tconst shop = new SimpleShop(\"test\", \"test\", \"test\");\n\n\t\tconst repository = new InMemoryShopRepository();\n\n\t\texpect(repository.getShopById(\"test\")).resolves.toBeNull();\n\n\t\tawait repository.createShop(\"test\", \"test\", \"test\");\n\n\t\texpect(repository.getShopById(\"test\")).resolves.toBeInstanceOf(SimpleShop);\n\n\t\tawait repository.deleteShop(\"test\");\n\n\t\texpect(repository.getShopById(\"test\")).resolves.toBeNull();\n\n\t\tawait repository.updateShop(shop);\n\n\t\texpect(repository.getShopById(\"test\")).resolves.toBe(shop);\n\t});\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.test.d.ts","sourceRoot":"","sources":["../../src/signer.test.ts"],"names":[],"mappings":""}
|