From 38729109f10c5c3f263227363c37aee6f6f0ff57 Mon Sep 17 00:00:00 2001
From: Dan Nguyen <45675930+DanND96@users.noreply.github.com>
Date: Wed, 14 Aug 2019 17:33:34 +0700
Subject: [PATCH] [CLD-182] Fix install button display in developer detail
modal (#73)
* [CLD-182] Fix button install display in developer detail modal
* Fix review
---
src/actions/__tests__/app-detail.ts | 2 +-
src/actions/app-detail.ts | 7 +-
src/components/pages/admin-approvals.tsx | 4 +-
src/components/pages/client.tsx | 19 +++--
src/components/pages/developer-home.tsx | 2 +-
src/components/pages/my-apps.tsx | 10 ++-
.../developer-app-modal.tsx.snap | 1 -
src/components/ui/app-detail.tsx | 50 +++++++------
src/constants/action-types.ts | 7 ++
src/sagas/__stubs__/app-detail.ts | 1 -
src/sagas/__tests__/app-detail.ts | 75 ++++++++++---------
src/sagas/app-detail.ts | 15 ++--
src/selector/client.ts | 2 +-
13 files changed, 110 insertions(+), 85 deletions(-)
diff --git a/src/actions/__tests__/app-detail.ts b/src/actions/__tests__/app-detail.ts
index 43129d43d4..bf8c3087e0 100644
--- a/src/actions/__tests__/app-detail.ts
+++ b/src/actions/__tests__/app-detail.ts
@@ -21,7 +21,7 @@ describe('appDetail actions', () => {
it('should create a appDetailRequestData action', () => {
expect(appDetailRequestData.type).toEqual(ActionTypes.APP_DETAIL_REQUEST_DATA)
- expect(appDetailRequestData('xxxx').data).toEqual('xxxx')
+ expect(appDetailRequestData({ id: '1', clientId: '1' }).data).toEqual({ id: '1', clientId: '1' })
})
it('should create a appDetailClearData action', () => {
diff --git a/src/actions/app-detail.ts b/src/actions/app-detail.ts
index 80f365a3d7..dbd00a7bc6 100644
--- a/src/actions/app-detail.ts
+++ b/src/actions/app-detail.ts
@@ -2,7 +2,12 @@ import { actionCreator } from '../utils/actions'
import ActionTypes from '../constants/action-types'
import { AppDetailItem } from '../reducers/app-detail'
-export const appDetailRequestData = actionCreator(ActionTypes.APP_DETAIL_REQUEST_DATA)
+export interface AppDetailParams {
+ id: string
+ clientId?: string
+}
+
+export const appDetailRequestData = actionCreator(ActionTypes.APP_DETAIL_REQUEST_DATA)
export const appDetailLoading = actionCreator(ActionTypes.APP_DETAIL_LOADING)
export const appDetailReceiveData = actionCreator(ActionTypes.APP_DETAIL_RECEIVE_DATA)
export const appDetailFailure = actionCreator(ActionTypes.APP_DETAIL_REQUEST_DATA_FAILURE)
diff --git a/src/components/pages/admin-approvals.tsx b/src/components/pages/admin-approvals.tsx
index a87f952723..68ba0a7a54 100644
--- a/src/components/pages/admin-approvals.tsx
+++ b/src/components/pages/admin-approvals.tsx
@@ -19,7 +19,7 @@ import Modal from '../ui/modal'
export interface AdminApprovalsMappedActions {
fetchRevisionDetail: (params: RevisionDetailRequestParams) => void
- fetchAppDetail: (appId: string) => void
+ fetchAppDetail: (id: string) => void
}
export interface AdminApprovalsMappedProps {
@@ -124,7 +124,7 @@ const mapStateToProps = (state: ReduxState): AdminApprovalsMappedProps => ({
const mapDispatchToProps = (dispatch: any): AdminApprovalsMappedActions => ({
fetchRevisionDetail: param => dispatch(revisionDetailRequestData(param)),
- fetchAppDetail: (appId: string) => dispatch(appDetailRequestData(appId))
+ fetchAppDetail: (id: string) => dispatch(appDetailRequestData({ id }))
})
export default withRouter(
diff --git a/src/components/pages/client.tsx b/src/components/pages/client.tsx
index ac0b261c78..64dfef47b2 100644
--- a/src/components/pages/client.tsx
+++ b/src/components/pages/client.tsx
@@ -12,19 +12,27 @@ import routes from '@/constants/routes'
import { appDetailRequestData } from '@/actions/app-detail'
import { AppDetailState } from '@/reducers/app-detail'
import AppDetailModal from '@/components/ui/app-detail-modal'
+import { selectClientId } from '@/selector/client'
export interface ClientMappedActions {
- fetchAppDetail: (id: string) => void
+ fetchAppDetail: (id: string, clientId: string) => void
}
export interface ClientMappedProps {
clientState: ClientState
appDetail: AppDetailState
+ clientId: string
}
export type ClientProps = ClientMappedActions & ClientMappedProps & RouteComponentProps<{ page?: any }>
-export const Client: React.FunctionComponent = ({ clientState, match, fetchAppDetail, appDetail }) => {
+export const Client: React.FunctionComponent = ({
+ clientState,
+ match,
+ fetchAppDetail,
+ appDetail,
+ clientId
+}) => {
const pageNumber = match.params && !isNaN(match.params.page) ? Number(match.params.page) : 1
const unfetched = !clientState.clientData
const loading = clientState.loading
@@ -44,7 +52,7 @@ export const Client: React.FunctionComponent = ({ clientState, matc
onCardClick={app => {
setVisible(true)
if (app.id && (!appDetail.appDetailData || appDetail.appDetailData.data.id !== app.id)) {
- fetchAppDetail(app.id)
+ fetchAppDetail(app.id, clientId)
}
}}
/>
@@ -56,11 +64,12 @@ export const Client: React.FunctionComponent = ({ clientState, matc
const mapStateToProps = (state: ReduxState): ClientMappedProps => ({
clientState: state.client,
- appDetail: state.appDetail
+ appDetail: state.appDetail,
+ clientId: selectClientId(state)
})
const mapDispatchToProps = (dispatch: any): ClientMappedActions => ({
- fetchAppDetail: (id: string) => dispatch(appDetailRequestData(id))
+ fetchAppDetail: (id: string, clientId: string) => dispatch(appDetailRequestData({ id, clientId }))
})
export default withRouter(
diff --git a/src/components/pages/developer-home.tsx b/src/components/pages/developer-home.tsx
index 8f00a13745..cbcb415325 100644
--- a/src/components/pages/developer-home.tsx
+++ b/src/components/pages/developer-home.tsx
@@ -73,7 +73,7 @@ const mapStateToProps = (state: ReduxState): DeveloperMappedProps => ({
})
const mapDispatchToProps = (dispatch: any): DeveloperMappedActions => ({
- fetchAppDetail: (id: string) => dispatch(appDetailRequestData(id))
+ fetchAppDetail: (id: string) => dispatch(appDetailRequestData({ id }))
})
export default withRouter(
diff --git a/src/components/pages/my-apps.tsx b/src/components/pages/my-apps.tsx
index 5ea161477e..557899174e 100644
--- a/src/components/pages/my-apps.tsx
+++ b/src/components/pages/my-apps.tsx
@@ -14,9 +14,10 @@ import { AppDetailState } from '@/reducers/app-detail'
import AppDetailModal from '../ui/app-detail-modal'
import { myAppsRequestData } from '@/actions/my-apps'
import { appUninstallDone } from '@/actions/app-uninstall'
+import { selectClientId } from '@/selector/client'
export interface MyAppsMappedActions {
- fetchAppDetail: (id: string) => void
+ fetchAppDetail: (id: string, clientId: string) => void
fetchMyApp: (page: number) => void
appUninstallDone: () => void
}
@@ -25,12 +26,14 @@ export interface MyAppsMappedProps {
myAppsState: MyAppsState
appDetail: AppDetailState
appUninstallFormState: FormState
+ clientId: string
}
export type MyAppsProps = MyAppsMappedActions & MyAppsMappedProps & RouteComponentProps<{ page?: any }>
export const MyApps: React.FunctionComponent = ({
myAppsState,
+ clientId,
match,
fetchMyApp,
fetchAppDetail,
@@ -67,7 +70,7 @@ export const MyApps: React.FunctionComponent = ({
onCardClick={app => {
setVisible(true)
if (app.id && (!appDetail.appDetailData || appDetail.appDetailData.data.id !== app.id)) {
- fetchAppDetail(app.id)
+ fetchAppDetail(app.id, clientId)
}
}}
/>
@@ -80,11 +83,12 @@ export const MyApps: React.FunctionComponent = ({
const mapStateToProps = (state: ReduxState): MyAppsMappedProps => ({
myAppsState: state.myApps,
appDetail: state.appDetail,
+ clientId: selectClientId(state),
appUninstallFormState: state.appUninstall.formState
})
const mapDispatchToProps = (dispatch: any): MyAppsMappedActions => ({
- fetchAppDetail: (id: string) => dispatch(appDetailRequestData(id)),
+ fetchAppDetail: (id: string, clientId: string) => dispatch(appDetailRequestData({ id, clientId })),
fetchMyApp: (page: number) => dispatch(myAppsRequestData(page)),
appUninstallDone: () => dispatch(appUninstallDone())
})
diff --git a/src/components/ui/__tests__/__snapshots__/developer-app-modal.tsx.snap b/src/components/ui/__tests__/__snapshots__/developer-app-modal.tsx.snap
index 96c56e4eed..f8d3f40dad 100644
--- a/src/components/ui/__tests__/__snapshots__/developer-app-modal.tsx.snap
+++ b/src/components/ui/__tests__/__snapshots__/developer-app-modal.tsx.snap
@@ -19,7 +19,6 @@ exports[`DeveloperAppModalInner should match a snapshot when LOADING false 1`] =
"developer": "Pete's Proptech World Ltd",
"homePage": "http://myawesomeproptechproduct.io",
"id": "9b6fd5f7-2c15-483d-b925-01b650538e52",
- "installationId": "7b2517b3-aad3-4ad8-b31c-988a4b3d112d",
"media": Array [
Object {
"description": "Application Icon",
diff --git a/src/components/ui/app-detail.tsx b/src/components/ui/app-detail.tsx
index 4fef12e7be..d85075e1e1 100644
--- a/src/components/ui/app-detail.tsx
+++ b/src/components/ui/app-detail.tsx
@@ -41,7 +41,8 @@ export const AppDetail: React.FunctionComponent = ({
setAppDetailModalStatePermission,
fetchAppPermission,
requestUninstall,
- appUninstallFormState
+ appUninstallFormState,
+ isCurrentLoggedUserClient
}) => {
if (!data) {
return null
@@ -88,29 +89,30 @@ export const AppDetail: React.FunctionComponent = ({
{summary}
- {installedOn ? (
-
- ) : (
- {
- if (!id) {
- return
- }
- fetchAppPermission(id)
- setAppDetailModalStatePermission()
- }}
- >
- Install App
-
- )}
+ {isCurrentLoggedUserClient &&
+ (installedOn ? (
+
+ ) : (
+ {
+ if (!id) {
+ return
+ }
+ fetchAppPermission(id)
+ setAppDetailModalStatePermission()
+ }}
+ >
+ Install App
+
+ ))}
diff --git a/src/constants/action-types.ts b/src/constants/action-types.ts
index fe3b1a5755..92ee1b6b27 100644
--- a/src/constants/action-types.ts
+++ b/src/constants/action-types.ts
@@ -61,6 +61,13 @@ const ActionTypes = {
APP_DETAIL_RECEIVE_DATA: 'APP_DETAIL_RECEIVE_DATA',
APP_DETAIL_CLEAR_DATA: 'APP_DETAIL_CLEAR_DATA',
+ // Client App Detail actions
+ CLIENT_APP_DETAIL_REQUEST_DATA: 'CLIENT_APP_DETAIL_REQUEST_DATA',
+ CLIENT_APP_DETAIL_LOADING: 'CLIENT_APP_DETAIL_LOADING',
+ CLIENT_APP_DETAIL_REQUEST_DATA_FAILURE: 'CLIENT_APP_DETAIL_REQUEST_DATA_FAILURE',
+ CLIENT_APP_DETAIL_RECEIVE_DATA: 'CLIENT_APP_DETAIL_RECEIVE_DATA',
+ CLIENT_APP_DETAIL_CLEAR_DATA: 'CLIENT_APP_DETAIL_CLEAR_DATA',
+
// App Permission actions
APP_PERMISION_REQUEST_DATA: 'APP_PERMISION_REQUEST_DATA',
APP_PERMISION_REQUEST_DATA_FAILURE: 'APP_PERMISION_REQUEST_DATA_FAILURE',
diff --git a/src/sagas/__stubs__/app-detail.ts b/src/sagas/__stubs__/app-detail.ts
index 90d5f09642..18ed27829e 100644
--- a/src/sagas/__stubs__/app-detail.ts
+++ b/src/sagas/__stubs__/app-detail.ts
@@ -3,7 +3,6 @@ import { AppDetailItem } from '@/reducers/app-detail'
export const appDetailDataStub: AppDetailItem = {
data: {
id: '9b6fd5f7-2c15-483d-b925-01b650538e52',
- installationId: '7b2517b3-aad3-4ad8-b31c-988a4b3d112d',
name: "Peter's Properties",
summary: 'vitae elementum curabitur vitae nunc sed velit eget gravida cum sociis natoque!!',
description:
diff --git a/src/sagas/__tests__/app-detail.ts b/src/sagas/__tests__/app-detail.ts
index f6e295b328..f3bbe567a2 100644
--- a/src/sagas/__tests__/app-detail.ts
+++ b/src/sagas/__tests__/app-detail.ts
@@ -1,62 +1,67 @@
import appDetailSagas, { appDetailDataFetch, appDetailDataListen } from '../app-detail'
import { appDetailDataStub } from '../__stubs__/app-detail'
import ActionTypes from '@/constants/action-types'
-import { put, takeLatest, all, fork, call, select } from '@redux-saga/core/effects'
-import { appDetailLoading, appDetailReceiveData, appDetailFailure } from '@/actions/app-detail'
+import { put, takeLatest, all, fork, call } from '@redux-saga/core/effects'
+import { appDetailLoading, appDetailReceiveData, appDetailFailure, AppDetailParams } from '@/actions/app-detail'
import { Action } from '@/types/core'
import fetcher from '@/utils/fetcher'
import { URLS, MARKETPLACE_HEADERS } from '@/constants/api'
import { cloneableGenerator } from '@redux-saga/testing-utils'
import { REAPIT_API_BASE_URL } from '../../constants/api'
-import { selectClientId } from '@/selector/client'
-import { errorThrownServer } from '@/actions/error'
-import errorMessages from '@/constants/error-messages'
jest.mock('../../utils/fetcher')
-const params = { data: '9b6fd5f7-2c15-483d-b925-01b650538e52' }
+const paramsClientId = { data: { id: '9b6fd5f7-2c15-483d-b925-01b650538e52', clientId: 'DAC' } }
+const params = { data: { id: '9b6fd5f7-2c15-483d-b925-01b650538e52' } }
-describe('app-detail fetch data', () => {
- const gen = cloneableGenerator(appDetailDataFetch)(params)
+describe('app-detail fetch data with clientId', () => {
+ const gen = cloneableGenerator(appDetailDataFetch)(paramsClientId)
expect(gen.next().value).toEqual(put(appDetailLoading(true)))
- expect(gen.next('1').value).toEqual(select(selectClientId))
- test('clientId not exist', () => {
+ expect(gen.next().value).toEqual(
+ call(fetcher, {
+ url: `${URLS.apps}/${paramsClientId.data.id}?clientId=${paramsClientId.data.clientId}`,
+ api: REAPIT_API_BASE_URL,
+ method: 'GET',
+ headers: MARKETPLACE_HEADERS
+ })
+ )
+
+ test('api call success', () => {
const clone = gen.clone()
- expect(clone.next().value).toEqual(
- put(
- errorThrownServer({
- type: 'SERVER',
- message: errorMessages.DEFAULT_SERVER_ERROR
- })
- )
- )
+ expect(clone.next(appDetailDataStub.data).value).toEqual(put(appDetailReceiveData(appDetailDataStub)))
+ expect(clone.next().done).toBe(true)
})
+ test('api call fail', () => {
+ const clone = gen.clone()
+ expect(clone.next().value).toEqual(put(appDetailFailure()))
+ expect(clone.next().done).toBe(true)
+ })
+})
+
+describe('app-detail fetch data without clientId', () => {
+ const gen = cloneableGenerator(appDetailDataFetch)(params)
+ expect(gen.next().value).toEqual(put(appDetailLoading(true)))
+
+ expect(gen.next().value).toEqual(
+ call(fetcher, {
+ url: `${URLS.apps}/${params.data.id}`,
+ api: REAPIT_API_BASE_URL,
+ method: 'GET',
+ headers: MARKETPLACE_HEADERS
+ })
+ )
+
test('api call success', () => {
const clone = gen.clone()
- expect(clone.next(1).value).toEqual(
- call(fetcher, {
- url: `${URLS.apps}/${params.data}?clientId=1`,
- api: REAPIT_API_BASE_URL,
- method: 'GET',
- headers: MARKETPLACE_HEADERS
- })
- )
expect(clone.next(appDetailDataStub.data).value).toEqual(put(appDetailReceiveData(appDetailDataStub)))
expect(clone.next().done).toBe(true)
})
test('api call fail', () => {
const clone = gen.clone()
- expect(clone.next(undefined).value).toEqual(
- put(
- errorThrownServer({
- type: 'SERVER',
- message: errorMessages.DEFAULT_SERVER_ERROR
- })
- )
- )
+ expect(clone.next().value).toEqual(put(appDetailFailure()))
expect(clone.next().done).toBe(true)
})
})
@@ -66,7 +71,7 @@ describe('app-detail thunks', () => {
it('should trigger request data when called', () => {
const gen = appDetailDataListen()
expect(gen.next().value).toEqual(
- takeLatest>(ActionTypes.APP_DETAIL_REQUEST_DATA, appDetailDataFetch)
+ takeLatest>(ActionTypes.APP_DETAIL_REQUEST_DATA, appDetailDataFetch)
)
expect(gen.next().done).toBe(true)
})
diff --git a/src/sagas/app-detail.ts b/src/sagas/app-detail.ts
index a51d35738f..f1f7c40914 100644
--- a/src/sagas/app-detail.ts
+++ b/src/sagas/app-detail.ts
@@ -1,23 +1,18 @@
import fetcher from '../utils/fetcher'
import { URLS, MARKETPLACE_HEADERS, REAPIT_API_BASE_URL } from '../constants/api'
-import { appDetailLoading, appDetailReceiveData, appDetailFailure } from '../actions/app-detail'
+import { appDetailLoading, appDetailReceiveData, appDetailFailure, AppDetailParams } from '../actions/app-detail'
import { put, call, fork, takeLatest, all, select } from '@redux-saga/core/effects'
import ActionTypes from '../constants/action-types'
import { errorThrownServer } from '../actions/error'
import errorMessages from '../constants/error-messages'
import { Action } from '@/types/core'
-import { selectClientId } from '@/selector/client'
-export const appDetailDataFetch = function*({ data: id }) {
+export const appDetailDataFetch = function*({ data }) {
+ const { id, clientId } = data
yield put(appDetailLoading(true))
try {
- const clientId = yield select(selectClientId)
-
- if (!clientId) {
- throw new Error('Client id does not exist in state')
- }
const response = yield call(fetcher, {
- url: `${URLS.apps}/${id}?clientId=${clientId}`,
+ url: clientId ? `${URLS.apps}/${id}?clientId=${clientId}` : `${URLS.apps}/${id}`,
api: REAPIT_API_BASE_URL,
method: 'GET',
headers: MARKETPLACE_HEADERS
@@ -39,7 +34,7 @@ export const appDetailDataFetch = function*({ data: id }) {
}
export const appDetailDataListen = function*() {
- yield takeLatest>(ActionTypes.APP_DETAIL_REQUEST_DATA, appDetailDataFetch)
+ yield takeLatest>(ActionTypes.APP_DETAIL_REQUEST_DATA, appDetailDataFetch)
}
const appDetailSagas = function*() {
diff --git a/src/selector/client.ts b/src/selector/client.ts
index e686705c79..f414f3c396 100644
--- a/src/selector/client.ts
+++ b/src/selector/client.ts
@@ -2,7 +2,7 @@ import { ReduxState } from '@/types/core'
import { oc } from 'ts-optchain'
export const selectClientId = (state: ReduxState) => {
- return oc(state.auth.loginSession).loginIdentity.clientId()
+ return oc(state.auth.loginSession).loginIdentity.clientId('')
}
export const selectLoggedUserEmail = (state: ReduxState) => {