From 69ba306f0249f3b0a86ef59238abc6ee24fe8d35 Mon Sep 17 00:00:00 2001 From: Paolo Miguel de Leon Date: Tue, 13 Sep 2022 16:52:41 +0800 Subject: [PATCH] fix: history log for batch revoke --- machines/activityLog.ts | 6 +- machines/app.ts | 9 +- machines/revoke.ts | 383 +++++++++++++-------------- machines/store.ts | 5 +- screens/Home/HistoryTab.tsx | 2 +- screens/Home/ViewVcModal.tsx | 7 +- screens/Profile/Revoke.tsx | 42 +-- screens/Profile/RevokeController.tsx | 7 +- shared/request.ts | 2 - 9 files changed, 229 insertions(+), 234 deletions(-) diff --git a/machines/activityLog.ts b/machines/activityLog.ts index be298310b4..a0780ae3e0 100644 --- a/machines/activityLog.ts +++ b/machines/activityLog.ts @@ -12,7 +12,7 @@ const model = createModel( { events: { STORE_RESPONSE: (response: unknown) => ({ response }), - LOG_ACTIVITY: (log: ActivityLog) => ({ log }), + LOG_ACTIVITY: (log: ActivityLog | ActivityLog[]) => ({ log }), REFRESH: () => ({}), }, } @@ -93,7 +93,9 @@ export const activityLogMachine = prependActivity: model.assign({ activities: (context, event) => - [event.response, ...context.activities] as ActivityLog[], + (Array.isArray(event.response) + ? [...event.response, ...context.activities] + : [event.response, ...context.activities]) as ActivityLog[], }), }, } diff --git a/machines/app.ts b/machines/app.ts index b6c643a790..815b4bc78a 100644 --- a/machines/app.ts +++ b/machines/app.ts @@ -14,7 +14,7 @@ import { createVcMachine, vcMachine } from './vc'; import { createActivityLogMachine, activityLogMachine } from './activityLog'; import { createRequestMachine, requestMachine } from './request'; import { createScanMachine, scanMachine } from './scan'; -import { createRevoke, revokeVidsMachine } from './revoke'; +import { createRevokeMachine, revokeVidsMachine } from './revoke'; import { pure, respond } from 'xstate/lib/actions'; import { AppServices } from '../shared/GlobalContext'; @@ -174,15 +174,19 @@ export const appMachine = model.createMachine( const serviceRefs = { ...context.serviceRefs, }; + serviceRefs.auth = spawn( createAuthMachine(serviceRefs), authMachine.id ); + serviceRefs.vc = spawn(createVcMachine(serviceRefs), vcMachine.id); + serviceRefs.settings = spawn( createSettingsMachine(serviceRefs), settingsMachine.id ); + serviceRefs.activityLog = spawn( createActivityLogMachine(serviceRefs), activityLogMachine.id @@ -192,13 +196,14 @@ export const appMachine = model.createMachine( createScanMachine(serviceRefs), scanMachine.id ); + serviceRefs.request = spawn( createRequestMachine(serviceRefs), requestMachine.id ); serviceRefs.revoke = spawn( - createRevoke(serviceRefs), + createRevokeMachine(serviceRefs), revokeVidsMachine.id ); diff --git a/machines/revoke.ts b/machines/revoke.ts index b5cfbc1edf..07a9168aa5 100644 --- a/machines/revoke.ts +++ b/machines/revoke.ts @@ -1,12 +1,13 @@ import { TextInput } from 'react-native'; -import { assign, ErrorPlatformEvent, StateFrom, send } from 'xstate'; +import { assign, ErrorPlatformEvent, StateFrom, send, EventFrom } from 'xstate'; +import { log } from 'xstate/lib/actions'; + +import i18n from '../i18n'; import { AppServices } from '../shared/GlobalContext'; import { ActivityLogEvents } from '../machines/activityLog'; import { createModel } from 'xstate/lib/model'; import { request } from '../shared/request'; import { VcIdType } from '../types/vc'; -import i18n from '../i18n'; -import { log, pure } from 'xstate/lib/actions'; const model = createModel( { @@ -17,7 +18,7 @@ const model = createModel( otpError: '', transactionId: '', requestId: '', - VIDs: [], + VIDs: [] as string[], }, { events: { @@ -31,237 +32,217 @@ const model = createModel( } ); -export const revokeVidsMachine = -/** @xstate-layout N4IgpgJg5mDOIC5QCUwDcD2BrMA1AlhLAHSEA2YAxMgKK4DyA0jQPq4DCAyoqAA4ax8AF3wYAdjxAAPRACYAHAFZiARhUB2WQDZFABgCcO-boDMWgDQgAnohWyALLOK6X9k-J3zZirfYC+fpaomDgERKRiaACGZISUAJIAcgAKAKoAKiz06cmS-IIi4pIyCOq6TuomjvYqWlpe3iaWNgiKik4u5fK69vr2uipmAUHo2HiEJPiRMXEAIvGcALIL3Egg+cKiEmslZRVVDrX1so3Nclr6zi61pvLyKoY9wyDBY2EkUQDGn2C8ImJQXDxWaTCAUah0JisDirPgCTZFHaIEyydTEe6yEyaQzqXrtM4IeQmXRXcrqDz9dQPEzPV6hCbEL4-P5TQHAkgAJzAAEcAK5wf5QehCXiUCDiMAREKSunjcJM36CoEg4hcvkC1nC3gIKaYT5RQpiADaugAunl4Ybioh8dZbHtiHoevpFESlIp9DTAi9RvT5d9FazlZyefzYIKtZQwByORgOcReGQDQAzOMAW2IsvejIDLIBwdVoY1AK1OsiGH1hpN5rWGytSIQJix6LsWNkOLxsgJKLRnVMlXJdxq-m9WYZCrzbJBlHmSxWFoKW2tjdRLcx2K0uJdXbtCGMpN03Qe-X03VktN9co+uYjIviYl4vKECRSGSyOQXCO2oBKKkPaNdRQqXsRRBi0EwXQJep5EdFxURdTczB0C9pWzCdb14e9H2fWdlk4WF1ktJcGwUZQ1E0HQDCMUwLF3NQ+lJVFN3kXFvBQt4GTVMNBVlMUJSlMZM0vbMuOLKBZTLPUDS2atP3rH9bH-R0lGA0CzAgxQCRqS4nQ0F0-10SoRxGVDOKLcNWV46NY3jRMU3TITTPCUSLIBCTdQraTxFk2siMRBTSh3FpBhdWCeiAqkiSA4yfSckMQiDT4ZwWPCCLrYiAq8AlBgeYg6jKLF1DaB5ygCb0xAwCA4EkMdwnIMA5Iy6RbEMPKNGJSo2nuD17Gyh5lDqC59HUTwVBqL0TI4urpliCBiAwEVGv85rWipZx5GGxRiV8GjNN3BQALg8bMS0citHYv1JhmwhiAAIy+HAxAgJbvxWto0T6doTlkFwtBOFRuzaMK1G6TdqWGi6rylGZnt8xdlpKLaTGIT7vp+3Q-tkAH9pUZRdNOux9KxGLauvZklXZUgwQauGv2XH7LhMOwai+zcfvkXrd3sHQwsPal7C8WpFEhtCbyDSmXIwl7l00btud5twLj-Op5BF8cxfzdlpYbAXspYhW9iG3w1f9cnNTvB8n21gKBbROpHEAhn20UTngp+lRiCx7nN1uAxMRNkN1Vc8TL2tlaiTRQZtBMCKBe+gkXZJJ1tB6ECBf0FQA8LBL80+MPf3bGC7lRDHT1TptuzuMLDhRMwzoD-O5Fd2wYOuVF-raXx7HUMq-CAA */ -model.createMachine( - { - context: model.initialContext, - tsTypes: {} as import('./revoke.typegen').Typegen0, - id: 'RevokeVids', - initial: 'acceptingVIDs', - states: { - idle: { - on: { - REVOKE_VCS: { - actions: ['setTransactionId', 'clearOtp'], - target: 'acceptingOtpInput', - }, +export const revokeVidsMachine = + /** @xstate-layout N4IgpgJg5mDOIC5QCUwDcD2BrMA1AlhLAHSEA2YAxMgKK4DyA0jQPq4DCAyoqAA4ax8AF3wYAdjxAAPRACYAHAFZiARhUB2WQDZFABgCcO-boDMWgDQgAnohWyALLOK6X9k-J3zZirfYC+fpaomDgERKRiaACGZISUAJIAcgAKAKoAKiz06cmS-IIi4pIyCOq6TuomjvYqWlpe3iaWNgiKik4u5fK69vr2uipmAUHo2HiEJPiRMXEAIvGcALIL3Egg+cKiEmslZRVVDrX1so3Nclr6zi61pvLyKoY9wyDBY2EkUQDGn2C8ImJQXDxWaTCAUah0JisDirPgCTZFHaIEyydTEe6yEyaQzqXrtM4IeQmXRXcrqDz9dQPEzPV6hCbEL4-P5TQHAkgAJzAAEcAK5wf5QehCXiUCDiMAREKSunjcJM36CoEg4hcvkC1nC3gIKaYT5RQpiADaugAunl4Ybioh8dZbHtiHoevpFESlIp9DTAi9RvT5d9FazlZyefzYIKtZQwByORgOcReGQDQAzOMAW2IsvejIDLIBwdVoY1AK1OsiGH1hpN5rWGytSIQJix6LsWNkOLxsgJKLRnVMlXJdxq-m9WYZCrzbJBlHmSxWFoKW2tjdRLcx2K0uJdXbtCGMpN03Qe-X03VktN9co+uYjIviYl4vKECRSGSyOQXCO2oBKKkPaNdRQqXsRRBi0EwXQJep5EdFxURdTczB0C9pWzCdb14e9H2fWdlk4WF1ktJcGwUZQ1E0HQDCMUwLF3NQ+lJVFN3kXFvBQt4GTVMNBVlMUJSlMZM0vbMuOLKBZTLPUDS2atP3rH9bH-R0lGA0CzAgxQCRqS4nQ0F0-10SoRxGVDOKLcNWV46NY3jRMU3TITTPCUSLIBCTdQraTxFk2siMRBTSh3FpBhdWCeiAqkiSA4yfSckMQiDT4ZwWPCCLrYiAq8AlBgeYg6jKLF1DaB5ygCb0xAwCA4EkMdwnIMA5Iy6RbEMPKNGJSo2nuD17Gyh5lDqC59HUTwVBqL0TI4urpliCBiAwEVGv85rWipZx5GGxRiV8GjNN3BQALg8bMS0citHYv1JhmwhiAAIy+HAxAgJbvxWto0T6doTlkFwtBOFRuzaMK1G6TdqWGi6rylGZnt8xdlpKLaTGIT7vp+3Q-tkAH9pUZRdNOux9KxGLauvZklXZUgwQauGv2XH7LhMOwai+zcfvkXrd3sHQwsPal7C8WpFEhtCbyDSmXIwl7l00btud5twLj-Op5BF8cxfzdlpYbAXspYhW9iG3w1f9cnNTvB8n21gKBbROpHEAhn20UTngp+lRiCx7nN1uAxMRNkN1Vc8TL2tlaiTRQZtBMCKBe+gkXZJJ1tB6ECBf0FQA8LBL80+MPf3bGC7lRDHT1TptuzuMLDhRMwzoD-O5Fd2wYOuVF-raXx7HUMq-CAA */ + model.createMachine( + { + tsTypes: {} as import('./revoke.typegen').Typegen0, + schema: { + context: model.initialContext, + events: {} as EventFrom, }, - }, - invalid: { - states: { - otp: {}, - backend: {}, - }, - on: { - INPUT_OTP: { - actions: 'setOtp', - target: 'requestingRevoke', - }, - DISMISS: { - target: 'idle', - }, - }, - }, - acceptingVIDs: { - entry: ['setTransactionId', 'clearOtp'], - initial: 'idle', + id: 'RevokeVids', + initial: 'acceptingVIDs', states: { idle: { on: { REVOKE_VCS: { - actions: 'setVIDs', - target: '#RevokeVids.acceptingOtpInput', + actions: ['setTransactionId', 'clearOtp'], + target: 'acceptingOtpInput', }, }, }, - requestingOtp: { - invoke: { - src: 'requestOtp', - onDone: [ - { - actions: log('accepting OTP'), - target: '#RevokeVids.acceptingOtpInput', + invalid: { + states: { + otp: {}, + backend: {}, + }, + on: { + INPUT_OTP: { + actions: 'setOtp', + target: 'requestingRevoke', + }, + DISMISS: { + target: 'idle', + }, + }, + }, + acceptingVIDs: { + entry: ['setTransactionId', 'clearOtp'], + initial: 'idle', + states: { + idle: { + on: { + REVOKE_VCS: { + actions: 'setVIDs', + target: '#RevokeVids.acceptingOtpInput', + }, }, - ], - onError: [ - { - actions: [log('error OTP'), 'setIdBackendError'], - target: '#RevokeVids.invalid.backend', + }, + requestingOtp: { + invoke: { + src: 'requestOtp', + onDone: [ + { + actions: log('accepting OTP'), + target: '#RevokeVids.acceptingOtpInput', + }, + ], + onError: [ + { + actions: [log('error OTP'), 'setIdBackendError'], + target: '#RevokeVids.invalid.backend', + }, + ], }, - ], + }, + }, + on: { + DISMISS: { + target: 'idle', + }, }, }, - }, - on: { - DISMISS: { - target: 'idle', - }, - }, - }, - acceptingOtpInput: { - entry: 'clearOtp', - on: { - INPUT_OTP: { - actions: 'setOtp', - target: 'requestingRevoke', - }, - DISMISS: { - target: 'idle', + acceptingOtpInput: { + entry: 'clearOtp', + on: { + INPUT_OTP: { + actions: 'setOtp', + target: 'requestingRevoke', + }, + DISMISS: { + target: 'idle', + }, + }, }, - }, - }, - requestingRevoke: { - invoke: { - src: 'requestRevoke', - onDone: [ - { - target: 'revokingVc', + requestingRevoke: { + invoke: { + src: 'requestRevoke', + onDone: { + target: 'revokingVc', + }, + onError: { + actions: [log('error on Revoking'), 'setOtpError'], + target: 'acceptingOtpInput', + }, }, - ], - onError: [ - { - actions: [log('error on Revoking'), 'setOtpError'], - target: 'acceptingOtpInput', + }, + revokingVc: { + entry: 'logRevoked', + on: { + DISMISS: { + target: 'idle', + }, }, - ], - }, - }, - revokingVc: { - entry: 'logRevoked', - on: { - DISMISS: { - target: 'idle', }, }, }, - }, -}, - { - actions: { - setOtp: model.assign({ - otp: (_context, event) => event.otp, - }), + { + actions: { + setOtp: model.assign({ + otp: (_context, event) => event.otp, + }), - setTransactionId: assign({ - transactionId: () => String(new Date().valueOf()).substring(3, 13), - }), + setTransactionId: assign({ + transactionId: () => String(new Date().valueOf()).substring(3, 13), + }), - setVIDs: model.assign({ - VIDs: (_context, event) => event.vcKeys, - }), - - setIdBackendError: assign({ - idError: (context, event) => { - const message = (event as ErrorPlatformEvent).data.message; - const ID_ERRORS_MAP = { - 'UIN invalid': 'invalidUin', - 'VID invalid': 'invalidVid', - 'UIN not available in database': 'missingUin', - 'VID not available in database': 'missingVid', - 'Invalid Input Parameter - individualId': - context.idType === 'UIN' ? 'invalidUin' : 'invalidVid', - }; - return ID_ERRORS_MAP[message] - ? i18n.t(`errors.backend.${ID_ERRORS_MAP[message]}`, { - ns: 'RevokeVids', - }) - : message; - }, - }), + setVIDs: model.assign({ + VIDs: (_context, event) => event.vcKeys, + }), - setOtpError: assign({ - otpError: (_context, event) => { - const message = (event as ErrorPlatformEvent).data.message; - const OTP_ERRORS_MAP = { - 'OTP is invalid': 'invalidOtp', - }; - return OTP_ERRORS_MAP[message] - ? i18n.t(`errors.backend.${OTP_ERRORS_MAP[message]}`, { - ns: 'RevokeVids', - }) - : message; - }, - }), + setIdBackendError: assign({ + idError: (context, event) => { + const message = (event as ErrorPlatformEvent).data.message; + const ID_ERRORS_MAP = { + 'UIN invalid': 'invalidUin', + 'VID invalid': 'invalidVid', + 'UIN not available in database': 'missingUin', + 'VID not available in database': 'missingVid', + 'Invalid Input Parameter - individualId': + context.idType === 'UIN' ? 'invalidUin' : 'invalidVid', + }; + return ID_ERRORS_MAP[message] + ? i18n.t(`errors.backend.${ID_ERRORS_MAP[message]}`, { + ns: 'RevokeVids', + }) + : message; + }, + }), - clearOtp: assign({ otp: '' }), + setOtpError: assign({ + otpError: (_context, event) => { + const message = (event as ErrorPlatformEvent).data.message; + const OTP_ERRORS_MAP = { + 'OTP is invalid': 'invalidOtp', + }; + return OTP_ERRORS_MAP[message] + ? i18n.t(`errors.backend.${OTP_ERRORS_MAP[message]}`, { + ns: 'RevokeVids', + }) + : message; + }, + }), - // logRevoked: pure((context) => - // context.VIDs.map((vc) => - // send( - // ActivityLogEvents.LOG_ACTIVITY({ - // _vcKey: vc, - // action: 'revoked', - // timestamp: Date.now(), - // deviceName: '', - // vcLabel: vc.split(':')[2], - // }), - // { to: () => context.serviceRefs.activityLog } - // ) - // ) - // ), + clearOtp: assign({ otp: '' }), - logRevoked: pure( - (context) => context.VIDs.map( - (vc) => send( - () => - ActivityLogEvents.LOG_ACTIVITY({ + logRevoked: send( + (context) => + ActivityLogEvents.LOG_ACTIVITY( + context.VIDs.map((vc) => ({ _vcKey: vc, action: 'revoked', timestamp: Date.now(), deviceName: '', vcLabel: vc.split(':')[2], - }), - { - to: (_context) => _context.serviceRefs.activityLog, - } - ) - ) - ), - - }, - - services: { - requestOtp: async (context) => { - const transactionId = String(new Date().valueOf()).substring(3, 13); - return request('POST', '/req/otp', { - individualId: context.VIDs[0].split(':')[2], - individualIdType: 'VID', - otpChannel: ['EMAIL', 'PHONE'], - transactionID: transactionId, - }); + })) + ), + { + to: (context) => context.serviceRefs.activityLog, + } + ), }, - requestRevoke: async (context) => { - console.log('context.otp', context.otp) - try { - return await Promise.all( - context.VIDs.map((vid: string) => { - const vidID = vid.split(':')[2]; - const transactionId = String(new Date().valueOf()).substring( - 3, - 13 - ); - return request('PATCH', `/vid/${vidID}`, { - transactionID: transactionId, - vidStatus: 'REVOKED', - individualId: vidID, - individualIdType: 'VID', - otp: context.otp, - }); - }) - ); - } catch (error) { - console.error(error); - } + services: { + requestOtp: async (context) => { + const transactionId = String(new Date().valueOf()).substring(3, 13); + return request('POST', '/req/otp', { + individualId: context.VIDs[0].split(':')[2], + individualIdType: 'VID', + otpChannel: ['EMAIL', 'PHONE'], + transactionID: transactionId, + }); + }, + + requestRevoke: async (context) => { + try { + return await Promise.all( + context.VIDs.map((vid: string) => { + const vidID = vid.split(':')[2]; + const transactionId = String(new Date().valueOf()).substring( + 3, + 13 + ); + return request('PATCH', `/vid/${vidID}`, { + transactionID: transactionId, + vidStatus: 'REVOKED', + individualId: vidID, + individualIdType: 'VID', + otp: context.otp, + }); + }) + ); + } catch (error) { + console.error(error); + } + }, }, - }, - guards: {}, - } -); + guards: {}, + } + ); -export function createRevoke(serviceRefs: AppServices) { +export function createRevokeMachine(serviceRefs: AppServices) { return revokeVidsMachine.withContext({ ...revokeVidsMachine.context, serviceRefs, diff --git a/machines/store.ts b/machines/store.ts index ff5a2ba9f9..29b2318633 100644 --- a/machines/store.ts +++ b/machines/store.ts @@ -310,8 +310,11 @@ export async function prependItem( ) { try { const list = await getItem(key, [], encryptionKey); + const newList = Array.isArray(value) + ? [...value, ...list] + : [value, ...list]; - await setItem(key, [value, ...list], encryptionKey); + await setItem(key, newList, encryptionKey); } catch (e) { console.error('error prependItem:', e); throw e; diff --git a/screens/Home/HistoryTab.tsx b/screens/Home/HistoryTab.tsx index e6e84748c5..cbbe2d0130 100644 --- a/screens/Home/HistoryTab.tsx +++ b/screens/Home/HistoryTab.tsx @@ -38,7 +38,7 @@ export const HistoryTab: React.FC = (props) => { }> {controller.activities.map((activity) => ( diff --git a/screens/Home/ViewVcModal.tsx b/screens/Home/ViewVcModal.tsx index 3289e03600..fa4b5915bf 100644 --- a/screens/Home/ViewVcModal.tsx +++ b/screens/Home/ViewVcModal.tsx @@ -10,7 +10,7 @@ import { VcDetails } from '../../components/VcDetails'; import { MessageOverlay } from '../../components/MessageOverlay'; import { ToastItem } from '../../components/ui/ToastItem'; import { Passcode } from '../../components/Passcode'; -import { OtpVerificationModal } from './MyVcs/OtpVerificationModal'; +// import { OtpVerificationModal } from './MyVcs/OtpVerificationModal'; import { OIDcAuthenticationModal } from '../../components/OIDcAuth'; import { useViewVcModal, ViewVcModalProps } from './ViewVcModalController'; import { useTranslation } from 'react-i18next'; @@ -58,11 +58,10 @@ export const ViewVcModal: React.FC = (props) => { isVisible={controller.isAcceptingOtpInput} onDismiss={controller.DISMISS} onVerify={() => { - console.log('onVerify') - controller.revokeVc('111111') + controller.revokeVc('111111'); }} error={controller.otpError} - /> + /> {/* = (props) => { const controller = useRevoke(); const { t } = useTranslation('ProfileScreen'); - console.log('isAcceptingOtpInput', controller.isAcceptingOtpInput) + const styles = StyleSheet.create({ buttonContainer: { position: 'absolute', @@ -25,8 +25,8 @@ export const Revoke: React.FC = (props) => { width: Dimensions.get('screen').width, }, revokeView: { padding: 20 }, - flexRow: {flexDirection: 'row', margin: 0, padding: 0}, - rowStyle: { flexDirection: 'column', justifyContent: 'space-between'} + flexRow: { flexDirection: 'row', margin: 0, padding: 0 }, + rowStyle: { flexDirection: 'column', justifyContent: 'space-between' }, }); return ( @@ -54,7 +54,9 @@ export const Revoke: React.FC = (props) => { - {controller.toastVisible && } + {controller.toastVisible && ( + + )} {controller.vidKeys.length > 0 && ( = (props) => { isVisible={controller.isAuthenticating} onDismiss={() => controller.setAuthenticating(false)} onVerify={() => { - controller.setAuthenticating(false) - controller.setIsViewing(true) + controller.setAuthenticating(false); + controller.setIsViewing(true); }} /> { - controller.revokeVc('111111') + controller.revokeVc('111111'); }} /> = (props) => { {controller.selectedVidKeys.map((vcKey, index) => ( - {'\u2022'} - {vcKey.split(":")[2]} + + {'\u2022'} + + + {vcKey.split(':')[2]} + ))} - - {t('revokingVidsAfter')} - + {t('revokingVidsAfter')}