Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Feature/sc 3505/add attachment service #49

Merged
merged 11 commits into from
Mar 16, 2020
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
2 changes: 1 addition & 1 deletion src/logger/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const systemLogger = winston.createLogger({
},
message: true,
}),
format.printf(log => log.message),
format.printf((log) => log.message),
),
transports: [
new transports.Console({
Expand Down
44 changes: 44 additions & 0 deletions src/services/attachments/AttachmentModel.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const service = require('feathers-mongoose');
const { disallow } = require('feathers-hooks-common');

const { AttachmentModel } = require('./models');
const { scopeFilter } = require('./hooks');

const hooks = {};
hooks.before = {
all: [
disallow('external'),
],
create: [
],
patch: [
],
remove: [
],
get: [
scopeFilter,
],
find: [
scopeFilter,
],
};

const AttachmentModelService = (app) => {
const option = {
Model: AttachmentModel,
lean: true,
paginate: {
default: 150,
max: 250,
},
whitelist: ['$elemMatch'],
};
app.use('models/AttachmentModel', service(option));
const modelService = app.service('models/AttachmentModel');
modelService.hooks(hooks);
};

module.exports = {
AttachmentModelService,
hooks,
};
109 changes: 109 additions & 0 deletions src/services/attachments/attachment.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const { Forbidden, NotFound } = require('@feathersjs/errors');
const { validateSchema } = require('feathers-hooks-common');
const Ajv = require('ajv');

const logger = require('../../logger');
const { patchSchema, createSchema } = require('./schemas');

const {
prepareParams,
permissions: permissionsHelper,
} = require('../../utils');

const AttachmentServiceHooks = {
before: {
create: [
validateSchema(createSchema, Ajv),
],
patch: [
validateSchema(patchSchema, Ajv),
],
},
};

/**
* Attachments bind to the parents over target and targetModel.
* They can not get, or find over this external services.
*/
class AttachmentService {
constructor({ docs = {}, baseService, typesToModelServices } = {}) {
if (!baseService || !typesToModelServices) {
logger.error('AttachmentService: missing params!', { baseService, typesToModelServices });
}
this.docs = docs;
this.baseService = baseService;
this.typesToModelServices = typesToModelServices;

this.err = Object.freeze({
noAccess: 'You have no access',
notFound: 'Element do not exist.',
});
}

setup(app) {
this.app = app;
}

scopeParams(params, options = {}) {
return prepareParams(params, {
$select: ['title', 'description', 'type', 'value'],
...options,
});
}

getTarget(id, params) {
return this.app.service(this.baseService)
.get(id, prepareParams(params, {
$select: ['target', 'targetModel'],
}))
.catch((err) => {
throw new NotFound(this.err.notFound, err);
});
}

async hasPermission(target, targetModel, params) {
const modelServiceName = this.typesToModelServices[targetModel];
const result = await this.app.service(modelServiceName)
.get(target, prepareParams(params, {
$populate: [
{ path: 'permissions.group', select: 'users' },
],
}));

if (!result || !permissionsHelper.hasWrite(result.permissions, params.user)) {
throw new Forbidden(this.err.noAccess);
}
return result;
}

async create(data, params) {
await this.hasPermission(data.target, data.targetModel, params);
return this.app.service(this.baseService)
.create(data, this.scopeParams(params));
}

// target and targetModel is disallowed
async patch(id, data, params) {
const { target, targetModel } = await this.getTarget(id, params);
await this.hasPermission(target, targetModel, params);
return this.app.service(this.baseService)
.patch(id, data, this.scopeParams(params));
}

async remove(_id, params) {
const { target, targetModel } = await this.getTarget(_id, params);
await this.hasPermission(target, targetModel, params);
const deletedAt = new Date();
return this.app.service(this.baseService)
.patch(_id, { deletedAt }, prepareParams(params, {
$select: '_id',
}))
.then(() => ({ _id, deletedAt }));
}
}


module.exports = {
AttachmentService,
AttachmentServiceHooks,
};
1 change: 1 addition & 0 deletions src/services/attachments/hooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.scopeFilter = require('./scopeFilter');
6 changes: 6 additions & 0 deletions src/services/attachments/hooks/scopeFilter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = (context) => {
if (!context.params.query.$select) {
context.params.query.$select = ['value', 'type', 'title', 'description'];
}
return context;
};
19 changes: 19 additions & 0 deletions src/services/attachments/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { AttachmentService, AttachmentServiceHooks } = require('./attachment.service');
const { AttachmentModelService } = require('./AttachmentModel.service');

module.exports = (app) => {
app.configure(AttachmentModelService);

const path = 'attachments';

app.use(path, new AttachmentService({
baseService: '/models/AttachmentModel',
typesToModelServices: {
lesson: '/models/LessonModel',
section: '/models/SectionModel',
},
}));

const externerService = app.service(path);
externerService.hooks(AttachmentServiceHooks);
};
35 changes: 35 additions & 0 deletions src/services/attachments/models/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const mongoose = require('mongoose');

const { Schema } = mongoose;

const targetModels = ['lesson']; // 'section'

const attachmentSchema = new Schema({
title: { type: String },
description: { type: String },
type: { type: String, required: true },
value: { type: Schema.Types.Mixed, default: null }, // Object, Number, String, Boolean
target: {
type: Schema.Types.ObjectId,
refPath: 'targetModel',
required: function requiredTarget() {
return !!this.targetModel;
},
},
targetModel: {
type: String,
enum: targetModels,
required: function requiredTargetModel() {
return !!this.target;
},
},
deletedAt: { type: Date, expires: (60 * 60 * 24 * 30) },
createdBy: { type: Schema.Types.ObjectId },
updatedBy: { type: Schema.Types.ObjectId },
}, {
timestamps: true,
});

module.exports = {
AttachmentModel: mongoose.model('attachment', attachmentSchema),
};
38 changes: 38 additions & 0 deletions src/services/attachments/schemas/attachment.create.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "attachmentCreate",
"required": [
"createdBy",
"type",
"target",
"targetModel"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"type": {
"type": "string"
},
"value": {
"type": ["integer","string", "boolean", "object"]
},
"target": {
"type": "string",
"pattern": "[a-f0-9]{24}"
},
"targetModel": {
"type": "string",
"enum": ["lesson"]
},
"createdBy": {
"type": "string",
"pattern": "[a-f0-9]{24}"
}
},
"additionalProperties": false
}
24 changes: 24 additions & 0 deletions src/services/attachments/schemas/attachment.patch.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "attachmentCreate",
"required": [
"updatedBy"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"value": {
"type": ["integer","string", "boolean", "object"]
},
"updatedBy": {
"type": "string",
"pattern": "[a-f0-9]{24}"
}
},
"additionalProperties": false
}
2 changes: 2 additions & 0 deletions src/services/attachments/schemas/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
exports.createSchema = require('./attachment.create.schema.json');
exports.patchSchema = require('./attachment.patch.schema.json');
Empty file.
62 changes: 31 additions & 31 deletions src/services/groups/hooks/index.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
const { disallow } = require('feathers-hooks-common');
exports.before = {
all: [],
find: [],
get: [],
create: [],
update: [disallow()],
patch: [],
remove: [],
};
exports.after = {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: [],
};
exports.error = {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: [],
};
const { disallow } = require('feathers-hooks-common');

exports.before = {
all: [],
find: [],
get: [],
create: [],
update: [disallow()],
patch: [],
remove: [],
};

exports.after = {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: [],
};

exports.error = {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: [],
};
1 change: 1 addition & 0 deletions src/services/groups/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const hooks = require('./hooks/');

// todo add additional services for extern groups with names,
// or force that it can only add names over force event operation
// TODO: beta
module.exports = function setup() {
const app = this;
const option = {
Expand Down
2 changes: 1 addition & 1 deletion src/services/groupviews/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const service = require('feathers-mongoose');

const { GroupViewModel } = require('./models');
const hooks = require('./hooks');

// TODO: deprecated ?
module.exports = (app) => {
const option = {
Model: GroupViewModel,
Expand Down
Loading