Skip to content

Commit

Permalink
task(core): add joigoose, refactor some models [#56]
Browse files Browse the repository at this point in the history
- tag
- topic
- metadata
- storage
  • Loading branch information
Drapegnik committed Aug 31, 2019
1 parent 60b027f commit 6a6f0ca
Show file tree
Hide file tree
Showing 15 changed files with 178 additions and 151 deletions.
10 changes: 0 additions & 10 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,6 @@ module.exports = {

// we use named export in utils
'import/prefer-default-export': 'off',
// disable comma-dangle in functions
'comma-dangle': [
'error',
{
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
functions: 'never',
},
],
// allow `console.error` & `console.warning`
'no-console': ['error', { allow: ['warn', 'error'] }],
// `_id` comes from Mongo
Expand Down
50 changes: 50 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"http-status-codes": "^1.3.0",
"joi": "^14.3.1",
"joi-objectid": "^2.0.0",
"joigoose": "^4.0.8",
"lodash": "^4.17.15",
"mailchimp-api-v3": "^1.12.1",
"md5": "^2.2.1",
Expand Down
6 changes: 3 additions & 3 deletions src/api/article/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export const update = async ({ params: { slugOrId }, body, user }, res, next) =>
locale,
articleId,
},
mergeWithUpdateMetadata(localeData, user._id),
mergeWithUpdateMetadata(localeData, user),
{ new: true }
)
// unless found, create a new one.
Expand All @@ -174,7 +174,7 @@ export const update = async ({ params: { slugOrId }, body, user }, res, next) =>
localesToUpdate.map(_id =>
LocalizedArticle.findOneAndUpdate(
{ _id },
mergeWithUpdateMetadata({ active: false }, user._id)
mergeWithUpdateMetadata({ active: false }, user)
).catch(next)
)
);
Expand Down Expand Up @@ -207,7 +207,7 @@ export const update = async ({ params: { slugOrId }, body, user }, res, next) =>
export const remove = ({ params: { slugOrId }, user }, res, next) =>
retrieveArticleId(slugOrId, { active: true })
.then(articleId =>
Article.updateOne({ _id: articleId }, mergeWithUpdateMetadata({ active: false }, user._id))
Article.updateOne({ _id: articleId }, mergeWithUpdateMetadata({ active: false }, user))
)
.then(() => res.sendStatus(HttpStatus.OK))
.catch(next);
2 changes: 1 addition & 1 deletion src/api/article/localized/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const create = async ({ params: { articleId }, user, body }, res, next) =
};

export const update = ({ params: { slug }, user, body }, res, next) =>
LocalizedArticle.findOneAndUpdate({ slug }, mergeWithUpdateMetadata(body, user._id), {
LocalizedArticle.findOneAndUpdate({ slug }, mergeWithUpdateMetadata(body, user), {
new: true,
})
.then(checkIsFound)
Expand Down
9 changes: 8 additions & 1 deletion src/api/helpers/metadata/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import {
getInitObjectMetadata,
updateObjectMetadata,
mergeWithUpdateMetadata,
joiMetadataSchema,
} from './model';

export { ObjectMetadata, getInitObjectMetadata, updateObjectMetadata, mergeWithUpdateMetadata };
export {
ObjectMetadata,
getInitObjectMetadata,
updateObjectMetadata,
mergeWithUpdateMetadata,
joiMetadataSchema,
};
44 changes: 16 additions & 28 deletions src/api/helpers/metadata/model.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,36 @@
import mongoose from 'mongoose';

const { Schema } = mongoose;
import { Joi, joiToMongoose, joiSchemas } from 'validation';

// ObjectMetadataSchema model is a general-purpose object to be used across the project.
const ObjectMetadataSchema = new Schema({
createdAt: {
type: Date,
required: true,
},
createdBy: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
updatedAt: {
type: Date,
required: true,
},
updatedBy: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
export const joiMetadataSchema = Joi.object({
createdAt: Joi.date().required(),
updatedAt: Joi.date().required(),
createdBy: joiSchemas.userRef.required(),
updatedBy: joiSchemas.userRef.required(),
});

// ObjectMetadataSchema model is a general-purpose object to be used across the project.
const ObjectMetadataSchema = joiToMongoose(joiMetadataSchema);

export const ObjectMetadata = mongoose.model('ObjectMetadata', ObjectMetadataSchema);

export const getInitObjectMetadata = userId => ({
export const getInitObjectMetadata = ({ _id }) => ({
createdAt: Date.now(),
createdBy: userId,
createdBy: _id,
updatedAt: Date.now(),
updatedBy: userId,
updatedBy: _id,
});

export const updateObjectMetadata = (oldMetadata, userId) => ({
export const updateObjectMetadata = (oldMetadata, { _id }) => ({
...oldMetadata,
updatedAt: Date.now(),
updatedBy: userId,
updatedBy: _id,
});

export const mergeWithUpdateMetadata = (data, userId) => ({
export const mergeWithUpdateMetadata = (data, { _id }) => ({
$set: {
...data,
'metadata.updatedBy': userId,
'metadata.updatedBy': _id,
'metadata.updatedAt': Date.now(),
},
});
42 changes: 16 additions & 26 deletions src/api/storage/model.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,39 @@
import mongoose from 'mongoose';

import {
ObjectMetadata,
joiMetadataSchema,
getInitObjectMetadata,
mergeWithUpdateMetadata,
} from 'api/helpers/metadata';
import { Joi, joiToMongoose } from 'validation';

const { Schema } = mongoose;

const StorageEntitySchema = new Schema({
key: {
type: String,
required: true,
unique: true,
},
document: {
type: Schema.Types.Mixed,
required: true,
},
accessPolicy: {
type: String,
enum: ['public'],
default: 'public',
required: true,
},
metadata: {
type: ObjectMetadata.schema,
required: true,
},
const joiStorageEntitySchema = Joi.object({
key: Joi.string()
.required()
.meta({ unique: true }),
document: Joi.object().required(),
accessPolicy: Joi.string()
.valid('public')
.default('public'),
metadata: joiMetadataSchema.required(),
});

const StorageEntitySchema = joiToMongoose(joiStorageEntitySchema);

StorageEntitySchema.statics.getValue = function(key) {
return this.findOne({ key });
};

StorageEntitySchema.statics.setValue = function(key, value, userId) {
return this.findOneAndUpdate({ key }, mergeWithUpdateMetadata({ document: value }, userId), {
StorageEntitySchema.statics.setValue = function(key, value, _id) {
return this.findOneAndUpdate({ key }, mergeWithUpdateMetadata({ document: value }, { _id }), {
new: true,
}).then(
entity =>
entity ||
this({
key,
document: value,
metadata: getInitObjectMetadata(userId),
metadata: getInitObjectMetadata({ _id }),
}).save()
);
};
Expand Down
41 changes: 15 additions & 26 deletions src/api/tag/model.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,26 @@
import mongoose from 'mongoose';
import set from 'lodash/set';

import Joi from 'joi';

import { ObjectMetadata } from 'api/helpers/metadata';
import { joiMetadataSchema } from 'api/helpers/metadata';
import { Topic } from 'api/topic';
import { TAG_CONTENT_SCHEMA } from 'constants/topic';
import { ValidationError } from 'utils/validation';

const { Schema } = mongoose;

const TagSchema = new Schema({
topic: {
type: Schema.Types.ObjectId,
ref: 'Topic',
required: true,
},
slug: {
type: String,
required: true,
unique: true,
},
content: {
// Content depends on which Topic this Tag belongs to.
type: Schema.Types.Mixed,
required: true,
},
metadata: {
type: ObjectMetadata.schema,
required: true,
},
import { Joi, joiToMongoose } from 'validation';

const joiTagSchema = Joi.object({
topic: Joi.string()
.meta({ type: 'ObjectId', ref: 'Topic' })
.required(),
slug: Joi.string()
.required()
.meta({ unique: true }),
// content depends on which `Topic` this `Tag` belongs to.
content: Joi.object().required(),
metadata: joiMetadataSchema.required(),
});

const TagSchema = joiToMongoose(joiTagSchema);

TagSchema.pre('validate', async function(next) {
const topic = await Topic.findOne({ _id: this.topic });

Expand Down
24 changes: 10 additions & 14 deletions src/api/topic/model.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import mongoose from 'mongoose';

import { ObjectMetadata } from 'api/helpers/metadata';
import { joiMetadataSchema } from 'api/helpers/metadata';
import { Joi, joiToMongoose } from 'validation';
import { TOPIC_SLUGS } from 'constants/topic';

const { Schema } = mongoose;

const TopicSchema = new Schema({
slug: {
type: String,
required: true,
unique: true,
enum: TOPIC_SLUGS,
},
metadata: {
type: ObjectMetadata.schema,
required: true,
},
const joiTopicSchema = Joi.object({
slug: Joi.string()
.valid(TOPIC_SLUGS)
.required()
.meta({ unique: true }),
metadata: joiMetadataSchema.required(),
});

const TopicSchema = joiToMongoose(joiTopicSchema);

TopicSchema.statics.getAll = function() {
return this.find().select('slug');
};
Expand Down
20 changes: 7 additions & 13 deletions src/constants/storage.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import Joi from 'joi';
import joiObjectId from 'joi-objectid';

Joi.objectId = joiObjectId(Joi);

const DATA_SCHEMA = entities =>
Joi.object().pattern(Joi.string().valid(entities), Joi.array().items(Joi.objectId()));
import { Joi } from 'validation';

export const MAIN_PAGE_KEY = 'main-page-key';
export const SIDEBAR_KEY = 'sidebar-key';

// This is a list of Main Page Entities to be accepted by setMainPage() method.
// Note: 'topics' are not accepted; get method always returns all topics available.
// TODO: to add 'banners', 'diary'.
const MAIN_PAGE_ENTITIES = ['articles', 'tags'];

export const MAIN_PAGE_DATA_SCHEMA = DATA_SCHEMA(MAIN_PAGE_ENTITIES);

export const SIDEBAR_KEY = 'sidebar-key';

const SIDEBAR_ENTITIES = ['tags'];

export const SIDEBAR_DATA_SCHEMA = DATA_SCHEMA(SIDEBAR_ENTITIES);
const getDataSchema = entities =>
Joi.object().pattern(Joi.string().valid(entities), Joi.array().items(Joi.objectId()));

export const joiMainPageDataSchema = getDataSchema(MAIN_PAGE_ENTITIES);
export const joiSidebarDataSchema = getDataSchema(SIDEBAR_ENTITIES);
Loading

0 comments on commit 6a6f0ca

Please sign in to comment.