Skip to content

Commit

Permalink
Merge pull request #14 from amarlankri/feat/handle-iframe-mode
Browse files Browse the repository at this point in the history
feat: handle iframe mode
  • Loading branch information
meriamBenSassi authored Aug 6, 2021
2 parents 49bd0b8 + 9b7048d commit 9fd18e5
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 15 deletions.
3 changes: 2 additions & 1 deletion json-server/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"id": "5f58a224aa55a30021bc7b01",
"customIdentifier": "client_unique_identifier",
"aggregationDetails": {
"callbackUrl": "http://localhost:4000/callback"
"callbackUrl": "http://localhost:4000/callback",
"mode": "REDIRECT"
}
}
],
Expand Down
1 change: 1 addition & 0 deletions src/algoan/dto/customer.enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
export enum AggregationDetailsMode {
redirect = 'REDIRECT',
api = 'API',
iframe = 'IFRAME',
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/algoan/dto/customer.inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface AggregationDetailsUpdateInput {
token?: string;
redirectUrl?: string;
apiUrl?: string;
iframeUrl?: string;
userId?: string;
clientId?: string;
}
1 change: 1 addition & 0 deletions src/algoan/dto/customer.objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface AggregationDetails {
mode?: AggregationDetailsMode;
redirectUrl?: string;
apiUrl?: string;
iframeUrl?: string;
userId?: string;
clientId?: string;
}
Expand Down
36 changes: 33 additions & 3 deletions src/hooks/services/hooks.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { TINK_LINK_ACTOR_CLIENT_ID } from '../../tink/contstants/tink.constants'

import { bankDetailsRequiredMock } from '../dto/bank-details-required-payload.dto.mock';
import { mapTinkDataToAlgoanAnalysis } from '../mappers/analysis.mapper';
import { AggregationDetailsMode } from '../../algoan/dto/customer.enums';
import { HooksService } from './hooks.service';

describe('HookService', () => {
Expand Down Expand Up @@ -161,7 +162,7 @@ describe('HookService', () => {
);
});

it('should do these steps if pricing STANDARD', async () => {
it('should do these steps if pricing STANDARD (redirect mode)', async () => {
serviceAccountConfigMock.pricing = ClientPricing.STANDARD;
await hookService.handleAggregatorLinkRequiredEvent(aggregatorLinkRequiredMock);

Expand Down Expand Up @@ -192,7 +193,7 @@ describe('HookService', () => {
});
});

it('should do these steps if pricing PREMIUM WITHOUT an existing tink user', async () => {
it('should do these steps if pricing PREMIUM WITHOUT an existing tink user (redirect mode)', async () => {
serviceAccountConfigMock.pricing = ClientPricing.PREMIUM;
await hookService.handleAggregatorLinkRequiredEvent(aggregatorLinkRequiredMock);

Expand Down Expand Up @@ -235,7 +236,7 @@ describe('HookService', () => {
});
});

it('should do these steps if pricing PREMIUM WITH an existing tink user', async () => {
it('should do these steps if pricing PREMIUM WITH an existing tink user (redirect mode)', async () => {
// mock to return an existing userId
getCustomerByIdSpy = jest.spyOn(algoanCustomerService, 'getCustomerById').mockResolvedValue({
...customerMock,
Expand Down Expand Up @@ -279,6 +280,35 @@ describe('HookService', () => {
},
});
});

it('should generate an iframe link and should update the customer with the new iframe URL', async () => {
getCustomerByIdSpy = jest.spyOn(algoanCustomerService, 'getCustomerById').mockResolvedValue({
...customerMock,
aggregationDetails: {
...customerMock.aggregationDetails,
mode: AggregationDetailsMode.iframe,
},
});
await hookService.handleAggregatorLinkRequiredEvent(aggregatorLinkRequiredMock);
expect(getLinkSpy).toHaveBeenCalledWith({
client_id: serviceAccountConfigMock.clientId,
redirect_uri: customerMock.aggregationDetails.callbackUrl,
market: serviceAccountConfigMock.market,
locale: serviceAccountConfigMock.locale,
scope: 'accounts:read,transactions:read,credentials:read',
test: config.tink.test,
authorization_code: createAuthorizationObjectMock.code,
iframe: true,
});

// update
expect(updateCustomerSpy).toHaveBeenCalledWith(aggregatorLinkRequiredMock.customerId, {
aggregationDetails: {
iframeUrl: 'MY_LINK_URL',
userId: createUserObject.user_id,
},
});
});
});

describe('handleBankDetailsRequiredEvent', () => {
Expand Down
58 changes: 47 additions & 11 deletions src/hooks/services/hooks.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { ServiceAccount } from '@algoan/rest';
import { Injectable, Inject } from '@nestjs/common';
import { Config } from 'node-config-ts';

import { AggregationDetailsMode } from '../../algoan/dto/customer.enums';
import { CustomerUpdateInput } from '../../algoan/dto/customer.inputs';
import { assertsTypeValidation } from '../../shared/utils/common.utils';
import { TinkAccountObject } from '../../tink/dto/account.objects';
import { TinkAccountService } from '../../tink/services/tink-account.service';
Expand All @@ -27,6 +29,7 @@ import { AlgoanHttpService } from '../../algoan/services/algoan-http.service';
import { AggregatorLinkRequiredDTO } from '../dto/aggregator-link-required-payload.dto';
import { BankDetailsRequiredDTO } from '../dto/bank-details-required-payload.dto';
import { mapTinkDataToAlgoanAnalysis } from '../mappers/analysis.mapper';
import { AccountCheckArgs } from '../../tink/dto/account-check.args';

/**
* Hook service
Expand Down Expand Up @@ -107,8 +110,34 @@ export class HooksService {
});
}

// Generate the link
const redirectUrl: string = this.tinkLinkService.getAuthorizeLink({
const linkData: CustomerUpdateInput['aggregationDetails'] = this.generateLinkDataFromAggregationMode(
customer.aggregationDetails.mode,
{ clientConfig, callbackUrl, authorizationCode },
);

// Update user with redirect link information and userId if provided
await this.algoanCustomerService.updateCustomer(payload.customerId, {
aggregationDetails: {
...linkData,
userId: tinkUserId,
},
});

return;
}

/**
* Returns the correct link according to the aggregation mode.
* @param mode aggregation mode
* @param data the input data used for to generate the link data
* @returns
*/
private generateLinkDataFromAggregationMode(
mode: AggregationDetailsMode | undefined,
data: { clientConfig: ClientConfig; callbackUrl: string; authorizationCode?: string },
): CustomerUpdateInput['aggregationDetails'] {
const { clientConfig, callbackUrl, authorizationCode } = data;
const sharedLinkParameters: AccountCheckArgs = {
client_id: clientConfig.clientId,
redirect_uri: callbackUrl,
market: clientConfig.market,
Expand All @@ -120,17 +149,24 @@ export class HooksService {
'credentials:read', // To list providers: https://docs.tink.com/api#provider-list-providers-required-scopes-
].join(','),
authorization_code: authorizationCode,
});
};

// Update user with redirect link information and userId if provided
await this.algoanCustomerService.updateCustomer(payload.customerId, {
aggregationDetails: {
redirectUrl,
userId: tinkUserId,
},
});
switch (mode) {
case AggregationDetailsMode.redirect:
const redirectUrl: string | undefined = this.tinkLinkService.getAuthorizeLink(sharedLinkParameters);

return;
return { redirectUrl };
case AggregationDetailsMode.iframe:
const iframeUrl: string | undefined = this.tinkLinkService.getAuthorizeLink({
...sharedLinkParameters,
iframe: true,
});

return { iframeUrl };

default:
throw new Error(`Invalid bank connection mode ${mode}`);
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/tink/dto/account-check.args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export interface AccountCheckArgs {
scope?: string;
test: boolean;
authorization_code?: string;
iframe?: boolean;
}

0 comments on commit 9fd18e5

Please sign in to comment.