Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor features with terms and conditions #303

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions src/__tests__/unit/features.controller.unit.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import {
createStubInstance,
expect, stubExpressContext,
} from '@loopback/testlab';
import { FeaturesController } from '../../controllers/features.controller';
import { FeaturesDataService } from '../../services';
import { TermsDataService } from '../../services/terms-data.service';
import { StubbedInstanceWithSinonAccessor} from "@loopback/testlab";
import { FeaturesMongoDbDataService } from '../../services/features-mongo.service';
import { FeaturesDbDataModel } from '../../models/features-data.model';
Fixed Show fixed Hide fixed
import { TermsMongoDbDataService } from '../../services/terms-mongo.service';

describe('FeaturesController (unit)', () => {
const mockedService = <FeaturesDataService>{};
let mockedService: StubbedInstanceWithSinonAccessor<FeaturesDataService>;
let mockedTermsService:StubbedInstanceWithSinonAccessor<TermsDataService>;
let context = stubExpressContext();
mockedService = createStubInstance(FeaturesMongoDbDataService);
mockedTermsService = createStubInstance(TermsMongoDbDataService);


describe('get()',() => {
it('retrieves the features flags Information', async() => {
const controller = new FeaturesController(context.response, mockedService);
await controller.get();
let result = await context.result;
expect(result.payload).not.null();
mockedService.stubs.getAll.resolves([FeaturesDbDataModel.clone({
name: 'feature1', value: true,
creationDate: undefined,
lastUpdateDate: undefined,
enabled: false,
version: 0,
})]);
mockedTermsService.stubs.getVersion.resolves();
const controller = new FeaturesController(context.response, mockedService, mockedTermsService);
const response = await controller.get();
expect(response.statusCode).to.equal(200);
});
});

Expand Down
47 changes: 34 additions & 13 deletions src/controllers/features.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@ import { RestBindings, get, getModelSchemaRef, Response, } from '@loopback/rest'
import { ServicesBindings } from '../dependency-injection-bindings';
import { FeaturesDataService } from '../services/features-data.service';
import { FeaturesDbDataModel } from '../models/features-data.model';
import { TermsDataService } from '../services/terms-data.service';

export class FeaturesController {
logger: Logger;
private featuresDatService: FeaturesDataService;
private termsDatService: TermsDataService;
HTTP_SUCCESS_OK = 200;
HTTP_ERROR = 500;
constructor(
@inject(RestBindings.Http.RESPONSE) private response: Response,
@inject(ServicesBindings.FEATURES_SERVICE)
featuresDatService: FeaturesDataService,
@inject(ServicesBindings.TERMS_SERVICE)
termsDatService: TermsDataService,
) {
this.featuresDatService = featuresDatService;
this.termsDatService = termsDatService;
this.logger = getLogger('features-controller');
}

Expand Down Expand Up @@ -50,19 +55,35 @@ export class FeaturesController {
},
})
public async get(): Promise<Response> {
this.logger.debug('[get] started');
let retorno = [new FeaturesDbDataModel()];
let responseCode = this.HTTP_ERROR;
try {
retorno = await this.featuresDatService.getAll();
return new Promise<Response>((resolve) => {
this.logger.debug('[get] started');
let features = [new FeaturesDbDataModel()];
let responseCode = this.HTTP_ERROR;
this.featuresDatService.getAll()
.then((featuresFromDb) => {
features = featuresFromDb;
const termsIdx = featuresFromDb.findIndex((feature) => feature.name === 'terms_and_conditions');
if (!termsIdx) {
responseCode = this.HTTP_SUCCESS_OK;
this.response.contentType('application/json').status(responseCode)
.send(features);
resolve(this.response);
}
this.logger.info(`[get] Retrieved terms idx: ${termsIdx}`);
return Promise.all([this.termsDatService.getVersion(features[termsIdx].version), termsIdx]);
})
.then(([terms, termsIdx]) => {
features[termsIdx].value = terms ? terms.value : 'Version not found';
this.logger.info(`[get] Retrieved the features: ${JSON.stringify(features)}`);
responseCode = this.HTTP_SUCCESS_OK;
this.logger.info(`[get] Retrieved the features: ${JSON.stringify(retorno)}`);
} catch (e) {
this.logger.warn(`[get] Got an error: ${e}`);
}
this.response.contentType('application/json').status(responseCode).send(
retorno
);
return this.response;
this.response.contentType('application/json').status(responseCode)
.send(features);
resolve(this.response);
})
.catch((error) => {
this.logger.warn(`[get] Got an error: ${error}`);
resolve(this.response);
});
});
}
}
1 change: 1 addition & 0 deletions src/dependency-injection-bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ export const ServicesBindings = {
REGISTER_SERVICE: 'services.RegisterService',
FEATURES_SERVICE: 'services.FeaturesDataService',
FLYOVER_SERVICE: 'services.FlyoverService',
TERMS_SERVICE: 'services.TermsDataService',
};
14 changes: 10 additions & 4 deletions src/dependency-injection-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {RskNodeService} from './services/rsk-node.service';
import {SyncStatusMongoService} from './services/sync-status-mongo.service';
import { PegoutDataProcessor } from './services/pegout-data.processor';
import { FeaturesMongoDbDataService } from './services/features-mongo.service';
import { TermsMongoDbDataService } from './services/terms-mongo.service';

export class DependencyInjectionHandler {
public static configureDependencies(app: Application): void {
Expand Down Expand Up @@ -155,9 +156,14 @@ export class DependencyInjectionHandler {
.toClass(RegisterService)
.inScope(BindingScope.SINGLETON);

app
.bind(ServicesBindings.FEATURES_SERVICE)
.toClass(FeaturesMongoDbDataService)
.inScope(BindingScope.SINGLETON);
app
.bind(ServicesBindings.FEATURES_SERVICE)
.toClass(FeaturesMongoDbDataService)
.inScope(BindingScope.SINGLETON);

app
.bind(ServicesBindings.TERMS_SERVICE)
.toClass(TermsMongoDbDataService)
.inScope(BindingScope.SINGLETON);
}
}
28 changes: 18 additions & 10 deletions src/models/features-data.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface FeaturesDataModel {
creationDate: Date;
lastUpdateDate: Date;
name: string;
value: string;
enabled: boolean;
version: number;
}

Expand All @@ -17,7 +17,7 @@ export class FeaturesAppDataModel implements FeaturesDataModel{
creationDate: Date;
lastUpdateDate: Date;
name: string;
value: string;
enabled: boolean;
version: number;
}

Expand All @@ -42,29 +42,37 @@ export class FeaturesDbDataModel implements SearchableModel, FeaturesDataModel {
name: string;

@property({
type: 'string',
type: 'boolean',
})
value: string;
enabled: boolean;

@property({
type: 'number',
})
version: number;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string]: any;

getId() {
return this.name;
}
getIdFieldName(): string {
return 'name';
}

public static clone(other: FeaturesDbDataModel): FeaturesDbDataModel {
public static clone(other: Partial<FeaturesDbDataModel>): FeaturesDbDataModel {
const sanitizedData: Partial<FeaturesDbDataModel> = {};
const features: FeaturesDbDataModel = new FeaturesDbDataModel();
features.creationDate = other.creationDate;
features.lastUpdateDate = other.lastUpdateDate;
features.name = other.name;
features.value = other.value;
features.version = other.version;
Object.entries(other).forEach(([key, value]) => {
const theKey = key as keyof FeaturesDbDataModel;
if (value !== undefined) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
sanitizedData[theKey] = value;
}
});
Object.assign(features, sanitizedData);
return features;
}

Expand Down
1 change: 1 addition & 0 deletions src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from './fee-amount.model';
export * from './register-payload.model';
export * from './features.model';
export * from './pegout-status.model';
export * from './terms-db-data.model';
31 changes: 31 additions & 0 deletions src/models/terms-db-data.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { model, property} from '@loopback/repository';
import { SearchableModel } from "./rsk/searchable-model";

@model()
export class TermsDbDataModel implements SearchableModel {

@property({
type: 'number',
required: true,
})
version: number;

@property({
type: 'string',
required: true,
})
value: string;


constructor(data?: Partial<TermsDbDataModel>) {
Object.assign(this, data);;
}

getId() {
return this.version;
}
// eslint-disable-next-line class-methods-use-this
getIdFieldName(): string {
return "version";
}
}
2 changes: 1 addition & 1 deletion src/services/features-mongo.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const FeaturesSchema = new mongoose.Schema({
creationDate: {type: Date},
lastUpdateDate: {type: Date},
name: {type: String, required: true},
value: {type: String, required: true},
enabled: {type: Boolean, required: true},
version: {type: Number, required: true},
});

Expand Down
6 changes: 6 additions & 0 deletions src/services/terms-data.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { GenericDataService } from './generic-data-service';
import { TermsDbDataModel } from '../models';

export interface TermsDataService extends GenericDataService<TermsDbDataModel> {
getVersion(version: number): Promise<TermsDbDataModel>;
}
44 changes: 44 additions & 0 deletions src/services/terms-mongo.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import mongoose from 'mongoose';
import {MongoDbDataService} from './mongodb-data.service';
import { TermsDbDataModel } from '../models';
import { TermsDataService } from './terms-data.service';

/*
- THESE MODEL INTERFACES AND CLASSES ARE REQUIRED FOR MONGO BUT WE DON'T WANT THEM EXPOSED OUT OF THIS LAYER
*/
interface TermsMongoModel extends mongoose.Document, TermsDbDataModel {
}

const TermsSchema = new mongoose.Schema({
version: {type: Number, required: true},
value: {type: String, required: true},
});

const TermsConnector = mongoose.model<TermsMongoModel>("Terms", TermsSchema);

export class TermsMongoDbDataService extends MongoDbDataService<TermsDbDataModel, TermsMongoModel> implements TermsDataService {
protected getByIdFilter(id: any) {
throw new Error('Method not implemented.');
}
protected getManyFilter(filter?: any) {
throw new Error('Method not implemented.');
}
protected getLoggerName(): string {
return 'TermsMongoService';
}
protected getConnector(): mongoose.Model<TermsMongoModel, {}, {}> {
this.verifyAndCreateConnectionIfIsNecessary();
return TermsConnector;
}
async verifyAndCreateConnectionIfIsNecessary() {
await this.ensureConnection();
}
public async getVersion(version: number): Promise<TermsDbDataModel> {
const [document] = await this.getConnector()
.find({ version })
.exec();
return document;
}

}
Loading