From 87fdb684fc2bd31af44fc1bda59c4847a6b2a07b Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Wed, 1 Apr 2020 13:20:34 -0400 Subject: [PATCH] feat(services,redux): issues/240 add account opt-in services (#243) * build, rhsm API opt-in endpoint * redux, layout for actions, reducers, types * rhsmServices, jsdoc annotation updates * userServices, rhsm opt-in services, mock layouts for delete, put, get --- .env | 1 + .env.development | 1 + .../actions/__tests__/userActions.test.js | 48 +++- src/redux/actions/userActions.js | 56 +++- .../__snapshots__/userReducer.test.js.snap | 265 ++++++++++++++++++ .../reducers/__tests__/userReducer.test.js | 12 +- src/redux/reducers/userReducer.js | 11 +- .../__snapshots__/index.test.js.snap | 12 + src/redux/types/userTypes.js | 16 +- src/services/__tests__/userServices.test.js | 20 +- src/services/rhsmServices.js | 24 +- src/services/userServices.js | 210 +++++++++++++- .../__snapshots__/rhsmApiTypes.test.js.snap | 12 + src/types/rhsmApiTypes.js | 27 ++ 14 files changed, 684 insertions(+), 31 deletions(-) diff --git a/.env b/.env index dc0123d18..a8a92bd2e 100644 --- a/.env +++ b/.env @@ -27,3 +27,4 @@ REACT_APP_INCLUDE_CONTENT_BODY= { }); it('should handle all defined error types', () => { - const specificTypes = [types.USER_AUTH]; + const specificTypes = [types.USER_AUTH, types.DELETE_USER_OPTIN, types.GET_USER_OPTIN, types.UPDATE_USER_OPTIN]; specificTypes.forEach(value => { const dispatched = { @@ -55,7 +55,7 @@ describe('UserReducer', () => { }); it('should handle all defined pending types', () => { - const specificTypes = [types.USER_AUTH]; + const specificTypes = [types.USER_AUTH, types.DELETE_USER_OPTIN, types.GET_USER_OPTIN, types.UPDATE_USER_OPTIN]; specificTypes.forEach(value => { const dispatched = { @@ -71,7 +71,13 @@ describe('UserReducer', () => { }); it('should handle all defined fulfilled types', () => { - const specificTypes = [types.USER_AUTH, types.USER_LOCALE]; + const specificTypes = [ + types.USER_AUTH, + types.USER_LOCALE, + types.DELETE_USER_OPTIN, + types.GET_USER_OPTIN, + types.UPDATE_USER_OPTIN + ]; specificTypes.forEach(value => { const dispatched = { diff --git a/src/redux/reducers/userReducer.js b/src/redux/reducers/userReducer.js index 34ac664b8..10c722a84 100644 --- a/src/redux/reducers/userReducer.js +++ b/src/redux/reducers/userReducer.js @@ -9,6 +9,7 @@ import { reduxHelpers } from '../common/reduxHelpers'; * errorStatus: (string|number), error: boolean, locale: string}}} */ const initialState = { + optin: {}, session: { error: false, errorMessage: null, @@ -103,7 +104,15 @@ const userReducer = (state = initialState, action) => { return state; default: - return state; + return reduxHelpers.generatedPromiseActionReducer( + [ + { ref: 'optin', type: userTypes.DELETE_USER_OPTIN }, + { ref: 'optin', type: userTypes.GET_USER_OPTIN }, + { ref: 'optin', type: userTypes.UPDATE_USER_OPTIN } + ], + state, + action + ); } }; diff --git a/src/redux/types/__tests__/__snapshots__/index.test.js.snap b/src/redux/types/__tests__/__snapshots__/index.test.js.snap index 30e0eea16..29534f8a6 100644 --- a/src/redux/types/__tests__/__snapshots__/index.test.js.snap +++ b/src/redux/types/__tests__/__snapshots__/index.test.js.snap @@ -28,6 +28,9 @@ Object { "SET_GRAPH_SLA_RHSM": "SET_GRAPH_SLA_RHSM", }, "user": Object { + "DELETE_USER_OPTIN": "DELETE_USER_OPTIN", + "GET_USER_OPTIN": "GET_USER_OPTIN", + "UPDATE_USER_OPTIN": "UPDATE_USER_OPTIN", "USER_AUTH": "USER_AUTH", "USER_LOCALE": "USER_LOCALE", "USER_LOGOUT": "USER_LOGOUT", @@ -64,6 +67,9 @@ Object { "SET_GRAPH_SLA_RHSM": "SET_GRAPH_SLA_RHSM", }, "user": Object { + "DELETE_USER_OPTIN": "DELETE_USER_OPTIN", + "GET_USER_OPTIN": "GET_USER_OPTIN", + "UPDATE_USER_OPTIN": "UPDATE_USER_OPTIN", "USER_AUTH": "USER_AUTH", "USER_LOCALE": "USER_LOCALE", "USER_LOGOUT": "USER_LOGOUT", @@ -77,6 +83,9 @@ Object { "SET_GRAPH_SLA_RHSM": "SET_GRAPH_SLA_RHSM", }, "userTypes": Object { + "DELETE_USER_OPTIN": "DELETE_USER_OPTIN", + "GET_USER_OPTIN": "GET_USER_OPTIN", + "UPDATE_USER_OPTIN": "UPDATE_USER_OPTIN", "USER_AUTH": "USER_AUTH", "USER_LOCALE": "USER_LOCALE", "USER_LOGOUT": "USER_LOGOUT", @@ -107,6 +116,9 @@ Object { "SET_GRAPH_SLA_RHSM": "SET_GRAPH_SLA_RHSM", }, "user": Object { + "DELETE_USER_OPTIN": "DELETE_USER_OPTIN", + "GET_USER_OPTIN": "GET_USER_OPTIN", + "UPDATE_USER_OPTIN": "UPDATE_USER_OPTIN", "USER_AUTH": "USER_AUTH", "USER_LOCALE": "USER_LOCALE", "USER_LOGOUT": "USER_LOGOUT", diff --git a/src/redux/types/userTypes.js b/src/redux/types/userTypes.js index f671042bf..55d66e049 100644 --- a/src/redux/types/userTypes.js +++ b/src/redux/types/userTypes.js @@ -1,3 +1,6 @@ +const DELETE_USER_OPTIN = 'DELETE_USER_OPTIN'; +const GET_USER_OPTIN = 'GET_USER_OPTIN'; +const UPDATE_USER_OPTIN = 'UPDATE_USER_OPTIN'; const USER_AUTH = 'USER_AUTH'; const USER_LOCALE = 'USER_LOCALE'; const USER_LOGOUT = 'USER_LOGOUT'; @@ -7,6 +10,15 @@ const USER_LOGOUT = 'USER_LOGOUT'; * * @type {{USER_LOGOUT: string, USER_AUTH: string, USER_LOCALE: string}} */ -const userTypes = { USER_AUTH, USER_LOCALE, USER_LOGOUT }; +const userTypes = { DELETE_USER_OPTIN, GET_USER_OPTIN, UPDATE_USER_OPTIN, USER_AUTH, USER_LOCALE, USER_LOGOUT }; -export { userTypes as default, userTypes, USER_AUTH, USER_LOCALE, USER_LOGOUT }; +export { + userTypes as default, + userTypes, + DELETE_USER_OPTIN, + GET_USER_OPTIN, + UPDATE_USER_OPTIN, + USER_AUTH, + USER_LOCALE, + USER_LOGOUT +}; diff --git a/src/services/__tests__/userServices.test.js b/src/services/__tests__/userServices.test.js index bc3716279..e70300cfd 100644 --- a/src/services/__tests__/userServices.test.js +++ b/src/services/__tests__/userServices.test.js @@ -1,15 +1,33 @@ import Cookies from 'js-cookie'; +import moxios from 'moxios'; import userServices from '../userServices'; describe('UserServices', () => { + beforeEach(() => { + moxios.install(); + + moxios.stubRequest(/\/(opt-in).*?/, { + status: 200, + responseText: 'success', + timeout: 1 + }); + }); + + afterEach(() => { + moxios.uninstall(); + }); + it('should export a specific number of methods and classes', () => { - expect(Object.keys(userServices)).toHaveLength(3); + expect(Object.keys(userServices)).toHaveLength(6); }); it('should have specific methods', () => { expect(userServices.authorizeUser).toBeDefined(); expect(userServices.getLocale).toBeDefined(); expect(userServices.logoutUser).toBeDefined(); + expect(userServices.deleteAccountOptIn).toBeDefined(); + expect(userServices.getAccountOptIn).toBeDefined(); + expect(userServices.updateAccountOptIn).toBeDefined(); }); /** diff --git a/src/services/rhsmServices.js b/src/services/rhsmServices.js index 7c0becc40..136c2528d 100644 --- a/src/services/rhsmServices.js +++ b/src/services/rhsmServices.js @@ -18,8 +18,8 @@ import { serviceCall } from './config'; * "gitHash": "0000000000000000" * } * - * @apiError {String} detail - * @apiErrorExample {text} Error-Response: + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: * HTTP/1.1 500 Internal Server Error * { * "errors": [ @@ -394,7 +394,7 @@ const getApiVersion = () => * } * } * - * @apiError {String} detail + * @apiError {Array} errors * @apiErrorExample {json} Error-Response: * HTTP/1.1 400 Bad Request * { @@ -407,8 +407,8 @@ const getApiVersion = () => * } * ] * } - * @apiError {String} detail - * @apiErrorExample {text} Error-Response: + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: * HTTP/1.1 404 Internal Server Error * { * "errors": [ @@ -420,8 +420,8 @@ const getApiVersion = () => * } * ] * } - * @apiError {String} detail - * @apiErrorExample {text} Error-Response: + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: * HTTP/1.1 500 Internal Server Error * { * "errors": [ @@ -563,7 +563,7 @@ const getGraphReports = (id, params = {}) => * } * } * - * @apiError {String} detail + * @apiError {Array} errors * @apiErrorExample {json} Error-Response: * HTTP/1.1 400 Bad Request * { @@ -576,8 +576,8 @@ const getGraphReports = (id, params = {}) => * } * ] * } - * @apiError {String} detail - * @apiErrorExample {text} Error-Response: + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: * HTTP/1.1 404 Internal Server Error * { * "errors": [ @@ -589,8 +589,8 @@ const getGraphReports = (id, params = {}) => * } * ] * } - * @apiError {String} detail - * @apiErrorExample {text} Error-Response: + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: * HTTP/1.1 500 Internal Server Error * { * "errors": [ diff --git a/src/services/userServices.js b/src/services/userServices.js index 6b377943f..7ba31c6fc 100644 --- a/src/services/userServices.js +++ b/src/services/userServices.js @@ -2,6 +2,7 @@ import Cookies from 'js-cookie'; import LocaleCode from 'locale-code'; import _isPlainObject from 'lodash/isPlainObject'; import { getUser } from './platformServices'; +import { serviceCall } from './config'; /** * Apply an emulated API response to the platforms getUser method. @@ -67,6 +68,211 @@ const logoutUser = () => resolve({}); }); -const userServices = { authorizeUser, getLocale, logoutUser }; +/** + * @apiMock {DelayResponse} 2000 + * @api {delete} /api/rhsm-subscriptions/v1/opt-in + * @apiDescription Delete a RHSM account opt-in config + * + * Reference [RHSM API](https://github.com/RedHatInsights/rhsm-subscriptions/blob/master/api/rhsm-subscriptions-api-spec.yaml) + * + * @apiSuccessExample {text} Success-Response: + * HTTP/1.1 204 OK + * + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 400 Bad Request + * { + * "errors": [ + * { + * "status": "string", + * "code": "string", + * "title": "string", + * "detail": "string" + * } + * ] + * } + * + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 403 Forbidden + * { + * "errors": [ + * { + * "status": "string", + * "code": "string", + * "title": "string", + * "detail": "string" + * } + * ] + * } + * + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 404 Not Found + * { + * "errors": [ + * { + * "status": "string", + * "code": "string", + * "title": "string", + * "detail": "string" + * } + * ] + * } + * + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 500 Internal Server Error + * { + * "errors": [ + * { + * "status": "string", + * "code": "string", + * "title": "string", + * "detail": "string" + * } + * ] + * } + */ +/** + * Delete a RHSM account opt-in config. + * + * @returns {Promise<*>} + */ +const deleteAccountOptIn = () => + serviceCall({ + method: 'delete', + url: process.env.REACT_APP_SERVICES_RHSM_OPTIN + }); + +/** + * @api {get} /api/rhsm-subscriptions/v1/opt-in + * @apiDescription Get a RHSM account opt-in config + * + * Reference [RHSM API](https://github.com/RedHatInsights/rhsm-subscriptions/blob/master/api/rhsm-subscriptions-api-spec.yaml) + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * { + * "data": { + * "opt_in_complete": true, + * "account": { + * "account_numner": 12345, + * "tally_sync_enabled": true, + * "tally_reporting_enabled": true, + * "opt_in_type": "API", + * "created": "2017-08-04T17:32:05Z", + * "last_updated": "2017-08-04T17:32:05Z" + * }, + * "org": { + * "org_id": 1111, + * "conduit_sync_enabled": true, + * "opt_in_type": "API", + * "created": "2017-08-04T17:32:05Z", + * "last_updated": "2017-08-04T17:32:05Z" + * } + * }, + * "meta": { + * "account_number": 12345, + * "org_id": 1111 + * } + * } + * + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 500 Internal Server Error + * { + * "errors": [ + * { + * "status": "string", + * "code": "string", + * "title": "string", + * "detail": "string" + * } + * ] + * } + */ +/** + * Get a RHSM account opt-in config. + * + * @returns {Promise<*>} + */ +const getAccountOptIn = () => + serviceCall({ + url: process.env.REACT_APP_SERVICES_RHSM_OPTIN, + cancel: true + }); -export { userServices as default, userServices, authorizeUser, getLocale, logoutUser }; +/** + * @api {put} /api/rhsm-subscriptions/v1/opt-in + * @apiDescription Create/Update an account's opt-in configuration. Account and Org ID are defined by + * the identity header. If no parameters are specified, everything will be enabled. + * + * Reference [RHSM API](https://github.com/RedHatInsights/rhsm-subscriptions/blob/master/api/rhsm-subscriptions-api-spec.yaml) + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * { + * "data": { + * "opt_in_complete": true, + * "account": { + * "account_numner": 12345, + * "tally_sync_enabled": true, + * "tally_reporting_enabled": true, + * "opt_in_type": "API", + * "created": "2017-08-04T17:32:05Z", + * "last_updated": "2017-08-04T17:32:05Z" + * }, + * "org": { + * "org_id": 1111, + * "conduit_sync_enabled": true, + * "opt_in_type": "API", + * "created": "2017-08-04T17:32:05Z", + * "last_updated": "2017-08-04T17:32:05Z" + * } + * }, + * "meta": { + * "account_number": 12345, + * "org_id": 1111 + * } + * } + * + * @apiError {Array} errors + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 500 Internal Server Error + * { + * "errors": [ + * { + * "status": "string", + * "code": "string", + * "title": "string", + * "detail": "string" + * } + * ] + * } + */ +/** + * Update a RHSM account opt-in config. + * + * @param {object} params Query/search params + * @returns {Promise<*>} + */ +const updateAccountOptIn = (params = {}) => + serviceCall({ + method: 'put', + url: process.env.REACT_APP_SERVICES_RHSM_OPTIN, + params + }); + +const userServices = { authorizeUser, getLocale, logoutUser, deleteAccountOptIn, getAccountOptIn, updateAccountOptIn }; + +export { + userServices as default, + userServices, + authorizeUser, + getLocale, + logoutUser, + deleteAccountOptIn, + getAccountOptIn, + updateAccountOptIn +}; diff --git a/src/types/__tests__/__snapshots__/rhsmApiTypes.test.js.snap b/src/types/__tests__/__snapshots__/rhsmApiTypes.test.js.snap index 0b8f954e7..fab749fb8 100644 --- a/src/types/__tests__/__snapshots__/rhsmApiTypes.test.js.snap +++ b/src/types/__tests__/__snapshots__/rhsmApiTypes.test.js.snap @@ -24,6 +24,9 @@ Object { }, "RHSM_API_QUERY_LIMIT": "limit", "RHSM_API_QUERY_OFFSET": "offset", + "RHSM_API_QUERY_OPTIN_CONDUIT_SYNC": "enable_conduit_sync", + "RHSM_API_QUERY_OPTIN_TALLY_REPORT": "enable_tally_reporting", + "RHSM_API_QUERY_OPTIN_TALLY_SYNC": "enable_tally_sync", "RHSM_API_QUERY_SLA": "sla", "RHSM_API_QUERY_SLA_TYPES": Object { "NONE": "", @@ -91,6 +94,9 @@ Object { }, "RHSM_API_QUERY_LIMIT": "limit", "RHSM_API_QUERY_OFFSET": "offset", + "RHSM_API_QUERY_OPTIN_CONDUIT_SYNC": "enable_conduit_sync", + "RHSM_API_QUERY_OPTIN_TALLY_REPORT": "enable_tally_reporting", + "RHSM_API_QUERY_OPTIN_TALLY_SYNC": "enable_tally_sync", "RHSM_API_QUERY_SLA": "sla", "RHSM_API_QUERY_SLA_TYPES": Object { "NONE": "", @@ -159,6 +165,9 @@ Object { }, "RHSM_API_QUERY_LIMIT": "limit", "RHSM_API_QUERY_OFFSET": "offset", + "RHSM_API_QUERY_OPTIN_CONDUIT_SYNC": "enable_conduit_sync", + "RHSM_API_QUERY_OPTIN_TALLY_REPORT": "enable_tally_reporting", + "RHSM_API_QUERY_OPTIN_TALLY_SYNC": "enable_tally_sync", "RHSM_API_QUERY_SLA": "sla", "RHSM_API_QUERY_SLA_TYPES": Object { "NONE": "", @@ -231,6 +240,9 @@ Object { }, "RHSM_API_QUERY_LIMIT": "limit", "RHSM_API_QUERY_OFFSET": "offset", + "RHSM_API_QUERY_OPTIN_CONDUIT_SYNC": "enable_conduit_sync", + "RHSM_API_QUERY_OPTIN_TALLY_REPORT": "enable_tally_reporting", + "RHSM_API_QUERY_OPTIN_TALLY_SYNC": "enable_tally_sync", "RHSM_API_QUERY_SLA": "sla", "RHSM_API_QUERY_SLA_TYPES": Object { "NONE": "", diff --git a/src/types/rhsmApiTypes.js b/src/types/rhsmApiTypes.js index 826ad724b..f4a129a93 100644 --- a/src/types/rhsmApiTypes.js +++ b/src/types/rhsmApiTypes.js @@ -142,6 +142,27 @@ const RHSM_API_QUERY_LIMIT = 'limit'; */ const RHSM_API_QUERY_OFFSET = 'offset'; +/** + * RHSM API query/search parameter OPT-IN TALLY SYNC type. + * + * @type {string} + */ +const RHSM_API_QUERY_OPTIN_TALLY_SYNC = 'enable_tally_sync'; + +/** + * RHSM API query/search parameter OPT-IN TALLY REPORT type. + * + * @type {string} + */ +const RHSM_API_QUERY_OPTIN_TALLY_REPORT = 'enable_tally_reporting'; + +/** + * RHSM API query/search parameter OPTIN CONDUIT SYNC type. + * + * @type {string} + */ +const RHSM_API_QUERY_OPTIN_CONDUIT_SYNC = 'enable_conduit_sync'; + /** * RHSM API query/search parameter SLA type. * @@ -210,6 +231,9 @@ const rhsmApiTypes = { RHSM_API_QUERY_GRANULARITY_TYPES, RHSM_API_QUERY_LIMIT, RHSM_API_QUERY_OFFSET, + RHSM_API_QUERY_OPTIN_TALLY_SYNC, + RHSM_API_QUERY_OPTIN_TALLY_REPORT, + RHSM_API_QUERY_OPTIN_CONDUIT_SYNC, RHSM_API_QUERY_SLA, RHSM_API_QUERY_SLA_TYPES, RHSM_API_QUERY_START_DATE, @@ -232,6 +256,9 @@ export { RHSM_API_QUERY_GRANULARITY_TYPES, RHSM_API_QUERY_LIMIT, RHSM_API_QUERY_OFFSET, + RHSM_API_QUERY_OPTIN_TALLY_SYNC, + RHSM_API_QUERY_OPTIN_TALLY_REPORT, + RHSM_API_QUERY_OPTIN_CONDUIT_SYNC, RHSM_API_QUERY_SLA, RHSM_API_QUERY_SLA_TYPES, RHSM_API_QUERY_START_DATE,