Skip to content

Commit

Permalink
chore(core): mongoose models rewritten with Joi
Browse files Browse the repository at this point in the history
  • Loading branch information
UladBohdan authored and Drapegnik committed Aug 31, 2019
1 parent 8586ef3 commit 12e1d9e
Show file tree
Hide file tree
Showing 22 changed files with 180 additions and 337 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ module.exports = {
// allow `console.error` & `console.warning`
'no-console': ['error', { allow: ['warn', 'error'] }],
// `_id` comes from Mongo
'no-underscore-dangle': ['error', { allow: ['_id'] }],
'no-underscore-dangle': ['error', { allow: ['_id', '_doc'] }],

'func-names': 'off',
},
Expand Down
29 changes: 7 additions & 22 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion src/api/article/article.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const joiArticleSchema = Joi.object({
images: Joi.object().required(),
// Can only be present when Article type is video.
video: joiVideoSchema,
color: Joi.color().default('000000'),
color: Joi.color().default('#000000'),
// Text on article card may be rendered in one of the following ways.
// This depends on the color and is set manually.
textColorTheme: Joi.string()
Expand Down
2 changes: 1 addition & 1 deletion src/api/article/article.model.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe('Article model', () => {
expect(errorHandler).to.have.been.called();
});

it('should fail to save article | imvalid image', async () => {
it('should fail to save article | invalid image', async () => {
const errorHandler = spy(({ message }) => {
expect(message).to.not.empty();
expect(message.images.page).to.includes('uri');
Expand Down
60 changes: 19 additions & 41 deletions src/api/article/collection/model.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,28 @@
import mongoose from 'mongoose';
import omit from 'lodash/omit';

import { slugValidator } from 'utils/validation';

import { serializeArticle, queryUnpublished } from 'api/article/article.model';

const { Schema } = mongoose;
import Joi, { joiToMongoose } from 'utils/joi';

const joiArticleCollectionSchema = Joi.object({
name: Joi.localizedText().required(),
description: Joi.localizedText(),
// The order of articles below is essential and defines the structure of the collection.
articles: Joi.array().items(Joi.string().meta({ type: 'ObjectId', ref: 'Article' })),
slug: Joi.slug()
.meta({ unique: true })
.required(),
active: Joi.boolean().default(true),
imageUrl: Joi.image(),
createdAt: Joi.date()
.default(Date.now, 'time of creation')
.required(),
});

const ArticleCollectionSchema = new Schema(
{
// name and description (below) map locales (be, ru, ...) to strings.
// Once amount of localized data increases implementation of LocalizedArticleCollection
// model might be considered.
name: {
type: Schema.Types.Mixed,
required: true,
},
description: Schema.Types.Mixed,
// The order of articles below is essential and defines the structure of the collection.
articles: [
{
type: Schema.Types.ObjectId,
ref: 'Article',
},
],
slug: {
type: String,
required: true,
unique: true,
validate: slugValidator,
},
active: {
type: Boolean,
default: true,
},
imageUrl: String,
createdAt: {
type: Date,
default: Date.now,
required: true,
},
},
{
usePushEach: true,
}
);
const ArticleCollectionSchema = joiToMongoose(joiArticleCollectionSchema, {
usePushEach: true,
});

const ArticleCollection = mongoose.model('ArticleCollection', ArticleCollectionSchema);

Expand Down
8 changes: 5 additions & 3 deletions src/api/article/collection/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ describe('Collections API', () => {
for (let i = 1; i <= 5; i += 1) {
promises.push(
new ArticleCollection({
name: { en: `Collection ${i}` },
description: { en: `a description` },
name: { be: `Калекцыя ${i}`, en: `Collection ${i}` },
description: { be: `апісанне`, en: `a description` },
slug: `collection-${i}`,
articles: articlesList[i - 1],
imageUrl: NEW_IMAGE_URL,
}).save()
);
}
Expand Down Expand Up @@ -100,7 +101,8 @@ describe('Collections API', () => {
.set('Cookie', sessionCookie)
.send({
slug: 'collection-6',
name: { en: 'New Collection' },
name: { be: 'New Collection' },
imageUrl: NEW_IMAGE_URL,
})
.expect(HttpStatus.OK)
.expect(({ body }) => {
Expand Down
3 changes: 0 additions & 3 deletions src/api/article/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { requireAuth, verifyPermission } from 'auth';
import { requireFields, precheck } from 'utils/validation';

import collectionRoutes from 'api/article/collection';
import localeRoutes from 'api/article/localized';

import * as controller from './controller';

Expand All @@ -16,8 +15,6 @@ const router = Router();

router.use('/collections', collectionRoutes);

router.use('/localize', localeRoutes);

router.get('/', controller.getAll);
router.post(
'/',
Expand Down
44 changes: 0 additions & 44 deletions src/api/article/localized/controller.js

This file was deleted.

11 changes: 0 additions & 11 deletions src/api/article/localized/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
import { Router } from 'express';

import { requireAuth, verifyPermission } from 'auth';

import * as controller from './controller';
import LocalizedArticle from './model';

const router = Router();

router.post('/:articleId', requireAuth, verifyPermission('canCreateArticle'), controller.create);
router.put('/:slug', requireAuth, verifyPermission('canCreateArticle'), controller.update);

export { LocalizedArticle };
export default router;
70 changes: 19 additions & 51 deletions src/api/article/localized/model.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,26 @@
import mongoose from 'mongoose';

import { slugValidator } from 'utils/validation';
import Joi, { joiToMongoose } from 'utils/joi';

import { ObjectMetadata } from 'api/helpers/metadata';
const joiLocalizedArticleSchema = Joi.object({
articleId: Joi.objectId().required(),
locale: Joi.locale().required(),
title: Joi.string().required(),
subtitle: Joi.string().required(),
content: Joi.object(),
slug: Joi.slug()
.meta({ unique: true })
.required(),
metadata: Joi.metadata().required(),
active: Joi.boolean().default(true),
// Keywords are for SEO optimization and search engines.
keywords: Joi.array().items(Joi.string()),
});

const { Schema } = mongoose;

const LocalizedArticleSchema = new Schema(
{
articleId: {
type: Schema.Types.ObjectId,
required: true,
},
locale: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
subtitle: {
type: String,
required: true,
},
video: String,
image: String,
content: Schema.Types.Mixed,
slug: {
type: String,
required: true,
unique: true,
validate: slugValidator,
},
metadata: {
type: ObjectMetadata.schema,
required: true,
},
active: {
type: Boolean,
default: true,
},
keywords: [
// Keywords are for SEO optimization and search engines.
{
type: String,
},
],
},
{
usePushEach: true,
minimize: false,
}
);
const LocalizedArticleSchema = joiToMongoose(joiLocalizedArticleSchema, {
usePushEach: true,
minimize: false,
});

const LocalizedArticle = mongoose.model('LocalizedArticle', LocalizedArticleSchema);

Expand Down
38 changes: 38 additions & 0 deletions src/api/article/localized/model.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import mongoose from 'mongoose';

import { expect, dropData, loginTestAdmin, defaultObjectMetadata, spy } from 'utils/testing';

import 'db/connect';

import LocalizedArticle from './model';

describe('LocalizedArticle model', () => {
let metadata;

before(async function() {
this.timeout(5000);
await dropData();

await loginTestAdmin();
metadata = await defaultObjectMetadata();
});

it('should fail to save article | no locale', async () => {
const errorHandler = spy(({ message }) => {
expect(message).to.not.empty();
expect(message.locale).to.includes('required');
});

await LocalizedArticle({
articleId: mongoose.Types.ObjectId().toString(),
title: 'Title',
subtitle: 'Subtitle',
slug: 'slug1',
metadata,
})
.save()
.catch(errorHandler);

expect(errorHandler).to.have.been.called();
});
});
Loading

0 comments on commit 12e1d9e

Please sign in to comment.