Merge branch 'api' into stage

This commit is contained in:
David Baldwynn 2016-10-07 08:31:15 -07:00
commit 2455f5dd9a
26 changed files with 1440 additions and 400 deletions

View file

@ -21,3 +21,11 @@ exports.form = function(req, res) {
request: req
});
};
exports.redoc = function(req, res) {
res.render('redoc', {
request: req
});
};

View file

@ -118,8 +118,6 @@ exports.createSubmission = function(req, res) {
percentageComplete: req.body.percentageComplete
});
if(!!form.plugins.oscarhost.baseUrl) submission.hasPlugins.oscarhost = true;
if(form.pdf) submission.pdf = form.pdf;
//Save submitter's IP Address
@ -189,23 +187,27 @@ exports.listSubmissions = function(req, res) {
* Create a new form
*/
exports.create = function(req, res) {
if(!req.body.form){
console.log(err);
return res.status(400).send({
message: "Invalid Input"
});
}
var form = new Form(req.body.form);
form.admin = req.user._id;
console.log('Create a new form');
console.log(form);
console.log(req.body.form);
console.log(req.user);
form.save(function(err) {
if (err) {
console.log(err);
res.status(400).send({
return res.status(405).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(form);
}
res.json(form);
});
};
@ -213,10 +215,7 @@ exports.create = function(req, res) {
* Show the current form
*/
exports.read = function(req, res) {
var validUpdateTypes= Form.schema.path('plugins.oscarhost.settings.updateType').enumValues;
var newForm = req.form.toJSON({virtuals : true});
newForm.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes;
if (req.userId) {
if(req.form.admin._id+'' === req.userId+''){
@ -239,6 +238,7 @@ exports.update = function(req, res) {
delete req.body.form.__v;
delete req.body.form._id;
*/
if(req.user.roles.indexOf('admin') === -1) delete req.body.form.admin;
if(req.body.changes){
var formChanges = req.body.changes;
@ -248,7 +248,6 @@ exports.update = function(req, res) {
});
} else {
//Unless we have 'admin' privileges, updating form admin is disabled
if(req.user.roles.indexOf('admin') === -1) delete req.body.form.admin;
//Do this so we can create duplicate fields
var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$');
@ -265,7 +264,7 @@ exports.update = function(req, res) {
form.save(function(err, form) {
if (err) {
console.log(err);
res.status(400).send({
res.status(405).send({
message: errorHandler.getErrorMessage(err)
});
} else {
@ -325,7 +324,7 @@ exports.formByID = function(req, res, next, id) {
if (err) {
return next(err);
} else if (form === undefined || form === null) {
res.status(400).send({
res.status(404).send({
message: 'Form not found'
});
}

View file

@ -11,7 +11,9 @@ var _ = require('lodash'),
config = require('../../../config/config'),
nodemailer = require('nodemailer'),
crypto = require('crypto'),
User = mongoose.model('User');
User = mongoose.model('User'),
tokgen = require("../../libs/tokenGenerator");
var nev = require('email-verification')(mongoose);
@ -172,7 +174,6 @@ exports.signin = function(req, res, next) {
*/
exports.signout = function(req, res) {
req.logout();
//res.redirect('/');
return res.status(200).send('You have successfully logged out.');
};
@ -304,3 +305,43 @@ exports.removeOAuthProvider = function(req, res, next) {
});
}
};
/* Generate API Key for User */
exports.generateAPIKey = function(req, res) {
if (!req.isAuthenticated()){
return res.status(400).send({
message: 'User is not Authorized'
});
}
User.findById(req.user.id)
.exec( function(err, user) {
if (err) return res.status(400).send(err);
if (!user) {
return res.status(400).send({
message: 'User does not Exist'
});
}
user.apiKey = tokgen();
user.save(function(err, _user) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
}
var newUser = _user.toObject();
delete newUser.salt;
delete newUser.__v;
delete newUser.passwordHash;
delete newUser.provider;
console.log(newUser);
return res.json(newUser);
});
});
};

View file

@ -23,7 +23,9 @@ exports.userByID = function (req, res, next, id) {
if (err) {
return next(err);
} else if (!user) {
return next(new Error('Failed to load User ' + id));
return res.status(404).send({
message: 'User does not exist'
});
}
req.profile = user;

View file

@ -27,13 +27,13 @@ exports.update = function(req, res) {
user.save(function(err) {
if (err) {
return res.status(400).send({
return res.status(500).send({
message: errorHandler.getErrorMessage(err)
});
} else {
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
res.status(500).send(err);
} else {
res.json(user);
}
@ -41,7 +41,7 @@ exports.update = function(req, res) {
}
});
} else {
res.status(400).send({
res.status(401).send({
message: 'User is not signed in'
});
}

View file

@ -0,0 +1,8 @@
"use strict";
let TokenGenerator = require("uuid-token-generator");
let tokgen = new TokenGenerator(256, TokenGenerator.BASE62);
module.exports = function() {
return tokgen.generate();
};

View file

@ -92,11 +92,6 @@ var FormSchema = new Schema({
default: 'en',
required: 'Form must have a language'
},
description: {
type: String,
default: ''
},
analytics:{
gaCode: {
type: String
@ -187,8 +182,7 @@ var FormSchema = new Schema({
default: '#333'
}
},
font: String,
backgroundImage: { type: Schema.Types.Mixed }
font: String
},
plugins: {
@ -367,11 +361,6 @@ FormSchema.pre('save', function (next) {
}
});
}, function(cb) {
//DAVID: TODO: Make this so we don't have to update the validFields property ever save
if (that.plugins.oscarhost.hasOwnProperty('baseUrl')) {
var validUpdateTypes = mongoose.model('Form').schema.path('plugins.oscarhost.settings.updateType').enumValues;
that.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes;
}
return cb(null);
},
function(cb) {

View file

@ -157,88 +157,7 @@ FormSubmissionSchema.pre('save', function (next) {
// console.log(_form);
// console.log('should push to api');
// console.log( (!this.oscarDemoNum && !!_form.plugins.oscarhost.baseUrl && !!_form.plugins.oscarhost.settings.fieldMap) );
if(!this.oscarDemoNum && _form.plugins.oscarhost.baseUrl && _form.plugins.oscarhost.settings.fieldMap){
console.log('OSCARHOST API HOOK');
var url_login = _form.plugins.oscarhost.baseUrl+'/LoginService?wsdl',
url_demo = _form.plugins.oscarhost.baseUrl+'/DemographicService?wsdl';
var args_login = {arg0: config.oscarhost.auth.user, arg1: config.oscarhost.auth.pass};
var options = {
ignoredNamespaces: {
namespaces: ['targetNamespace', 'typedNamespace'],
override: true
}
};
// console.log(self.form_fields);
//Generate demographics from hashmap
var generateDemo = function(formFields, conversionMap, demographicsTemplate){
console.log('generating Demo fields');
console.log(conversionMap);
var _generatedDemo = {}, currField, propertyName;
for(var y=0; y<formFields.length; y++){
currField = formFields[y];
propertyName = conversionMap[currField._id];
if(demographicsTemplate.hasOwnProperty(conversionMap[currField._id])){
_generatedDemo[propertyName] = currField.fieldValue+'';
}else if(propertyName === 'DOB'){
var date = new Date(currField.fieldValue);
_generatedDemo.dateOfBirth = date.getDate()+'';
_generatedDemo.yearOfBirth = date.getFullYear()+'';
_generatedDemo.monthOfBirth = date.getMonth()+'';
}
}
var currDate = new Date();
var dateString = currDate.toISOString().split('T')[0] + ' ' + currDate.toISOString().split('T')[1].slice(0,8);
_generatedDemo.lastUpdateDate = currDate.toISOString();
return _generatedDemo;
};
var submissionDemographic = generateDemo(self.form_fields, _form.plugins.oscarhost.settings.fieldMap, newDemoTemplate);
console.log(submissionDemographic);
async.waterfall([
function (callback) {
//Authenticate with API
soap.createClient(url_login, options, function(err, client) {
client.login(args_login, function (err, result) {
if(err) return callback(err);
console.log('SOAP authenticated');
return callback(null, result.return);
});
});
},
function (security_obj, callback) {
//Force Add Demographic
if(_form.plugins.oscarhost.settings.updateType === 'force_add'){
soap.createClient(url_demo, options, function(err, client) {
if(err) return callback(err);
client.setSecurity(new OscarSecurity(security_obj.securityId, security_obj.securityTokenKey) );
client.addDemographic({ arg0: submissionDemographic }, function (err, result) {
console.log('FORCE ADDING DEMOGRAPHIC \n');
// console.log(result.return);
if(err) return callback(err);
return callback(null, result);
});
});
}
}
], function(err, result) {
if(err) return next(err);
self.oscarDemoNum = parseInt(result.return, 10);
console.log('self.oscarDemoNum: '+self.oscarDemoNum);
return next();
});
}else{
return next();
}
return next();
});
}else{
return next();

View file

@ -119,7 +119,13 @@ var UserSchema = new Schema({
resetPasswordExpires: {
type: Date
},
token: String
token: String,
apiKey: {
type: String,
unique: true,
index: true,
sparse: true
},
});
UserSchema.virtual('displayName').get(function () {

View file

@ -9,8 +9,9 @@ var forms = require('../../app/controllers/forms.server.controller'),
module.exports = function(app) {
// Root routing
app.route('/').get(core.index);
app.route('/subdomain/([a-zA-Z0-9]+)/').get(core.form);
app.route('/subdomain/*/forms/:formId([a-zA-Z0-9]+)')
app.route('/subdomain/api/').get(core.redoc);
app.route('/subdomain/^(?!api$)[A-Za-z0-9]*/').get(core.form);
app.route('/subdomain/^(?!api$)[A-Za-z0-9]*/forms/:formId([a-zA-Z0-9]+)')
.get(forms.read)
.post(forms.createSubmission);
};

View file

@ -6,7 +6,8 @@
var users = require('../../app/controllers/users.server.controller'),
forms = require('../../app/controllers/forms.server.controller'),
multer = require('multer'),
config = require('../../config/config');
config = require('../../config/config'),
auth = require('../../config/passport_helpers');
// Setting the pdf upload route and folder
var storage = multer.diskStorage({
@ -27,21 +28,21 @@ var upload = multer({
module.exports = function(app) {
// Form Routes
app.route('/upload/pdf')
.post(users.requiresLogin, upload.single('file'), forms.uploadPDF);
.post(auth.isAuthenticatedOrApiKey, upload.single('file'), forms.uploadPDF);
app.route('/forms')
.get(users.requiresLogin, forms.list)
.post(users.requiresLogin, forms.create);
.get(auth.isAuthenticatedOrApiKey, forms.list)
.post(auth.isAuthenticatedOrApiKey, forms.create);
app.route('/forms/:formId([a-zA-Z0-9]+)')
.get(forms.read)
.post(forms.createSubmission)
.put(users.requiresLogin, forms.hasAuthorization, forms.update)
.delete(users.requiresLogin, forms.hasAuthorization, forms.delete);
.put(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.update)
.delete(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.delete);
app.route('/forms/:formId([a-zA-Z0-9]+)/submissions')
.get(users.requiresLogin, forms.hasAuthorization, forms.listSubmissions)
.delete(users.requiresLogin, forms.hasAuthorization, forms.deleteSubmissions);
.get(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.listSubmissions)
.delete(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.deleteSubmissions);
// Finish by binding the form middleware
app.param('formId', forms.formByID);

View file

@ -3,16 +3,17 @@
/**
* Module dependencies.
*/
var passport = require('passport');
var config = require('../../config/config');
var passport = require('passport'),
config = require('../../config/config'),
auth = require('../../config/passport_helpers');
module.exports = function(app) {
// User Routes
var users = require('../../app/controllers/users.server.controller');
// Setting up the users profile api
app.route('/users/me').get(users.requiresLogin, users.getUser);
app.route('/users').put(users.requiresLogin, users.update);
app.route('/users/me').get(auth.isAuthenticatedOrApiKey, users.getUser);
app.route('/users').put(auth.isAuthenticatedOrApiKey, users.update);
app.route('/users/accounts').delete(users.requiresLogin, users.removeOAuthProvider);
// Setting up the users account verification api
@ -32,6 +33,8 @@ module.exports = function(app) {
app.route('/auth/signin').post(users.signin);
app.route('/auth/signout').get(users.signout);
app.route('/auth/genkey').get(users.requiresLogin, users.generateAPIKey);
// // Setting the facebook oauth routes
// app.route('/auth/facebook').get(passport.authenticate('facebook', {
// scope: ['email']

View file

@ -150,13 +150,6 @@ describe('FormSubmission Model Unit Tests:', function() {
beforeEach(function(done){
var myFieldMap = {};
myFieldMap[myForm.form_fields[0]._id+''] = 'firstName';
myFieldMap[myForm.form_fields[1]._id+''] = 'lastName';
myFieldMap[myForm.form_fields[2]._id+''] = 'sex';
myFieldMap[myForm.form_fields[3]._id+''] = 'DOB';
myFieldMap[myForm.form_fields[4]._id+''] = 'phone';
myForm.plugins.oscarhost.settings.fieldMap = myFieldMap;
myForm.save(function(err, form){
if(err) done(err);
@ -175,49 +168,6 @@ describe('FormSubmission Model Unit Tests:', function() {
});
});
// it('should add Patient to OscarHost EMR after save', function(done){
// var url_login = myForm.plugins.oscarhost.baseUrl+'/LoginService?wsdl',
// url_demo = myForm.plugins.oscarhost.baseUrl+'/DemographicService?wsdl',
// args_login = {arg0: config.oscarhost.auth.user, arg1: config.oscarhost.auth.pass};
// var options = {
// ignoredNamespaces: {
// namespaces: ['targetNamespace', 'typedNamespace'],
// override: true
// }
// };
// async.waterfall([
// function (callback) {
// //Authenticate with API
// soap.createClient(url_login, options, function(err, client) {
// client.login(args_login, function (err, result) {
// if(err) callback(err);
// callback(null, result.return);
// });
// });
// },
// function (security_obj, callback) {
// soap.createClient(url_demo, options, function(err, client) {
// client.setSecurity(new OscarSecurity(security_obj.securityId, security_obj.securityTokenKey) );
// client.getDemographic({ arg0: oscar_demo_num }, function (err, result) {
// if(err) callback(err);
// callback(null, result);
// });
// });
// },
// ], function(err, result) {
// if(err) done(err);
// should.exist(result);
// console.log(result.return);
// done();
// });
// });
});
describe('Method Find', function(){

View file

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{title}}</title>
<!-- General META -->
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!-- Semantic META -->
<meta name="keywords" content="{{keywords}}">
<meta name="description" content="{{description}}">
<!-- Facebook META -->
<meta property="og:site_name" content="{{title}}">
<meta property="og:title" content="{{title}}">
<meta property="og:description" content="{{description}}">
<meta property="og:url" content="{{url}}">
<meta property="og:image" content="/img/brand/logo.png">
<meta property="og:type" content="website">
<!-- Twitter META -->
<meta name="twitter:title" content="{{title}}">
<meta name="twitter:description" content="{{description}}">
<meta name="twitter:url" content="{{url}}">
<meta name="twitter:image" content="/img/brand/logo.png">
<!-- Fav Icon -->
<link href="/static/modules/core/img/brand/favicon.ico" rel="shortcut icon" type="image/x-icon">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,900'>
<!--Bower CSS dependencies-->
<!-- end Bower CSS dependencies-->
<!--Application CSS Files-->
{% for cssFile in cssFiles %}
<link rel="stylesheet" href="{{cssFile}}">
{% endfor %}
<!-- end Application CSS Files-->
<!-- HTML5 Shim -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<redoc spec-url='/static/swagger.json'></redoc>
<script src="https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js"> </script>
<!--Bower JS dependencies-->
{% for bowerJSFile in bowerJSFiles %}
<script type="text/javascript" src="{{bowerJSFile}}"></script>
{% endfor %}
<!-- end Bower JS dependencies-->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-strap/2.3.8/angular-strap.min.js"></script>
{% if process.env.NODE_ENV === 'development' %}
<script src="https://cdn.ravenjs.com/2.3.0/angular/raven.min.js"></script>
<script>
Raven.config('https://825fefd6b4ed4a4da199c1b832ca845c@sentry.tellform.com/2').install();
</script>
<!-- [if lt IE 9]>
<section class="browsehappy jumbotron hide">
<h1>Hello there!</h1>
<p>You are using an old browser which we unfortunately do not support.</p>
<p>Please <a href="http://browsehappy.com/">click here</a> to update your browser before using the website.</p>
<p><a href="http://browsehappy.com" class="btn btn-primary btn-lg" role="button">Yes, upgrade my browser!</a></p>
</section>
<![endif] -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '{{google_analytics_id}}', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

View file

@ -36,16 +36,11 @@
"js-yaml": "^3.6.1",
"angular-ui-select": "https://github.com/whitef0x0/ui-select.git#compiled",
"angular-translate": "~2.11.0",
<<<<<<< HEAD
"ng-device-detector": "^3.0.1",
"ng-translate": "*",
"deep-diff": "^0.3.4"
=======
"ng-device-detector": "~3.0.1",
"ng-translate": "*",
"deep-diff": "^0.3.4",
"mathjs": "^3.4.1",
"jsep": "^0.3.1"
>>>>>>> logic_jump
},
"resolutions": {
"angular-bootstrap": "^0.14.0",

View file

@ -72,8 +72,6 @@ module.exports = function(db) {
var subdomains = req.subdomains;
var host = req.hostname;
console.log(subdomains);
if(subdomains.slice(0, 4).join('.')+'' === '1.0.0.127'){
subdomains = subdomains.slice(4);
}
@ -92,6 +90,20 @@ module.exports = function(db) {
return next();
}
//console.log(subdomains);
//console.log("is api subdomain: "+ (subdomains.indexOf("api") > -1));
//console.log(req.url);
if(subdomains.indexOf('api') > -1){
// rebuild url
path += 'api' + req.url;
console.log(req.url);
// TODO: check path and query strings are preserved
// reassign url
req.url = path;
console.log(req.url);
return next();
}
User.findOne({username: req.subdomains.reverse()[0]}).exec(function (err, user) {
console.log(user);
if (err) {

View file

@ -0,0 +1,54 @@
"use strict";
var config = require("./config");
var passport = require("passport");
var User = require('mongoose').model('User');
module.exports.isAuthenticatedOrApiKey = function isAuthenticated(req, res, next) {
debugger;
if (req.isAuthenticated()) {
return next();
} else {
// Try authenticate with API KEY
if (req.headers.apikey || req.query.apikey || req.body.apikey) {
passport.authenticate("localapikey", function (err, user, info) {
if (err)
return res.sendStatus(500);
if (!user)
return res.status(401).send(info.message || "");
req.login(user, function(err) {
if (err) return res.sendStatus(500);
req.user = user;
return next();
});
})(req, res, next);
} else {
return res.sendStatus(401);
}
}
};
module.exports.hasRole = function hasRole(roleRequired) {
if (!roleRequired)
throw new Error("Required role needs to be set");
return function(req, res, next) {
return module.exports.isAuthenticated(req, res, function() {
if (req.user && req.user.roles && req.user.roles.indexOf(roleRequired) !== -1)
next();
else
res.sendStatus(403);
});
};
};
module.exports.hasAdminRole = function hasAdminRole() {
return module.exports.hasRole("admin");
};

View file

@ -0,0 +1,25 @@
"use strict";
var passport = require("passport");
var LocalAPIKeyStrategy = require("passport-localapikey-update").Strategy;
var User = require('mongoose').model('User');
module.exports = function() {
passport.use(new LocalAPIKeyStrategy({
passReqToCallback : true
}, function(req, apiKey, done) {
return User.findOne({
"apiKey": apiKey
}, function(err, user) {
if (err)
return done(err);
if (!user)
return done(null, false, {
message: "Unknown API Key"
});
return done(null, user);
});
}));
};

View file

@ -94,6 +94,7 @@
"socket.io": "^1.4.6",
"socket.io-redis": "^1.0.0",
"swig": "~1.4.1",
"uuid-token-generator": "^0.5.0",
"wildcard-subdomains": "github:whitef0x0/wildcard-subdomains"
},
"devDependencies": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -55,6 +55,15 @@ section.auth {
color: #4c4c4c;
}
section.auth .btn {
border-radius: 100px;
font-size: 14px;
padding: 12px 28px;
margin-top: 1em;
text-transform: uppercase;
}
.btn-rounded.btn-signup {
background-color: #FFD747;
color: #896D0B;

882
public/swagger.json Normal file
View file

@ -0,0 +1,882 @@
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "TellForm API",
"contact": {
"name": "TellForm Team",
"email": "team@tellform.com"
}
},
"externalDocs": {
"description": "Find out how to host your own TellForm instance.",
"url": "https://github.com/whitef0x0/tellform"
},
"host": "api.tellform.com",
"basePath": "/",
"schemes": [
"http",
"https"
],
"tags": [
{
"name": "form",
"description": "Everything about your Forms"
},
{
"name": "user",
"description": "Everything about your Account"
}
],
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "apikey",
"in": "header"
}
},
"paths": {
"/forms": {
"get": {
"tags": [
"form"
],
"summary": "Find all forms",
"responses": {
"405": {
"description": "Missing Form Input"
},
"400": {
"description": "Form is Invalid"
},
"404": {
"description": "Form not Found"
},
"200": {
"description": "forms response",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Form"
}
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/form/:form_id": {
"get": {
"tags": [
"form"
],
"summary": "Find form by ID",
"responses": {
"200": {
"description": "forms response",
"schema": {
"$ref": "#/definitions/Form"
},
"headers": {
"x-expires": {
"type": "string"
}
}
}
},
"security": [
{
"api_key": []
}
]
},
"post": {
"tags": [
"form"
],
"summary": "Create a new form",
"description": "Create and save a new form",
"operationId": "addForm",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "Form object that is to be created",
"required": true,
"schema": {
"$ref": "#/definitions/Form"
}
}
],
"responses": {
"405": {
"description": "Missing Form Input"
},
"400": {
"description": "Form is Invalid"
},
"404": {
"description": "Form not Found"
},
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Form"
}
}
},
"security": [
{
"api_key": []
}
],
"x-code-samples": [
]
},
"put": {
"tags": [
"form"
],
"summary": "Update an existing form",
"description": "",
"operationId": "updateForm",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"in": "body",
"name": "form",
"description": "Form object that needs to be updated",
"required": true,
"schema": {
"$ref": "#/definitions/Form"
}
}
],
"responses": {
"405": {
"description": "Missing Form Input"
},
"400": {
"description": "Form is Invalid"
},
"404": {
"description": "Form not Found"
},
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Form"
}
}
},
"security": [
{
"api_key": []
}
],
"x-code-samples": [
]
}
},
"/users/me": {
"get": {
"tags": [
"user"
],
"summary": "Retrieve current User",
"description": "",
"operationId": "getUser",
"produces": [
"application/json"
],
"responses": {
"500": {
"description": "Could not Update User"
},
"401": {
"description": "User is not Signed in"
},
"403": {
"description": "User is not Authorized"
},
"404": {
"description": "User does not exsit"
},
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/User"
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/users": {
"put": {
"tags": [
"user"
],
"summary": "Update the current user",
"description": "",
"operationId": "updateUser",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "User object that needs to be updated",
"required": true,
"schema": {
"$ref": "#/definitions/User"
}
}
],
"responses": {
"500": {
"description": "Could not Update User"
},
"401": {
"description": "User is not Signed in"
},
"403": {
"description": "User is not Authorized"
},
"404": {
"description": "User does not exsit"
},
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/User"
}
}
},
"security": [
{
"api_key": []
}
],
"x-code-samples": [
]
}
}
},
"definitions": {
"User": {
"type": "object",
"required": [
"language",
"email",
"username"
],
"properties": {
"firstName": {
"type": "string",
"description": "First name of User",
"example": "John"
},
"lastName": {
"type": "string",
"description": "First name of User",
"example": "Doe"
},
"language": {
"type": "string",
"enum": [
"en",
"fr",
"es",
"it",
"de"
],
"default": "en",
"required": "User must have a language",
"description": "Language of User (for internationalization)",
"example": "fr"
},
"email": {
"type": "string",
"format": "email",
"required": "User email cannot be blank",
"unique": "true",
"description": "Email of User",
"example": "john.doe@somewhere.com"
},
"username": {
"type": "string",
"required": "Username cannot be blank",
"unique": "true",
"description": "Username of User",
"example": "johndoe"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": [
"user",
"admin",
"superuser"
]
},
"default": [ "user" ],
"description": "Security Roles of User"
}
},
"lastModified": {
"type": "date",
"description": "Date that user was last modified",
"example": "2016-08-26T20:19:30.146Z"
},
"created": {
"type": "date",
"description": "Date that user was created on",
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
},
"resetPasswordToken": {
"type": "string",
"description": "Reset password token of User",
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
},
"resetPasswordExpires": {
"type": "date",
"example": "2016-08-26T20:19:30.146Z",
"description": "Date that the User's password reset token expires"
},
"token": {
"type": "string",
"description": "Verification token of User",
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
},
"apiKey": {
"type": "string",
"unique": true,
"index": true,
"sparse": true,
"description": "API Key of User",
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
}
},
"LogicJump": {
"expressionString": {
"type": "string",
"enum": [
"field == static",
"field != static",
"field > static",
"field >= static",
"field <= static",
"field < static",
"field contains static",
"field !contains static",
"field begins static",
"field !begins static",
"field ends static",
"field !ends static"
]
},
"fieldA": {
"$ref": "#/definitions/FormField"
},
"valueB": {
"type": "string"
},
"jumpTo": {
"$ref": "#/definitions/FormField"
}
},
"FieldOption": {
"type": "object",
"properties": {
"option_id": {
"type": "number"
},
"option_title": {
"type": "string"
},
"option_value": {
"type": "string",
"trim": true
}
}
},
"RatingField": {
"type": "object",
"properties": {
"steps": {
"type": "number",
"min": 1,
"max": 10
},
"shape": {
"type": "string",
"enum": [
"Heart",
"Star",
"thumbs-up",
"thumbs-down",
"Circle",
"Square",
"Check Circle",
"Smile Outlined",
"Hourglass",
"bell",
"Paper Plane",
"Comment",
"Trash"
]
},
"validShapes": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"FormField": {
"required": [
"title",
"fieldType"
],
"properties": {
"isSubmission": {
"type": "boolean",
"default": false,
"description": "Specifies whether Field is part of a Submission or not",
"example": true
},
"submissionId": {
"type": "string",
"description": "ID of Submission that this Field belongs to",
"example": "57bca0969ca8e18b825bcc2b"
},
"title": {
"type": "string",
"trim": true,
"required": "Field Title cannot be blank",
"description": "Description of Field",
"example": "Your Current University"
},
"description": {
"type": "string",
"default": "",
"description": "Description of Field",
"example": "Please do not use abbreviations for your school name"
},
"logicJump": {
"$ref": "#/definitions/FormField"
},
"ratingOptions": {
"type": "#/definitions/RatingField"
},
"fieldOptions": {
"type": "array",
"items": {
"type": "FieldOption"
}
},
"required": {
"type": "boolean",
"default": true,
"description": "Specifies whether Field is required",
"example": true
},
"disabled": {
"type": "boolean",
"default": false,
"description": "Specifies whether Field is disabled",
"example": true
},
"deletePreserved": {
"type": "boolean",
"default": false,
"description": "Specifies whether Field should be preserved if it is deleted",
"example": false
},
"validFieldTypes": {
"type": "array",
"items": {
"type": "string"
}
},
"fieldType": {
"type": "string",
"required": true,
"enum": [
"textfield",
"date",
"email",
"link",
"legal",
"url",
"textarea",
"statement",
"welcome",
"thankyou",
"file",
"dropdown",
"scale",
"rating",
"radio",
"checkbox",
"hidden",
"yes_no",
"natural",
"stripe",
"number"
],
"description": "Type of Field",
"example": "textfield"
},
"fieldValue": {
"type": "string",
"description": "Value of Field",
"example": "University of British Columbia"
}
}
},
"VisitorData": {
"type": "object",
"properties": {
"referrer": {
"type": "string",
"description": "Referring site of Form Visitor",
"example": "http://google.com"
},
"lastActiveField": {
"type": "string",
"description": "ID of Last Active Field",
"example": "57bca0969ca8e18b825bcc2b"
},
"timeElapsed": {
"type": "number",
"description": "Time Elasped for Visitor on Form (in seconds)",
"example": "333.33"
},
"isSubmitted": {
"type": "boolean",
"description": "Specifies whether user submitted form before leaving page",
"example": false
},
"language": {
"type": "string",
"description": "Language of User using form",
"example": "en"
},
"ipAddr": {
"type": "string",
"default": "",
"description": "IP Address of User",
"example": "324.332.322.333"
},
"deviceType": {
"type": "string",
"enum": [
"desktop",
"phone",
"tablet",
"other"
],
"default": "other",
"description": "Device Type of User",
"example": "phone"
},
"userAgent": {
"type": "string",
"description": "User Agent of User",
"example": "Mozilla/5.0 (Linux; <Android Version>; <Build Tag etc.>) AppleWebKit/<WebKit Rev> (KHTML, like Gecko) Chrome/<Chrome Rev> Mobile Safari/<WebKit Rev>"
}
}
},
"Button": {
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "url",
"description": "URL of Button Link",
"example": "http://you-are-awesome.com"
},
"action": {
"type": "string",
"description": "Angular Action fired during Button click",
"example": "openModal()"
},
"text": {
"type": "string",
"description": "Text of Button",
"example": "Go to HomePage"
},
"bgColor": {
"type": "string",
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
"default": "#5bc0de",
"description": "Background Color of Button (in hex)",
"example": "#5bc0de"
},
"color": {
"type": "string",
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
"default": "#ffffff",
"description": "Font Color of Button (in hex)",
"example": "#ffffff"
}
}
},
"FormSubmission": {
"type": "object",
"required": [
"language",
"admin",
"title"
],
"properties": {
"title": {
"type": "string",
"required": "Form Title cannot be blank"
},
"language": {
"type": "string",
"enum": [
"en",
"fr",
"es",
"it",
"de"
],
"default": "en",
"required": "Form must have a language"
},
"admin": {
"$ref": "#/definitions/User",
},
"ipAddr": {
"type": "string"
},
"geoLocation": {
"type": "object",
"properties": {
"Country": {
"type": "string"
},
"Region": {
"type": "string"
},
"City": {
"type": "string"
}
}
},
"device": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"pdfFilePath": {
"type": "string"
},
"pdf": {
"type": "string"
},
"timeElapsed": {
"type": "number"
},
"percentageComplete": {
"type": "number"
}
}
},
"Form": {
"type": "object",
"required": [
"language",
"admin",
"title"
],
"properties": {
"title": {
"type": "string",
"required": "Form Title cannot be blank",
"description": "Public Title of Form",
"example": "UBC CPSC221 Course Waitlist Form"
},
"language": {
"type": "string",
"enum": [
"en",
"fr",
"es",
"it",
"de"
],
"default": "en",
"required": "Form must have a language",
"description": "Language of Form",
"example": "en"
},
"analytics": {
"type": "object",
"description": "Analytics of Form",
"properties": {
"gaCode": {
"type": "string",
"description": "Analytics of Form",
"example": "UA-000000-01"
},
"visitors": {
"type": "array",
"items": {
"type": "#/definitions/VisitorData"
}
}
}
},
"form_fields": {
"type": "array",
"items": {
"type": "FormField"
}
},
"submissions": {
"type": "array",
"items": {
"$ref": "#/definitions/FormSubmission"
}
},
"admin": {
"type": "User",
"description": "User that this Form belongs to"
},
"startPage": {
"type": "object",
"properties": {
"showStart": {
"type": "boolean",
"default": false,
"description": "Specifies whether Form StarPage should be displayed",
"example": false
},
"introTitle": {
"type": "string",
"default": "Welcome to Form",
"description": "Title of Form's StartPage",
"example": "Welcome to our Awesome Form!"
},
"introParagraph": {
"type": "string",
"description": "Introduction paragraph for Form's StartPage.",
"example": "Welcome to our Awesome Form!"
},
"introButtonText": {
"type": "string",
"default": "Start",
"description": "StartPage Continue Button",
"example": "Continue"
},
"buttons": {
"type": "array",
"items": {
type: "Button"
}
}
}
},
"hideFooter": {
"type": "boolean",
"default": false,
"description": "Specifies whether to hide or show Form Footer",
"example": true
},
"isLive": {
"type": "boolean",
"default": false,
"description": "Specifies whether form is Publically available or Private",
"example": true
},
"design": {
"type": "object",
"properties": {
"colors": {
"type": "object",
"properties": {
"backgroundColor": {
"type": "string",
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
"default": "#fff",
"description": "Background color of Form",
"example": "#4c4c4c"
},
"questionColor": {
"type": "string",
"match": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
"default": "#333",
"description": "Question text font color (in hex)",
"example": "#fff"
},
"answerColor": {
"type": "string",
"match": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
"default": "#333",
"description": "Answer text font color (in hex)",
"example": "#f9f9f9"
},
"buttonColor": {
"type": "string",
"match": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
"default": "#fff",
"description": "Background color of Form Buttons (in hex)",
"example": "#555"
},
"buttonTextColor": {
"type": "string",
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
"default": "#333",
"description": "Font color of Form Buttons (in hex)",
"example": "#fff"
}
}
},
"font": {
"type": "string"
}
}
}
}
}
}
}