@ogcio/building-blocks-sdk 0.0.10 → 0.0.11
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/README.md +55 -0
- package/package.json +14 -3
- package/src/client/clients/featureFlags/const.ts +3 -0
- package/src/client/clients/featureFlags/index.test.ts +63 -0
- package/src/client/clients/featureFlags/index.ts +74 -0
- package/src/client/clients/featureFlags/open-api-definition.json +40422 -0
- package/src/client/clients/featureFlags/schema.ts +33768 -0
- package/src/client/clients/featureFlags/utils.test.ts +51 -0
- package/src/client/clients/featureFlags/utils.ts +42 -0
- package/src/clients-configurations/clients-configuration.json +8 -0
- package/src/index.ts +5 -0
- package/src/types/index.ts +5 -1
- package/tap.yml +14 -0
- package/tsconfig.json +1 -0
- package/vitest.config.cts +9 -0
package/README.md
CHANGED
|
@@ -162,3 +162,58 @@ To fix the formatting and linting errors run:
|
|
|
162
162
|
npm run fix:formatting
|
|
163
163
|
npm run fix:linting
|
|
164
164
|
```
|
|
165
|
+
|
|
166
|
+
### Testing
|
|
167
|
+
|
|
168
|
+
The project uses `vitest` for testing. To run the tests:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm test
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Feature Flags
|
|
175
|
+
|
|
176
|
+
### Pre-requisites
|
|
177
|
+
For local development, you should have the Feature Flags service running.
|
|
178
|
+
Refer to the [unleash](https://github.com/ogcio/unleash) repository for instructions on how to run the service.
|
|
179
|
+
You can also find examples on how to run Unleash with different auth strategies in the [unleash-examples](https://github.com/ogcio/unleash-examples/tree/feat/oidc-auth) repository.
|
|
180
|
+
|
|
181
|
+
### Usage
|
|
182
|
+
|
|
183
|
+
To use the Feature Flags service you will need:
|
|
184
|
+
|
|
185
|
+
- `baseUrl` A valid `Feature Flags` service URL. Use `http://localhost:4242` for local development.
|
|
186
|
+
- `token` A valid `Feature Flags` service token. Refer to [Client Tokens](https://docs.getunleash.io/reference/api-tokens-and-client-keys#client-tokens)
|
|
187
|
+
for instructions on how to generate a token.
|
|
188
|
+
|
|
189
|
+
Initialize the SDK with the `featureFlags` service:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const sdk = getBuildingBlockSDK({
|
|
193
|
+
services: {
|
|
194
|
+
featureFlags: {
|
|
195
|
+
baseUrl,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
getTokenFn: () => token,
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Use the `featureFlags` service to check if a feature is enabled (without any context):
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const isEnabled = await sdk.featureFlags.isFlagEnabled("feature-name");
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Use the `featureFlags` service to check if a feature is enabled with context:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const isEnabled = await sdk.featureFlags.isFlagEnabled("feature-name", {
|
|
212
|
+
userId: "userId",
|
|
213
|
+
sessionId: "sessionId",
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
*Note*: The `isFlagEnabled` is asynchronous because if the client is not connected yet,
|
|
218
|
+
it will wait for the connection to be established before checking the flag.
|
|
219
|
+
Once the client is connected, the flag will be checked synchronously.
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ogcio/building-blocks-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "
|
|
8
|
+
"test": "TAP_RCFILE=tap.yml tap --reporter junit --reporter-file results.xml",
|
|
9
|
+
"test:local": "TAP_RCFILE=tap.yml tap",
|
|
10
|
+
"test:e2e": "cd ./e2e && bru run --env local",
|
|
11
|
+
"test:smoke:e2e": "echo \"Error: no test specified\" && exit 0",
|
|
12
|
+
"test:regression:e2e": "cd ./e2e && mkdir -p test-results && bru run --env dev --output ./test-results/results.xml --format junit",
|
|
9
13
|
"build": "rm -rf dist && tsc -p tsconfig.prod.json",
|
|
10
14
|
"clients:update": "node --import=tsx src/cli/index.ts clients:update -c src/clients-configurations/clients-configuration.json",
|
|
11
15
|
"clients:outdated": "node --import=tsx src/cli/index.ts clients:outdated -c src/clients-configurations/clients-configuration.json",
|
|
@@ -32,15 +36,22 @@
|
|
|
32
36
|
"@types/http-errors": "^2.0.4",
|
|
33
37
|
"commitlint": "^19.5.0",
|
|
34
38
|
"husky": "^9.1.6",
|
|
39
|
+
"tap": "^21.0.1",
|
|
40
|
+
"testdouble": "^3.20.2",
|
|
41
|
+
"vitest": "^2.1.3",
|
|
35
42
|
"tsx": "^4.19.2",
|
|
36
43
|
"typescript": "^5.6.3"
|
|
37
44
|
},
|
|
38
45
|
"peerDependencies": {
|
|
39
|
-
"@logto/node": "2.5.5"
|
|
46
|
+
"@logto/node": "2.5.5",
|
|
47
|
+
"unleash-client": "^6.1.1"
|
|
40
48
|
},
|
|
41
49
|
"peerDependenciesMeta": {
|
|
42
50
|
"@logto/node": {
|
|
43
51
|
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"unleash-client": {
|
|
54
|
+
"optional": true
|
|
44
55
|
}
|
|
45
56
|
},
|
|
46
57
|
"keywords": [],
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import t from "tap";
|
|
2
|
+
import * as td from "testdouble";
|
|
3
|
+
import FeatureFlags from "./index.js";
|
|
4
|
+
|
|
5
|
+
let isEnabled = true;
|
|
6
|
+
|
|
7
|
+
await td.replaceEsm("unleash-client", {
|
|
8
|
+
initialize: td.func(),
|
|
9
|
+
InMemStorageProvider: td.func(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
let fetchResponse = {};
|
|
13
|
+
|
|
14
|
+
global.fetch = async () =>
|
|
15
|
+
({
|
|
16
|
+
ok: true,
|
|
17
|
+
status: 200,
|
|
18
|
+
json: async () => fetchResponse,
|
|
19
|
+
headers: new Headers(),
|
|
20
|
+
}) as Response;
|
|
21
|
+
|
|
22
|
+
t.test("FeatureFlags", async (t) => {
|
|
23
|
+
const baseUrl = "http://fakehost";
|
|
24
|
+
const getTokenFn = () => "test-token";
|
|
25
|
+
let featureFlags: FeatureFlags;
|
|
26
|
+
|
|
27
|
+
t.beforeEach(async () => {
|
|
28
|
+
featureFlags = new FeatureFlags({ baseUrl, getTokenFn });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
t.test(
|
|
32
|
+
"should initialize unleash client with correct parameters",
|
|
33
|
+
async (t) => {
|
|
34
|
+
t.ok(featureFlags.isConnected);
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
t.test("should return false if flag is not enabled", async (t) => {
|
|
39
|
+
isEnabled = false;
|
|
40
|
+
const result = await featureFlags.isFlagEnabled("test-flag");
|
|
41
|
+
t.equal(result, isEnabled);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
t.test("should return true if flag is enabled", async (t) => {
|
|
45
|
+
isEnabled = true;
|
|
46
|
+
const result = await featureFlags.isFlagEnabled("test-flag");
|
|
47
|
+
t.equal(result, isEnabled);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
t.test(
|
|
51
|
+
"should call GET method on client when getFeatureFlags is called",
|
|
52
|
+
async () => {
|
|
53
|
+
fetchResponse = { data: { features: [] }, metadata: {} };
|
|
54
|
+
const result = await featureFlags.getFeatureFlags();
|
|
55
|
+
t.ok(result);
|
|
56
|
+
t.same(result, {
|
|
57
|
+
data: [],
|
|
58
|
+
metadata: {},
|
|
59
|
+
error: null,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type createClient from "openapi-fetch";
|
|
2
|
+
import {
|
|
3
|
+
type Context,
|
|
4
|
+
InMemStorageProvider,
|
|
5
|
+
type Unleash,
|
|
6
|
+
initialize,
|
|
7
|
+
} from "unleash-client";
|
|
8
|
+
import type { BaseApiClientParams } from "../../../types/index.js";
|
|
9
|
+
import { FEATURE_FLAGS } from "../../../types/index.js";
|
|
10
|
+
import BaseClient from "../../base-client.js";
|
|
11
|
+
import { DEFAULT_PROJECT_ID } from "./const.js";
|
|
12
|
+
import type { components, paths } from "./schema.js";
|
|
13
|
+
import { waitForConnection } from "./utils.js";
|
|
14
|
+
|
|
15
|
+
class FeatureFlags extends BaseClient<paths> {
|
|
16
|
+
declare client: ReturnType<typeof createClient<paths>>;
|
|
17
|
+
protected serviceName = FEATURE_FLAGS;
|
|
18
|
+
|
|
19
|
+
private unleashClient: Unleash | null = null;
|
|
20
|
+
public isConnected = false;
|
|
21
|
+
|
|
22
|
+
constructor({ baseUrl, getTokenFn }: BaseApiClientParams) {
|
|
23
|
+
super({ baseUrl, getTokenFn });
|
|
24
|
+
const token = getTokenFn ? (getTokenFn(FEATURE_FLAGS) as string) : "";
|
|
25
|
+
this.unleashClient = initialize({
|
|
26
|
+
appName: this.serviceName,
|
|
27
|
+
url: `${baseUrl}/api`,
|
|
28
|
+
refreshInterval: 1000,
|
|
29
|
+
customHeaders: {
|
|
30
|
+
Authorization: token,
|
|
31
|
+
},
|
|
32
|
+
storageProvider: new InMemStorageProvider(),
|
|
33
|
+
});
|
|
34
|
+
this.unleashClient.on("error", console.error);
|
|
35
|
+
this.unleashClient.on("synchronized", () => {
|
|
36
|
+
this.isConnected = true;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async isFlagEnabled(name: string, context?: Context) {
|
|
41
|
+
await this.waitForConnection();
|
|
42
|
+
return this.unleashClient?.isEnabled(name, context, () => false) ?? false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getFeatureFlags(projectId = DEFAULT_PROJECT_ID) {
|
|
46
|
+
return await this.client
|
|
47
|
+
.GET("/api/admin/projects/{projectId}/features", {
|
|
48
|
+
params: {
|
|
49
|
+
path: {
|
|
50
|
+
projectId,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
.then(
|
|
55
|
+
(response) => {
|
|
56
|
+
// @ts-expect-error: TODO: fix me
|
|
57
|
+
const { data, metadata, error } = this.formatResponse(response);
|
|
58
|
+
return {
|
|
59
|
+
data: data?.features as components["schemas"]["projectFeatureSchema"][],
|
|
60
|
+
metadata,
|
|
61
|
+
error,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
(reason) => this.formatError(reason),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async waitForConnection(everyMs = 10) {
|
|
69
|
+
return waitForConnection(this, everyMs);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default FeatureFlags;
|
|
74
|
+
export type { Context as FeatureFlagsEvaluationContext };
|