Skip to content

Commit

Permalink
refactor(sandbox): refactor feature toggle example sandbox (#2236)
Browse files Browse the repository at this point in the history
* refactor(feature-toggle): refactor code

refactor code

MIGRATION CHANGE:
migration-20230612141912- refactor code
migration-20210913104858- refactor code

0

* refactor(feature-toggle): lint issues

lint issues

0
lint issues
  • Loading branch information
vipul-sourcefuse authored Jan 28, 2025
1 parent f5fc1e1 commit 20257a1
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 31 deletions.
38 changes: 28 additions & 10 deletions sandbox/feature-toggle-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,45 @@
This application is generated using [LoopBack 4 CLI](https://loopback.io/doc/en/lb4/Command-line-interface.html) with the
[initial project layout](https://loopback.io/doc/en/lb4/Loopback-application-layout.html).

## Install dependencies
## STEP 1: Install dependencies

By default, dependencies were installed when this application was generated.
Whenever dependencies in `package.json` are changed, run the following command:
To install the project dependencies, run the following command:

```sh
npm install
```
## STEP 2: Set up Environment Variables
1. Copy the .env.example file to .env:

To only install resolved dependencies in `package-lock.json`:
```sh
cp .env.example .env
```
2. Open the .env file and configure the necessary environment variables (e.g., database connection settings, JWT secret keys).

## STEP 3: Database Migration
Run the following command to migrate the database:
```sh
npm ci
npm run db:migrate
```

## Run the application
## STEP 4: Set up JWT Asymmetric Authentication
This project uses JWT tokens for authentication. Ensure you have the following:
* A private key (JWT_PRIVATE_KEY) for signing the JWT tokens.
* A public key (JWT_PUBLIC_KEY) for verifying the JWT tokens.

Add these keys to the .env file or ensure they are properly configured in your authentication service.

## STEP 5: Start the application
Once everything is set up, you can start the Node.js application with the following command:
```sh
npm start
```
This will start the server, and you can begin testing the API.

You can also run `node .` to skip the build step.
## STEP 6: Testing the API
After starting the application, you can test the API using Swagger UI. Open your browser and visit:

Open http://127.0.0.1:3000 in your browser.
http://127.0.0.1:3000 Replace the URL according to your configuration

## Rebuild the project

Expand Down Expand Up @@ -55,8 +70,6 @@ npm run lint:fix
```

## Other useful commands

- `npm run migrate`: Migrate database schemas for models
- `npm run openapi-spec`: Generate OpenAPI spec into a file
- `npm run docker:build`: Build a Docker image for this application
- `npm run docker:run`: Run this application inside a Docker container
Expand All @@ -67,6 +80,11 @@ npm run lint:fix
npm test
```

## Troubleshooting
* Ensure that your database is correctly configured and running.
* Make sure that the JWT private and public keys are properly set up in the .env file.
* If you encounter issues with Swagger UI not loading, verify that the server has successfully started and that the correct port is specified.

## What's next

Please check out [LoopBack 4 documentation](https://loopback.io/doc/en/lb4/) to
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

var dbm;
var type;
var seed;
var fs = require('fs');
var path = require('path');
var Promise;

/**
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
Promise = options.Promise;
};

exports.up = function (db) {
var filePath = path.join(
__dirname,
'sqls',
'20230612141912-update-tables-up.sql',
);
return new Promise(function (resolve, reject) {
fs.readFile(filePath, {encoding: 'utf-8'}, function (err, data) {
if (err) return reject(err);
console.log('received data: ' + data);

resolve(data);
});
}).then(function (data) {
return db.runSql(data);
});
};

exports.down = function (db) {
var filePath = path.join(
__dirname,
'sqls',
'20230612141912-update-tables-down.sql',
);
return new Promise(function (resolve, reject) {
fs.readFile(filePath, {encoding: 'utf-8'}, function (err, data) {
if (err) return reject(err);
console.log('received data: ' + data);

resolve(data);
});
}).then(function (data) {
return db.runSql(data);
});
};

exports._meta = {
version: 1,
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,30 @@ GRANT ALL ON SCHEMA main TO public;

CREATE TABLE main.features (
id uuid DEFAULT md5(random()::text || clock_timestamp()::text)::uuid NOT NULL,
name varchar(50) NOT NULL ,
key varchar(50) NOT NULL ,
description varchar(50) ,
default_value bool DEFAULT true NOT NULL ,
name text NOT NULL ,
key text NOT NULL ,
description text ,
default_value text ,
type text ,
CONSTRAINT pk_features_id PRIMARY KEY ( id )
);

CREATE TABLE main.strategies (
id uuid DEFAULT md5(random()::text || clock_timestamp()::text)::uuid NOT NULL,
name varchar(50) NOT NULL ,
key varchar(50) NOT NULL ,
priority integer ,
name text NOT NULL ,
key text NOT NULL ,
priority integer ,
CONSTRAINT pk_strategies_id PRIMARY KEY ( id )
);

CREATE TABLE main.feature_toggles (
id uuid DEFAULT md5(random()::text || clock_timestamp()::text)::uuid NOT NULL,
feature_key varchar(50) NOT NULL ,
CREATE TABLE main.feature_values (
id uuid DEFAULT md5(random()::text || clock_timestamp()::text)::uuid NOT NULL,
feature_key varchar(50) NOT NULL ,
strategy_key varchar(50) NOT NULL ,
strategy_entity_id uuid ,
status bool DEFAULT true NOT NULL ,
CONSTRAINT pk_feature_toggles_id PRIMARY KEY ( id )
strategy_entity_id uuid NULL,
status bool DEFAULT true NOT NULL ,
value text,
CONSTRAINT pk_feature_values_id PRIMARY KEY ( id )
);

INSERT INTO main.strategies(name, key, priority)
Expand All @@ -36,7 +38,4 @@ INSERT INTO main.strategies(name, key, priority)
VALUES ('Tenant', 'Tenant', '2');

INSERT INTO main.strategies(name, key, priority)
VALUES ('User', 'User', '3');

INSERT INTO main.features(name, key, default_value)
VALUES ('Calendar', 'Calendar', 'true');
VALUES ('User', 'User', '3');
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ALTER TABLE main.features
drop column metadata,
drop column created_by,
drop column modified_by ,
drop column created_on ,
drop column modified_on
drop column deleted ,
drop column deleted_on ,
drop column deleted_by;


ALTER TABLE main.strategies
drop column created_by,
drop column modified_by ,
drop column created_on ,
drop column modified_on
drop column deleted ,
drop column deleted_on ,
drop column deleted_by;

ALTER TABLE main.feature_values
drop column created_by,
drop column modified_by ,
drop column created_on ,
drop column modified_on
drop column deleted ,
drop column deleted_on ,
drop column deleted_by;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* Replace with your SQL commands */

ALTER TABLE main.features
ADD metadata TEXT,
ADD created_by varchar(100),
ADD modified_by varchar(100),
ADD created_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL ,
ADD modified_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL ,
ADD deleted bool DEFAULT false NOT NULL ,
ADD deleted_on timestamptz ,
ADD deleted_by uuid ;

ALTER TABLE main.strategies
ADD created_by varchar(100),
ADD modified_by varchar(100),
ADD created_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL ,
ADD modified_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL ,
ADD deleted bool DEFAULT false NOT NULL ,
ADD deleted_on timestamptz ,
ADD deleted_by uuid ;

ALTER TABLE main.feature_values
ADD created_by varchar(100),
ADD modified_by varchar(100),
ADD created_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL ,
ADD modified_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL ,
ADD deleted bool DEFAULT false NOT NULL ,
ADD deleted_on timestamptz ,
ADD deleted_by uuid ;
6 changes: 3 additions & 3 deletions sandbox/feature-toggle-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
"start": "node -r source-map-support/register .",
"clean": "lb-clean dist *.tsbuildinfo .eslintcache",
"rebuild": "npm run clean && npm run build",
"db:migrate": "./node_modules/db-migrate/bin/db-migrate up --config './migrations/database.json'",
"db:migrate:down": "./node_modules/db-migrate/bin/db-migrate down --config './migrations/database.json'",
"db:migrate:reset": "./node_modules/db-migrate/bin/db-migrate reset --config './migrations/database.json'"
"db:migrate": "../../node_modules/db-migrate/bin/db-migrate up --config './migrations/database.json'",
"db:migrate:down": "../../node_modules/db-migrate/bin/db-migrate down --config './migrations/database.json'",
"db:migrate:reset": "../../node_modules/db-migrate/bin/db-migrate reset --config './migrations/database.json'"
},
"repository": {
"type": "git",
Expand Down
24 changes: 23 additions & 1 deletion sandbox/feature-toggle-example/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ import {
} from 'loopback4-authorization';
import path from 'path';
import {MySequence} from './sequence';
import {
BearerVerifierBindings,
BearerVerifierComponent,
BearerVerifierConfig,
BearerVerifierType,
ServiceSequence,
} from '@sourceloop/core';
import {AuthenticationComponent} from 'loopback4-authentication';
dotenv.config();

export {ApplicationConfig};
Expand All @@ -46,10 +54,24 @@ export class FeatureToggleExampleApplication extends BootMixin(

this.bind(FeatureToggleBindings.Config).to({
bindControllers: true,
useCustomSequence: false,
useCustomSequence: true,
});
this.component(FeatureToggleServiceComponent);

this.sequence(ServiceSequence);

// Mount authentication component for default sequence
this.component(AuthenticationComponent);
// Mount bearer verifier component
this.bind(BearerVerifierBindings.Config).to({
authServiceUrl: '',
useSymmetricEncryption: true,
type: BearerVerifierType.service,
} as BearerVerifierConfig);
this.component(BearerVerifierComponent);

// Mount authorization component for default sequence

this.bind(AuthorizationBindings.CONFIG).to({
allowAlwaysPaths: ['/explorer'],
});
Expand Down

0 comments on commit 20257a1

Please sign in to comment.