From c0e26cdc4b92ab70e12af814cb5524d3365ea22e Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Sat, 19 Feb 2022 13:15:59 +0100 Subject: [PATCH] fix(core): update --- readme.md | 4 ++ test/test.nonci.ts | 8 ++- ts/tink.classes.tinkaccount.ts | 21 ++++++- ts/tink.classes.tinkproviderconsent.ts | 30 ++++++++++ ts/tink.classes.tinkuser.ts | 76 ++++++++++++++++++++++++-- 5 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 ts/tink.classes.tinkproviderconsent.ts diff --git a/readme.md b/readme.md index 4da2ecf..eafe458 100644 --- a/readme.md +++ b/readme.md @@ -27,6 +27,10 @@ Platform support | [![Supports Windows 10](https://badgen.net/badge/supports%20W Use TypeScript for best inclass intellisense +```typescript +import * as tink from '@mojoio/tink'; + + ## Contribution We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :) diff --git a/test/test.nonci.ts b/test/test.nonci.ts index eaeab3d..650ec05 100644 --- a/test/test.nonci.ts +++ b/test/test.nonci.ts @@ -20,13 +20,15 @@ tap.test('should report tink as healthy', async () => { }); tap.test('should create a valid request', async (toolsArg) => { - const tinkuser = await tinkTestAccount.createTinkUser("user_1234_abc"); - console.log(await tinkuser.getTinkLink()); - process.exit(0); + const tinkuser: tink.TinkUser = await tinkTestAccount.getTinkUser("user_1234_abc"); + console.log(tinkuser); + console.log(await tinkuser.getTinkLinkForMarket()); // defaults to 'DE'; + console.log(await tinkuser.getProviderConsents()) await toolsArg.delayFor(10000); }) tap.test('should delete existing users', async () => { + process.exit(0); expect(tinkTestAccount).toBeInstanceOf(tink.TinkAccount); const tinkUser = new tink.TinkUser(tinkTestAccount, null, 'user_1234_abc'); await tinkUser.delete(); diff --git a/ts/tink.classes.tinkaccount.ts b/ts/tink.classes.tinkaccount.ts index 010a985..9ea5713 100644 --- a/ts/tink.classes.tinkaccount.ts +++ b/ts/tink.classes.tinkaccount.ts @@ -16,7 +16,9 @@ export class TinkAccount { public async getTinkHealthyBoolean(): Promise { const response = await plugins.smartrequest.request( 'https://api.tink.com/api/v1/monitoring/healthy', - {} + { + keepAlive: false, + } ); return response.body === 'ok'; } @@ -25,7 +27,9 @@ export class TinkAccount { // lets get an accessToken for the request const response = await plugins.smartrequest.postFormDataUrlEncoded( `${this.apiBaseUrl}/api/v1/oauth/token`, - {}, + { + keepAlive: false, + }, [ { key: 'client_id', @@ -46,6 +50,8 @@ export class TinkAccount { ] ); if (response.statusCode !== 200) { + console.log(response.statusCode); + console.log(response.body); throw new Error('there was an error aquiring an access token.'); } const clientAccessToken = response.body.access_token; @@ -61,6 +67,7 @@ export class TinkAccount { const response = await plugins.smartrequest.postFormDataUrlEncoded( `${this.apiBaseUrl}/api/v1/oauth/authorization-grant/delegate`, { + keepAlive: false, headers: { Authorization: `Bearer ${accessToken}`, }, @@ -101,7 +108,9 @@ export class TinkAccount { const accessToken = await this.getClientAccessTokenForScope('authorization:grant'); const response = await plugins.smartrequest.postFormDataUrlEncoded( `${this.apiBaseUrl}/api/v1/oauth/token`, - {}, + { + keepAlive: false, + }, [ { key: 'code', @@ -145,6 +154,7 @@ export class TinkAccount { console.log('tink is healthy, continuing...'); } const response = await plugins.smartrequest.request(`${this.apiBaseUrl}${optionsArg.urlArg}`, { + keepAlive: false, headers: { Authorization: `Bearer ${optionsArg.accessToken}`, 'Content-Type': 'application/json', @@ -156,6 +166,11 @@ export class TinkAccount { return response.body; } + public async getTinkUser(externalUserIdArg: string) { + const tinkuser = await TinkUser.getTinkUser(this, externalUserIdArg); + return tinkuser; + } + public async createTinkUser(externalUserIdArg: string) { const tinkuser = await TinkUser.createNewTinkUser(this, externalUserIdArg); return tinkuser; diff --git a/ts/tink.classes.tinkproviderconsent.ts b/ts/tink.classes.tinkproviderconsent.ts new file mode 100644 index 0000000..3b90a9e --- /dev/null +++ b/ts/tink.classes.tinkproviderconsent.ts @@ -0,0 +1,30 @@ +import * as plugins from './tink.plugins'; + +import { TinkUser } from './tink.classes.tinkuser'; + +/** + * a provider consent maps to tinks bank consents + */ +export class TinkProviderConsent { + // STATIC + public static async getProviderConsentsForUser(tinkUserRefArg: TinkUser) { + const returnProviderConsents: TinkProviderConsent[] = []; + const authorizationCode = await tinkUserRefArg.tinkAccountRef.getUserAuthorizationCode( + tinkUserRefArg.externalUserIdArg, + tinkUserRefArg.tinkAccountRef.clientId, + 'accounts:read,balances:read,transactions:read,provider-consents:read' + ); + const accessToken = await tinkUserRefArg.tinkAccountRef.getUserAccessToken(authorizationCode); + const responseData = await tinkUserRefArg.tinkAccountRef.request({ + urlArg: '/api/v1/provider-consents', + accessToken, + methodArg: 'GET', + payloadArg: null + }) + console.log(responseData); + return returnProviderConsents; + } + + // INSTANCE + constructor(tinkUserRefArg: TinkUser) {} +} diff --git a/ts/tink.classes.tinkuser.ts b/ts/tink.classes.tinkuser.ts index b8b4278..a635744 100644 --- a/ts/tink.classes.tinkuser.ts +++ b/ts/tink.classes.tinkuser.ts @@ -1,12 +1,16 @@ import * as plugins from './tink.plugins'; import { TinkAccount } from './tink.classes.tinkaccount'; +import { TinkProviderConsent } from './tink.classes.tinkproviderconsent'; export class TinkUser { // STATIC - public static async createNewTinkUser(tinkaccountArg: TinkAccount, externalUserIdArg: string) { - const accessToken = await tinkaccountArg.getClientAccessTokenForScope('user:create'); - const response = await tinkaccountArg.request({ + public static async createNewTinkUser(tinkAccountArg: TinkAccount, externalUserIdArg: string) { + const accessToken = await tinkAccountArg.getClientAccessTokenForScope('user:create'); + const responseData: { + external_user_id: string; + user_id: string; + } = await tinkAccountArg.request({ urlArg: '/api/v1/user/create', accessToken, methodArg: 'POST', @@ -17,7 +21,57 @@ export class TinkUser { }, }); - const newTinkUser = new TinkUser(tinkaccountArg, response.user_id, response.external_user_id); + const newTinkUser = await TinkUser.getTinkUser(tinkAccountArg, externalUserIdArg); + return newTinkUser; + } + + public static async getTinkUser(tinkAccountArg: TinkAccount, externalUserIdArg: string) { + const authorizationCode = await tinkAccountArg.getUserAuthorizationCode( + externalUserIdArg, + tinkAccountArg.clientId, + 'user:read' + ); + const accessToken = await tinkAccountArg.getUserAccessToken(authorizationCode); + const responseData: { + appId: string; + created: string; + externalUserId: string; + flags: string[]; + id: string; + nationalId: string; + profile: { + // cashbackEnabled: boolean; // deprecated + currency: string; + locale: string; + market: string; + notificationSettings: { + balance: boolean; + budget: boolean; + doubleCharge: boolean; + einvoices: boolean; + fraud: boolean; + income: boolean; + largeExpense: boolean; + leftToSpend: boolean; + loanUpdate: boolean; + summaryMonthly: boolean; + summaryWeekly: boolean; + transaction: boolean; + unusualAccount: boolean; + unusualCategory: boolean; + }; + periodAdjustedDay: 25; + periodMode: 'MONTHLY_ADJUSTED' | 'MONTHLY'; + timeZone: string; + }; + // username: string; // not relevant + } = await tinkAccountArg.request({ + urlArg: '/api/v1/user', + accessToken, + methodArg: 'GET', + payloadArg: null, + }); + const newTinkUser = new TinkUser(tinkAccountArg, responseData.id, responseData.externalUserId); return newTinkUser; } @@ -53,13 +107,23 @@ export class TinkUser { console.log(`successfully deleted user with externalId ${this.externalUserIdArg}`); } - public async getTinkLink(): Promise { + /** + * gets a tink link that can be used by a user to connect accounts + * @returns + */ + public async getTinkLinkForMarket(countryIdArg: string = 'DE'): Promise { const authorizationCode = await this.tinkAccountRef.getUserAuthorizationCode( this.externalUserIdArg, 'df05e4b379934cd09963197cc855bfe9', // this is a hardcoded app id for tink link, as recommended by tink.com 'authorization:read,authorization:grant,credentials:refresh,credentials:read,credentials:write,providers:read,user:read' ); - const tinkLinkUrl = `https://link.tink.com/1.0/business-transactions/connect-accounts?client_id=${'teststate'}&redirect_uri=https://console.tink.com/callback&authorization_code=${authorizationCode}&market=DE`; + const tinkLinkUrl = `https://link.tink.com/1.0/business-transactions/connect-accounts?client_id=${'teststate'}&redirect_uri=https://console.tink.com/callback&authorization_code=${authorizationCode}&market=${countryIdArg}`; return tinkLinkUrl; } + + public async getProviderConsents(): Promise { + const providerConsents: TinkProviderConsent[] = + await TinkProviderConsent.getProviderConsentsForUser(this); + return providerConsents; + } }