p5.js-web-editor/server/controllers/project.controller/createProject.js
Andrew Nicolaou d44a058fd8 Public API: Create new project (fixes #1095) (#1106)
* Converts import script to use public API endpoints

The endpoints don't exist yet, but this is a good way to see how
the implementation of the data structures differ.

* Exposes public API endpoint to fetch user's sketches

* Implements public API delete endpoint

* Adds helper to create custom ApplicationError classes

* Adds create project endpoint that understand API's data structure

This transforms the nested tree of file data into a mongoose
Project model

* Returns '201 Created' to match API spec

* Removes 'CustomError' variable assignment as it shows up in test output

* transformFiles will return file validation errors

* Tests API project controller

* Tests toModel()

* Creates default files if no root-level .html file is provided

* Do not auto-generate a slug if it is provided

Fixes a bug where the slug was auto-generated using the sketch name,
even if a slug property had been provided.

* Validates uniqueness of slugs for projects created by the public API

* Adds tests for slug uniqueness

* Configures node's Promise implementation for mongoose (fixes warnings)

* Moves createProject tests to match controller location

* Adds support for code to ApplicationErrors

* deleteProject controller tests

* getProjectsForUser controller tests

- implements tests
- update apiKey tests to use new User mocks

* Ensure error objects have consistent property names

`message` is used as a high-level description of the errors
`detail` is optional and has an plain language explanation of the
individual errors
`errors` is an array of each individual problem from `detail` in a
machine-readable format

* Assert environment variables are provided at script start

* Version public API

* Expect "files" property to always be provided

* Fixes linting error

* Converts import script to use public API endpoints

The endpoints don't exist yet, but this is a good way to see how
the implementation of the data structures differ.

* Exposes public API endpoint to fetch user's sketches

* Implements public API delete endpoint

* Adds helper to create custom ApplicationError classes

* Adds create project endpoint that understand API's data structure

This transforms the nested tree of file data into a mongoose
Project model

* Returns '201 Created' to match API spec

* Removes 'CustomError' variable assignment as it shows up in test output

* transformFiles will return file validation errors

* Tests API project controller

* Tests toModel()

* Creates default files if no root-level .html file is provided

* Do not auto-generate a slug if it is provided

Fixes a bug where the slug was auto-generated using the sketch name,
even if a slug property had been provided.

* Validates uniqueness of slugs for projects created by the public API

* Adds tests for slug uniqueness

* Configures node's Promise implementation for mongoose (fixes warnings)

* Moves createProject tests to match controller location

* deleteProject controller tests

* Adds support for code to ApplicationErrors

* getProjectsForUser controller tests

- implements tests
- update apiKey tests to use new User mocks

* Ensure error objects have consistent property names

`message` is used as a high-level description of the errors
`detail` is optional and has an plain language explanation of the
individual errors
`errors` is an array of each individual problem from `detail` in a
machine-readable format

* Assert environment variables are provided at script start

* Version public API

* Expect "files" property to always be provided

* Fixes linting error

* Checks that authenticated user has permission to create under this namespace

Previously, the project was always created under the authenticated user's
namespace, but this not obvious behaviour.
2019-08-30 14:26:57 -04:00

97 lines
2.4 KiB
JavaScript

import Project from '../../models/project';
import { toModel, FileValidationError, ProjectValidationError } from '../../domain-objects/Project';
export default function createProject(req, res) {
let projectValues = {
user: req.user._id
};
projectValues = Object.assign(projectValues, req.body);
function sendFailure() {
res.json({ success: false });
}
function populateUserData(newProject) {
return Project.populate(
newProject,
{ path: 'user', select: 'username' },
(err, newProjectWithUser) => {
if (err) {
sendFailure();
return;
}
res.json(newProjectWithUser);
}
);
}
return Project.create(projectValues)
.then(populateUserData)
.catch(sendFailure);
}
// TODO: What happens if you don't supply any files?
export function apiCreateProject(req, res) {
const params = Object.assign({ user: req.user._id }, req.body);
function sendValidationErrors(err, type, code = 422) {
res.status(code).json({
message: `${type} Validation Failed`,
detail: err.message,
errors: err.files,
});
}
// TODO: Error handling to match spec
function sendFailure(err) {
res.status(500).end();
}
function handleErrors(err) {
if (err instanceof FileValidationError) {
sendValidationErrors(err, 'File', err.code);
} else if (err instanceof ProjectValidationError) {
sendValidationErrors(err, 'Sketch', err.code);
} else {
sendFailure();
}
}
function checkUserHasPermission() {
if (req.user.username !== req.params.username) {
console.log('no permission');
const error = new ProjectValidationError(`'${req.user.username}' does not have permission to create for '${req.params.username}'`);
error.code = 401;
throw error;
}
}
try {
checkUserHasPermission();
const model = toModel(params);
return model.isSlugUnique()
.then(({ isUnique, conflictingIds }) => {
if (isUnique) {
return model.save()
.then((newProject) => {
res.status(201).json({ id: newProject.id });
});
}
const error = new ProjectValidationError(`Slug "${model.slug}" is not unique. Check ${conflictingIds.join(', ')}`);
error.code = 409;
throw error;
})
.then(checkUserHasPermission)
.catch(handleErrors);
} catch (err) {
handleErrors(err);
return Promise.reject(err);
}
}