diff --git a/backend/src/controllers/course-requirement.controller.ts b/backend/src/controllers/course-requirement.controller.ts new file mode 100644 index 0000000..fd2ddd8 --- /dev/null +++ b/backend/src/controllers/course-requirement.controller.ts @@ -0,0 +1,110 @@ +import { + Count, + CountSchema, + Filter, + repository, + Where, +} from '@loopback/repository'; +import { + del, + get, + getModelSchemaRef, + getWhereSchemaFor, + param, + patch, + post, + requestBody, +} from '@loopback/rest'; +import { + Course, + Requirement, +} from '../models'; +import {CourseRepository} from '../repositories'; + +export class CourseRequirementController { + constructor( + @repository(CourseRepository) protected courseRepository: CourseRepository, + ) { } + + @get('/courses/{id}/requirements', { + responses: { + '200': { + description: 'Array of Course has many Requirement', + content: { + 'application/json': { + schema: {type: 'array', items: getModelSchemaRef(Requirement)}, + }, + }, + }, + }, + }) + async find( + @param.path.number('id') id: number, + @param.query.object('filter') filter?: Filter, + ): Promise { + return this.courseRepository.requirements(id).find(filter); + } + + @post('/courses/{id}/requirements', { + responses: { + '200': { + description: 'Course model instance', + content: {'application/json': {schema: getModelSchemaRef(Requirement)}}, + }, + }, + }) + async create( + @param.path.number('id') id: typeof Course.prototype.id, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(Requirement, { + title: 'NewRequirementInCourse', + exclude: ['id'], + optional: ['courseId'] + }), + }, + }, + }) requirement: Omit, + ): Promise { + return this.courseRepository.requirements(id).create(requirement); + } + + @patch('/courses/{id}/requirements', { + responses: { + '200': { + description: 'Course.Requirement PATCH success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async patch( + @param.path.number('id') id: number, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(Requirement, {partial: true}), + }, + }, + }) + requirement: Partial, + @param.query.object('where', getWhereSchemaFor(Requirement)) where?: Where, + ): Promise { + return this.courseRepository.requirements(id).patch(requirement, where); + } + + @del('/courses/{id}/requirements', { + responses: { + '200': { + description: 'Course.Requirement DELETE success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async delete( + @param.path.number('id') id: number, + @param.query.object('where', getWhereSchemaFor(Requirement)) where?: Where, + ): Promise { + return this.courseRepository.requirements(id).delete(where); + } +} diff --git a/backend/src/controllers/course-subject.controller.ts b/backend/src/controllers/course-subject.controller.ts new file mode 100644 index 0000000..2312d04 --- /dev/null +++ b/backend/src/controllers/course-subject.controller.ts @@ -0,0 +1,38 @@ +import { + repository, +} from '@loopback/repository'; +import { + param, + get, + getModelSchemaRef, +} from '@loopback/rest'; +import { + Course, + Subject, +} from '../models'; +import {CourseRepository} from '../repositories'; + +export class CourseSubjectController { + constructor( + @repository(CourseRepository) + public courseRepository: CourseRepository, + ) { } + + @get('/courses/{id}/subject', { + responses: { + '200': { + description: 'Subject belonging to Course', + content: { + 'application/json': { + schema: {type: 'array', items: getModelSchemaRef(Subject)}, + }, + }, + }, + }, + }) + async getSubject( + @param.path.number('id') id: typeof Course.prototype.id, + ): Promise { + return this.courseRepository.subject(id); + } +} diff --git a/backend/src/controllers/course-user.controller.ts b/backend/src/controllers/course-user.controller.ts new file mode 100644 index 0000000..371c1ee --- /dev/null +++ b/backend/src/controllers/course-user.controller.ts @@ -0,0 +1,109 @@ +import { + Count, + CountSchema, + Filter, + repository, + Where, +} from '@loopback/repository'; + import { + del, + get, + getModelSchemaRef, + getWhereSchemaFor, + param, + patch, + post, + requestBody, +} from '@loopback/rest'; +import { +Course, +User, +} from '../models'; +import {CourseRepository} from '../repositories'; + +export class CourseUserController { + constructor( + @repository(CourseRepository) protected courseRepository: CourseRepository, + ) { } + + @get('/courses/{id}/users', { + responses: { + '200': { + description: 'Array of Course has many User through CourseUser', + content: { + 'application/json': { + schema: {type: 'array', items: getModelSchemaRef(User)}, + }, + }, + }, + }, + }) + async find( + @param.path.number('id') id: number, + @param.query.object('filter') filter?: Filter, + ): Promise { + return this.courseRepository.users(id).find(filter); + } + + @post('/courses/{id}/users', { + responses: { + '200': { + description: 'create a User model instance', + content: {'application/json': {schema: getModelSchemaRef(User)}}, + }, + }, + }) + async create( + @param.path.number('id') id: typeof Course.prototype.id, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(User, { + title: 'NewUserInCourse', + exclude: ['id'], + }), + }, + }, + }) user: Omit, + ): Promise { + return this.courseRepository.users(id).create(user); + } + + @patch('/courses/{id}/users', { + responses: { + '200': { + description: 'Course.User PATCH success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async patch( + @param.path.number('id') id: number, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(User, {partial: true}), + }, + }, + }) + user: Partial, + @param.query.object('where', getWhereSchemaFor(User)) where?: Where, + ): Promise { + return this.courseRepository.users(id).patch(user, where); + } + + @del('/courses/{id}/users', { + responses: { + '200': { + description: 'Course.User DELETE success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async delete( + @param.path.number('id') id: number, + @param.query.object('where', getWhereSchemaFor(User)) where?: Where, + ): Promise { + return this.courseRepository.users(id).delete(where); + } +} diff --git a/backend/src/controllers/index.ts b/backend/src/controllers/index.ts index 88f9fd6..94dd678 100644 --- a/backend/src/controllers/index.ts +++ b/backend/src/controllers/index.ts @@ -1,2 +1,8 @@ export * from './ping.controller'; export * from './user.controller'; +export * from './subject-course.controller'; +export * from './course-subject.controller'; +export * from './course-user.controller'; +export * from './user-course.controller'; +export * from './course-requirement.controller'; +export * from './course-requirement.controller'; diff --git a/backend/src/controllers/subject-course.controller.ts b/backend/src/controllers/subject-course.controller.ts new file mode 100644 index 0000000..65de83c --- /dev/null +++ b/backend/src/controllers/subject-course.controller.ts @@ -0,0 +1,110 @@ +import { + Count, + CountSchema, + Filter, + repository, + Where, +} from '@loopback/repository'; +import { + del, + get, + getModelSchemaRef, + getWhereSchemaFor, + param, + patch, + post, + requestBody, +} from '@loopback/rest'; +import { + Subject, + Course, +} from '../models'; +import {SubjectRepository} from '../repositories'; + +export class SubjectCourseController { + constructor( + @repository(SubjectRepository) protected subjectRepository: SubjectRepository, + ) { } + + @get('/subjects/{id}/courses', { + responses: { + '200': { + description: 'Array of Subject has many Course', + content: { + 'application/json': { + schema: {type: 'array', items: getModelSchemaRef(Course)}, + }, + }, + }, + }, + }) + async find( + @param.path.number('id') id: number, + @param.query.object('filter') filter?: Filter, + ): Promise { + return this.subjectRepository.courses(id).find(filter); + } + + @post('/subjects/{id}/courses', { + responses: { + '200': { + description: 'Subject model instance', + content: {'application/json': {schema: getModelSchemaRef(Course)}}, + }, + }, + }) + async create( + @param.path.number('id') id: typeof Subject.prototype.id, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(Course, { + title: 'NewCourseInSubject', + exclude: ['id'], + optional: ['subjectId'] + }), + }, + }, + }) course: Omit, + ): Promise { + return this.subjectRepository.courses(id).create(course); + } + + @patch('/subjects/{id}/courses', { + responses: { + '200': { + description: 'Subject.Course PATCH success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async patch( + @param.path.number('id') id: number, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(Course, {partial: true}), + }, + }, + }) + course: Partial, + @param.query.object('where', getWhereSchemaFor(Course)) where?: Where, + ): Promise { + return this.subjectRepository.courses(id).patch(course, where); + } + + @del('/subjects/{id}/courses', { + responses: { + '200': { + description: 'Subject.Course DELETE success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async delete( + @param.path.number('id') id: number, + @param.query.object('where', getWhereSchemaFor(Course)) where?: Where, + ): Promise { + return this.subjectRepository.courses(id).delete(where); + } +} diff --git a/backend/src/controllers/user-course.controller.ts b/backend/src/controllers/user-course.controller.ts new file mode 100644 index 0000000..6785ff8 --- /dev/null +++ b/backend/src/controllers/user-course.controller.ts @@ -0,0 +1,109 @@ +import { + Count, + CountSchema, + Filter, + repository, + Where, +} from '@loopback/repository'; + import { + del, + get, + getModelSchemaRef, + getWhereSchemaFor, + param, + patch, + post, + requestBody, +} from '@loopback/rest'; +import { +User, +Course, +} from '../models'; +import {UserRepository} from '../repositories'; + +export class UserCourseController { + constructor( + @repository(UserRepository) protected userRepository: UserRepository, + ) { } + + @get('/users/{id}/courses', { + responses: { + '200': { + description: 'Array of User has many Course through CourseUser', + content: { + 'application/json': { + schema: {type: 'array', items: getModelSchemaRef(Course)}, + }, + }, + }, + }, + }) + async find( + @param.path.number('id') id: number, + @param.query.object('filter') filter?: Filter, + ): Promise { + return this.userRepository.courses(id).find(filter); + } + + @post('/users/{id}/courses', { + responses: { + '200': { + description: 'create a Course model instance', + content: {'application/json': {schema: getModelSchemaRef(Course)}}, + }, + }, + }) + async create( + @param.path.number('id') id: typeof User.prototype.id, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(Course, { + title: 'NewCourseInUser', + exclude: ['id'], + }), + }, + }, + }) course: Omit, + ): Promise { + return this.userRepository.courses(id).create(course); + } + + @patch('/users/{id}/courses', { + responses: { + '200': { + description: 'User.Course PATCH success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async patch( + @param.path.number('id') id: number, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(Course, {partial: true}), + }, + }, + }) + course: Partial, + @param.query.object('where', getWhereSchemaFor(Course)) where?: Where, + ): Promise { + return this.userRepository.courses(id).patch(course, where); + } + + @del('/users/{id}/courses', { + responses: { + '200': { + description: 'User.Course DELETE success count', + content: {'application/json': {schema: CountSchema}}, + }, + }, + }) + async delete( + @param.path.number('id') id: number, + @param.query.object('where', getWhereSchemaFor(Course)) where?: Where, + ): Promise { + return this.userRepository.courses(id).delete(where); + } +} diff --git a/backend/src/models/course-user.model.ts b/backend/src/models/course-user.model.ts new file mode 100644 index 0000000..83c3716 --- /dev/null +++ b/backend/src/models/course-user.model.ts @@ -0,0 +1,40 @@ +import {Entity, model, property} from '@loopback/repository'; + +@model() +export class CourseUser extends Entity { + @property({ + type: 'number', + id: true, + generated: true, + }) + id?: number; + + @property({ + type: 'number', + required: true, + }) + courseId: number; + + @property({ + type: 'number', + required: true, + }) + userId: number; + + @property({ + type: 'string', + required: true, + }) + role: string; + + + constructor(data?: Partial) { + super(data); + } +} + +export interface CourseUserRelations { + // describe navigational properties here +} + +export type CourseUserWithRelations = CourseUser & CourseUserRelations; diff --git a/backend/src/models/course.model.ts b/backend/src/models/course.model.ts new file mode 100644 index 0000000..731c877 --- /dev/null +++ b/backend/src/models/course.model.ts @@ -0,0 +1,40 @@ +import {Entity, model, property, belongsTo, hasMany} from '@loopback/repository'; +import {Subject} from './subject.model'; +import {User} from './user.model'; +import {CourseUser} from './course-user.model'; +import {Requirement} from './requirement.model'; + +@model() +export class Course extends Entity { + @property({ + type: 'number', + id: true, + generated: true, + }) + id?: number; + + @property({ + type: 'string', + required: true, + }) + semester: string; + + @belongsTo(() => Subject) + subjectId: number; + + @hasMany(() => User, {through: {model: () => CourseUser}}) + users: User[]; + + @hasMany(() => Requirement) + requirements: Requirement[]; + + constructor(data?: Partial) { + super(data); + } +} + +export interface CourseRelations { + // describe navigational properties here +} + +export type CourseWithRelations = Course & CourseRelations; diff --git a/backend/src/models/index.ts b/backend/src/models/index.ts index 5a670ce..72372ee 100644 --- a/backend/src/models/index.ts +++ b/backend/src/models/index.ts @@ -1 +1,5 @@ export * from './user.model'; +export * from './subject.model'; +export * from './requirement.model'; +export * from './course.model'; +export * from './course-user.model'; diff --git a/backend/src/models/requirement.model.ts b/backend/src/models/requirement.model.ts new file mode 100644 index 0000000..7a6f176 --- /dev/null +++ b/backend/src/models/requirement.model.ts @@ -0,0 +1,44 @@ +import {Entity, model, property} from '@loopback/repository'; + +@model() +export class Requirement extends Entity { + @property({ + type: 'number', + id: true, + generated: true, + }) + id?: number; + + @property({ + type: 'string', + required: true, + }) + name: string; + + @property({ + type: 'number', + required: true, + }) + maxPoints: number; + + @property({ + type: 'string', + required: true, + }) + type: string; + + @property({ + type: 'number', + }) + courseId?: number; + + constructor(data?: Partial) { + super(data); + } +} + +export interface RequirementRelations { + // describe navigational properties here +} + +export type RequirementWithRelations = Requirement & RequirementRelations; diff --git a/backend/src/models/subject.model.ts b/backend/src/models/subject.model.ts new file mode 100644 index 0000000..053a8b2 --- /dev/null +++ b/backend/src/models/subject.model.ts @@ -0,0 +1,36 @@ +import {Entity, model, property, hasMany} from '@loopback/repository'; +import {Course} from './course.model'; + +@model() +export class Subject extends Entity { + @property({ + type: 'number', + id: true, + generated: true, + }) + id?: number; + + @property({ + type: 'string', + required: true, + }) + name: string; + + @property({ + type: 'string', + }) + description?: string; + + @hasMany(() => Course) + courses: Course[]; + + constructor(data?: Partial) { + super(data); + } +} + +export interface SubjectRelations { + // describe navigational properties here +} + +export type SubjectWithRelations = Subject & SubjectRelations; diff --git a/backend/src/models/user.model.ts b/backend/src/models/user.model.ts index a36ac0c..af45432 100644 --- a/backend/src/models/user.model.ts +++ b/backend/src/models/user.model.ts @@ -1,4 +1,6 @@ -import {Entity, model, property} from '@loopback/repository'; +import {Entity, model, property, hasMany} from '@loopback/repository'; +import {Course} from './course.model'; +import {CourseUser} from './course-user.model'; @model() export class User extends Entity { @@ -44,6 +46,8 @@ export class User extends Entity { }) isAdmin: boolean; + @hasMany(() => Course, {through: {model: () => CourseUser}}) + courses: Course[]; constructor(data?: Partial) { super(data); diff --git a/backend/src/repositories/course-user.repository.ts b/backend/src/repositories/course-user.repository.ts new file mode 100644 index 0000000..e75d944 --- /dev/null +++ b/backend/src/repositories/course-user.repository.ts @@ -0,0 +1,16 @@ +import {inject} from '@loopback/core'; +import {DefaultCrudRepository} from '@loopback/repository'; +import {DatabaseDataSource} from '../datasources'; +import {CourseUser, CourseUserRelations} from '../models'; + +export class CourseUserRepository extends DefaultCrudRepository< + CourseUser, + typeof CourseUser.prototype.id, + CourseUserRelations +> { + constructor( + @inject('datasources.database') dataSource: DatabaseDataSource, + ) { + super(CourseUser, dataSource); + } +} diff --git a/backend/src/repositories/course.repository.ts b/backend/src/repositories/course.repository.ts new file mode 100644 index 0000000..56d22dd --- /dev/null +++ b/backend/src/repositories/course.repository.ts @@ -0,0 +1,36 @@ +import {inject, Getter} from '@loopback/core'; +import {DefaultCrudRepository, repository, BelongsToAccessor, HasManyThroughRepositoryFactory, HasManyRepositoryFactory} from '@loopback/repository'; +import {DatabaseDataSource} from '../datasources'; +import {Course, CourseRelations, Subject, User, CourseUser, Requirement} from '../models'; +import {SubjectRepository} from './subject.repository'; +import {CourseUserRepository} from './course-user.repository'; +import {UserRepository} from './user.repository'; +import {RequirementRepository} from './requirement.repository'; + +export class CourseRepository extends DefaultCrudRepository< + Course, + typeof Course.prototype.id, + CourseRelations +> { + + public readonly subject: BelongsToAccessor; + + public readonly users: HasManyThroughRepositoryFactory; + + public readonly requirements: HasManyRepositoryFactory; + + constructor( + @inject('datasources.database') dataSource: DatabaseDataSource, @repository.getter('SubjectRepository') protected subjectRepositoryGetter: Getter, @repository.getter('CourseUserRepository') protected courseUserRepositoryGetter: Getter, @repository.getter('UserRepository') protected userRepositoryGetter: Getter, @repository.getter('RequirementRepository') protected requirementRepositoryGetter: Getter, + ) { + super(Course, dataSource); + this.requirements = this.createHasManyRepositoryFactoryFor('requirements', requirementRepositoryGetter,); + this.registerInclusionResolver('requirements', this.requirements.inclusionResolver); + this.users = this.createHasManyThroughRepositoryFactoryFor('users', userRepositoryGetter, courseUserRepositoryGetter,); + this.registerInclusionResolver('users', this.users.inclusionResolver); + this.subject = this.createBelongsToAccessorFor('subject', subjectRepositoryGetter,); + this.registerInclusionResolver('subject', this.subject.inclusionResolver); + } +} diff --git a/backend/src/repositories/index.ts b/backend/src/repositories/index.ts index 9fb5d34..bf51bc8 100644 --- a/backend/src/repositories/index.ts +++ b/backend/src/repositories/index.ts @@ -1 +1,5 @@ export * from './user.repository'; +export * from './subject.repository'; +export * from './requirement.repository'; +export * from './course.repository'; +export * from './course-user.repository'; diff --git a/backend/src/repositories/requirement.repository.ts b/backend/src/repositories/requirement.repository.ts new file mode 100644 index 0000000..655a047 --- /dev/null +++ b/backend/src/repositories/requirement.repository.ts @@ -0,0 +1,16 @@ +import {inject} from '@loopback/core'; +import {DefaultCrudRepository} from '@loopback/repository'; +import {DatabaseDataSource} from '../datasources'; +import {Requirement, RequirementRelations} from '../models'; + +export class RequirementRepository extends DefaultCrudRepository< + Requirement, + typeof Requirement.prototype.id, + RequirementRelations +> { + constructor( + @inject('datasources.database') dataSource: DatabaseDataSource, + ) { + super(Requirement, dataSource); + } +} diff --git a/backend/src/repositories/subject.repository.ts b/backend/src/repositories/subject.repository.ts new file mode 100644 index 0000000..09c76de --- /dev/null +++ b/backend/src/repositories/subject.repository.ts @@ -0,0 +1,23 @@ +import {inject, Getter} from '@loopback/core'; +import {DefaultCrudRepository, repository, HasManyRepositoryFactory} from '@loopback/repository'; +import {DatabaseDataSource} from '../datasources'; +import {Subject, SubjectRelations, Course} from '../models'; +import {UserRepository} from './user.repository'; +import {CourseRepository} from './course.repository'; + +export class SubjectRepository extends DefaultCrudRepository< + Subject, + typeof Subject.prototype.id, + SubjectRelations +> { + + public readonly courses: HasManyRepositoryFactory; + + constructor( + @inject('datasources.database') dataSource: DatabaseDataSource, @repository.getter('UserRepository') protected userRepositoryGetter: Getter, @repository.getter('CourseRepository') protected courseRepositoryGetter: Getter, + ) { + super(Subject, dataSource); + this.courses = this.createHasManyRepositoryFactoryFor('courses', courseRepositoryGetter,); + this.registerInclusionResolver('courses', this.courses.inclusionResolver); + } +} diff --git a/backend/src/repositories/user.repository.ts b/backend/src/repositories/user.repository.ts index 8c9ce2f..16efa5d 100644 --- a/backend/src/repositories/user.repository.ts +++ b/backend/src/repositories/user.repository.ts @@ -1,16 +1,27 @@ -import {inject} from '@loopback/core'; -import {DefaultCrudRepository} from '@loopback/repository'; +import {inject, Getter} from '@loopback/core'; +import {DefaultCrudRepository, repository, HasManyThroughRepositoryFactory} from '@loopback/repository'; import {DatabaseDataSource} from '../datasources'; -import {User, UserRelations} from '../models'; +import {User, UserRelations, Course, CourseUser} from '../models'; +import {SubjectRepository} from './subject.repository'; +import {CourseUserRepository} from './course-user.repository'; +import {CourseRepository} from './course.repository'; export class UserRepository extends DefaultCrudRepository< User, typeof User.prototype.id, UserRelations > { + + public readonly courses: HasManyThroughRepositoryFactory; + constructor( - @inject('datasources.database') dataSource: DatabaseDataSource, + @inject('datasources.database') dataSource: DatabaseDataSource, @repository.getter('SubjectRepository') protected subjectRepositoryGetter: Getter, @repository.getter('CourseUserRepository') protected courseUserRepositoryGetter: Getter, @repository.getter('CourseRepository') protected courseRepositoryGetter: Getter, ) { super(User, dataSource); + this.courses = this.createHasManyThroughRepositoryFactoryFor('courses', courseRepositoryGetter, courseUserRepositoryGetter,); + this.registerInclusionResolver('courses', this.courses.inclusionResolver); } }