Skip to content

Commit

Permalink
Merge 88b2309 into be63da9
Browse files Browse the repository at this point in the history
  • Loading branch information
Tazaf committed Mar 26, 2019
2 parents be63da9 + 88b2309 commit c34e880
Show file tree
Hide file tree
Showing 25 changed files with 757 additions and 94 deletions.
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* add jshint
* export knex clean database
* document expectations
* check expected column list in the database (to make sure database tests are up to date)

## Development guide

Expand Down
3 changes: 3 additions & 0 deletions config/local.sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ exports.port = 3000;
// Session secret used to sign JWT tokens
// This should be a long random string, e.g. 100 characters.
exports.sessionSecret = 'changeme';

// Default pagination limit when listing resources with pagination feature
exports.defaultPaginationLimit = 100;
2 changes: 1 addition & 1 deletion docs/database/diagram.xml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/7.9.5 Chrome/58.0.3029.110 Electron/1.7.5 Safari/537.36" version="8.6.5" editor="www.draw.io" type="device"><diagram name="Page-1" id="efa7a0a1-bf9b-a30e-e6df-94a7791c09e9">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</diagram></mxfile>
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/7.9.5 Chrome/58.0.3029.110 Electron/1.7.5 Safari/537.36" version="9.1.3" editor="www.draw.io" type="device"><diagram name="Page-1" id="efa7a0a1-bf9b-a30e-e6df-94a7791c09e9">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</diagram></mxfile>
30 changes: 30 additions & 0 deletions migrations/20180913104602_create-registrations-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* eslint "require-jsdoc": ["off"] */
const { addTouchColumnsOn, logMigration } = require('../utils/migrations');

exports.up = async function(knex) {
logMigration(knex);

await createRegistrations(knex);
};

exports.down = async function(knex) {
logMigration(knex);

await dropRegistrations(knex);
};

function createRegistrations(knex) {
return knex.schema.createTable('registrations', t => {
t.bigIncrements('id').primary();
t.specificType('api_id', 'uuid').unique().notNullable().defaultTo(knex.raw('uuid_generate_v4()'));
t.string('firstname', 255).notNullable();
t.string('lastname', 255).notNullable();
t.string('email', 255).notNullable();
addTouchColumnsOn(t);
})
.raw('CREATE UNIQUE INDEX registrations_email_unique ON registrations (LOWER(email));');
}

function dropRegistrations(knex) {
return knex.schema.dropTable('registrations');
}
24 changes: 1 addition & 23 deletions server/api/actions/actions.api.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
const _ = require('lodash');

const allowedMethodsFor = require('./actions.routes').allowedMethods;
const expectActions = require('../../spec/expectations/action');
const expectTheme = require('../../spec/expectations/theme');
const actionFixtures = require('../../spec/fixtures/actions');
const { cleanDatabase, expect, expectErrors, initSuperRest, setUp, testMethodsNotAllowed } = require('../../spec/utils');
const { getExpectedTheme } = require('../themes/themes.api.spec');
const { cleanDatabase, expect, expectErrors, getExpectedAction, getExpectedTheme, initSuperRest, setUp, testMethodsNotAllowed } = require('../../spec/utils');

setUp();

Expand Down Expand Up @@ -139,22 +136,3 @@ describe('Actions API', function() {
});
});
});

/**
* Returns an object representing the expected properties of an Action, based on the specified Action.
* (Can be used, for example, to check if a returned API response matches an action in the database.)
*
* @param {action} action - The action to build the expectation from.
* @param {...Object} changes - Additional expected changes compared to the specified action (merged with Lodash's `assign`).
* @returns {Object} An expectations object.
**/
function getExpectedAction(action, ...changes) {
return _.assign({
id: action.get('api_id'),
title: action.get('title'),
description: action.get('description'),
themeId: action.related('theme').get('api_id'),
createdAt: action.get('created_at'),
updatedAt: action.get('updated_at')
}, ...changes);
}
1 change: 1 addition & 0 deletions server/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ router.use('/actions', require('./actions/actions.routes').router);
router.use('/auth', require('./auth/auth.routes').router);
router.use('/locations', require('./locations/locations.routes').router);
router.use('/me', require('./users/users.me.routes').router);
router.use('/registrations', require('./registrations/registrations.routes').router);
router.use('/themes', require('./themes/themes.routes').router);
router.use('/users', require('./users/users.routes').router);

Expand Down
3 changes: 3 additions & 0 deletions server/api/index.raml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ types:
AuthPost: !include auth/auth.model.post.raml
Location: !include locations/locations.model.raml
LocationWrite: !include locations/locations.model.write.raml
Registration: !include registrations/registrations.model.raml
RegistrationWrite: !include registrations/registrations.model.write.raml
Theme: !include themes/themes.model.raml
User: !include users/users.model.raml

Expand All @@ -38,5 +40,6 @@ types:
/auth: !include auth/auth.api.raml
/locations: !include locations/locations.api.raml
/me: !include users/users.me.raml
/registrations: !include registrations/registrations.api.raml
/themes: !include themes/themes.api.raml
/users: !include users/users.api.raml
37 changes: 3 additions & 34 deletions server/api/locations/locations.api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const expectLocation = require('../../spec/expectations/location');
const geoJsonFixtures = require('../../spec/fixtures/geojson');
const locationFixtures = require('../../spec/fixtures/location');
const userFixtures = require('../../spec/fixtures/user');
const { cleanDatabase, expect, expectDeleted, expectErrors, expectUnchanged, initSuperRest, setUp, testMethodsNotAllowed } = require('../../spec/utils');
const { cleanDatabase, expect, expectDeleted, expectErrors, expectUnchanged, getExpectedLocation, initSuperRest, setUp, testMethodsNotAllowed } = require('../../spec/utils');

setUp();

Expand Down Expand Up @@ -354,8 +354,8 @@ describe('Locations API', function() {
/**
* Returns an object representing the expected properties of a Location, based on the default request body for this test block.
*
* @param {...Object} changes - Additional expected changes compared to the requets body (merged with Lodash's `extend`).
* @returns {Object} An expectations object.
* @param {...Object} changes - Additional expected changes compared to the requets body (merged with Lodash's `merge`).
* @returns {Object} An expectation object.
*/
function getExpectedLocationFromRequestBody(...changes) {
return _.merge({}, reqBody, ...changes);
Expand Down Expand Up @@ -914,35 +914,4 @@ describe('Locations API', function() {
});
});
});

/**
* Returns an object representing the expected properties of a Location, based on the specified Location.
* (Can be used, for example, to check if a returned API response matches a Location in the database.)
*
* @param {Location} location - The location to build the expectations from.
* @param {...Object} changes - Additional expected changes compared to the specified Location (merged with Lodash's `extend`).
* @returns {Object} An expectations object.
*/
function getExpectedLocation(location, ...changes) {
return _.merge({
id: location.get('api_id'),
name: location.get('name'),
shortName: location.get('short_name'),
description: location.get('description'),
phone: location.get('phone'),
photoUrl: location.get('photo_url'),
siteUrl: location.get('site_url'),
geometry: location.get('geometry'),
properties: location.get('properties'),
address: {
street: location.get('address_street'),
number: location.get('address_number'),
city: location.get('address_city'),
state: location.get('address_state'),
zipCode: location.get('address_zip_code')
},
createdAt: location.get('created_at'),
updatedAt: location.get('updated_at')
}, ...changes);
}
});
89 changes: 89 additions & 0 deletions server/api/registrations/registrations.api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Registrations management API.
*
* @module server/api/registrations
*/
const Registration = require('../../models/registration');
const { fetcher, route, serialize } = require('../utils/api');
const { validateRequestBody } = require('../utils/validation');
const policy = require('./registrations.policy');

// API resource name (used in some API errors)
exports.resourceName = 'registration';

/**
* Creates a new registration.
*
* @function
*/
exports.create = route(async (req, res) => {

await validateCreateRequest(req);

const registration = policy.parse(req.body);
await registration.save();

res.status(201).send(await serialize(req, registration, policy));
});

/**
* Deletes a registration.
*/
exports.remove = route(async (req, res) => {
await req.registration.destroy();

res.sendStatus(204);
});

/**
* Checks if a registration with a specific email exists.
*
* @function
*/
exports.checkExistence = route((req, res) => res.sendStatus(200));

/**
* Middleware that fetches the registration associated with the email in the URL.
*
* @function
*/
exports.fetchRegistrationByEmail = fetcher({
column: 'email',
urlParameter: 'email',
model: Registration,
resourceName: exports.resourceName,
coerce: email => email.toLowerCase()
});

/**
* Validates the post parameters of a request to create a registration.
* @param {Request} req An Express request.
* @returns {Promise<ValidationErrorBundle>} A promise that will be resolved if the request is valid, or rejected with a bundle of errors if it is invalid.
*/
function validateCreateRequest(req) {
return validateRequestBody(req, function() {
return this.parallel(
this.validate(
this.json('/firstname'),
this.required(),
this.type('string'),
this.notBlank(),
this.string(1, 255)
),
this.validate(
this.json('/lastname'),
this.required(),
this.type('string'),
this.notBlank(),
this.string(1, 255)
),
this.validate(
this.json('/email'),
this.required(),
this.type('string'),
this.notBlank(),
this.string(1, 255)
)
);
});
}
94 changes: 94 additions & 0 deletions server/api/registrations/registrations.api.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
displayName: Registrations
description: Management of BioPocket registrations to news and updates on the project.

# POST /api/registrations
post:
description: Create a new registration.
is:
- jsonConsumer
body:
type: RegistrationWrite
example: |
POST /api/registrations HTTP/1.1
Content-Type: application/json
{
"firstname": "Dorothy",
"lastname": "Gale",
"email": "dorothy.gale@wizard.oz",
}
responses:
201:
description: The registration was successfully created.
body:
type: Registration
example: |
HTTP/1.1 201 Created
Content-Type: application/json
{
"firstname": "Dorothy",
"lastname": "Gale",
"email": "dorothy.gale@wizard.oz",
"id": "c821bc0f-85b4-44d5-9bbe-a30cf197c30a",
"createdAt": "2000-01-01T16:30:00.123Z",
"updatedAt": "2000-02-03T17:00:00.123Z"
}
422:
description: The request body contains invalid data.
body:
type: validationErrors
example: |
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"errors": [
{
"message": "is required",
"type": "json",
"location": "/firstname",
"validator": "required",
"valueSet": false
}
]
}
/{email}:
uriParameters:
email:
description: An email address that might be associated with a registration.
type: string

# HEAD /api/registrations/:email
head:
description: Checks the existence of a single registration.
is:
- identifiableResource:
name: registration
responses:
200:
description: A registration exists with this email.
404:
description: No registration was found with this email.
delete:
description: Delete the registration related to the given email address.
is:
- identifiableResource:
name: registration
responses:
204:
description: The registration has been deleted.
404:
body:
example: |
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"errors": [
{
"code": "record.notFound",
"message": "No registration was found with ID admin@worldgov.com."
}
]
}

0 comments on commit c34e880

Please sign in to comment.