From fd20b82779f040962e8148daa0d102a923a3b224 Mon Sep 17 00:00:00 2001 From: Alexandru Ciobanu Date: Thu, 20 Feb 2025 16:19:10 +0700 Subject: [PATCH] feat(openfeature-node-provider): enhance todo feature management with config and validation - Added support for configurable todo creation with max length validation - Updated feature flag names to be more consistent (e.g., "show-todos", "create-todos") - Implemented feature configuration retrieval for todo creation - Added type definition for create todos configuration - Improved feature tracking and error handling in todo operations --- .../openfeature-node-provider/example/app.ts | 30 ++++++++++++++----- .../example/bucket.ts | 20 ++++++++++++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/openfeature-node-provider/example/app.ts b/packages/openfeature-node-provider/example/app.ts index ea5b72cf..fdfcdd8b 100644 --- a/packages/openfeature-node-provider/example/app.ts +++ b/packages/openfeature-node-provider/example/app.ts @@ -1,15 +1,15 @@ import express from "express"; import "./bucket"; import { EvaluationContext, OpenFeature } from "@openfeature/server-sdk"; -import provider from "./bucket"; +import provider, { CreateTodosConfig } from "./bucket"; -// In the following, we assume that targetingKey is a unique identifier for the user +// In the following, we assume that targetingKey is a unique identifier for the user. type Context = EvaluationContext & { targetingKey: string; companyId: string; }; -// Augment the Express types to include the some context property on the `res.locals` object +// Augment the Express types to include the some context property on the `res.locals` object. declare global { namespace Express { interface Locals { @@ -36,6 +36,7 @@ const todos = ["Buy milk", "Walk the dog"]; app.get("/", (_req, res) => { const ofClient = OpenFeature.getClient(); ofClient.track("front-page-viewed", res.locals.context); + res.json({ message: "Ready to manage some TODOs!" }); }); @@ -46,7 +47,7 @@ app.get("/todos", async (req, res) => { // and that the indexing for feature name below is type-checked at compile time. const ofClient = OpenFeature.getClient(); const isEnabled = await ofClient.getBooleanValue( - "show-todo", + "show-todos", false, res.locals.context, ); @@ -75,10 +76,23 @@ app.post("/todos", async (req, res) => { res.locals.context, ); - // Check if the user has the "create-todos" feature enabled + // Check if the user has the "create-todos" feature enabled. if (isEnabled) { + // Get the configuration for the "create-todos" feature. + // We expect the configuration to be a JSON object with a `maxLength` property. + const config = await ofClient.getObjectValue( + "create-todos", + { maxLength: 100 }, + res.locals.context, + ); + + // Check if the todo is too long. + if (todo.length > config.maxLength) { + return res.status(400).json({ error: "Todo is too long" }); + } + // Track the feature usage - ofClient.track("create-todo", res.locals.context); + ofClient.track("create-todos", res.locals.context); todos.push(todo); return res.status(201).json({ todo }); @@ -98,7 +112,7 @@ app.delete("/todos/:idx", async (req, res) => { const ofClient = OpenFeature.getClient(); const isEnabled = await ofClient.getBooleanValue( - "delete-todo", + "delete-todos", false, res.locals.context, ); @@ -106,7 +120,7 @@ app.delete("/todos/:idx", async (req, res) => { if (isEnabled) { todos.splice(idx, 1); - ofClient.track("delete-todo", res.locals.context); + ofClient.track("delete-todos", res.locals.context); return res.json({}); } diff --git a/packages/openfeature-node-provider/example/bucket.ts b/packages/openfeature-node-provider/example/bucket.ts index 3462a72b..1121ee00 100644 --- a/packages/openfeature-node-provider/example/bucket.ts +++ b/packages/openfeature-node-provider/example/bucket.ts @@ -5,11 +5,29 @@ if (!process.env.BUCKET_SECRET_KEY) { throw new Error("BUCKET_SECRET_KEY is required"); } +export type CreateTodosConfig = { + maxLength: number; +}; + const provider = new BucketNodeProvider({ secretKey: process.env.BUCKET_SECRET_KEY!, - fallbackFeatures: ["show-todos"], + fallbackFeatures: { + "show-todos": { + isEnabled: true, + }, + "create-todos": { + isEnabled: true, + config: { + key: "default", + payload: { + maxLength: 100, + }, + }, + }, + }, logger: console, }); + OpenFeature.setProvider(provider); export default provider;