Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 15 additions & 5 deletions cucumber.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
let common = [
'tests/**/features/**/*.feature', // Specify our feature files
'--require-module ts-node/register', // Load TypeScript module
'--require tests/**/features/step_definitions/*.steps.ts' // Load step definitions
const common = [
'--require-module ts-node/register' // Load TypeScript module
];

const backoffice_backend = [
...common,
'tests/apps/backoffice/backend/features/**/*.feature',
'--require tests/apps/backoffice/backend/features/step_definitions/*.steps.ts'
].join(' ');
const mooc_backend = [
...common,
'tests/apps/mooc_backend/features/**/*.feature',
'--require tests/apps/mooc_backend/features/step_definitions/*.steps.ts'
].join(' ');

module.exports = {
default: common
backoffice_backend,
mooc_backend
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
"scripts": {
"dev": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules --inspect=0.0.0.0:9267 ./src/apps/mooc_backend/server.ts",
"dev:backoffice:frontend": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules ./src/apps/backoffice/frontend/server.ts",
"dev:backoffice:backend": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules ./src/apps/backoffice/backend/server.ts",
"test": "npm run test:unit && npm run test:features && npm run cypress:run",
"test:unit": "NODE_ENV=test jest",
"test:features": "NODE_ENV=test cucumber-js -p default",
"test:features": "npm run test:mooc:backend:features && npm run test:backoffice:backend:features",
"test:mooc:backend:features": "NODE_ENV=test cucumber-js -p mooc_backend",
"test:backoffice:backend:features": "NODE_ENV=test cucumber-js -p backoffice_backend",
"lint": "tslint src/**/*.ts{,x}",
"start": "NODE_ENV=production node dist/src/apps/mooc_backend/server",
"start:backoffice:frontend": "NODE_ENV=production node dist/src/apps/backoffice/frontend/server",
"start:backoffice:backend": "NODE_ENV=production node dist/src/apps/backoffice/backend/server",
"build": "npm run build:clean && npm run build:tsc && npm run build:di",
"build:tsc": "tsc -p tsconfig.prod.json",
"build:di": "copy 'src/**/*.{json,yaml,html,png}' dist/src",
Expand Down
12 changes: 12 additions & 0 deletions src/Contexts/Backoffice/application/SearchAll/CoursesFinder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BackofficeCourseRepository } from '../../domain/BackofficeCourseRepository';
import { SearchAllCoursesResponse } from './SearchAllCoursesResponse';

export class CoursesFinder {
constructor(private coursesRepository: BackofficeCourseRepository) {}

async run() {
const courses = await this.coursesRepository.searchAll();

return new SearchAllCoursesResponse(courses);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Query } from '../../../Shared/domain/Query';

export class SearchAllCoursesQuery implements Query {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Query } from '../../../Shared/domain/Query';
import { QueryHandler } from '../../../Shared/domain/QueryHandler';
import { CoursesFinder } from './CoursesFinder';
import { SearchAllCoursesQuery } from './SearchAllCoursesQuery';
import { SearchAllCoursesResponse } from './SearchAllCoursesResponse';

export class SearchAllCoursesQueryHandler implements QueryHandler<SearchAllCoursesQuery, SearchAllCoursesResponse> {
constructor(private coursesFinder: CoursesFinder) {}

subscribedTo(): Query {
return SearchAllCoursesQuery;
}

async handle(_query: SearchAllCoursesQuery): Promise<SearchAllCoursesResponse> {
return this.coursesFinder.run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { BackofficeCourse } from '../../domain/BackofficeCourse';

export class SearchAllCoursesResponse {
readonly courses: Array<BackofficeCourse>;

constructor(courses: Array<BackofficeCourse>) {
this.courses = courses;
}
}
19 changes: 19 additions & 0 deletions src/Contexts/Backoffice/domain/AggregateRoot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DomainEvent } from '../../Shared/domain/DomainEvent';

export abstract class AggregateRoot {
private domainEvents: Array<DomainEvent>;

constructor() {
this.domainEvents = [];
}

pullDomainEvents(): Array<DomainEvent> {
return this.domainEvents;
}

record(event: DomainEvent): void {
this.domainEvents.push(event);
}

abstract toPrimitives(): any;
}
43 changes: 43 additions & 0 deletions src/Contexts/Backoffice/domain/BackofficeCourse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { AggregateRoot } from '../../Mooc/Courses/domain/AggregateRoot';
import { BackofficeCourseDuration } from './BackofficeCourseDuration';
import { BackofficeCourseId } from './BackofficeCourseId';
import { BackofficeCourseName } from './BackofficeCourseName';

export class BackofficeCourse extends AggregateRoot {
readonly id: BackofficeCourseId;
readonly name: BackofficeCourseName;
readonly duration: BackofficeCourseDuration;

constructor(id: BackofficeCourseId, name: BackofficeCourseName, duration: BackofficeCourseDuration) {
super();
this.id = id;
this.name = name;
this.duration = duration;
}

static create(
id: BackofficeCourseId,
name: BackofficeCourseName,
duration: BackofficeCourseDuration
): BackofficeCourse {
const course = new BackofficeCourse(id, name, duration);

return course;
}

static fromPrimitives(plainData: { id: string; name: string; duration: string }): BackofficeCourse {
return new BackofficeCourse(
new BackofficeCourseId(plainData.id),
new BackofficeCourseName(plainData.name),
new BackofficeCourseDuration(plainData.duration)
);
}

toPrimitives() {
return {
id: this.id.value,
name: this.name.value,
duration: this.duration.value
};
}
}
3 changes: 3 additions & 0 deletions src/Contexts/Backoffice/domain/BackofficeCourseDuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { StringValueObject } from '../../Shared/domain/value-object/StringValueObject';

export class BackofficeCourseDuration extends StringValueObject {}
3 changes: 3 additions & 0 deletions src/Contexts/Backoffice/domain/BackofficeCourseId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Uuid } from '../../Shared/domain/value-object/Uuid';

export class BackofficeCourseId extends Uuid {}
3 changes: 3 additions & 0 deletions src/Contexts/Backoffice/domain/BackofficeCourseName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { StringValueObject } from '../../Shared/domain/value-object/StringValueObject';

export class BackofficeCourseName extends StringValueObject {}
5 changes: 5 additions & 0 deletions src/Contexts/Backoffice/domain/BackofficeCourseRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { BackofficeCourse } from './BackofficeCourse';

export interface BackofficeCourseRepository {
searchAll(): Promise<Array<BackofficeCourse>>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MongoRepository } from '../../Shared/infrastructure/persistence/mongo/MongoRepository';
import { BackofficeCourse } from '../domain/BackofficeCourse';
import { BackofficeCourseRepository } from '../domain/BackofficeCourseRepository';

export class MongoBackofficeCourseRepository extends MongoRepository<BackofficeCourse>
implements BackofficeCourseRepository {
protected moduleName(): string {
return 'backofficeCourses';
}

async searchAll(): Promise<BackofficeCourse[]> {
const collection = await this.collection();

const documents = await collection.find({});

const courses: Array<BackofficeCourse> = (await documents.toArray()).map(document =>
BackofficeCourse.fromPrimitives({ ...document, id: document._id })
);

return courses;
}

async save(course: BackofficeCourse) {
return this.persist(course.id.value, course);
}
}
21 changes: 21 additions & 0 deletions src/apps/backoffice/backend/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import bodyParser from 'body-parser';
import express from 'express';
import helmet from 'helmet';
import compress from 'compression';
import { registerRoutes } from './routes';

const app: express.Express = express();

app.set('port', process.env.PORT || 3000);

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(helmet.xssFilter());
app.use(helmet.noSniff());
app.use(helmet.hidePoweredBy());
app.use(helmet.frameguard({ action: 'deny' }));
app.use(compress());

registerRoutes(app);

export default app;
22 changes: 22 additions & 0 deletions src/apps/backoffice/backend/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import convict from 'convict';

const convictConfig = convict({
env: {
doc: 'The application environment.',
format: ['production', 'development', 'staging', 'test'],
default: 'default',
env: 'NODE_ENV'
},
mongo: {
url: {
doc: 'The Mongo connection URL',
format: String,
env: 'MONGO_URL',
default: 'mongodb://localhost:27017/backoffice-backend-dev'
}
}
});

convictConfig.loadFile([__dirname + '/default.json', __dirname + '/' + convictConfig.get('env') + '.json']);

export default convictConfig;
1 change: 1 addition & 0 deletions src/apps/backoffice/backend/config/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
services:
Backoffice.Backend.courses.BackofficeCourseRepository:
class: ../../../../../../Contexts/Backoffice/infrastructure/MongoBackofficeCourseRepository
arguments: ['@Shared.ConnectionManager']

Backoffice.Backend.courses.CoursesFinder:
class: ../../../../../../Contexts/Backoffice/application/SearchAll/CoursesFinder
arguments: ["@Backoffice.Backend.courses.BackofficeCourseRepository"]

Backoffice.Backend.courses.SearchAllCoursesQueryHandler:
class: ../../../../../../Contexts/Backoffice/application/SearchAll/SearchAllCoursesQueryHandler
arguments: ["@Backoffice.Backend.courses.CoursesFinder"]
tags:
- { name: 'queryHandler' }

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
services:
Shared.Logger:
class: ../../../../../../Contexts/Shared/infrastructure/WinstonLogger
arguments: []

Shared.ConnectionManager:
factory:
class: ../../../../../../Contexts/Shared/infrastructure/persistence/mongo/MongoClientFactory
method: 'createClient'
arguments: ['mooc']

Shared.QueryHandlersInformation:
class: ../../../../../../Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation
arguments: ['!tagged queryHandler']

Shared.QueryBus:
class: ../../../../../../Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus
arguments: ['@Shared.QueryHandlersInformation']
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
imports:
- { resource: ./Shared/application.yaml }
- { resource: ./apps/application.yaml }
- { resource: ./Courses/application.yaml }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports:
- { resource: ./application.yaml }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports:
- { resource: ./application.yaml }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports:
- { resource: ./application.yaml }
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
imports:
- { resource: ./application.yaml }

services:
Backoffice.Backend.EnvironmentArranger:
class: ../../../../../../tests/Contexts/Shared/infrastructure/mongo/MongoEnvironmentArranger
arguments: ['@Shared.ConnectionManager']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
services:
Apps.Backoffice.Backend.controllers.StatusGetController:
class: ../../../controllers/StatusGetController
arguments: []

Apps.Backoffice.Backend.controllers.CoursesGetController:
class: ../../../controllers/CoursesGetController
arguments: ['@Shared.QueryBus']
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ContainerBuilder, YamlFileLoader } from 'node-dependency-injection';

const container = new ContainerBuilder();
const loader = new YamlFileLoader(container);
const env = process.env.NODE_ENV || 'dev';

loader.load(`${__dirname}/application_${env}.yaml`);

export default container;
1 change: 1 addition & 0 deletions src/apps/backoffice/backend/config/staging.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions src/apps/backoffice/backend/config/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"mongo": {
"url": "mongodb://localhost:27017/backoffice-backend-test"
}
}
5 changes: 5 additions & 0 deletions src/apps/backoffice/backend/controllers/Controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Request, Response } from 'express';

export interface Controller {
run(req: Request, res: Response): Promise<void>;
}
16 changes: 16 additions & 0 deletions src/apps/backoffice/backend/controllers/CoursesGetController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Request, Response } from 'express';
import httpStatus from 'http-status';
import { SearchAllCoursesQuery } from '../../../../Contexts/Backoffice/application/SearchAll/SearchAllCoursesQuery';
import { QueryBus } from '../../../../Contexts/Shared/domain/QueryBus';
import { Controller } from './Controller';

export class CoursesGetController implements Controller {
constructor(private queryBus: QueryBus) {}

async run(_req: Request, res: Response) {
const query = new SearchAllCoursesQuery();
const courses = await this.queryBus.ask(query);

res.status(httpStatus.OK).send(courses);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Request, Response } from 'express';
import httpStatus from 'http-status';
import { Controller } from './Controller';

export default class StatusGetController implements Controller {
async run(req: Request, res: Response) {
res.status(httpStatus.OK).send();
}
}
8 changes: 8 additions & 0 deletions src/apps/backoffice/backend/routes/courses.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Express } from 'express';
import container from '../config/dependency-injection';
import { CoursesGetController } from '../controllers/CoursesGetController';

export const register = (app: Express) => {
const coursesGetController: CoursesGetController = container.get('Apps.Backoffice.Backend.controllers.CoursesGetController');
app.get('/courses', coursesGetController.run.bind(coursesGetController));
};
12 changes: 12 additions & 0 deletions src/apps/backoffice/backend/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Express } from 'express';
import glob from 'glob';

export function registerRoutes(app: Express) {
const routes = glob.sync(__dirname + '/**/*.route.*');
routes.map(route => register(route, app));
}

function register(routePath: string, app: Express) {
const route = require(routePath);
route.register(app);
}
8 changes: 8 additions & 0 deletions src/apps/backoffice/backend/routes/status.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Express } from 'express';
import container from '../config/dependency-injection';
import StatusController from '../controllers/StatusGetController';

export const register = (app: Express) => {
const controller: StatusController = container.get('Apps.Backoffice.Backend.controllers.StatusGetController');
app.get('/status', controller.run.bind(controller));
};
Loading