From 3f6a2c3792b2ed45a9424eb7a2aead51a5f38d5f Mon Sep 17 00:00:00 2001 From: matleppa Date: Mon, 12 Jun 2017 16:24:44 +0300 Subject: [PATCH 01/84] Generation of user part of swagger document - added users structure - added new object for fields for users part --- core/server/api.js | 240 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/core/server/api.js b/core/server/api.js index dfd1eb9ed8..88552c5e49 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -17,6 +17,95 @@ const ApiV1 = new Restivus({ enableCors: true, }); +const UserFields = {}; + +UserFields.params = { + company: { + name: 'company', + in: 'body', + description: 'Company name of user', + required: true, + type: 'string', + }, + email: { + name: 'email', + in: 'body', + description: 'Email address for user', + required: true, + type: 'string', + }, + limit: { + name: 'limit', + in: 'query', + description: 'Maximum number of records to return in query.', + required: false, + type: 'integer', + format: 'int32', + minimum: 0, + maximum: 50, + }, + optionalSearch: { + name: 'q', + in: 'query', + description: 'An optional search string for looking up inventory.', + required: false, + type: 'string', + }, + organizationId: { + name: 'id', + in: 'path', + description: 'ID of Organization', + required: false, + type: 'string', + }, + password: { + name: 'password', + in: 'body', + description: 'Password for user', + required: true, + type: 'string', + }, + skip: { + name: 'skip', + in: 'query', + description: 'Number of records to skip for pagination.', + required: false, + type: 'integer', + format: 'int32', + minimum: 0, + }, + since: { + name: 'since', + in: 'path', + description: 'Time frame in days', + required: true, + type: 'integer', + format: 'int32', + minimum: 1, + }, + sortBy: { + name: 'sort_by', + in: 'query', + description: 'Criteria for sort ', + required: false, + type: 'string', + }, + userId: { + name: 'id', + in: 'path', + description: 'ID of User', + required: true, + type: 'string', + }, + userName: { + name: 'username', + in: 'body', + description: 'Username', + required: true, + type: 'string', + }, +}; + // Add Restivus Swagger configuration - meta, tags, params, definitions ApiV1.swagger = { meta: { @@ -26,6 +115,155 @@ ApiV1.swagger = { version: '1.0.0', title: 'Admin API', }, + paths: { + '/users': { + getAll: { + description: 'Returns users', + parameters: [ + UserFields.params.optionalSearch, + UserFields.params.organizationId, + UserFields.params.skip, + UserFields.params.limit, + UserFields.params.sortBy, + ], + responses: { + 200: { + description: 'success', + }, + 400: { + description: 'Bad query parameters', + }, + }, + }, + + get: { + description: 'Returns user with given ID.', + parameters: { + name: 'id', + in: 'path', + description: 'ID of User', + required: true, + type: 'string', + }, + + responses: { + 200: { + description: 'One user.', + }, + }, + }, + post: { + description: 'Adds a new user. On success, returns newly added object.', + parameters: [ + UserFields.params.userId, + UserFields.params.userName, + UserFields.params.email, + UserFields.params.password, + ], + responses: { + 201: { + description: 'User successfully added', + }, + 400: { + description: 'Invalid input, object invalid', + }, + 401: { + description: 'Authentication is required', + }, + 409: { + description: 'User already exists', + }, + }, + security: [ + { + userSecurityToken: [], + userId: [], + }, + ], + delete: { + description: 'Deletes the identified Organization from catalog.', + parameters: [ + UserFields.params.userId, + ], + responses: { + 200: { + description: 'User deleted.', + }, + 400: { + description: 'Invalid input, invalid object', + }, + 401: { + description: 'Authentication is required', + }, + 403: { + description: 'User does not have permission', + }, + 404: { + description: 'User not found', + }, + }, + security: [ + { + userSecurityToken: [], + userId: [], + }, + ], + put: { + description: 'Update a User', + parameters: [ + UserFields.params.userId, + UserFields.params.userName, + UserFields.params.company, + UserFields.params.password, + ], + responses: { + 200: { + description: 'User successfully updated.', + }, + 401: { + description: 'Authentication is required', + }, + 403: { + description: 'User does not have permission', + }, + 404: { + description: 'No user found with given UserID', + }, + }, + security: [ + { + userSecurityToken: [], + userId: [], + }, + ], + + }, + + }, + }, + }, + '/users/updates': { + get: { + description: 'Returns users', + parameters: [ + UserFields.params.since, + UserFields.params.organizationId, + UserFields.params.skip, + UserFields.params.limit, + ], + responses: { + 200: { + description: 'success', + }, + 400: { + description: 'Bad query parameters', + }, + }, + }, + + }, + + }, securityDefinitions: { userSecurityToken: { in: 'header', @@ -244,6 +482,8 @@ ApiV1.swagger = { }, }, }, + + }; // Generate Swagger to route /rest/v1/swagger.json From f7625c3f152688f85c5db49fe8438f7b41339770 Mon Sep 17 00:00:00 2001 From: matleppa Date: Tue, 13 Jun 2017 08:52:32 +0300 Subject: [PATCH 02/84] Comment update --- core/server/api.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/server/api.js b/core/server/api.js index 88552c5e49..c5300a58f3 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -118,7 +118,7 @@ ApiV1.swagger = { paths: { '/users': { getAll: { - description: 'Returns users', + description: 'Returns: For Admin: all users data. For non-admin: only own data.', parameters: [ UserFields.params.optionalSearch, UserFields.params.organizationId, @@ -137,7 +137,7 @@ ApiV1.swagger = { }, get: { - description: 'Returns user with given ID.', + description: 'Returns user data with given ID.', parameters: { name: 'id', in: 'path', @@ -181,7 +181,7 @@ ApiV1.swagger = { }, ], delete: { - description: 'Deletes the identified Organization from catalog.', + description: 'Deletes the identified User.', parameters: [ UserFields.params.userId, ], @@ -244,7 +244,7 @@ ApiV1.swagger = { }, '/users/updates': { get: { - description: 'Returns users', + description: 'Returns users, who are created in given timeframe', parameters: [ UserFields.params.since, UserFields.params.organizationId, From fe3522ee0bfb93f9f24c082aaa00929a83cb70e2 Mon Sep 17 00:00:00 2001 From: matleppa Date: Tue, 13 Jun 2017 10:26:41 +0300 Subject: [PATCH 03/84] Added responses to users get verb --- core/server/api.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/server/api.js b/core/server/api.js index c5300a58f3..75bdf069a8 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -148,7 +148,13 @@ ApiV1.swagger = { responses: { 200: { - description: 'One user.', + description: 'Data of identified user.', + }, + 403: { + description: 'User does not have permission.', + }, + 404: { + description: 'No user found with given UserID.', }, }, }, From d54d67de95b254727368f548dc76f610942adfac Mon Sep 17 00:00:00 2001 From: matleppa Date: Tue, 13 Jun 2017 10:30:08 +0300 Subject: [PATCH 04/84] Added functionality of getting user by id --- users/collection/server/api.js | 76 +++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/users/collection/server/api.js b/users/collection/server/api.js index 9a1b0bf760..4916bfefe8 100644 --- a/users/collection/server/api.js +++ b/users/collection/server/api.js @@ -32,7 +32,7 @@ ApiV1.addCollection(Meteor.users, { tags: [ ApiV1.swagger.tags.users, ], - description: 'Returns users', + description: 'Returns: For Admin: all users data. For non-admin: only own data.', parameters: [ ApiV1.swagger.params.optionalSearch, ApiV1.swagger.params.organization_id, @@ -206,6 +206,78 @@ ApiV1.addCollection(Meteor.users, { }, }, }, + action () { + // Get requestor's id + const requestorId = this.userId; + + const userIsGettingOwnAccount = this.urlParams.id === requestorId; + + const userIsAdmin = Roles.userIsInRole(requestorId, ['admin']); + + if (userIsGettingOwnAccount || userIsAdmin) { + // Get ID of User to be fetched + const userId = this.urlParams.id; + + // Exclude password + const options = {}; + const excludeFields = {}; + + excludeFields.services = 0; + options.fields = excludeFields; + + // Check if user exists + const user = Meteor.users.findOne(userId, options); + if (user) { + // Array for Organization name and id + const orgDataList = []; + // Get user id + const userIdSearch = user._id; + // Find all Organizations, where User belongs to + const organizations = Organizations.find({ + managerIds: userIdSearch, + }).fetch(); + // If there are Users' Organizations + if (organizations.length > 0) { + // Loop through Users' Organizations + organizations.forEach((organization) => { + const orgData = {}; + // Put Organization name and id into an object + orgData.organizationId = organization._id; + orgData.organizationName = organization.name; + // Add this Organization data into Users' organization data list + orgDataList.push(orgData); + }); + // Add Organizations' information to Users' data + user.organization = orgDataList; + } + // Construct response + return { + statusCode: 200, + body: { + status: 'success', + data: user, + }, + }; + } + + // User didn't exist + return { + statusCode: 404, + body: { + status: 'Fail', + message: 'No user found with given UserID', + }, + }; + } + return { + statusCode: 403, + body: { + status: 'Fail', + message: 'User does not have permission', + }, + }; + }, + }, post: { authRequired: true, @@ -308,7 +380,7 @@ ApiV1.addCollection(Meteor.users, { tags: [ ApiV1.swagger.tags.users, ], - description: 'Deletes the identified Organization from catalog.', + description: 'Deletes the identified User.', parameters: [ ApiV1.swagger.params.userId, ], From 474776e042129190ba91b9dcde82152d30639535 Mon Sep 17 00:00:00 2001 From: matleppa Date: Wed, 14 Jun 2017 16:34:13 +0300 Subject: [PATCH 05/84] Added a new method to remove user from all Organizations --- organizations/server/methods.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/organizations/server/methods.js b/organizations/server/methods.js index 3980d0994d..3f4f2a7aa8 100644 --- a/organizations/server/methods.js +++ b/organizations/server/methods.js @@ -100,6 +100,23 @@ Meteor.methods({ } ); }, + removeUserFromAllOrganizations (userId) { + // Make sure userId is an String + check(userId, String); + // Check if user to be deleted is a manager in any Organizations + const organizations = Organizations.find({ + managerIds: userId, + }).fetch(); + + // If user is a manager in any Organization + if (organizations.length > 0) { + // Loop through Users' Organizations + organizations.forEach((organization) => { + // Remove user from organization manager list + Meteor.call('removeOrganizationManager', organization._id, userId); + }); + } + }, removeApiFromFeaturedList (organizationId, apiId) { // Make sure organizationId is an String check(organizationId, String); From 6c7f3c05dc292e013e39e908d2d6c4a3f7f9b74f Mon Sep 17 00:00:00 2001 From: matleppa Date: Wed, 14 Jun 2017 16:35:18 +0300 Subject: [PATCH 06/84] When user is deleted, user is removed from all Organizations, too. --- users/collection/server/api.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/users/collection/server/api.js b/users/collection/server/api.js index 4916bfefe8..bace8e574f 100644 --- a/users/collection/server/api.js +++ b/users/collection/server/api.js @@ -422,6 +422,9 @@ ApiV1.addCollection(Meteor.users, { // Check if user exists const user = Meteor.users.findOne(userId); if (user) { + // Remove user from all Organizations + Meteor.call('removeUserFromAllOrganizations', userId); + // Remove existing User account Meteor.users.remove(user._id); From 224c8d48a340f16ab9bdb770592fb906ba5a0ad2 Mon Sep 17 00:00:00 2001 From: matleppa Date: Fri, 16 Jun 2017 12:15:25 +0300 Subject: [PATCH 07/84] Corrected comment --- organizations/server/methods.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/organizations/server/methods.js b/organizations/server/methods.js index 3f4f2a7aa8..e45be462d1 100644 --- a/organizations/server/methods.js +++ b/organizations/server/methods.js @@ -103,7 +103,7 @@ Meteor.methods({ removeUserFromAllOrganizations (userId) { // Make sure userId is an String check(userId, String); - // Check if user to be deleted is a manager in any Organizations + // Get list of Organizations where user is a manager const organizations = Organizations.find({ managerIds: userId, }).fetch(); From 7b783bd56bc8ec028f28d1c5762e1d993ef28320 Mon Sep 17 00:00:00 2001 From: matleppa Date: Fri, 16 Jun 2017 14:05:14 +0300 Subject: [PATCH 08/84] Changed order to alphabetical --- core/server/api.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/server/api.js b/core/server/api.js index 75bdf069a8..169ec52cef 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -65,15 +65,6 @@ UserFields.params = { required: true, type: 'string', }, - skip: { - name: 'skip', - in: 'query', - description: 'Number of records to skip for pagination.', - required: false, - type: 'integer', - format: 'int32', - minimum: 0, - }, since: { name: 'since', in: 'path', @@ -83,6 +74,15 @@ UserFields.params = { format: 'int32', minimum: 1, }, + skip: { + name: 'skip', + in: 'query', + description: 'Number of records to skip for pagination.', + required: false, + type: 'integer', + format: 'int32', + minimum: 0, + }, sortBy: { name: 'sort_by', in: 'query', From c3f36fe91495b0cc6eb855ee3ff50da58a866a40 Mon Sep 17 00:00:00 2001 From: matleppa Date: Tue, 20 Jun 2017 14:39:05 +0300 Subject: [PATCH 09/84] Removed unnecessary parameter userId --- users/collection/server/api.js | 1 - 1 file changed, 1 deletion(-) diff --git a/users/collection/server/api.js b/users/collection/server/api.js index bace8e574f..e31b0af535 100644 --- a/users/collection/server/api.js +++ b/users/collection/server/api.js @@ -288,7 +288,6 @@ ApiV1.addCollection(Meteor.users, { ], description: 'Adds a new user. On success, returns newly added object.', parameters: [ - ApiV1.swagger.params.userId, ApiV1.swagger.params.userName, ApiV1.swagger.params.email, ApiV1.swagger.params.password, From 91eea9bf2aa22229514cab4f7c207e0fe4d94240 Mon Sep 17 00:00:00 2001 From: matleppa Date: Tue, 20 Jun 2017 14:45:47 +0300 Subject: [PATCH 10/84] Changes concerning users path - Renamed UserFields.params to UserFields.structs - added tags section into UserFields.structs - added section params and definitions into UserFields.struct - added definition for addUser and updateUser - renamed parameters according to UserFields changes - added users section into general definitions --- core/server/api.js | 407 +++++++++++++++++++++++++++------------------ 1 file changed, 247 insertions(+), 160 deletions(-) diff --git a/core/server/api.js b/core/server/api.js index 169ec52cef..6835ac6c58 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -19,90 +19,148 @@ const ApiV1 = new Restivus({ const UserFields = {}; -UserFields.params = { - company: { - name: 'company', - in: 'body', - description: 'Company name of user', - required: true, - type: 'string', - }, - email: { - name: 'email', - in: 'body', - description: 'Email address for user', - required: true, - type: 'string', - }, - limit: { - name: 'limit', - in: 'query', - description: 'Maximum number of records to return in query.', - required: false, - type: 'integer', - format: 'int32', - minimum: 0, - maximum: 50, - }, - optionalSearch: { - name: 'q', - in: 'query', - description: 'An optional search string for looking up inventory.', - required: false, - type: 'string', - }, - organizationId: { - name: 'id', - in: 'path', - description: 'ID of Organization', - required: false, - type: 'string', - }, - password: { - name: 'password', - in: 'body', - description: 'Password for user', - required: true, - type: 'string', - }, - since: { - name: 'since', - in: 'path', - description: 'Time frame in days', - required: true, - type: 'integer', - format: 'int32', - minimum: 1, - }, - skip: { - name: 'skip', - in: 'query', - description: 'Number of records to skip for pagination.', - required: false, - type: 'integer', - format: 'int32', - minimum: 0, - }, - sortBy: { - name: 'sort_by', - in: 'query', - description: 'Criteria for sort ', - required: false, - type: 'string', +UserFields.struct = { + tags: { + users: 'Users', }, - userId: { - name: 'id', - in: 'path', - description: 'ID of User', - required: true, - type: 'string', + + params: { + company: { + name: 'company', + in: 'body', + description: 'Company name of user', + required: true, + type: 'string', + }, + email: { + name: 'email', + in: 'body', + description: 'Email address for user', + required: true, + type: 'string', + }, + limit: { + name: 'limit', + in: 'query', + description: 'Maximum number of records to return in query.', + required: false, + type: 'integer', + format: 'int32', + minimum: 0, + maximum: 50, + }, + optionalSearch: { + name: 'q', + in: 'query', + description: 'An optional search string for looking up inventory.', + required: false, + type: 'string', + }, + organizationId: { + name: 'id', + in: 'path', + description: 'ID of Organization', + required: false, + type: 'string', + }, + password: { + name: 'password', + in: 'body', + description: 'Password for user', + required: true, + type: 'string', + }, + since: { + name: 'since', + in: 'path', + description: 'Time frame in days', + required: true, + type: 'integer', + format: 'int32', + minimum: 1, + }, + skip: { + name: 'skip', + in: 'query', + description: 'Number of records to skip for pagination.', + required: false, + type: 'integer', + format: 'int32', + minimum: 0, + }, + sortBy: { + name: 'sort_by', + in: 'query', + description: 'Criteria for sort ', + required: false, + type: 'string', + }, + updateUser: { + name: 'Update user', + in: 'body', + description: 'Data for editing User', + schema: { + $ref: '#/definitions/updateUser', + }, + }, + + userId: { + name: 'id', + in: 'path', + description: 'ID of User', + required: true, + type: 'string', + }, + userName: { + name: 'username', + in: 'body', + description: 'Username', + required: true, + type: 'string', + }, }, - userName: { - name: 'username', - in: 'body', - description: 'Username', - required: true, - type: 'string', + + definitions: { + addUser: { + required: ['userName', 'password', 'email'], + properties: { + userName: { + type: 'string', + example: 'John Doe', + }, + password: { + type: 'string', + example: 'secrecy_is_everything#¤%', + }, + email: { + type: 'string', + format: 'email', + description: 'E-mail address of user', + example: 'company-mail@gmail.com', + }, + }, + }, + + updateUser: { + required: [], + properties: { + userName: { + type: 'string', + example: 'John Doe', + }, + company: { + type: 'string', + example: 'Amazing Company Ltd.', + }, + email: { + type: 'string', + format: 'email', + description: 'E-mail address of user', + example: 'company-mail@gmail.com', + }, + }, + }, + }, }; @@ -118,13 +176,17 @@ ApiV1.swagger = { paths: { '/users': { getAll: { + tags: [ + UserFields.struct.tags.users, + ], + description: 'Returns: For Admin: all users data. For non-admin: only own data.', parameters: [ - UserFields.params.optionalSearch, - UserFields.params.organizationId, - UserFields.params.skip, - UserFields.params.limit, - UserFields.params.sortBy, + UserFields.struct.params.optionalSearch, + UserFields.struct.params.organizationId, + UserFields.struct.params.skip, + UserFields.struct.params.limit, + UserFields.struct.params.sortBy, ], responses: { 200: { @@ -135,16 +197,14 @@ ApiV1.swagger = { }, }, }, - get: { + tags: [ + UserFields.struct.tags.users, + ], description: 'Returns user data with given ID.', - parameters: { - name: 'id', - in: 'path', - description: 'ID of User', - required: true, - type: 'string', - }, + parameters: [ + UserFields.struct.params.userId, + ], responses: { 200: { @@ -158,13 +218,20 @@ ApiV1.swagger = { }, }, }, + }, + + '/users/{id}': { post: { + tags: [ + UserFields.struct.tags.users, + ], description: 'Adds a new user. On success, returns newly added object.', + consumes: 'application/json', + produces: 'application/json', + parameters: [ - UserFields.params.userId, - UserFields.params.userName, - UserFields.params.email, - UserFields.params.password, + UserFields.struct.params.userName, + UserFields.struct.params.addUser, ], responses: { 201: { @@ -186,76 +253,82 @@ ApiV1.swagger = { userId: [], }, ], - delete: { - description: 'Deletes the identified User.', - parameters: [ - UserFields.params.userId, - ], - responses: { - 200: { - description: 'User deleted.', - }, - 400: { - description: 'Invalid input, invalid object', - }, - 401: { - description: 'Authentication is required', - }, - 403: { - description: 'User does not have permission', - }, - 404: { - description: 'User not found', - }, + }, + delete: { + tags: [ + UserFields.struct.tags.users, + ], + description: 'Deletes the identified User.', + parameters: [ + UserFields.struct.params.userId, + ], + responses: { + 200: { + description: 'User deleted.', + }, + 400: { + description: 'Invalid input, invalid object', + }, + 401: { + description: 'Authentication is required', + }, + 403: { + description: 'User does not have permission', + }, + 404: { + description: 'User not found', + }, + }, + security: [ + { + userSecurityToken: [], + userId: [], + }, + ], + }, + put: { + tags: [ + UserFields.struct.tags.users, + ], + description: 'Update a User', + parameters: [ + UserFields.struct.params.userId, + UserFields.struct.params.updateUser, + ], + responses: { + 200: { + description: 'User successfully updated.', }, - security: [ - { - userSecurityToken: [], - userId: [], - }, - ], - put: { - description: 'Update a User', - parameters: [ - UserFields.params.userId, - UserFields.params.userName, - UserFields.params.company, - UserFields.params.password, - ], - responses: { - 200: { - description: 'User successfully updated.', - }, - 401: { - description: 'Authentication is required', - }, - 403: { - description: 'User does not have permission', - }, - 404: { - description: 'No user found with given UserID', - }, - }, - security: [ - { - userSecurityToken: [], - userId: [], - }, - ], - + 401: { + description: 'Authentication is required', + }, + 403: { + description: 'User does not have permission', + }, + 404: { + description: 'No user found with given UserID', }, - }, + security: [ + { + userSecurityToken: [], + userId: [], + }, + ], }, }, + '/users/updates': { get: { + tags: [ + UserFields.struct.tags.users, + ], description: 'Returns users, who are created in given timeframe', parameters: [ - UserFields.params.since, - UserFields.params.organizationId, - UserFields.params.skip, - UserFields.params.limit, + UserFields.struct.params.since, + UserFields.struct.params.organizationId, + UserFields.struct.params.skip, + UserFields.struct.params.limit, ], responses: { 200: { @@ -487,6 +560,20 @@ ApiV1.swagger = { }, }, }, + + users: { + required: ['name', 'email', 'password'], + properties: { + username: + UserFields.params.userName, + email: + UserFields.params.email, + password: + UserFields.params.password, + company: + UserFields.params.company, + }, + }, }, From 9969166a6b7d4792d7747d7ffee716bc3ef063e1 Mon Sep 17 00:00:00 2001 From: matleppa Date: Wed, 21 Jun 2017 12:21:29 +0300 Subject: [PATCH 11/84] Changes in swagger document generation - corrected parameter (since, organizationId) position from path to query - replaced references to updateUser and addUser with plain field descriptions - changes in descriptions - removed definitions part from UserFields object - added /users/{id} method - corrected references to UserFields object --- core/server/api.js | 186 +++++++++++++++++++++++++-------------------- 1 file changed, 104 insertions(+), 82 deletions(-) diff --git a/core/server/api.js b/core/server/api.js index 6835ac6c58..95744a0788 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -25,6 +25,30 @@ UserFields.struct = { }, params: { + addUser: { + name: 'Update user', + in: 'body', + description: 'Data for adding User', + schema: { + required: ['userName', 'password', 'email'], + properties: { + userName: { + type: 'string', + example: 'John Doe', + }, + password: { + type: 'string', + example: 'secrecy_is_everything#¤%', + }, + email: { + type: 'string', + format: 'email', + description: 'E-mail address of user', + example: 'company-mail@gmail.com', + }, + }, + }, + }, company: { name: 'company', in: 'body', @@ -39,6 +63,22 @@ UserFields.struct = { required: true, type: 'string', }, + // getUserData { + // properties: + // _id: + // type: string + // example: '7L4jNtdfNFGH3igPs' + // created_at: + // $ref: '#/definitions/get_user_created_at' + // username: + // type: string + // example: myusername + // profile: + // $ref: '#/definitions/get_user_profile' + // organization: + // $ref: '#/definitions/get_user_organization' + // + // }, limit: { name: 'limit', in: 'query', @@ -52,14 +92,14 @@ UserFields.struct = { optionalSearch: { name: 'q', in: 'query', - description: 'An optional search string for looking up inventory.', + description: 'Pass an optional search string for looking up users.', required: false, type: 'string', }, organizationId: { - name: 'id', - in: 'path', - description: 'ID of Organization', + name: 'organization_id', + in: 'query', + description: 'Limit results to the given organization.', required: false, type: 'string', }, @@ -72,9 +112,9 @@ UserFields.struct = { }, since: { name: 'since', - in: 'path', + in: 'query', description: 'Time frame in days', - required: true, + required: false, type: 'integer', format: 'int32', minimum: 1, @@ -91,7 +131,7 @@ UserFields.struct = { sortBy: { name: 'sort_by', in: 'query', - description: 'Criteria for sort ', + description: 'options "username, created_at".', required: false, type: 'string', }, @@ -100,68 +140,39 @@ UserFields.struct = { in: 'body', description: 'Data for editing User', schema: { - $ref: '#/definitions/updateUser', + required: [], + properties: { + userName: { + type: 'string', + example: 'John Doe', + }, + company: { + type: 'string', + example: 'Amazing Company Ltd.', + }, + password: { + type: 'string', + example: 'secrecy_is_everything#¤%', + }, + }, }, }, - userId: { name: 'id', in: 'path', - description: 'ID of User', + description: 'Id of the user to fetch.', required: true, type: 'string', }, userName: { name: 'username', in: 'body', - description: 'Username', + description: 'Name of user', required: true, type: 'string', }, }, - definitions: { - addUser: { - required: ['userName', 'password', 'email'], - properties: { - userName: { - type: 'string', - example: 'John Doe', - }, - password: { - type: 'string', - example: 'secrecy_is_everything#¤%', - }, - email: { - type: 'string', - format: 'email', - description: 'E-mail address of user', - example: 'company-mail@gmail.com', - }, - }, - }, - - updateUser: { - required: [], - properties: { - userName: { - type: 'string', - example: 'John Doe', - }, - company: { - type: 'string', - example: 'Amazing Company Ltd.', - }, - email: { - type: 'string', - format: 'email', - description: 'E-mail address of user', - example: 'company-mail@gmail.com', - }, - }, - }, - - }, }; // Add Restivus Swagger configuration - meta, tags, params, definitions @@ -175,7 +186,7 @@ ApiV1.swagger = { }, paths: { '/users': { - getAll: { + get: { tags: [ UserFields.struct.tags.users, ], @@ -195,32 +206,11 @@ ApiV1.swagger = { 400: { description: 'Bad query parameters', }, - }, - }, - get: { - tags: [ - UserFields.struct.tags.users, - ], - description: 'Returns user data with given ID.', - parameters: [ - UserFields.struct.params.userId, - ], - - responses: { - 200: { - description: 'Data of identified user.', - }, - 403: { - description: 'User does not have permission.', - }, - 404: { - description: 'No user found with given UserID.', + 401: { + description: 'Authentication is required', }, }, }, - }, - - '/users/{id}': { post: { tags: [ UserFields.struct.tags.users, @@ -230,7 +220,6 @@ ApiV1.swagger = { produces: 'application/json', parameters: [ - UserFields.struct.params.userName, UserFields.struct.params.addUser, ], responses: { @@ -254,6 +243,33 @@ ApiV1.swagger = { }, ], }, + }, + + '/users/{id}': { + get: { + tags: [ + UserFields.struct.tags.users, + ], + description: 'Returns user data with given ID.', + parameters: [ + UserFields.struct.params.userId, + ], + + responses: { + 200: { + description: 'Data of identified user.', + }, + 401: { + description: 'Authentication is required', + }, + 403: { + description: 'User does not have permission.', + }, + 404: { + description: 'No user found with given UserID.', + }, + }, + }, delete: { tags: [ UserFields.struct.tags.users, @@ -299,6 +315,9 @@ ApiV1.swagger = { 200: { description: 'User successfully updated.', }, + 400: { + description: 'Invalid input, object invalid, Erroneous new password', + }, 401: { description: 'Authentication is required', }, @@ -337,6 +356,9 @@ ApiV1.swagger = { 400: { description: 'Bad query parameters', }, + 401: { + description: 'Authentication is required', + }, }, }, @@ -565,13 +587,13 @@ ApiV1.swagger = { required: ['name', 'email', 'password'], properties: { username: - UserFields.params.userName, + UserFields.struct.params.userName, email: - UserFields.params.email, + UserFields.struct.params.email, password: - UserFields.params.password, + UserFields.struct.params.password, company: - UserFields.params.company, + UserFields.struct.params.company, }, }, }, From 91940008421dc2a77b7388e2c706e48523ff39e7 Mon Sep 17 00:00:00 2001 From: Daria Voytova Date: Wed, 21 Jun 2017 18:11:01 +0300 Subject: [PATCH 12/84] Replace path "/users/{id}" into related folder Remove path "/users/{id}" from ApiV1.swagger.meta.paths in file "core/server/api.js", save a placeholder Remove swagger part from origin REST API file. Because of restivus-swagger doesn't create properly Swagger documentation for Users collection, thispart wasremoved. Now it allows us two different part: one ofthis provides only feature for creating Swagger file (ApiV1.swagger.meta.paths) and another one provides only logic for REST API --- core/server/api.js | 25 +--------------------- users/collection/server/api.js | 38 +++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/core/server/api.js b/core/server/api.js index 95744a0788..ecf6750ad1 100644 --- a/core/server/api.js +++ b/core/server/api.js @@ -246,30 +246,7 @@ ApiV1.swagger = { }, '/users/{id}': { - get: { - tags: [ - UserFields.struct.tags.users, - ], - description: 'Returns user data with given ID.', - parameters: [ - UserFields.struct.params.userId, - ], - - responses: { - 200: { - description: 'Data of identified user.', - }, - 401: { - description: 'Authentication is required', - }, - 403: { - description: 'User does not have permission.', - }, - 404: { - description: 'No user found with given UserID.', - }, - }, - }, + get: {}, delete: { tags: [ UserFields.struct.tags.users, diff --git a/users/collection/server/api.js b/users/collection/server/api.js index e31b0af535..91cfe1a781 100644 --- a/users/collection/server/api.js +++ b/users/collection/server/api.js @@ -18,6 +18,30 @@ import Organizations from '/organizations/collection'; import _ from 'lodash'; +ApiV1.swagger.meta.paths['/users/{id}'].get = { + tags: [ + ApiV1.swagger.tags.users, + ], + description: 'Returns user data with given ID.', + parameters: [ + ApiV1.swagger.params.userId, + ], + responses: { + 200: { + description: 'Data of identified user.', + }, + 401: { + description: 'Authentication is required', + }, + 403: { + description: 'User does not have permission.', + }, + 404: { + description: 'No user found with given UserID.', + }, + }, +}; + // Generates: POST on /api/v1/users and GET, DELETE /api/v1/users/:id for // Meteor.users collection ApiV1.addCollection(Meteor.users, { @@ -192,20 +216,6 @@ ApiV1.addCollection(Meteor.users, { }, get: { authRequired: true, - swagger: { - tags: [ - ApiV1.swagger.tags.users, - ], - description: 'Returns user with given ID.', - parameters: [ - ApiV1.swagger.params.userId, - ], - responses: { - 200: { - description: 'One user.', - }, - }, - }, action () { // Get requestor's id const requestorId = this.userId; From adb808fc8acac9677e44349fad2d68ed4d2d9163 Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Mon, 12 Jun 2017 18:06:09 -0300 Subject: [PATCH 13/84] Setup chimp on travis --- .scripts/run-chimp.js | 94 ++++++++++++++++++++++++++++++++++++++++++ .scripts/start-xvfb.sh | 13 ++++++ .travis.yml | 15 +++++++ package.json | 2 +- 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 .scripts/run-chimp.js create mode 100644 .scripts/start-xvfb.sh diff --git a/.scripts/run-chimp.js b/.scripts/run-chimp.js new file mode 100644 index 0000000000..9d67b828b4 --- /dev/null +++ b/.scripts/run-chimp.js @@ -0,0 +1,94 @@ +/* Copyright 2017 Apinf Oy +This file is covered by the EUPL license. +You may obtain a copy of the licence at +https://joinup.ec.europa.eu/community/eupl/og_page/european-union-public-licence-eupl-v11 */ + +// Inspired by https://goo.gl/Rbd2s2 + +var path = require('path'), + fs = require('fs'), + extend = require('util')._extend, + exec = require('child_process').exec, + processes = []; + +var baseDir = path.resolve(__dirname, '..'), + srcDir = path.resolve(baseDir); + +var appOptions = { + env: { + PORT: 3000, + ROOT_URL: 'http://localhost:3000' + } +}; + +function startProcess(opts, callback) { + var proc = exec( + opts.command, + opts.options + ); + + if (opts.waitForMessage) { + proc.stdout.on('data', function waitForMessage(data) { + if (data.toString().match(opts.waitForMessage)) { + if (callback) { + callback(); + } + } + }); + } + + if (!opts.silent) { + proc.stdout.pipe(process.stdout); + proc.stderr.pipe(process.stderr); + } + + if (opts.logFile) { + var logStream = fs.createWriteStream(opts.logFile, {flags: 'a'}); + proc.stdout.pipe(logStream); + proc.stderr.pipe(logStream); + } + + proc.on('close', function(code) { + console.log(opts.name, 'exited with code ' + code); + for (var i = 0; i < processes.length; i += 1) { + processes[i].kill(); + } + process.exit(code); + }); + processes.push(proc); +} + +function startApp(callback) { + startProcess({ + name: 'Meteor App', + command: 'meteor', + waitForMessage: appOptions.waitForMessage, + options: { + cwd: srcDir, + env: extend(appOptions.env, process.env) + } + }, callback); +} + +function startChimp() { + startProcess({ + name: 'Chimp', + command: 'yarn run chimp-test', + options: { + env: Object.assign({}, process.env, { + NODE_PATH: process.env.NODE_PATH + + path.delimiter + srcDir + + path.delimiter + srcDir + '/node_modules' + }) + } + }); +} + +function chimpNoMirror() { + appOptions.waitForMessage = 'SERVER RUNNING'; + startApp(function() { + startChimp(); + }); +} + +chimpNoMirror(); \ No newline at end of file diff --git a/.scripts/start-xvfb.sh b/.scripts/start-xvfb.sh new file mode 100644 index 0000000000..6cfdf1536b --- /dev/null +++ b/.scripts/start-xvfb.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Copyright 2017 Apinf Oy +#This file is covered by the EUPL license. +#You may obtain a copy of the licence at +#https://joinup.ec.europa.eu/community/eupl/og_page/european-union-public-licence-eupl-v11 + +set -ev + +if [ "${TRAVIS_OS_NAME}" = "linux" ]; then + sh -e /etc/init.d/xvfb start + sleep 3 +fi diff --git a/.travis.yml b/.travis.yml index 93de32696e..df1fffb959 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,9 @@ language: node_js node_js: - "node" +addons: + chrome: stable + branches: only: - develop @@ -11,6 +14,7 @@ cache: yarn: true before_install: + - if [ ! -e "$HOME/.meteor/meteor" ]; then curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh; fi - curl -o- -L https://yarnpkg.com/install.sh | bash - export PATH=$HOME/.yarn/bin:$PATH @@ -18,7 +22,18 @@ services: - docker before_script: + # Start X Virtual Frame Buffer for headless testing with real browsers + - ./scripts/start-xvfb.sh + # Install node-gyp for running chimp + - meteor npm install -g node-gyp node-pre-gyp - yarn run lint script: + # Run meteor and chimp from node.js + - travis_retry yarn test - ./.scripts/docker_build.sh + +env: + global: + - DISPLAY=:99.0 + - TEST_MODE: "true" \ No newline at end of file diff --git a/package.json b/package.json index 4e54ee4f86..1b50fc17fe 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "chimp-watch": "chimp --ddp=http://localhost:3000 --mocha --path=tests/end-to-end --watch", "chimp-test": "chimp tests/chimp-config.js", "start": "meteor", - "test": "mocha ./.test/" + "test": "node ./scripts/start.js" }, "repository": { "type": "git", From 0e509b0754239e2800bb0d6761385c1f481dc22f Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Mon, 12 Jun 2017 18:21:23 -0300 Subject: [PATCH 14/84] Fix path to start-xvfb and set executable --- .scripts/start-xvfb.sh | 0 .travis.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 .scripts/start-xvfb.sh diff --git a/.scripts/start-xvfb.sh b/.scripts/start-xvfb.sh old mode 100644 new mode 100755 diff --git a/.travis.yml b/.travis.yml index df1fffb959..ff08398218 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ services: before_script: # Start X Virtual Frame Buffer for headless testing with real browsers - - ./scripts/start-xvfb.sh + - ./.scripts/start-xvfb.sh # Install node-gyp for running chimp - meteor npm install -g node-gyp node-pre-gyp - yarn run lint From 8e6c726022b57738c0cd96450e3876c9670110b0 Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Mon, 12 Jun 2017 21:57:47 -0300 Subject: [PATCH 15/84] Run correct script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1b50fc17fe..1e73910510 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "chimp-watch": "chimp --ddp=http://localhost:3000 --mocha --path=tests/end-to-end --watch", "chimp-test": "chimp tests/chimp-config.js", "start": "meteor", - "test": "node ./scripts/start.js" + "test": "node .scripts/run-chimp.js" }, "repository": { "type": "git", From 4a6d5f8e6cb8a07f39de99f29c598ddbab2c2063 Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Sun, 11 Jun 2017 14:43:58 -0300 Subject: [PATCH 16/84] Prevent travis to break when not running from apinf/platform --- .scripts/docker_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scripts/docker_build.sh b/.scripts/docker_build.sh index d9a6107e74..a476eacd58 100755 --- a/.scripts/docker_build.sh +++ b/.scripts/docker_build.sh @@ -7,7 +7,7 @@ set -ev -if [ "${TRAVIS_PULL_REQUEST}" = "false" ] +if [ "${TRAVIS_PULL_REQUEST}" = "false" -a "${TRAVIS_REPO_SLUG}" = "apinf/platform" ] then docker build -t apinf/platform:$DOCKER_TAG . docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" From 1f377f8813af38cad3ddfcf9d009dae27a1900d3 Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Tue, 13 Jun 2017 08:33:43 -0300 Subject: [PATCH 17/84] Fix waitForMessage - https://goo.gl/lAOTlt --- .scripts/run-chimp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scripts/run-chimp.js b/.scripts/run-chimp.js index 9d67b828b4..73ed4e6773 100644 --- a/.scripts/run-chimp.js +++ b/.scripts/run-chimp.js @@ -85,7 +85,7 @@ function startChimp() { } function chimpNoMirror() { - appOptions.waitForMessage = 'SERVER RUNNING'; + appOptions.waitForMessage = 'App running at:'; startApp(function() { startChimp(); }); From 6b10d54e803b93efe68c8dbbb1018342dbdcbf1d Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Fri, 16 Jun 2017 15:16:02 -0300 Subject: [PATCH 18/84] Downgrade node to 4 and install travis test packages --- .travis.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff08398218..1dfb498953 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,14 @@ sudo: false language: node_js node_js: - - "node" + - '4' addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 chrome: stable branches: @@ -13,27 +18,31 @@ branches: cache: yarn: true +services: + - docker + before_install: + # Install meteor locally on CI - if [ ! -e "$HOME/.meteor/meteor" ]; then curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh; fi + # Install Yarn - curl -o- -L https://yarnpkg.com/install.sh | bash - export PATH=$HOME/.yarn/bin:$PATH -services: - - docker - before_script: + - yarn run lint # Start X Virtual Frame Buffer for headless testing with real browsers - ./.scripts/start-xvfb.sh # Install node-gyp for running chimp - - meteor npm install -g node-gyp node-pre-gyp - - yarn run lint + - yarn install script: # Run meteor and chimp from node.js - travis_retry yarn test + # Build docker image - ./.scripts/docker_build.sh env: global: - DISPLAY=:99.0 - - TEST_MODE: "true" \ No newline at end of file + - TEST_MODE: "true" + - CXX=g++-4.8 \ No newline at end of file From d9b428454c79637e2836556e913017962d5f8247 Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Fri, 16 Jun 2017 16:29:00 -0300 Subject: [PATCH 19/84] Add some documentation --- .scripts/run-chimp.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.scripts/run-chimp.js b/.scripts/run-chimp.js index 73ed4e6773..d37fb6f5b1 100644 --- a/.scripts/run-chimp.js +++ b/.scripts/run-chimp.js @@ -21,6 +21,14 @@ var appOptions = { } }; +/** + * Start a process on the O.S. + * Optionally start a child process if options.waitForMessage + * is set to an output message emitted by this process. + * + * @param {command, options} opts + * @param {*} callback + */ function startProcess(opts, callback) { var proc = exec( opts.command, @@ -58,6 +66,11 @@ function startProcess(opts, callback) { processes.push(proc); } +/** + * Run meteor + * + * @param {*} callback + */ function startApp(callback) { startProcess({ name: 'Meteor App', @@ -70,6 +83,9 @@ function startApp(callback) { }, callback); } +/** + * Start chimp + */ function startChimp() { startProcess({ name: 'Chimp', @@ -84,6 +100,9 @@ function startChimp() { }); } +/** + * Run meteor and then chimp after application has started + */ function chimpNoMirror() { appOptions.waitForMessage = 'App running at:'; startApp(function() { From 2422f0f98e739d3db3569a3f90af3fd28d3690eb Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Fri, 16 Jun 2017 16:29:25 -0300 Subject: [PATCH 20/84] Run meteor in production mode --- .scripts/run-chimp.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.scripts/run-chimp.js b/.scripts/run-chimp.js index d37fb6f5b1..bc515ffff8 100644 --- a/.scripts/run-chimp.js +++ b/.scripts/run-chimp.js @@ -67,14 +67,15 @@ function startProcess(opts, callback) { } /** - * Run meteor + * Always run meteor in production-like environment + * since we do not want to reload changing files * * @param {*} callback */ function startApp(callback) { startProcess({ name: 'Meteor App', - command: 'meteor', + command: 'meteor --production', waitForMessage: appOptions.waitForMessage, options: { cwd: srcDir, From 5c360d8fb23e479911201c531e528963335f6fea Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Fri, 16 Jun 2017 16:29:52 -0300 Subject: [PATCH 21/84] Set travis to cache meteor properly --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1dfb498953..378389563c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,12 @@ branches: only: - develop +before_cache: + - rm -f $HOME/.meteor/log/*.log + cache: yarn: true + - $HOME/.meteor services: - docker @@ -45,4 +49,5 @@ env: global: - DISPLAY=:99.0 - TEST_MODE: "true" - - CXX=g++-4.8 \ No newline at end of file + - CXX=g++-4.8 + From 3638e8438a9777ed4f90c35e5a2beaec4f126d66 Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Sat, 17 Jun 2017 08:24:18 -0300 Subject: [PATCH 22/84] Cache yarn and meteor - Previous setup had syntax error --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 378389563c..ff7d36f218 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,9 @@ before_cache: - rm -f $HOME/.meteor/log/*.log cache: - yarn: true - - $HOME/.meteor + directories: + - $HOME/.meteor + - $HOME/.cache/yarn services: - docker @@ -49,5 +50,4 @@ env: global: - DISPLAY=:99.0 - TEST_MODE: "true" - - CXX=g++-4.8 - + - CXX=g++-4.8 \ No newline at end of file From 783b8e2cad15a0e5a751357f0e613b652cd75972 Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Tue, 20 Jun 2017 10:38:28 -0300 Subject: [PATCH 23/84] Don't run yarn install twice --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff7d36f218..a26b953b6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,9 +37,7 @@ before_script: - yarn run lint # Start X Virtual Frame Buffer for headless testing with real browsers - ./.scripts/start-xvfb.sh - # Install node-gyp for running chimp - - yarn install - + script: # Run meteor and chimp from node.js - travis_retry yarn test From b3e5fb624ac4e34cd25dd9652af5be275fa6eedd Mon Sep 17 00:00:00 2001 From: Mauricio Vieira Date: Tue, 20 Jun 2017 11:21:45 -0300 Subject: [PATCH 24/84] Add ids to setup modal buttons --- setup/client/modal.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/client/modal.html b/setup/client/modal.html index a5ae79087f..c92844e59a 100644 --- a/setup/client/modal.html +++ b/setup/client/modal.html @@ -21,10 +21,10 @@