add RedisAPL
Finally an alternative to SaaS or only dev suggested solutions
This commit is contained in:
parent
be49d584cb
commit
4d697195e2
6 changed files with 11075 additions and 5 deletions
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: pnpm/action-setup@v2.2.4
|
- uses: pnpm/action-setup@v2.2.4
|
||||||
with:
|
with:
|
||||||
version: 8.2.0
|
version: 8.2.0
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
|
|
10934
package-lock.json
generated
Normal file
10934
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -18,20 +18,20 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"graphql": ">=16.6.0",
|
||||||
"next": ">=12",
|
"next": ">=12",
|
||||||
"react": ">=17",
|
"react": ">=17",
|
||||||
"react-dom": ">=17",
|
"react-dom": ">=17"
|
||||||
"graphql": ">=16.6.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "4.3.4",
|
"debug": "4.3.4",
|
||||||
"jose": "4.14.4",
|
"jose": "4.14.4",
|
||||||
"raw-body": "2.5.2",
|
"raw-body": "2.5.2",
|
||||||
|
"redis": "^4.6.10",
|
||||||
"retes": "0.33.0",
|
"retes": "0.33.0",
|
||||||
"uuid": "9.0.0"
|
"uuid": "9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"graphql": "16.8.0",
|
|
||||||
"@changesets/cli": "2.26.2",
|
"@changesets/cli": "2.26.2",
|
||||||
"@testing-library/dom": "^8.17.1",
|
"@testing-library/dom": "^8.17.1",
|
||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
|
@ -54,6 +54,7 @@
|
||||||
"eslint-plugin-react": "^7.31.6",
|
"eslint-plugin-react": "^7.31.6",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-simple-import-sort": "^8.0.0",
|
"eslint-plugin-simple-import-sort": "^8.0.0",
|
||||||
|
"graphql": "16.8.0",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
"jsdom": "^20.0.3",
|
"jsdom": "^20.0.3",
|
||||||
"lint-staged": "^13.0.3",
|
"lint-staged": "^13.0.3",
|
||||||
|
|
56
src/APL/redis-apl.test.ts
Normal file
56
src/APL/redis-apl.test.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { AuthData } from "./apl";
|
||||||
|
import { RedisAPL } from "./redis-apl";
|
||||||
|
|
||||||
|
// Obviously, for this test to pass you need to have a docker container running redis :)
|
||||||
|
const localRedisServerUrl = new URL("redis://127.0.0.1:6379/1");
|
||||||
|
const stubAuthData: AuthData = {
|
||||||
|
domain: "example.com",
|
||||||
|
token: "example-token",
|
||||||
|
saleorApiUrl: "https://example.com/graphql/",
|
||||||
|
appId: "42",
|
||||||
|
jwks: "{}",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("APL", () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
const apl = new RedisAPL(localRedisServerUrl, stubAuthData.appId);
|
||||||
|
await apl.delete(stubAuthData.saleorApiUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("redisAPL", () => {
|
||||||
|
describe("get", () => {
|
||||||
|
it("Returns auth data for existing api url", async () => {
|
||||||
|
const apl = new RedisAPL(localRedisServerUrl, stubAuthData.appId);
|
||||||
|
await apl.set(stubAuthData);
|
||||||
|
|
||||||
|
expect(await apl.get(stubAuthData.saleorApiUrl)).toStrictEqual(stubAuthData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Returns undefined for unknown api url", async () => {
|
||||||
|
const apl = new RedisAPL(localRedisServerUrl, stubAuthData.appId);
|
||||||
|
|
||||||
|
expect(await apl.get("unknown-domain.example.com")).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("set", () => {
|
||||||
|
it("should save to redis and return value afterwards", async () => {
|
||||||
|
const apl = new RedisAPL(localRedisServerUrl, stubAuthData.appId);
|
||||||
|
|
||||||
|
await apl.set(stubAuthData);
|
||||||
|
expect(await apl.get(stubAuthData.saleorApiUrl)).toStrictEqual(stubAuthData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("delete", () => {
|
||||||
|
it("Should delete when called with known domain", async () => {
|
||||||
|
const apl = new RedisAPL(localRedisServerUrl, stubAuthData.appId);
|
||||||
|
|
||||||
|
await apl.delete("api.random.sk");
|
||||||
|
expect(await apl.get(stubAuthData.saleorApiUrl)).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
79
src/APL/redis-apl.ts
Normal file
79
src/APL/redis-apl.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { createClient } from "redis";
|
||||||
|
|
||||||
|
import { APL, AplConfiguredResult, AplReadyResult, AuthData } from "./apl";
|
||||||
|
import { createAPLDebug } from "./apl-debug";
|
||||||
|
|
||||||
|
const debug = createAPLDebug("UpstashAPL");
|
||||||
|
|
||||||
|
export const UpstashAPLVariables = {
|
||||||
|
UPSTASH_TOKEN: "UPSTASH_TOKEN",
|
||||||
|
UPSTASH_URL: "UPSTASH_URL",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis APL
|
||||||
|
* @param redisUrl - in format redis[s]://[[username][:password]@][host][:port][/db-number],
|
||||||
|
* so for example redis://alice:foobared@awesome.redis.server:6380
|
||||||
|
* For saleor-platform, thats: `redis://redis:6379/1`
|
||||||
|
*/
|
||||||
|
export class RedisAPL implements APL {
|
||||||
|
private client;
|
||||||
|
|
||||||
|
private appID;
|
||||||
|
|
||||||
|
constructor(redisURL: URL, appID: string) {
|
||||||
|
if (!redisURL) throw new Error("No redis url defined");
|
||||||
|
if (!appID) throw new Error("The RedisAPL requires to know the app ID beforehand");
|
||||||
|
this.appID = appID;
|
||||||
|
this.client = createClient({ url: redisURL.toString() });
|
||||||
|
debug("RedisAPL: createClient.url : %j", redisURL.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private prepareKey(saleorApiUrl: string) {
|
||||||
|
return `${this.appID}:${saleorApiUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(saleorApiUrl: string): Promise<AuthData | undefined> {
|
||||||
|
await this.client.connect();
|
||||||
|
try {
|
||||||
|
const res = await this.client.get(this.prepareKey(saleorApiUrl));
|
||||||
|
debug("RedisAPL: get - received: %j", res);
|
||||||
|
if (res) {
|
||||||
|
return JSON.parse(res) as AuthData;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
await this.client.disconnect();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(authData: AuthData): Promise<void> {
|
||||||
|
await this.client.connect();
|
||||||
|
await this.client.set(this.prepareKey(authData.saleorApiUrl), JSON.stringify(authData));
|
||||||
|
debug("RedisAPL: set - set sucessfully: %j", authData);
|
||||||
|
await this.client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(saleorApiUrl: string): Promise<void> {
|
||||||
|
await this.client.connect();
|
||||||
|
const val = await this.client.getDel(this.prepareKey(saleorApiUrl));
|
||||||
|
debug("RedisAPL: del - deleted successfuly: %j", val);
|
||||||
|
await this.client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll(): Promise<AuthData[]> {
|
||||||
|
throw new Error("redisAPL does not support getAll method");
|
||||||
|
}
|
||||||
|
|
||||||
|
async isReady(): Promise<AplReadyResult> {
|
||||||
|
return { ready: this.client.isReady } as AplReadyResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
async isConfigured(): Promise<AplConfiguredResult> {
|
||||||
|
return { configured: this.client.isReady } as AplConfiguredResult;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { describe, expect,it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { extractAppPermissionsFromJwt } from "./extract-app-permissions-from-jwt";
|
import { extractAppPermissionsFromJwt } from "./extract-app-permissions-from-jwt";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue