Skip to content

Commit 21ccf6c

Browse files
Feature/database (#20)
* Added ad database schema, missing restrictions Still needs type check to ensure it is as expected. * Removed unnecessary test for pushing purposes * Updated gitignore with vscode stuff * Added company model * Added required to some fields of Ad and 'foreign key' to Company * Continued work on Ad schema, added job type, technology and field * Added Date Validation to Publish/End Dates * Added important information regarding on what needs to be discussed/done * Updated comments and updated maxlength for title * Made contacts a Map of Strings * Added date validations * Changed 'enums' to arrays instead of objects, because there is no need to access specific values * Added mongo unique array plugin for ensuring that the elements inside some arrays are unique * Solved job duration data definition problem by splitting it into two fields * Started working on automatic tests for Schema Validation * Added more tests to the Ad Schema. Also added some WIP tests for future development * Added more test stubs for Ad Schema * Contacts test added and fixed typo * Added one more test to contacts (which helped fix a bug, nice) * Text fix * Added location (via Point Schema) to Ad Schema and relevant test and test stubs * Continued work on tests * Remove unnecessary DB dependency The Ad Schema tests are testing Schema validation, which does not require MongoDB to be running, only Mongoose validation. As such, clearing the Ad document was creating an unnecessary dependency on having a configured DB. This is also a good idea because of #21. * Improve Ad validator messages. Implement pending tests for Ad Schema * Improve company schema * Add tests for company schema validation * Implement review fixes
1 parent 1ea0824 commit 21ccf6c

13 files changed

+808
-154
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Extras
22
.env
3-
3+
.vscode
44

55
# Created by https://www.gitignore.io/api/node,macos,linux,windows
66
# Edit at https://www.gitignore.io/?templates=node,macos,linux,windows

package-lock.json

+35-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@
3737
"dotenv": "^6.2.0",
3838
"express": "^4.16.4",
3939
"express-session": "^1.15.6",
40-
"mongoose": "^5.4.11",
4140
"passport": "^0.4.0",
42-
"passport-local": "^1.0.0"
41+
"passport-local": "^1.0.0",
42+
"mongoose": "^5.4.11",
43+
"mongoose-unique-array": "^0.3.1"
4344
}
4445
}

src/models/Ad.js

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
const mongoose = require("mongoose");
2+
const { Schema, Types } = mongoose;
3+
const uniqueArrayPlugin = require("mongoose-unique-array");
4+
const JobTypes = require("./JobTypes");
5+
const {FieldTypes, MIN_FIELDS, MAX_FIELDS} = require("./FieldTypes");
6+
const {TechnologyTypes, MIN_TECHNOLOGIES, MAX_TECHNOLOGIES} = require("./TechnologyTypes");
7+
const PointSchema = require("./Point");
8+
9+
// Defining relevant constants
10+
const {MONTH_IN_MS, AD_MAX_LIFETIME_MONTHS} = require("./TimeConstants");
11+
12+
const AdSchema = new Schema({
13+
title: {type: String, maxlength: 90, required: true},
14+
publishDate: {
15+
type: Date,
16+
required: true,
17+
validate: [
18+
validatePublishDate,
19+
"`publishDate` must be earlier than `endDate`"
20+
]
21+
},
22+
23+
endDate: {
24+
type: Date,
25+
required: true,
26+
validate: [
27+
validateEndDate,
28+
`\`endDate\` must not differ from \`publishDate\` by more than ${AD_MAX_LIFETIME_MONTHS} months`
29+
]
30+
},
31+
32+
jobMinDuration: {
33+
type: Number,
34+
required: function() {
35+
// jobMinDuration is required if jobMaxDuration was specified
36+
return !!this.jobMaxDuration;
37+
}
38+
},
39+
jobMaxDuration: {
40+
type: Number,
41+
validate: [
42+
validateJobMaxDuration,
43+
"`jobMaxDuration` must be larger than `jobMinDuration`"
44+
]
45+
},
46+
47+
jobStartDate: {type: Date},
48+
description: {type: String, maxlength: 1500, required: true},
49+
50+
contacts: {
51+
type: Map,
52+
of: String,
53+
required: true,
54+
validate: [
55+
(val) => val.size >= 1,
56+
"There must be at least one contact"
57+
]
58+
},
59+
60+
isPaid: {type: Boolean},
61+
vacancies: {type: Number},
62+
jobType: {type: String, required: true, enum: JobTypes},
63+
fields: {
64+
// unique ensures that there are no repeated fields using mongoose-unique-array (see below)
65+
type:[{type: String, enum: FieldTypes, unique: true,}],
66+
required: true,
67+
validate: [
68+
(val) => val.length >= MIN_FIELDS && val.length <= MAX_FIELDS,
69+
`There must be between ${MIN_FIELDS} and ${MAX_FIELDS} fields`
70+
]
71+
},
72+
technologies: {
73+
// unique ensures that there are no repeated technologies using mongoose-unique-array (see below)
74+
type:[{type: String, enum: TechnologyTypes, unique: true,}],
75+
required: true,
76+
validate: [
77+
(val) => val.length >= MIN_TECHNOLOGIES && val.length <= MAX_TECHNOLOGIES,
78+
`There must be between ${MIN_TECHNOLOGIES} and ${MAX_TECHNOLOGIES} technologies`
79+
]
80+
},
81+
82+
isHidden: {type: Boolean},
83+
owner: {type: Types.ObjectId, ref: "Company", required: true},
84+
85+
location: {type: PointSchema, required: true},
86+
});
87+
88+
// Checking if the publication date is less than or equal than the end date.
89+
function validatePublishDate(value) {
90+
return value <= this.endDate;
91+
}
92+
93+
function validateEndDate(value) {
94+
// Milisseconds from publish date to end date (Ad is no longer valid)
95+
const timeDiff = value.getTime() - this.publishDate.getTime();
96+
const diffInMonths = timeDiff / MONTH_IN_MS;
97+
98+
return diffInMonths <= AD_MAX_LIFETIME_MONTHS;
99+
}
100+
101+
// jobMaxDuration must be larger than jobMinDuration
102+
function validateJobMaxDuration(value) {
103+
return value >= this.jobMinDuration;
104+
}
105+
106+
// Adding unique array mongo plugin - to ensure that the elements inside the arrays are in fact unique
107+
// See: https://thecodebarbarian.com/whats-new-in-mongoose-4.10-unique-in-arrays and https://www.npmjs.com/package/mongoose-unique-array
108+
AdSchema.plugin(uniqueArrayPlugin);
109+
110+
const Ad = mongoose.model("Ad", AdSchema);
111+
112+
// Useful for testing correct field implementation
113+
// console.log("DBG: ", AdSchema.path("location"));
114+
115+
module.exports = Ad;

src/models/Company.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const mongoose = require("mongoose");
2+
const { Schema } = mongoose;
3+
4+
const CompanySchema = new Schema({
5+
name: {
6+
type: String,
7+
required: true,
8+
},
9+
contacts: {
10+
type: Map,
11+
of: String,
12+
required: true,
13+
validate: [
14+
(val) => val.size >= 1,
15+
"There must be at least one contact"
16+
]
17+
},
18+
bio: {
19+
type: String,
20+
maxlength: 1500,
21+
required: true,
22+
},
23+
});
24+
25+
const Company = mongoose.model("Company", CompanySchema);
26+
27+
module.exports = Company;

src/models/FieldTypes.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const FieldTypes = Object.freeze([
2+
"BACK_END",
3+
"FRONT_END",
4+
"DEVOPS",
5+
"BLOCKCHAIN",
6+
"MACHINE LEARNING",
7+
"OTHER",
8+
]);
9+
10+
const MIN_FIELDS = 2;
11+
const MAX_FIELDS = 5;
12+
13+
module.exports = {
14+
FieldTypes,
15+
MIN_FIELDS,
16+
MAX_FIELDS,
17+
};

src/models/JobTypes.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const JobTypes = Object.freeze([
2+
"FULL_TIME",
3+
"PART_TIME",
4+
"SUMMER INTERNSHIP",
5+
"CURRICULAR INTERNSHIP",
6+
"OTHER",
7+
]);
8+
9+
module.exports = JobTypes;

src/models/Point.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const mongoose = require("mongoose");
2+
const { Schema } = mongoose;
3+
4+
// As per https://mongoosejs.com/docs/geojson.html#points
5+
// Cool for usage with $near - see https://docs.mongodb.com/manual/tutorial/query-a-2dsphere-index/
6+
7+
const PointSchema = new Schema({
8+
type: {
9+
type: String,
10+
enum: ["Point"],
11+
required: true
12+
},
13+
coordinates: {
14+
type: [Number],
15+
required: true
16+
},
17+
});
18+
19+
PointSchema.index("2dsphere");
20+
21+
// Only exporting the Schema because Point will only be used as a subdocument - no need to create a model
22+
module.exports = PointSchema;

0 commit comments

Comments
 (0)