From 533632f4e57d39a9ae17ab798573516416f1fa3a Mon Sep 17 00:00:00 2001
From: Pham Hai Duong
Date: Thu, 7 May 2020 22:51:32 +0700
Subject: [PATCH 1/7] feat: integrate api for billing bar chart (#1168)
Changes
- Integrate api for billing bar chart
- Write test
- Refactor some places
---
.../src/actions/__tests__/developer.ts | 23 +++
packages/marketplace/src/actions/developer.ts | 13 +-
.../pages/__tests__/developer-home.tsx | 16 +++
.../service-chart/__mocks__/billing.ts | 83 +++++++++++
.../__snapshots__/service-chart.test.tsx.snap | 133 ++++++++++-------
.../__tests__/service-chart.test.tsx | 134 +++++++++++++++++-
.../billing/service-chart/service-chart.tsx | 122 ++++++++++++----
.../detailed/detailed-tab.tsx | 4 +-
.../marketplace/src/constants/action-types.ts | 13 ++
packages/marketplace/src/constants/routes.ts | 1 +
packages/marketplace/src/core/store.ts | 97 +++++++------
.../src/reducers/__tests__/developer.ts | 39 +++++
.../marketplace/src/reducers/developer.ts | 69 ++++++++-
.../src/sagas/__stubs__/billing.ts | 83 +++++++++++
.../src/sagas/__stubs__/developer-identity.ts | 33 +++++
.../src/sagas/__stubs__/developer.ts | 6 +
.../src/sagas/__tests__/developer.ts | 111 +++++++++++++--
packages/marketplace/src/sagas/api.ts | 43 ++++++
packages/marketplace/src/sagas/developer.ts | 74 ++++++++--
.../src/selector/__tests__/developer.ts | 127 ++++++++++++++++-
.../marketplace/src/selector/developer.ts | 26 +++-
packages/marketplace/src/selector/index.ts | 1 +
.../src/utils/__tests__/route-dispatcher.ts | 15 +-
.../marketplace/src/utils/route-dispatcher.ts | 6 +-
24 files changed, 1104 insertions(+), 168 deletions(-)
create mode 100644 packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__mocks__/billing.ts
create mode 100644 packages/marketplace/src/sagas/__stubs__/billing.ts
create mode 100644 packages/marketplace/src/sagas/__stubs__/developer-identity.ts
create mode 100644 packages/marketplace/src/selector/index.ts
diff --git a/packages/marketplace/src/actions/__tests__/developer.ts b/packages/marketplace/src/actions/__tests__/developer.ts
index e3ae0d1806..3d79ce0c6c 100644
--- a/packages/marketplace/src/actions/__tests__/developer.ts
+++ b/packages/marketplace/src/actions/__tests__/developer.ts
@@ -5,11 +5,15 @@ import {
developerClearData,
developerCreate,
developerSetFormState,
+ fetchBilling,
+ fetchBillingSuccess,
+ fetchBillingFailure,
} from '../developer'
import ActionTypes from '../../constants/action-types'
import { appsDataStub } from '../../sagas/__stubs__/apps'
import { CreateDeveloperModel } from '@reapit/foundations-ts-definitions'
import { appPermissionStub } from '@/sagas/__stubs__/app-permission'
+import { billing } from '@/sagas/__stubs__/billing'
describe('developer actions', () => {
it('should create a developerLoading action', () => {
@@ -50,4 +54,23 @@ describe('developer actions', () => {
expect(developerSetFormState.type).toEqual(ActionTypes.DEVELOPER_SET_FORM_STATE)
expect(developerSetFormState('DONE').data).toEqual('DONE')
})
+
+ it('should create a fetchBilling action', () => {
+ expect(fetchBilling.type).toEqual(ActionTypes.DEVELOPER_FETCH_BILLING)
+ expect(fetchBilling({ dateFrom: '2020-01', dateTo: '2020-05', applicationId: ['123'] }).data).toEqual({
+ dateFrom: '2020-01',
+ dateTo: '2020-05',
+ applicationId: ['123'],
+ })
+ })
+
+ it('should create a fetchBillingFailure action', () => {
+ expect(fetchBillingSuccess.type).toEqual(ActionTypes.DEVELOPER_FETCH_BILLING_SUCCESS)
+ expect(fetchBillingSuccess(billing).data).toEqual(billing)
+ })
+
+ it('should create a fetchBillingFailure action', () => {
+ expect(fetchBillingFailure.type).toEqual(ActionTypes.DEVELOPER_FETCH_BILLING_FAILURE)
+ expect(fetchBillingFailure('error').data).toEqual('error')
+ })
})
diff --git a/packages/marketplace/src/actions/developer.ts b/packages/marketplace/src/actions/developer.ts
index 29457a1c4c..91f2870f5b 100644
--- a/packages/marketplace/src/actions/developer.ts
+++ b/packages/marketplace/src/actions/developer.ts
@@ -1,8 +1,10 @@
+// TODO: WILL MOVE ALL DEVELOPER ACTIONS TO HERE
import { actionCreator } from '../utils/actions'
import ActionTypes from '../constants/action-types'
-import { DeveloperItem, DeveloperRequestParams } from '../reducers/developer'
-import { CreateDeveloperModel } from '@reapit/foundations-ts-definitions'
-import { FormState } from '../types/core'
+import { DeveloperItem, DeveloperRequestParams, Billing } from '../reducers/developer'
+import { CreateDeveloperModel, DeveloperModel } from '@reapit/foundations-ts-definitions'
+import { FormState } from '@/types/core'
+import { FetchBillingParams } from '@/sagas/api'
export const developerRequestData = actionCreator(ActionTypes.DEVELOPER_REQUEST_DATA)
export const developerRequestDataFailure = actionCreator(ActionTypes.DEVELOPER_REQUEST_DATA_FAILURE)
@@ -11,3 +13,8 @@ export const developerReceiveData = actionCreator(Act
export const developerClearData = actionCreator(ActionTypes.DEVELOPER_CLEAR_DATA)
export const developerCreate = actionCreator(ActionTypes.DEVELOPER_CREATE)
export const developerSetFormState = actionCreator(ActionTypes.DEVELOPER_SET_FORM_STATE)
+export const fetchMyIdentity = actionCreator(ActionTypes.DEVELOPER_FETCH_MY_IDENTITY)
+export const setMyIdentity = actionCreator(ActionTypes.DEVELOPER_SET_MY_IDENTITY)
+export const fetchBilling = actionCreator(ActionTypes.DEVELOPER_FETCH_BILLING)
+export const fetchBillingSuccess = actionCreator(ActionTypes.DEVELOPER_FETCH_BILLING_SUCCESS)
+export const fetchBillingFailure = actionCreator(ActionTypes.DEVELOPER_FETCH_BILLING_FAILURE)
diff --git a/packages/marketplace/src/components/pages/__tests__/developer-home.tsx b/packages/marketplace/src/components/pages/__tests__/developer-home.tsx
index f5818c6bf6..6ea9d7a8d1 100644
--- a/packages/marketplace/src/components/pages/__tests__/developer-home.tsx
+++ b/packages/marketplace/src/components/pages/__tests__/developer-home.tsx
@@ -18,6 +18,14 @@ describe('DeveloperHome', () => {
it('should match a snapshot', () => {
const mockProps: DeveloperProps = {
developerState: {
+ myIdentity: {},
+ error: null,
+ billing: {
+ from: '',
+ to: '',
+ requestsByPeriod: [],
+ },
+ isServiceChartLoading: false,
loading: false,
isVisible: false,
developerData: {
@@ -52,6 +60,14 @@ describe('DeveloperHome', () => {
it('should match a snapshot', () => {
const mockProps: DeveloperProps = {
developerState: {
+ isServiceChartLoading: false,
+ myIdentity: {},
+ error: null,
+ billing: {
+ from: '',
+ to: '',
+ requestsByPeriod: [],
+ },
loading: false,
isVisible: true,
developerData: {
diff --git a/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__mocks__/billing.ts b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__mocks__/billing.ts
new file mode 100644
index 0000000000..e319cefd39
--- /dev/null
+++ b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__mocks__/billing.ts
@@ -0,0 +1,83 @@
+export const billing = {
+ from: '2019-11',
+ to: '2020-05',
+ requestsByPeriod: [
+ {
+ period: '2019-11',
+ periodStart: '2019-11-01',
+ periodEnd: '2019-11-30',
+ periodName: 'November 2019',
+ requestCount: 0,
+ endpointCount: 0,
+ netAmount: 0.0,
+ grossAmount: 0.0,
+ vatAmount: 0.0,
+ },
+ {
+ period: '2019-12',
+ periodStart: '2019-12-01',
+ periodEnd: '2019-12-31',
+ periodName: 'December 2019',
+ requestCount: 0,
+ endpointCount: 0,
+ netAmount: 0.0,
+ grossAmount: 0.0,
+ vatAmount: 0.0,
+ },
+ {
+ period: '2020-01',
+ periodStart: '2020-01-01',
+ periodEnd: '2020-01-31',
+ periodName: 'January 2020',
+ requestCount: 0,
+ endpointCount: 0,
+ netAmount: 0.0,
+ grossAmount: 0.0,
+ vatAmount: 0.0,
+ },
+ {
+ period: '2020-02',
+ periodStart: '2020-02-01',
+ periodEnd: '2020-02-29',
+ periodName: 'February 2020',
+ requestCount: 0,
+ endpointCount: 0,
+ netAmount: 0.0,
+ grossAmount: 0.0,
+ vatAmount: 0.0,
+ },
+ {
+ period: '2020-03',
+ periodStart: '2020-03-01',
+ periodEnd: '2020-03-31',
+ periodName: 'March 2020',
+ requestCount: 0,
+ endpointCount: 0,
+ netAmount: 0.0,
+ grossAmount: 0.0,
+ vatAmount: 0.0,
+ },
+ {
+ period: '2020-04',
+ periodStart: '2020-04-01',
+ periodEnd: '2020-04-30',
+ periodName: 'April 2020',
+ requestCount: 614,
+ endpointCount: 6,
+ netAmount: 8.1,
+ grossAmount: 6.75,
+ vatAmount: 1.35,
+ },
+ {
+ period: '2020-05',
+ periodStart: '2020-05-01',
+ periodEnd: '2020-05-31',
+ periodName: 'May 2020',
+ requestCount: 0,
+ endpointCount: 0,
+ netAmount: 0.0,
+ grossAmount: 0.0,
+ vatAmount: 0.0,
+ },
+ ],
+}
diff --git a/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/__snapshots__/service-chart.test.tsx.snap b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/__snapshots__/service-chart.test.tsx.snap
index 1e537c93bd..320f18a50c 100644
--- a/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/__snapshots__/service-chart.test.tsx.snap
+++ b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/__snapshots__/service-chart.test.tsx.snap
@@ -1,56 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`ServiceChart should match a snapshot 1`] = `
-
-
-
- Services
-
-
+
-
-
+ }
+ width={50}
+ />
+
+`;
+
+exports[`ServiceChart handleUseEffect should render Loader 1`] = `
+
+
+
+`;
+
+exports[`ServiceChart should match a snapshot 1`] = `
+
+
+
`;
diff --git a/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/service-chart.test.tsx b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/service-chart.test.tsx
index 97e369e03a..f20af62e17 100644
--- a/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/service-chart.test.tsx
+++ b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/__tests__/service-chart.test.tsx
@@ -1,10 +1,140 @@
import * as React from 'react'
+import * as ReactRedux from 'react-redux'
+import { ReduxState } from '@/types/core'
import { shallow } from 'enzyme'
+import configureStore from 'redux-mock-store'
+import {
+ ServiceChart,
+ handleUseEffect,
+ HandleUseEffectParams,
+ renderChart,
+ mapServiceChartDataSet,
+} from '../service-chart'
+import { developerState } from '@/sagas/__stubs__/developer'
+import { fetchBilling } from '@/actions/developer'
+import { ChartData } from 'react-chartjs-2'
+import { billing } from '../__mocks__/billing'
-import { ServiceChart } from '../service-chart'
+const mockState = {
+ developer: developerState,
+} as ReduxState
describe('ServiceChart', () => {
+ let store
+ let spyDispatch
+ beforeEach(() => {
+ /* mocking store */
+ const mockStore = configureStore()
+ store = mockStore(mockState)
+ /* mocking useDispatch on our mock store */
+ spyDispatch = jest.spyOn(ReactRedux, 'useDispatch').mockImplementation(() => store.dispatch)
+ })
it('should match a snapshot', () => {
- expect(shallow()).toMatchSnapshot()
+ expect(
+ shallow(
+
+
+ ,
+ ),
+ ).toMatchSnapshot()
+ })
+
+ describe('handleFetchAppUsageStatsDataUseCallback', () => {
+ it('should call billing API correctly', () => {
+ const mockParams = {
+ myAppIds: ['123', '456'],
+ dateFrom: '2020-01',
+ dateTo: '2020-05',
+ dispatch: spyDispatch,
+ } as HandleUseEffectParams
+ const fn = handleUseEffect(mockParams)
+ fn()
+ expect(spyDispatch).toBeCalledWith(
+ fetchBilling({ applicationId: mockParams.myAppIds, dateFrom: mockParams.dateFrom, dateTo: mockParams.dateTo }),
+ )
+ })
+
+ it('should not call fetch billing data', () => {
+ const mockParams = {
+ myAppIds: [],
+ dateFrom: '2020-01',
+ dateTo: '2020-05',
+ dispatch: spyDispatch,
+ } as HandleUseEffectParams
+ const fn = handleUseEffect(mockParams)
+ fn()
+ expect(spyDispatch).not.toBeCalledWith(
+ fetchBilling({ applicationId: mockParams.myAppIds, dateFrom: mockParams.dateFrom, dateTo: mockParams.dateTo }),
+ )
+ })
+ })
+
+ describe('handleUseEffect', () => {
+ it('should render Loader', () => {
+ const datasets = {
+ label: ['A', 'B', 'C'],
+ datasets: [
+ {
+ label: 'Resource',
+ backgroundColor: 'rgba(255,99,132,0.2)',
+ borderColor: 'rgba(255,99,132,1)',
+ borderWidth: 1,
+ hoverBackgroundColor: 'rgba(255,99,132,0.4)',
+ hoverBorderColor: 'rgba(255,99,132,1)',
+ data: [1, 2, 3] as number[],
+ },
+ ],
+ } as ChartData
+ const wrapper = shallow({renderChart(true, datasets)}
)
+ expect(wrapper).toMatchSnapshot()
+ })
+
+ it('should render Chart', () => {
+ const datasets = {
+ label: ['A', 'B', 'C'],
+ datasets: [
+ {
+ label: 'Resource',
+ backgroundColor: 'rgba(255,99,132,0.2)',
+ borderColor: 'rgba(255,99,132,1)',
+ borderWidth: 1,
+ hoverBackgroundColor: 'rgba(255,99,132,0.4)',
+ hoverBorderColor: 'rgba(255,99,132,1)',
+ data: [1, 2, 3] as number[],
+ },
+ ],
+ } as ChartData
+ const wrapper = shallow({renderChart(false, datasets)}
)
+ expect(wrapper).toMatchSnapshot()
+ })
+ })
+
+ describe('mapServiceChartDataSet', () => {
+ it('should return correctly', () => {
+ const result = mapServiceChartDataSet(billing)
+ const expected = {
+ labels: [
+ 'November 2019',
+ 'December 2019',
+ 'January 2020',
+ 'February 2020',
+ 'March 2020',
+ 'April 2020',
+ 'May 2020',
+ ],
+ datasets: [
+ {
+ label: 'Resource',
+ backgroundColor: 'rgba(255,99,132,0.2)',
+ borderColor: 'rgba(255,99,132,1)',
+ borderWidth: 1,
+ hoverBackgroundColor: 'rgba(255,99,132,0.4)',
+ hoverBorderColor: 'rgba(255,99,132,1)',
+ data: [0, 0, 0, 0, 0, 6.75, 0] as number[],
+ },
+ ],
+ }
+ expect(result).toEqual(expected)
+ })
})
})
diff --git a/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/service-chart.tsx b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/service-chart.tsx
index d607af3ec0..fb0e461988 100644
--- a/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/service-chart.tsx
+++ b/packages/marketplace/src/components/ui/developer-analytics/billing/service-chart/service-chart.tsx
@@ -1,12 +1,23 @@
import React from 'react'
-import { Bar } from 'react-chartjs-2'
-import { H4, FlexContainerResponsive } from '@reapit/elements'
+import { Bar, ChartData } from 'react-chartjs-2'
+import { H4, FlexContainerResponsive, DATE_TIME_FORMAT, Loader } from '@reapit/elements'
+import { AppSummaryModel, DeveloperModel } from '@reapit/foundations-ts-definitions'
import styles from '@/styles/pages/developer-analytics.scss?mod'
+import { useDispatch, useSelector } from 'react-redux'
+import {
+ selectDeveloperApps,
+ selectMyIdentity,
+ selectBilling,
+ selectDeveloperLoading,
+ selectIsServiceChartLoading,
+} from '@/selector'
+import { fetchBilling } from '@/actions/developer'
+import dayjs from 'dayjs'
+import { Billing, RequestByPeriod } from '@/reducers/developer'
+import { Dispatch } from 'redux'
-// TODO will replace mock data when have API
-const data = {
- labels: ['January', 'February', 'March', 'April'],
- datasets: [
+export const mapServiceChartDataSet = (billing: Billing | null) => {
+ let datasets = [
{
label: 'Resource',
backgroundColor: 'rgba(255,99,132,0.2)',
@@ -14,34 +25,93 @@ const data = {
borderWidth: 1,
hoverBackgroundColor: 'rgba(255,99,132,0.4)',
hoverBorderColor: 'rgba(255,99,132,1)',
- data: [10, 5, 10, 20],
+ data: [] as number[],
},
- ],
+ ]
+ let labels: string[] = []
+ if (!billing) {
+ return {
+ labels,
+ datasets,
+ }
+ }
+ billing.requestsByPeriod.forEach((item: RequestByPeriod) => {
+ labels.push(item.periodName)
+ datasets[0].data.push(item.grossAmount)
+ })
+ return {
+ labels,
+ datasets,
+ }
+}
+
+export type StateProps = {
+ myApps: AppSummaryModel[]
+ myIdentity: DeveloperModel
+ billing: Billing | null
+ loading: boolean
+ isServiceChartLoading: boolean
+}
+
+export type HandleUseEffectParams = {
+ myAppIds: string[]
+ dateFrom: string
+ dateTo: string
+ dispatch: Dispatch
+}
+
+export const handleUseEffect = ({ myAppIds, dateFrom, dateTo, dispatch }: HandleUseEffectParams) => () => {
+ const isHaveDeveloperApps = myAppIds.length > 0
+ if (isHaveDeveloperApps) {
+ dispatch(fetchBilling({ applicationId: myAppIds, dateFrom: dateFrom, dateTo: dateTo }))
+ }
+}
+
+export const renderChart = (isLoading: boolean, datasets: ChartData) => {
+ if (isLoading) {
+ return
+ }
+ return (
+
+ )
}
export const ServiceChart: React.FC = () => {
+ const dispatch = useDispatch()
+ const myApps = useSelector(selectDeveloperApps)
+ const myIdentity = useSelector(selectMyIdentity)
+ const billing = useSelector(selectBilling)
+ const loading = useSelector(selectDeveloperLoading)
+ const isServiceChartLoading = useSelector(selectIsServiceChartLoading)
+
+ const myAppIds = myApps.map((item: AppSummaryModel) => item.id) as string[]
+ const dateFrom = dayjs(myIdentity.created).format(DATE_TIME_FORMAT.YYYY_MM) as string
+ const dateTo = dayjs().format(DATE_TIME_FORMAT.YYYY_MM)
+ React.useEffect(handleUseEffect({ myAppIds, dateFrom, dateTo, dispatch }), [myIdentity.id, myApps.length])
+ const datasets = mapServiceChartDataSet(billing)
+ const isLoading = loading || isServiceChartLoading
return (
Services
-
+ {renderChart(isLoading, datasets)}
)
diff --git a/packages/marketplace/src/components/ui/developer-analytics/detailed/detailed-tab.tsx b/packages/marketplace/src/components/ui/developer-analytics/detailed/detailed-tab.tsx
index 76b47fe95b..2c7d362e88 100644
--- a/packages/marketplace/src/components/ui/developer-analytics/detailed/detailed-tab.tsx
+++ b/packages/marketplace/src/components/ui/developer-analytics/detailed/detailed-tab.tsx
@@ -9,7 +9,7 @@ import { httpTrafficPerDayRequestData } from '@/actions/app-http-traffic-event'
import { appInstallationsRequestData, appInstallationsFilterRequestData } from '@/actions/app-installations'
import { InstallationModel, AppSummaryModel } from '@reapit/foundations-ts-definitions'
import { getAppUsageStats, getAppHttpTraffic } from '@/selector/analytics'
-import { getDevelopers } from '@/selector/developer'
+import { selectDeveloper } from '@/selector/developer'
import { getInstallations } from '@/selector/installations'
import { FlexContainerBasic, Grid, GridItem, FlexContainerResponsive, DATE_TIME_FORMAT } from '@reapit/elements'
@@ -120,7 +120,7 @@ export type MapState = {
export const mapState = (state: ReduxState): MapState => {
return {
installations: getInstallations(state),
- developer: getDevelopers(state),
+ developer: selectDeveloper(state),
appUsageStats: getAppUsageStats(state),
appHttpTraffic: getAppHttpTraffic(state),
}
diff --git a/packages/marketplace/src/constants/action-types.ts b/packages/marketplace/src/constants/action-types.ts
index 80d844dcde..f7e2b854e4 100644
--- a/packages/marketplace/src/constants/action-types.ts
+++ b/packages/marketplace/src/constants/action-types.ts
@@ -1,7 +1,20 @@
/**
+ * TODO: will refactor this to separate DEVELOPER PORTAL ACTIONS, CLIENT AND ADMIN
+ * by <>_<>
* Please follow the <>_<> pattern and group actions by STATE
*/
const ActionTypes = {
+ // DEVELOPER PORTAL
+ DEVELOPER_FETCH_MY_IDENTITY: 'DEVELOPER_FETCH_MY_IDENTITY',
+ DEVELOPER_SET_MY_IDENTITY: 'DEVELOPER_SET_MY_IDENTITY',
+ DEVELOPER_FETCH_BILLING: 'DEVELOPER_FETCH_BILLING',
+ DEVELOPER_FETCH_BILLING_SUCCESS: 'DEVELOPER_FETCH_BILLING_SUCCESS',
+ DEVELOPER_FETCH_BILLING_FAILURE: 'DEVELOPER_FETCH_BILLING_FAILURE',
+ // CLIENT PORTAL
+
+ // ADMIN PORTAL
+
+ // MISC
// Developer App Modal
SET_DEVELOPER_APP_MODAL_STATE_EDIT_DETAIL: 'SET_DEVELOPER_APP_MODAL_STATE_EDIT_DETAIL',
SET_DEVELOPER_APP_MODAL_STATE_VIEW_DETAIL: 'SET_DEVELOPER_APP_MODAL_STATE_VIEW_DETAIL',
diff --git a/packages/marketplace/src/constants/routes.ts b/packages/marketplace/src/constants/routes.ts
index 706188fcec..4e3a26d282 100644
--- a/packages/marketplace/src/constants/routes.ts
+++ b/packages/marketplace/src/constants/routes.ts
@@ -15,6 +15,7 @@ const Routes = {
DEVELOPER_MY_APPS_EDIT: '/developer/apps/:appid/edit',
DEVELOPER_API_DOCS: '/developer/api-docs',
DEVELOPER_ANALYTICS: '/developer/analytics',
+ DEVELOPER_ANALYTICS_BILLING: '/developer/analytics/billing',
DEVELOPER_ANALYTICS_TAB: '/developer/analytics/:activeTab?',
DEVELOPER_RESET_PASSWORD: '/developer/reset-password',
DEVELOPER_WEBHOOKS: '/developer/webhooks',
diff --git a/packages/marketplace/src/core/store.ts b/packages/marketplace/src/core/store.ts
index 98865c6192..82fe54f174 100644
--- a/packages/marketplace/src/core/store.ts
+++ b/packages/marketplace/src/core/store.ts
@@ -2,56 +2,55 @@ import { createStore, applyMiddleware, compose, combineReducers, Store as ReduxS
import createSagaMiddleware from 'redux-saga'
import { fork, all } from '@redux-saga/core/effects'
import { ReduxState } from '../types/core'
-
-import auth from '../reducers/auth'
-import client from '../reducers/client'
-import installedApps from '../reducers/installed-apps'
-import myApps from '../reducers/my-apps'
-import developer from '../reducers/developer'
-import appDetail from '../reducers/app-detail'
-import error from '../reducers/error'
-import submitApp from '../reducers/submit-app'
-import submitRevision from '../reducers/submit-revision'
-import adminApprovals from '../reducers/admin-approvals'
-import adminDevManagement from '../reducers/admin-dev-management'
-import developerSetStatus from '../reducers/developer-set-status'
-import revisionDetail from '../reducers/revision-detail'
-import appDetailModal from '../reducers/app-detail-modal'
-import appDeleteReducer from '../reducers/app-delete'
-import appCategories from '../reducers/app-categories'
-import settingsReducer from '../reducers/settings'
-import adminApps from '../reducers/admin-apps'
-import appInstallationsReducer from '../reducers/app-installations'
-import appUsageStatsReducer from '../reducers/app-usage-stats'
-import adminStatsReducer from '../reducers/admin-stats'
-import revisionsReducer from '../reducers/revisions'
-import appHttpTrafficEventReducer from '../reducers/app-http-traffic-event'
-import integrationTypes from '../reducers/app-integration-types'
+import auth from '@/reducers/auth'
+import client from '@/reducers/client'
+import installedApps from '@/reducers/installed-apps'
+import myApps from '@/reducers/my-apps'
+import developer from '@/reducers/developer'
+import appDetail from '@/reducers/app-detail'
+import error from '@/reducers/error'
+import submitApp from '@/reducers/submit-app'
+import submitRevision from '@/reducers/submit-revision'
+import adminApprovals from '@/reducers/admin-approvals'
+import adminDevManagement from '@/reducers/admin-dev-management'
+import developerSetStatus from '@/reducers/developer-set-status'
+import revisionDetail from '@/reducers/revision-detail'
+import appDetailModal from '@/reducers/app-detail-modal'
+import appDeleteReducer from '@/reducers/app-delete'
+import appCategories from '@/reducers/app-categories'
+import settingsReducer from '@/reducers/settings'
+import adminApps from '@/reducers/admin-apps'
+import appInstallationsReducer from '@/reducers/app-installations'
+import appUsageStatsReducer from '@/reducers/app-usage-stats'
+import adminStatsReducer from '@/reducers/admin-stats'
+import revisionsReducer from '@/reducers/revisions'
+import appHttpTrafficEventReducer from '@/reducers/app-http-traffic-event'
+import integrationTypes from '@/reducers/app-integration-types'
import webhookEditReducer from '../reducers/webhook-edit-modal'
-import webhookSubscriptions from '../reducers/webhook-subscriptions'
-
-import authSagas from '../sagas/auth'
-import clientSagas from '../sagas/client'
-import appDetailSagas from '../sagas/app-detail'
-import installedAppsSagas from '../sagas/installed-apps'
-import appUsageStatsSagas from '../sagas/app-usage-stats'
-import appHttpTrafficEventSagas from '../sagas/app-http-trafic-event'
-import myAppsSagas from '../sagas/my-apps'
-import developerSagas from '../sagas/developer'
-import submitAppSagas from '../sagas/submit-app'
-import submitRevisionSagas from '../sagas/submit-revision'
-import adminApprovalSagas from '../sagas/admin-approvals'
-import adminDevManagementSagas from '../sagas/admin-dev-management'
-import developerSetStatusSagas from '../sagas/developer-set-status'
-import revisionDetailSagas from '../sagas/revision-detail'
-import revisionsSagas from '../sagas/revisions'
-import appDeleteSagas from '../sagas/app-delete'
-import settingSagas from '../sagas/settings'
-import adminAppsSagas from '../sagas/admin-apps'
-import appInstallationsSagas from '../sagas/app-installations'
-import noticationMessage from '../reducers/notification-message'
-import adminStatsSaga from '../sagas/admin-stats'
-import webhookSubscriptionsSagas from '../sagas/webhook-subscriptions'
+import webhookSubscriptions from '@/reducers/webhook-subscriptions'
+
+import authSagas from '@/sagas/auth'
+import clientSagas from '@/sagas/client'
+import appDetailSagas from '@/sagas/app-detail'
+import installedAppsSagas from '@/sagas/installed-apps'
+import appUsageStatsSagas from '@/sagas/app-usage-stats'
+import appHttpTrafficEventSagas from '@/sagas/app-http-trafic-event'
+import myAppsSagas from '@/sagas/my-apps'
+import developerSagas from '@/sagas/developer'
+import submitAppSagas from '@/sagas/submit-app'
+import submitRevisionSagas from '@/sagas/submit-revision'
+import adminApprovalSagas from '@/sagas/admin-approvals'
+import adminDevManagementSagas from '@/sagas/admin-dev-management'
+import developerSetStatusSagas from '@/sagas/developer-set-status'
+import revisionDetailSagas from '@/sagas/revision-detail'
+import revisionsSagas from '@/sagas/revisions'
+import appDeleteSagas from '@/sagas/app-delete'
+import settingSagas from '@/sagas/settings'
+import adminAppsSagas from '@/sagas/admin-apps'
+import appInstallationsSagas from '@/sagas/app-installations'
+import noticationMessage from '@/reducers/notification-message'
+import adminStatsSaga from '@/sagas/admin-stats'
+import webhookSubscriptionsSagas from '@/sagas/webhook-subscriptions'
import { injectSwitchModeToWindow } from '@reapit/elements'
import webhookEditSagas from '../sagas/webhook-edit-modal'
diff --git a/packages/marketplace/src/reducers/__tests__/developer.ts b/packages/marketplace/src/reducers/__tests__/developer.ts
index d145a733e2..048be6b4cc 100644
--- a/packages/marketplace/src/reducers/__tests__/developer.ts
+++ b/packages/marketplace/src/reducers/__tests__/developer.ts
@@ -2,6 +2,7 @@ import developerReducer, { defaultState } from '../developer'
import { ActionType } from '../../types/core'
import ActionTypes from '../../constants/action-types'
import { appsDataStub } from '../../sagas/__stubs__/apps'
+import { billing } from '@/sagas/__stubs__/billing'
describe('developer reducer', () => {
it('should return default state if action not matched', () => {
@@ -62,4 +63,42 @@ describe('developer reducer', () => {
}
expect(newState).toEqual(expected)
})
+
+ it('should set isServiceChartLoading when call DEVELOPER_FETCH_BILLING is called', () => {
+ const newState = developerReducer(undefined, {
+ type: ActionTypes.DEVELOPER_FETCH_BILLING as ActionType,
+ data: null,
+ })
+ const expected = {
+ ...defaultState,
+ isServiceChartLoading: true,
+ }
+ expect(newState).toEqual(expected)
+ })
+
+ it('should set billing and isServiceChartLoading when call DEVELOPER_FETCH_BILLING_SUCCESS is called', () => {
+ const newState = developerReducer(undefined, {
+ type: ActionTypes.DEVELOPER_FETCH_BILLING_SUCCESS as ActionType,
+ data: billing,
+ })
+ const expected = {
+ ...defaultState,
+ isServiceChartLoading: false,
+ billing: billing,
+ }
+ expect(newState).toEqual(expected)
+ })
+
+ it('should set fetchBillingFailure when call DEVELOPER_FETCH_BILLING_FAILURE is called', () => {
+ const newState = developerReducer(undefined, {
+ type: ActionTypes.DEVELOPER_FETCH_BILLING_FAILURE as ActionType,
+ data: 'error',
+ })
+ const expected = {
+ ...defaultState,
+ isServiceChartLoading: false,
+ error: 'error',
+ }
+ expect(newState).toEqual(expected)
+ })
})
diff --git a/packages/marketplace/src/reducers/developer.ts b/packages/marketplace/src/reducers/developer.ts
index 9f4b370be7..8df1a151b2 100644
--- a/packages/marketplace/src/reducers/developer.ts
+++ b/packages/marketplace/src/reducers/developer.ts
@@ -1,7 +1,16 @@
import { Action, FormState } from '../types/core'
import { isType } from '../utils/actions'
-import { developerLoading, developerReceiveData, developerClearData, developerSetFormState } from '../actions/developer'
-import { PagedResultAppSummaryModel_, ScopeModel } from '@reapit/foundations-ts-definitions'
+import {
+ developerLoading,
+ developerReceiveData,
+ developerClearData,
+ developerSetFormState,
+ setMyIdentity,
+ fetchBilling,
+ fetchBillingSuccess,
+ fetchBillingFailure,
+} from '@/actions/developer'
+import { PagedResultAppSummaryModel_, ScopeModel, DeveloperModel } from '@reapit/foundations-ts-definitions'
import { developerAppShowModal } from '@/actions/developer-app-modal'
export interface DeveloperRequestParams {
@@ -14,11 +23,33 @@ export interface DeveloperItem {
scopes: ScopeModel[]
}
+export type RequestByPeriod = {
+ period: string
+ periodStart: string
+ periodEnd: string
+ periodName: string
+ requestCount: number
+ endpointCount: number
+ netAmount: number
+ grossAmount: number
+ vatAmount: number
+}
+
+export type Billing = {
+ from: string
+ to: string
+ requestsByPeriod: RequestByPeriod[]
+}
+
export interface DeveloperState {
loading: boolean
developerData: DeveloperItem | null
formState: FormState
isVisible: boolean
+ myIdentity: DeveloperModel | null
+ billing: Billing | null
+ isServiceChartLoading: boolean
+ error: unknown
}
export const defaultState: DeveloperState = {
@@ -26,6 +57,10 @@ export const defaultState: DeveloperState = {
developerData: null,
formState: 'PENDING',
isVisible: false,
+ myIdentity: null,
+ billing: null,
+ isServiceChartLoading: true,
+ error: null,
}
const developerReducer = (state: DeveloperState = defaultState, action: Action): DeveloperState => {
@@ -66,6 +101,36 @@ const developerReducer = (state: DeveloperState = defaultState, action: Action {
})
})
+describe('fetchMyIdentitySagas', () => {
+ const developerId = '1'
+ const gen = cloneableGenerator(fetchMyIdentitySagas as any)({ data: params })
+ expect(gen.next().value).toEqual(put(developerLoading(true)))
+ expect(gen.next().value).toEqual(select(selectDeveloperId))
+ expect(gen.next(developerId).value).toEqual(call(api.fetchMyIdentity, developerId))
+ it('api call success', () => {
+ const clone = gen.clone()
+ expect(clone.next(developerIdentity).value).toEqual(put(setMyIdentity(developerIdentity)))
+ expect(clone.next().value).toEqual(put(developerLoading(false)))
+ expect(clone.next().done).toEqual(true)
+ })
+
+ it('api call error', () => {
+ const clone = gen.clone()
+ // @ts-ignore
+ expect(clone.throw('error').value).toEqual(put(developerLoading(false)))
+ expect(clone.next().value).toEqual(
+ put(
+ errorThrownServer({
+ type: 'SERVER',
+ message: errorMessages.DEFAULT_SERVER_ERROR,
+ }),
+ ),
+ )
+ expect(clone.next().done).toEqual(true)
+ })
+})
+
+describe('fetchBillingSagas', () => {
+ const params = {
+ dateFrom: '2020-01',
+ dateTo: '2020-05',
+ applicationId: ['1', '2'],
+ } as FetchBillingParams
+ const gen = cloneableGenerator(fetchBillingSagas as any)({ data: params })
+ expect(gen.next().value).toEqual(call(api.fetchBilling, params))
+ it('api call success', () => {
+ const clone = gen.clone()
+ expect(clone.next(billing).value).toEqual(put(fetchBillingSuccess(billing)))
+ expect(clone.next().done).toEqual(true)
+ })
+ it('api call error', () => {
+ const clone = gen.clone()
+ // @ts-ignore
+ expect(clone.throw('error').value).toEqual(put(fetchBillingFailure('error')))
+ expect(clone.next().value).toEqual(
+ put(
+ errorThrownServer({
+ type: 'SERVER',
+ message: errorMessages.DEFAULT_SERVER_ERROR,
+ }),
+ ),
+ )
+ expect(clone.next().done).toEqual(true)
+ })
+})
+
describe('developer thunks', () => {
describe('developerRequestDataListen', () => {
it('should request data listen', () => {
@@ -136,6 +204,22 @@ describe('developer thunks', () => {
})
})
+ describe('developerCreateListen', () => {
+ it('should trigger developerCreact action', () => {
+ const gen = fetchMyIdentitySagasListen()
+ expect(gen.next().value).toEqual(takeLatest(ActionTypes.DEVELOPER_FETCH_MY_IDENTITY, fetchMyIdentitySagas))
+ expect(gen.next().done).toBe(true)
+ })
+ })
+
+ describe('developerCreateListen', () => {
+ it('should trigger developerCreact action', () => {
+ const gen = fetchBillingSagasListen()
+ expect(gen.next().value).toEqual(takeLatest(ActionTypes.DEVELOPER_FETCH_BILLING, fetchBillingSagas))
+ expect(gen.next().done).toBe(true)
+ })
+ })
+
describe('developerCreateListen', () => {
it('should trigger developerCreact action', () => {
const gen = developerCreateListen()
@@ -148,7 +232,14 @@ describe('developer thunks', () => {
it('should listen developer request data & create app action', () => {
const gen = developerSagas()
- expect(gen.next().value).toEqual(all([fork(developerRequestDataListen), fork(developerCreateListen)]))
+ expect(gen.next().value).toEqual(
+ all([
+ fork(developerRequestDataListen),
+ fork(developerCreateListen),
+ fork(fetchMyIdentitySagasListen),
+ fork(fetchBillingSagasListen),
+ ]),
+ )
expect(gen.next().done).toBe(true)
})
})
diff --git a/packages/marketplace/src/sagas/api.ts b/packages/marketplace/src/sagas/api.ts
index acc7d137d5..bccf24b994 100644
--- a/packages/marketplace/src/sagas/api.ts
+++ b/packages/marketplace/src/sagas/api.ts
@@ -1,9 +1,12 @@
+// TODO: TO MOVE ALL API CALL TO HERE
+
import { fetcher, setQueryParams } from '@reapit/elements'
import { URLS, generateHeader } from '../constants/api'
import { RevisionsRequestParams } from '@/actions/revisions'
import { APPS_PER_PAGE } from '@/constants/paginator'
import { logger } from 'logger'
+// Apps API
export const fetchAdminApps = async ({ params }) => {
try {
const response = await fetcher({
@@ -53,7 +56,47 @@ export const fetchAppRevisions = async (params: RevisionsRequestParams) => {
}
}
+// developer API
+export const fetchMyIdentity = async (developerId: string) => {
+ try {
+ const response = await fetcher({
+ url: `${URLS.developers}/${developerId}`,
+ api: window.reapit.config.marketplaceApiUrl,
+ method: 'GET',
+ headers: generateHeader(window.reapit.config.marketplaceApiKey),
+ })
+ return response
+ } catch (error) {
+ logger(error)
+ throw new Error(error)
+ }
+}
+
+// billing API
+export type FetchBillingParams = {
+ dateFrom: string
+ dateTo: string
+ applicationId: string[]
+}
+
+export const fetchBilling = async (params: FetchBillingParams) => {
+ try {
+ const response = await fetcher({
+ url: `${URLS.trafficEvents}/billing?${setQueryParams(params)}`,
+ api: window.reapit.config.marketplaceApiUrl,
+ method: 'GET',
+ headers: generateHeader(window.reapit.config.marketplaceApiKey),
+ })
+ return response
+ } catch (error) {
+ logger(error)
+ throw new Error(error)
+ }
+}
+
export default {
fetchAdminApps,
deleteApp,
+ fetchMyIdentity,
+ fetchBilling,
}
diff --git a/packages/marketplace/src/sagas/developer.ts b/packages/marketplace/src/sagas/developer.ts
index 5c0a945afc..c9fc56b416 100644
--- a/packages/marketplace/src/sagas/developer.ts
+++ b/packages/marketplace/src/sagas/developer.ts
@@ -1,21 +1,25 @@
import { fetcher } from '@reapit/elements'
import { put, fork, takeLatest, all, call, select } from '@redux-saga/core/effects'
-import { errorThrownServer } from '../actions/error'
import { CreateDeveloperModel } from '@reapit/foundations-ts-definitions'
-import { APPS_PER_PAGE } from '@/constants/paginator'
-import { selectDeveloperId } from '@/selector/developer'
-import { DeveloperItem, DeveloperRequestParams } from '@/reducers/developer'
import { logger } from 'logger'
import {
developerLoading,
developerReceiveData,
developerSetFormState,
developerRequestDataFailure,
-} from '../actions/developer'
-import ActionTypes from '../constants/action-types'
-import errorMessages from '../constants/error-messages'
-import { URLS, generateHeader } from '../constants/api'
-import { Action } from '../types/core'
+ setMyIdentity,
+ fetchBillingSuccess,
+ fetchBillingFailure,
+} from '@/actions/developer'
+import { APPS_PER_PAGE } from '@/constants/paginator'
+import { DeveloperItem, DeveloperRequestParams } from '@/reducers/developer'
+import { errorThrownServer } from '@/actions/error'
+import ActionTypes from '@/constants/action-types'
+import errorMessages from '@/constants/error-messages'
+import { URLS, generateHeader } from '@/constants/api'
+import { Action } from '@/types/core'
+import { selectDeveloperId } from '@/selector'
+import api, { FetchBillingParams } from './api'
export const developerDataFetch = function*({ data }) {
yield put(developerLoading(true))
@@ -88,6 +92,43 @@ export const developerCreate = function*({ data }: Action)
}
}
+export const fetchMyIdentitySagas = function*() {
+ try {
+ yield put(developerLoading(true))
+ const developerId = yield select(selectDeveloperId)
+ const developerIdentity = yield call(api.fetchMyIdentity, developerId)
+ if (developerIdentity) {
+ yield put(setMyIdentity(developerIdentity))
+ }
+ yield put(developerLoading(false))
+ } catch (err) {
+ logger(err)
+ yield put(developerLoading(false))
+ yield put(
+ errorThrownServer({
+ type: 'SERVER',
+ message: errorMessages.DEFAULT_SERVER_ERROR,
+ }),
+ )
+ }
+}
+
+export const fetchBillingSagas = function*({ data }: Action) {
+ try {
+ const billingResponse = yield call(api.fetchBilling, data)
+ yield put(fetchBillingSuccess(billingResponse))
+ } catch (err) {
+ logger(err)
+ yield put(fetchBillingFailure(err))
+ yield put(
+ errorThrownServer({
+ type: 'SERVER',
+ message: errorMessages.DEFAULT_SERVER_ERROR,
+ }),
+ )
+ }
+}
+
export const developerRequestDataListen = function*() {
yield takeLatest>(ActionTypes.DEVELOPER_REQUEST_DATA, developerDataFetch)
}
@@ -96,8 +137,21 @@ export const developerCreateListen = function*() {
yield takeLatest(ActionTypes.DEVELOPER_CREATE, developerCreate)
}
+export const fetchMyIdentitySagasListen = function*() {
+ yield takeLatest(ActionTypes.DEVELOPER_FETCH_MY_IDENTITY, fetchMyIdentitySagas)
+}
+
+export const fetchBillingSagasListen = function*() {
+ yield takeLatest(ActionTypes.DEVELOPER_FETCH_BILLING, fetchBillingSagas)
+}
+
const developerSagas = function*() {
- yield all([fork(developerRequestDataListen), fork(developerCreateListen)])
+ yield all([
+ fork(developerRequestDataListen),
+ fork(developerCreateListen),
+ fork(fetchMyIdentitySagasListen),
+ fork(fetchBillingSagasListen),
+ ])
}
export default developerSagas
diff --git a/packages/marketplace/src/selector/__tests__/developer.ts b/packages/marketplace/src/selector/__tests__/developer.ts
index 537c60dc49..3f2f35aeeb 100644
--- a/packages/marketplace/src/selector/__tests__/developer.ts
+++ b/packages/marketplace/src/selector/__tests__/developer.ts
@@ -1,5 +1,14 @@
import { ReduxState } from '@/types/core'
-import { selectDeveloperId, selectDeveloperEmail } from '../developer'
+import {
+ selectDeveloperId,
+ selectDeveloperEmail,
+ selectDeveloper,
+ selectDeveloperApps,
+ selectMyIdentity,
+ selectBilling,
+ selectDeveloperLoading,
+ selectIsServiceChartLoading,
+} from '../developer'
describe('selectDeveloperId', () => {
it('should run correctly', () => {
@@ -44,3 +53,119 @@ describe('selectDeveloperEmail', () => {
expect(result).toEqual(undefined)
})
})
+
+describe('selectDeveloper', () => {
+ it('should run correctly', () => {
+ const input = {
+ developer: {
+ isServiceChartLoading: false,
+ developerData: {},
+ },
+ } as ReduxState
+ const result = selectDeveloper(input)
+ expect(result).toEqual(input.developer)
+ })
+
+ it('should run correctly and return undefined', () => {
+ const input = {} as ReduxState
+ const result = selectDeveloper(input)
+ expect(result).toEqual(undefined)
+ })
+})
+
+describe('selectDeveloperApps', () => {
+ it('should run correctly', () => {
+ const input = {
+ developer: {
+ isServiceChartLoading: false,
+ developerData: {
+ data: {
+ data: [{}],
+ },
+ },
+ },
+ } as ReduxState
+ const result = selectDeveloperApps(input)
+ expect(result).toEqual(input.developer.developerData?.data.data)
+ })
+
+ it('should run correctly and return []', () => {
+ const input = {} as ReduxState
+ const result = selectDeveloperApps(input)
+ expect(result).toEqual([])
+ })
+})
+
+describe('selectMyIdentity', () => {
+ it('should run correctly', () => {
+ const input = {
+ developer: {
+ isServiceChartLoading: false,
+ myIdentity: {},
+ },
+ } as ReduxState
+ const result = selectMyIdentity(input)
+ expect(result).toEqual(input.developer.myIdentity)
+ })
+
+ it('should run correctly and return {}', () => {
+ const input = {} as ReduxState
+ const result = selectMyIdentity(input)
+ expect(result).toEqual({})
+ })
+})
+
+describe('selectBilling', () => {
+ it('should run correctly', () => {
+ const input = {
+ developer: {
+ isServiceChartLoading: false,
+ billing: {},
+ },
+ } as ReduxState
+ const result = selectBilling(input)
+ expect(result).toEqual(input.developer.billing)
+ })
+
+ it('should run correctly and return null', () => {
+ const input = {} as ReduxState
+ const result = selectBilling(input)
+ expect(result).toEqual(null)
+ })
+})
+
+describe('selectDeveloperLoading', () => {
+ it('should run correctly', () => {
+ const input = {
+ developer: {
+ loading: true,
+ },
+ } as ReduxState
+ const result = selectDeveloperLoading(input)
+ expect(result).toEqual(true)
+ })
+
+ it('should run correctly and return null', () => {
+ const input = {} as ReduxState
+ const result = selectDeveloperLoading(input)
+ expect(result).toEqual(undefined)
+ })
+})
+
+describe('selectIsServiceChartLoading', () => {
+ it('should run correctly', () => {
+ const input = {
+ developer: {
+ isServiceChartLoading: true,
+ },
+ } as ReduxState
+ const result = selectIsServiceChartLoading(input)
+ expect(result).toEqual(true)
+ })
+
+ it('should run correctly and return null', () => {
+ const input = {} as ReduxState
+ const result = selectIsServiceChartLoading(input)
+ expect(result).toEqual(undefined)
+ })
+})
diff --git a/packages/marketplace/src/selector/developer.ts b/packages/marketplace/src/selector/developer.ts
index 15506849cd..452340f456 100644
--- a/packages/marketplace/src/selector/developer.ts
+++ b/packages/marketplace/src/selector/developer.ts
@@ -1,5 +1,7 @@
+// TODO: Will move all developerSelector to here for reusable
import { ReduxState } from '@/types/core'
-import { AppSummaryModel } from '@reapit/foundations-ts-definitions'
+import { AppSummaryModel, DeveloperModel } from '@reapit/foundations-ts-definitions'
+import { Billing } from '@/reducers/developer'
export const selectDeveloperId = (state: ReduxState) => {
return state.auth.loginSession?.loginIdentity.developerId
@@ -9,10 +11,26 @@ export const selectDeveloperEmail = (state: ReduxState) => {
return state?.settings?.developerInfomation?.email
}
-export const getDevelopers = (state: ReduxState) => {
+export const selectDeveloper = (state: ReduxState) => {
return state.developer
}
-export const selectDeveloperApps = (state: ReduxState) => {
- return state?.developer?.developerData?.data?.data || ([] as AppSummaryModel[])
+export const selectDeveloperApps = (state: ReduxState): AppSummaryModel[] => {
+ return state.developer?.developerData?.data.data || ([] as AppSummaryModel[])
+}
+
+export const selectMyIdentity = (state: ReduxState): DeveloperModel => {
+ return state.developer?.myIdentity || {}
+}
+
+export const selectBilling = (state: ReduxState): Billing | null => {
+ return state.developer?.billing || null
+}
+
+export const selectDeveloperLoading = (state: ReduxState): boolean => {
+ return state.developer?.loading
+}
+
+export const selectIsServiceChartLoading = (state: ReduxState): boolean => {
+ return state.developer?.isServiceChartLoading
}
diff --git a/packages/marketplace/src/selector/index.ts b/packages/marketplace/src/selector/index.ts
new file mode 100644
index 0000000000..9aab0d3c22
--- /dev/null
+++ b/packages/marketplace/src/selector/index.ts
@@ -0,0 +1 @@
+export * from './developer'
diff --git a/packages/marketplace/src/utils/__tests__/route-dispatcher.ts b/packages/marketplace/src/utils/__tests__/route-dispatcher.ts
index 20e9d329b3..05c7450dd3 100644
--- a/packages/marketplace/src/utils/__tests__/route-dispatcher.ts
+++ b/packages/marketplace/src/utils/__tests__/route-dispatcher.ts
@@ -3,17 +3,17 @@ import store from '../../core/store'
import Routes from '../../constants/routes'
import { GET_ALL_PAGE_SIZE } from '../../constants/paginator'
import { RouteValue } from '../../types/core'
-import { clientRequestData } from '../../actions/client'
-import { developerRequestData } from '../../actions/developer'
-import { myAppsRequestData } from '../../actions/my-apps'
-import { installedAppsRequestData } from '../../actions/installed-apps'
-import { adminApprovalsRequestData } from '../../actions/admin-approvals'
-import { getAccessToken } from '../../utils/session'
+import { clientRequestData } from '@/actions/client'
+import { developerRequestData, fetchMyIdentity } from '@/actions/developer'
+import { myAppsRequestData } from '@/actions/my-apps'
+import { installedAppsRequestData } from '@/actions/installed-apps'
+import { adminApprovalsRequestData } from '@/actions/admin-approvals'
+import { getAccessToken } from '@/utils/session'
import { requestDeveloperData } from '@/actions/settings'
import { getParamsFromPath } from '@/utils/client-url-params'
jest.mock('@reapit/elements')
-jest.mock('../../utils/session')
+jest.mock('@/utils/session')
jest.mock('../../core/store')
jest.mock('../../sagas/client')
jest.mock('../../sagas/developer')
@@ -77,5 +77,6 @@ describe('routeDispatcher', () => {
it('should dispatch to appInstallationsRequestData & developerRequestData for the analytics route', async () => {
await routeDispatcher(Routes.DEVELOPER_ANALYTICS_TAB as RouteValue)
expect(store.dispatch).toHaveBeenCalledWith(developerRequestData({ appsPerPage: GET_ALL_PAGE_SIZE, page: 1 }))
+ expect(store.dispatch).toHaveBeenCalledWith(fetchMyIdentity())
})
})
diff --git a/packages/marketplace/src/utils/route-dispatcher.ts b/packages/marketplace/src/utils/route-dispatcher.ts
index c2a50a8a65..0d7c63f6d4 100644
--- a/packages/marketplace/src/utils/route-dispatcher.ts
+++ b/packages/marketplace/src/utils/route-dispatcher.ts
@@ -7,7 +7,7 @@ import store from '../core/store'
import { clientRequestData } from '../actions/client'
import { myAppsRequestData } from '../actions/my-apps'
import { installedAppsRequestData } from '../actions/installed-apps'
-import { developerRequestData } from '../actions/developer'
+import { developerRequestData, fetchMyIdentity } from '@/actions/developer'
import { adminApprovalsRequestData } from '../actions/admin-approvals'
import { adminDevManagementRequestData } from '../actions/admin-dev-management'
import { submitAppRequestData } from '../actions/submit-app'
@@ -24,7 +24,6 @@ const routeDispatcher = async (route: RouteValue, params?: StringMap, search?: s
const id = params && params.appid ? params.appid : ''
const queryParams = new URLSearchParams(search)
const appId = queryParams.get('appId')
- const PAGE_SIZE_FOR_ALL_APPS = 999
switch (route) {
case Routes.CLIENT:
@@ -47,6 +46,7 @@ const routeDispatcher = async (route: RouteValue, params?: StringMap, search?: s
break
case Routes.DEVELOPER_ANALYTICS_TAB: {
// Fetch all apps to map app name to installations
+ store.dispatch(fetchMyIdentity())
store.dispatch(developerRequestData({ page: 1, appsPerPage: GET_ALL_PAGE_SIZE }))
if (appId) {
const clientId = selectClientId(store.state)
@@ -90,7 +90,7 @@ const routeDispatcher = async (route: RouteValue, params?: StringMap, search?: s
store.dispatch(requestDeveloperData())
break
case Routes.DEVELOPER_WEBHOOKS:
- store.dispatch(developerRequestData({ page: 1, appsPerPage: PAGE_SIZE_FOR_ALL_APPS } as DeveloperRequestParams))
+ store.dispatch(developerRequestData({ page: 1, appsPerPage: GET_ALL_PAGE_SIZE } as DeveloperRequestParams))
store.dispatch(webhookTopicsRequestData())
break
case Routes.DEVELOPER_HELP:
From ac134898d1fe5d6f26e45a0918e9ae606c65e6ba Mon Sep 17 00:00:00 2001
From: Pham Hai Duong
Date: Thu, 7 May 2020 22:51:54 +0700
Subject: [PATCH 2/7] feat: #1164 add SBOX to customer of subscription drop
down (#1172)
feat: #1164 add SBOX to customer of subscription drop down (#1172)
Changes
- modify generateCustomerOptions for return correct value
---
.../__snapshots__/webhook-edit-modal.tsx.snap | 28 +++++----
.../ui/__tests__/webhook-edit-modal.tsx | 22 +++----
.../src/components/ui/webhook-edit-modal.tsx | 60 ++++++++++---------
3 files changed, 60 insertions(+), 50 deletions(-)
diff --git a/packages/marketplace/src/components/ui/__tests__/__snapshots__/webhook-edit-modal.tsx.snap b/packages/marketplace/src/components/ui/__tests__/__snapshots__/webhook-edit-modal.tsx.snap
index 9a02b0abf6..a3fbd9b47b 100644
--- a/packages/marketplace/src/components/ui/__tests__/__snapshots__/webhook-edit-modal.tsx.snap
+++ b/packages/marketplace/src/components/ui/__tests__/__snapshots__/webhook-edit-modal.tsx.snap
@@ -32,10 +32,10 @@ exports[`WebhookEditModal should match a snapshot 5`] = `
diff --git a/packages/marketplace/src/components/ui/__tests__/webhook-edit-modal.tsx b/packages/marketplace/src/components/ui/__tests__/webhook-edit-modal.tsx
index 58be2ea916..dfdd7e262e 100644
--- a/packages/marketplace/src/components/ui/__tests__/webhook-edit-modal.tsx
+++ b/packages/marketplace/src/components/ui/__tests__/webhook-edit-modal.tsx
@@ -55,9 +55,9 @@ describe('WebhookEditModal', () => {
const appId = ''
const fn = onEdit(editWebhook, webhookId, appId)
const values: FormValuesType = {
- WebhookURL: '',
- SubscriptionTopics: [],
- SubscriptionCustomers: [],
+ url: '',
+ topicIds: [],
+ customerIds: [],
active: false,
}
fn(values)
@@ -68,9 +68,9 @@ describe('WebhookEditModal', () => {
const webhookId = ''
const fn = onCreate(createWebhook, webhookId)
const values = {
- WebhookURL: '',
- SubscriptionTopics: [],
- SubscriptionCustomers: [],
+ url: '',
+ topicIds: [],
+ customerIds: [],
active: false,
}
fn(values)
@@ -147,7 +147,7 @@ describe('WebhookEditModal', () => {
created: 'string',
appId: 'string',
client: 'client',
- status: '',
+ status: 'Terminated',
authFlow: '',
},
{
@@ -155,16 +155,16 @@ describe('WebhookEditModal', () => {
created: 'string',
appId: 'appId',
client: 'client',
- status: '',
+ status: 'Active',
authFlow: '',
},
]
const expected = [
{
- value: 'client',
- label: 'client',
- description: 'client',
+ value: 'SBOX',
+ label: 'SBOX',
+ description: 'SBOX',
},
{
value: 'client',
diff --git a/packages/marketplace/src/components/ui/webhook-edit-modal.tsx b/packages/marketplace/src/components/ui/webhook-edit-modal.tsx
index a95c32ee2e..6eb717b195 100644
--- a/packages/marketplace/src/components/ui/webhook-edit-modal.tsx
+++ b/packages/marketplace/src/components/ui/webhook-edit-modal.tsx
@@ -90,14 +90,23 @@ export const generateTopicOptions = (topics: TopicItem[]) => {
}
export const generateCustomerOptions = (customers: CustomerItem[]) => {
- return customers.map(
- customer =>
- ({
+ const customerOptions: SelectOption[] = [
+ {
+ value: 'SBOX',
+ label: 'SBOX',
+ description: 'SBOX',
+ } as SelectOption,
+ ]
+ customers.forEach((customer: CustomerItem) => {
+ if (customer.status === 'Active') {
+ customerOptions.push({
value: customer.client,
label: customer.client,
description: customer.client,
- } as SelectOption),
- )
+ } as SelectOption)
+ }
+ })
+ return customerOptions
}
export const onCreate = (createWebhook: (data: CreateWebhookParams) => void, appId: string) => (
@@ -105,10 +114,10 @@ export const onCreate = (createWebhook: (data: CreateWebhookParams) => void, app
) => {
const params: CreateWebhookParams = {
applicationId: appId,
- url: values.WebhookURL,
+ url: values.url,
description: '',
- topicIds: values.SubscriptionTopics,
- customerIds: values.SubscriptionCustomers,
+ topicIds: values.topicIds,
+ customerIds: values.customerIds,
active: values.active,
}
createWebhook(params)
@@ -120,10 +129,10 @@ export const onEdit = (editWebhook: (data: EditWebhookParams) => void, webhookId
const params: EditWebhookParams = {
applicationId: appId,
webhookId,
- url: values.WebhookURL,
+ url: values.url,
description: '',
- topicIds: values.SubscriptionTopics,
- customerIds: values.SubscriptionCustomers,
+ topicIds: values.topicIds,
+ customerIds: values.customerIds,
active: values.active,
}
editWebhook(params)
@@ -138,9 +147,9 @@ export type WebhookModalInnerProps = WebhookModalInnerMappedAction &
}
export type FormValuesType = {
- WebhookURL: string
- SubscriptionTopics: string[]
- SubscriptionCustomers: string[]
+ url: string
+ topicIds: string[]
+ customerIds: string[]
active: boolean
}
@@ -158,9 +167,9 @@ export const WebhookModalInner: React.FunctionComponent
const modalConfig = isUpdate ? EDIT_MODAL : CREATE_MODAL
const initFormValues: FormValuesType = {
- WebhookURL: webhookData?.url,
- SubscriptionTopics: webhookData?.topicIds,
- SubscriptionCustomers: webhookData?.customerIds,
+ url: webhookData?.url,
+ topicIds: webhookData?.topicIds,
+ customerIds: webhookData?.customerIds,
active: webhookData?.active,
}
@@ -200,27 +209,20 @@ export const WebhookModalInner: React.FunctionComponent
-
+
Date: Fri, 8 May 2020 00:09:22 +0700
Subject: [PATCH 3/7] feat: #1106 web component config update (#1157)
* feat: #1106 Web components update config
* feat: #1106 Fixing customerId not included
---
packages/demo-site/config.example.json | 1 +
packages/demo-site/index.ejs | 4 +++-
packages/demo-site/scripts/build.js | 2 +-
.../client/components/appointment-bookings.svelte | 1 +
.../src/appointment-bookings/client/core/index.ts | 2 ++
.../src/appointment-bookings/client/index.html | 4 +++-
.../src/common/utils/__tests__/get-client-headers.ts | 4 +++-
.../web-components/src/common/utils/fetcher-client.ts | 2 +-
.../src/common/utils/get-client-headers.ts | 3 ++-
packages/web-components/src/scripts/start-dev.js | 5 +++--
.../src/search-widget/client/api/__tests__/properties.ts | 7 ++++++-
.../client/api/__tests__/property-images.ts | 4 ++--
.../src/search-widget/client/api/__tests__/property.ts | 3 ++-
.../src/search-widget/client/api/properties.ts | 9 +++++----
.../src/search-widget/client/api/property-images.ts | 3 ++-
.../src/search-widget/client/api/property.ts | 5 +++--
.../client/components/__tests__/search-widget.ts | 2 ++
.../client/components/property-detail.svelte | 4 +++-
.../search-widget/client/components/search-form.svelte | 2 ++
.../search-widget/client/components/search-widget.svelte | 2 ++
.../client/core/__tests__/__snapshots__/index.ts.snap | 4 +++-
.../src/search-widget/client/core/__tests__/app.ts | 1 +
.../src/search-widget/client/core/__tests__/index.ts | 1 +
.../src/search-widget/client/core/__tests__/store.ts | 1 +
.../src/search-widget/client/core/app.svelte | 5 +++--
.../src/search-widget/client/core/index.ts | 4 +++-
.../src/search-widget/client/core/store.ts | 1 +
.../web-components/src/search-widget/client/index.html | 3 ++-
.../client/components/viewing-booking.svelte | 2 ++
.../client/core/__tests__/__snapshots__/index.ts.snap | 6 ++++--
.../src/viewing-booking/client/core/__tests__/index.ts | 1 +
.../src/viewing-booking/client/core/__tests__/store.ts | 1 +
.../src/viewing-booking/client/core/index.ts | 9 ++++++++-
.../src/viewing-booking/client/core/store.ts | 1 +
.../web-components/src/viewing-booking/client/index.html | 3 ++-
35 files changed, 83 insertions(+), 29 deletions(-)
diff --git a/packages/demo-site/config.example.json b/packages/demo-site/config.example.json
index a91582428c..51aae992af 100644
--- a/packages/demo-site/config.example.json
+++ b/packages/demo-site/config.example.json
@@ -1,4 +1,5 @@
{
"API_KEY": "",
+ "CUSTOMER_ID": "",
"CDN_URL": "https://web-components.reapit.cloud"
}
diff --git a/packages/demo-site/index.ejs b/packages/demo-site/index.ejs
index e1d2a81d53..c05e9d2380 100644
--- a/packages/demo-site/index.ejs
+++ b/packages/demo-site/index.ejs
@@ -4,7 +4,7 @@
- Web Components
+ DEMO