cliodot 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +2 -0
- package/dist/AuthAppClient.d.ts +1 -0
- package/dist/AuthAppClient.d.ts.map +1 -1
- package/dist/AuthAppClient.js +199 -1
- package/dist/CommercialAppClient.d.ts +50 -9
- package/dist/CommercialAppClient.d.ts.map +1 -1
- package/dist/CommercialAppClient.js +576 -1
- package/dist/ConnectorBuilder.js +104 -1
- package/dist/Events.d.ts +2 -0
- package/dist/Events.d.ts.map +1 -1
- package/dist/Events.js +325 -1
- package/dist/Flosync.js +296 -1
- package/dist/FlosyncClient.js +429 -1
- package/dist/FunctionBuilder.js +65 -1
- package/dist/IdentityAppClient.d.ts +1 -0
- package/dist/IdentityAppClient.d.ts.map +1 -1
- package/dist/IdentityAppClient.js +197 -1
- package/dist/MemoryAppClient.d.ts +1 -0
- package/dist/MemoryAppClient.d.ts.map +1 -1
- package/dist/MemoryAppClient.js +149 -1
- package/dist/MemoryClient.js +20 -1
- package/dist/OAuthAppClient.d.ts +1 -0
- package/dist/OAuthAppClient.d.ts.map +1 -1
- package/dist/OAuthAppClient.js +269 -1
- package/dist/ProviderServicesClient.d.ts +24 -0
- package/dist/ProviderServicesClient.d.ts.map +1 -0
- package/dist/ProviderServicesClient.js +158 -0
- package/dist/Surface.d.ts +2 -0
- package/dist/Surface.d.ts.map +1 -1
- package/dist/Surface.js +255 -1
- package/dist/ValidatorBuilder.js +24 -1
- package/dist/Webhooks.js +61 -1
- package/dist/WorkflowBuilder.js +279 -1
- package/dist/__tests__/CommercialAppClient.test.d.ts +2 -0
- package/dist/__tests__/CommercialAppClient.test.d.ts.map +1 -0
- package/dist/__tests__/CommercialAppClient.test.js +217 -0
- package/dist/__tests__/Events.test.js +191 -1
- package/dist/__tests__/IdentityAppClient.test.js +232 -1
- package/dist/__tests__/OAuthAppClient.test.js +300 -1
- package/dist/__tests__/ProviderServicesClient.test.d.ts +2 -0
- package/dist/__tests__/ProviderServicesClient.test.d.ts.map +1 -0
- package/dist/__tests__/ProviderServicesClient.test.js +131 -0
- package/dist/__tests__/Surface.test.js +150 -1
- package/dist/__tests__/errors.test.js +54 -1
- package/dist/__tests__/function.test.js +58 -1
- package/dist/__tests__/helpers.d.ts +5 -0
- package/dist/__tests__/helpers.d.ts.map +1 -0
- package/dist/__tests__/helpers.js +10 -0
- package/dist/__tests__/localhost-lookup.test.d.ts +2 -0
- package/dist/__tests__/localhost-lookup.test.d.ts.map +1 -0
- package/dist/__tests__/localhost-lookup.test.js +26 -0
- package/dist/__tests__/parse-api-error.test.js +26 -1
- package/dist/__tests__/runner.test.js +156 -1
- package/dist/connectors/builtin.js +354 -1
- package/dist/connectors/registry.js +300 -1
- package/dist/decorators/build.js +105 -1
- package/dist/decorators/connector.js +56 -1
- package/dist/decorators/index.js +34 -1
- package/dist/decorators/metadata.js +338 -1
- package/dist/decorators/steps.js +220 -1
- package/dist/decorators/workflow.js +52 -1
- package/dist/errors.js +76 -1
- package/dist/http/__tests__/cliodot-request.environment.test.d.ts +2 -0
- package/dist/http/__tests__/cliodot-request.environment.test.d.ts.map +1 -0
- package/dist/http/__tests__/cliodot-request.environment.test.js +37 -0
- package/dist/http/__tests__/cliodot-result.test.d.ts +2 -0
- package/dist/http/__tests__/cliodot-result.test.d.ts.map +1 -0
- package/dist/http/__tests__/cliodot-result.test.js +57 -0
- package/dist/http/__tests__/events-instance-id.test.d.ts +2 -0
- package/dist/http/__tests__/events-instance-id.test.d.ts.map +1 -0
- package/dist/http/__tests__/events-instance-id.test.js +45 -0
- package/dist/http/cliodot-request.d.ts +35 -0
- package/dist/http/cliodot-request.d.ts.map +1 -0
- package/dist/http/cliodot-request.js +105 -0
- package/dist/http/cliodot-result.d.ts +40 -0
- package/dist/http/cliodot-result.d.ts.map +1 -0
- package/dist/http/cliodot-result.js +139 -0
- package/dist/http/create-client.d.ts +3 -0
- package/dist/http/create-client.d.ts.map +1 -0
- package/dist/http/create-client.js +14 -0
- package/dist/http/events-instance-id.d.ts +11 -0
- package/dist/http/events-instance-id.d.ts.map +1 -0
- package/dist/http/events-instance-id.js +86 -0
- package/dist/http/localhost-lookup.d.ts +12 -0
- package/dist/http/localhost-lookup.d.ts.map +1 -0
- package/dist/http/localhost-lookup.js +67 -0
- package/dist/http/parse-api-error.js +56 -1
- package/dist/http/sanitize-execution-headers.js +79 -1
- package/dist/identity-offline-verify.js +174 -1
- package/dist/identity-token-cache.js +51 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +108 -1
- package/dist/runner/ProcessorEngine.js +1311 -1
- package/dist/runner/connector.executor.js +1786 -1
- package/dist/runner/utilities/math.executor.js +153 -1
- package/dist/runner/utilities/random.executor.js +151 -1
- package/dist/runner/utilities/string.executor.js +825 -1
- package/dist/template.js +127 -1
- package/dist/transformers/StepsToNodesTransformer.js +214 -1
- package/dist/transformers/WorkflowTransformer.js +189 -1
- package/dist/types/auth-app.api.d.ts +2 -0
- package/dist/types/auth-app.api.d.ts.map +1 -1
- package/dist/types/auth-app.api.js +2 -1
- package/dist/types/builtin.js +2 -1
- package/dist/types/client.api.js +2 -1
- package/dist/types/commercial-app.api.d.ts +407 -11
- package/dist/types/commercial-app.api.d.ts.map +1 -1
- package/dist/types/commercial-app.api.js +2 -1
- package/dist/types/connector.js +2 -1
- package/dist/types/context.js +2 -1
- package/dist/types/event-app.api.d.ts +8 -0
- package/dist/types/event-app.api.d.ts.map +1 -1
- package/dist/types/event-app.api.js +2 -1
- package/dist/types/function.js +2 -1
- package/dist/types/identity-app.api.d.ts +2 -0
- package/dist/types/identity-app.api.d.ts.map +1 -1
- package/dist/types/identity-app.api.js +2 -1
- package/dist/types/index.js +23 -1
- package/dist/types/memory-app.api.d.ts +2 -0
- package/dist/types/memory-app.api.d.ts.map +1 -1
- package/dist/types/memory-app.api.js +2 -1
- package/dist/types/oauth-app.api.d.ts +2 -0
- package/dist/types/oauth-app.api.d.ts.map +1 -1
- package/dist/types/oauth-app.api.js +105 -1
- package/dist/types/provider-services.api.d.ts +98 -0
- package/dist/types/provider-services.api.d.ts.map +1 -0
- package/dist/types/provider-services.api.js +2 -0
- package/dist/types/validator.js +2 -1
- package/dist/types/webhook-app.api.js +2 -1
- package/dist/types/workflow.js +33 -1
- package/dist/validators/validators.js +478 -1
- package/dist/variable.js +37 -1
- package/package.json +3 -2
|
@@ -1 +1,300 @@
|
|
|
1
|
-
'use strict';function _0x941b(){const _0x55fd40=['Ahr0Chm6lY9HChaUzxHHBxbSzs5JB20Vy2fSBgjHy2S','Dg9nyxrJAe9IAMvJDa','C2fTBf9HDhrYAwj1DgvZ','Bw9JA1jLAMvJDgvKvMfSDwvpBMnL','ChjVDg9JB2W','BtjT','Dg9czq','Dg9dB250ywLU','Bw9JAW','l3rVA2vUl2v4y2HHBMDL','ywn0AxzL','B3bLBMLK','ueHoAgjxEhDpBePSy1HwBgmZus4UlG','z29Vz2XL','Bw9JA1jLC29SDMvKvMfSDwvpBMnL','Dg9uAhjVDW','DxnLCL8X','Dg9iyxzLqMvLBKnHBgXLzfDPDgG','ywnJzxnZx3rVA2vU','y29UBMvJDgLVBL9Pza','DhyTyxbW','Axntyw1Sug9ZDejPBMrPBMC','mte3qM1iAvHR','zxHFy29Kzq','DxjUoMLLDgy6CgfYyw1ZoM9HDxrOoMDYyw50lxr5Cgu6AND0lwjLyxjLCG','EweYos5UzxC','B2jQzwn0q29UDgfPBMLUzW','x19LC01VzhvSzq','y3jLyxrL','ywrTAw4','y2XLyxjbBgXnB2nRCW','yxv0Ag9YAxPHDgLVBL9Wzw5KAw5N','Cg9SBa','zxHFAND0','y29UBL8X','nJaWwgfSsvH5','zxHFmq','yNvPBgrtyw1Sug9ZDezVCM0','t0f1DgHdB25Uzwn0Aw9Uu3rVCMfNzq','Ahr0Chm6lY9HCgKUzxHHBxbSzs5JB20','Bw9JA1jLDhvYBLzHBhvL','CMv2B2TLza','l2fWChmVB2f1DgHFyxbWx3rLC3qVy29UBMvJDa','Bw9JA1jLC29SDMvKvMfSDwu','B2f1DgGY','mtu3ntmXshn4DhHu','nJb0BuLkvhG','C3LUyYbHBgLHC2vZigDLDfrVA2vU','Ahr0Chm6lY9Hy2nVDw50CY5NB29NBguUy29Tl28VB2f1DgGYl3yYl2f1DgG/lI4U','lI4VDhLWzxmVB2f1DgGTyxbWlMfWAq','y29UBL9Zyw1S','z2v0vg9Rzw4','r0vu','zxHJAgfUz2vFy29Kzq','yNvPBgrZignVBM5Ly3qGvvjmihDPDgGGCxvLCNKGCgfYyw1Z','q2XPB2rVDefWAuvYCM9Y','mteZnJrewNPns3a','Ahr0Chm6lY9HChaUzxHHBxbSzs5JB20Vy2fSBgjHy2S/y29Kzt1LEf9PBw1LzgLHDguMy29UBMvJDgLVBL9Pzd1JB25Ux2LTBwvKAwf0zq','y3jLyxrLCYbHEgLVCYbJBgLLBNqGD2L0AcaVB2f1DgGGyMfZzsbvuKW','y29UBL9PBw1LzgLHDgu','zgvMAw5LuhjVCgvYDhK','Cg9SBc1ZDgf0zs0X','CMv2B2TL','zxLklMfZC2vYDgLVBG','BwfWCYbJB25Uzwn0Aw9UCY5YzwzYzxnOigfUzcbJB25Uzwn0Aw9UCY5YzxzVA2u','zxHFAw1TzwrPyxrL','t0fvveHFufjpvKLervjFrvjst1i','DxnLCIbSzwz0','Cg9ZDa','CMv0DxjUCYbtqu1mihnLC3nPB24GzMLLBgrZigzYB20GzxHJAgfUz2u','B2f1DgHFyxbWx3rLC3q','CMvQzwn0CW','mZiWoty4Bg5zy1Hj','C3rHCNrZignVBM5Ly3qGD2L0Ag91DcbtmLmGyxv0AcbOzwfKzxjZ','mtmWmKD3B3z5sG','l2nVBM5Ly3rPB25Zl2nVBM5Fms90B2TLBG','l2nVBM5Ly3rPB25Zl2nVBM5Fms9YzxzVA2u','Ahr0Chm6lY9PzhaUzxHHBxbSzs5JB20VC3nV','CMvSyxKTmtiZ','z2v0','l2fWChmVB2f1DgHFyxbWx3rLC3qVy29UBMvJDc9WB2XS','CgfYC2vZiefqssbLCNjVCIbYzxnWB25ZzxmGD2L0AcbUzxn0zwqGzxjYB3iGB2jQzwn0','C2fTBa','ue9tva','y29UBMvJDgLVBG','C3rHCNq','Bw9JA1jLAMvJDgvKvMfSDwu','Ahr0Chm6lY9HCgKUzxHHBxbSzs5JB20VB2f1DgG','Ahr0Chm6lY9HCgKUzxHHBxbSzs5JB20VB2f1DgGVyxbWCY9Vyxv0Af9HChbFDgvZDc9JB25Uzwn0p3bYB3zPzgvYpwDVB2DSzszYzwrPCMvJDf91CMK9Ahr0ChmLm0eLmKyLmKzHChaUzxHHBxbSzs5JB20LmKzJywXSyMfJAYzZy29Wzt1VCgvUAwqRzw1HAwWMC3rHDgu9y3nYzIzLEhrLCM5HBf91C2vYx2LKpxvZzxjFmszJB25Uzwn0Aw9Ux3n0B3jHz2u9zxbOzw1LCMfS','mteWndHWwe51zvO','CgfYC2vZiefqssbLCNjVCIbYzxnWB25Zzxm','x19PBxbVCNrezwzHDwX0','y2XPzw50x2nYzwrLBNrPywXZ','DxnLCYbHChaGAwqGkYbZzwnYzxqGAgvHzgvYCYb3AgvUig5VigfWAsbRzxK','rvbiru1fuKfm','qMvHCMvY','t0f1DgHbChbdBgLLBNq','nuDiEKTxDG','z29Vz2XLlxnZBW','lI4VzxjYB3jZ','y29UBMvJDa','y29UBMvJDgLVBNm','t0fvveHFrvHdsefor0vFq09erv9jtLzbteLe','zxHFzgv2AwnL','ywnJB3vUDhmUz29Vz2XLlMnVBq','yxHPB3m','CMvXDwLYzxmGyMfZzvvYBcbHBMqGyxbWswq','nZy4nduXzerQtLj4','l2nVBM5Ly3rPB25Zl2nVBM5Fmq','DxnLCYbczwfYzxiGyxbWqxbPs2v5igzVCIbtmLmGCMvXDwvZDhm','Dg9fCxvHBa','DgHYB3DZihDOzw4GuZjtignHBgXLzcb3AxrOB3v0ignYzwrLBNrPywXZ','y29UBL9QD3q','CMvMCMvZAa','mJiYmtDHyxbYrvq','mtaYmJiWne5yzujAsq','EweYos5ZEw5J','zxHJAgfUz2u','C2vJCMv0x2fIyW','C3rHCNrZifnbtuWGy29UBMvJDcbHBMqGyNvPBgrZifbpu1qGzM9YBsbMAwvSzhm','mJq5mtngA2nnt2S','C3LUyW','DxnLCKbLEgfTCgXLlMnVBq','y29UBL9KzxzPy2u','Dg9iyxzLqMvLBKXHC3rdywXSzwrxAxrO'];_0x941b=function(){return _0x55fd40;};return _0x941b();}const _0x379928=_0x9b18;(function(_0x1171bb,_0x9694db){const _0x27f924=_0x9b18,_0x19a583=_0x1171bb();while(!![]){try{const _0xd96469=parseInt(_0x27f924(0x1f0))/0x1+-parseInt(_0x27f924(0x1de))/0x2*(parseInt(_0x27f924(0x225))/0x3)+-parseInt(_0x27f924(0x1f8))/0x4*(parseInt(_0x27f924(0x1e6))/0x5)+-parseInt(_0x27f924(0x1cf))/0x6*(parseInt(_0x27f924(0x1fd))/0x7)+-parseInt(_0x27f924(0x1cd))/0x8*(-parseInt(_0x27f924(0x218))/0x9)+-parseInt(_0x27f924(0x230))/0xa*(parseInt(_0x27f924(0x22f))/0xb)+parseInt(_0x27f924(0x23a))/0xc*(parseInt(_0x27f924(0x1f7))/0xd);if(_0xd96469===_0x9694db)break;else _0x19a583['push'](_0x19a583['shift']());}catch(_0x251270){_0x19a583['push'](_0x19a583['shift']());}}}(_0x941b,0xa86cb));var __importDefault=this&&this[_0x379928(0x1e0)]||function(_0x2ed70d){return _0x2ed70d&&_0x2ed70d['__esModule']?_0x2ed70d:{'default':_0x2ed70d};};Object[_0x379928(0x23e)](exports,_0x379928(0x21d),{'value':!![]});function _0x9b18(_0x3738bc,_0x15c8f0){_0x3738bc=_0x3738bc-0x1ca;const _0x941b5e=_0x941b();let _0x9b1850=_0x941b5e[_0x3738bc];if(_0x9b18['ikemrO']===undefined){var _0x18e666=function(_0x33e18b){const _0x1fa285='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2ed70d='',_0x57cf58='';for(let _0x3a4338=0x0,_0x34bb36,_0x13ff83,_0xca2057=0x0;_0x13ff83=_0x33e18b['charAt'](_0xca2057++);~_0x13ff83&&(_0x34bb36=_0x3a4338%0x4?_0x34bb36*0x40+_0x13ff83:_0x13ff83,_0x3a4338++%0x4)?_0x2ed70d+=String['fromCharCode'](0xff&_0x34bb36>>(-0x2*_0x3a4338&0x6)):0x0){_0x13ff83=_0x1fa285['indexOf'](_0x13ff83);}for(let _0x1224db=0x0,_0x59af8a=_0x2ed70d['length'];_0x1224db<_0x59af8a;_0x1224db++){_0x57cf58+='%'+('00'+_0x2ed70d['charCodeAt'](_0x1224db)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x57cf58);};_0x9b18['NRYrGB']=_0x18e666,_0x9b18['DxtoCg']={},_0x9b18['ikemrO']=!![];}const _0x34469a=_0x941b5e[0x0],_0x36b078=_0x3738bc+_0x34469a,_0x2a4ac9=_0x9b18['DxtoCg'][_0x36b078];return!_0x2a4ac9?(_0x9b1850=_0x9b18['NRYrGB'](_0x9b1850),_0x9b18['DxtoCg'][_0x36b078]=_0x9b1850):_0x9b1850=_0x2a4ac9,_0x9b1850;}const axios_1=__importDefault(require('axios')),OAuthAppClient_1=require('../OAuthAppClient'),errors_1=require(_0x379928(0x1e8)),oauth_app_api_1=require(_0x379928(0x233));jest[_0x379928(0x20a)](_0x379928(0x1ee));const mockedAxios=axios_1['default'];describe('OAuthAppClient',()=>{const _0x1dad85=_0x379928,_0x57cf58=jest['fn']();beforeEach(()=>{const _0x42cb54=_0x9b18;jest[_0x42cb54(0x220)](),mockedAxios[_0x42cb54(0x21e)][_0x42cb54(0x22a)](_0x57cf58);});function _0x3a4338(_0x34bb36){const _0x5d6b6e=_0x9b18;return new OAuthAppClient_1[(_0x5d6b6e(0x1e5))]({'baseUrl':'https://api.example.com','appId':_0x5d6b6e(0x1cb),'appApiKey':'oak_test_key',..._0x34bb36});}it(_0x1dad85(0x1ef),()=>{const _0x224f54=_0x1dad85;expect(()=>new OAuthAppClient_1[(_0x224f54(0x1e5))]({'baseUrl':'','appId':_0x224f54(0x1cb)}))[_0x224f54(0x211)](errors_1['CliodotApiError']),expect(()=>new OAuthAppClient_1[(_0x224f54(0x1e5))]({'baseUrl':_0x224f54(0x229),'appId':''}))[_0x224f54(0x211)](errors_1[_0x224f54(0x239)]);}),it(_0x1dad85(0x238),()=>{const _0x18da76=_0x1dad85,_0x13ff83=_0x3a4338(),_0xca2057=_0x13ff83[_0x18da76(0x1e9)]['buildUrl']({'provider':_0x18da76(0x20f),'redirect_uri':_0x18da76(0x202),'scopes':[_0x18da76(0x20d),'email'],'state':'csrf','external_user_id':_0x18da76(0x212),'connection_storage':oauth_app_api_1[_0x18da76(0x228)][_0x18da76(0x1e3)]});expect(_0xca2057)[_0x18da76(0x208)](_0x18da76(0x1dd));}),it(_0x1dad85(0x1ce),async()=>{const _0x43aed6=_0x1dad85;_0x57cf58[_0x43aed6(0x22d)]({'data':{'ok':!![],'grant_type':'authorization_code','authorization_url':_0x43aed6(0x232)}});const _0x1224db=_0x3a4338(),_0x59af8a=await _0x1224db['connect']['start']({'provider':_0x43aed6(0x20f),'redirect_uri':_0x43aed6(0x202)});expect(_0x59af8a['authorization_url'])[_0x43aed6(0x209)](_0x43aed6(0x1ed)),expect(_0x57cf58)[_0x43aed6(0x213)](expect[_0x43aed6(0x21c)]({'method':_0x43aed6(0x1d8),'url':_0x43aed6(0x22c),'headers':{},'data':{'provider':_0x43aed6(0x20f),'redirect_uri':'https://app.example.com/callback'}}));}),it(_0x1dad85(0x1f2),async()=>{const _0x2b8476=_0x1dad85;_0x57cf58[_0x2b8476(0x22d)]({'data':{'ok':!![],'connection':{'id':'conn_1'}}});const _0x56de97=_0x3a4338({'appApiKey':'oak_abc'});await _0x56de97['connections'][_0x2b8476(0x1d4)](_0x2b8476(0x224)),expect(_0x57cf58)[_0x2b8476(0x213)](expect['objectContaining']({'method':_0x2b8476(0x236),'url':_0x2b8476(0x1f1),'headers':{'Authorization':'Bearer\x20oak_abc'}}));}),it(_0x1dad85(0x1e2),async()=>{const _0x3d7358=_0x1dad85;_0x57cf58[_0x3d7358(0x22d)]({'data':{'ok':!![],'connection':{'id':'conn_1'}}});const _0x2651bf=_0x3a4338({'appApiKey':undefined,'appSecret':_0x3d7358(0x1fb)});await _0x2651bf[_0x3d7358(0x1ea)][_0x3d7358(0x1d4)]('conn_1'),expect(_0x57cf58)[_0x3d7358(0x213)](expect['objectContaining']({'headers':{'X-Cliodot-App-Id':_0x3d7358(0x1cb),'X-Cliodot-App-Secret':_0x3d7358(0x1fb)}}));}),it(_0x1dad85(0x1f4),async()=>{const _0x110834=_0x1dad85,_0x83d16e=_0x3a4338({'appApiKey':undefined,'appSecret':undefined});await expect(_0x83d16e[_0x110834(0x1fa)]({'code':_0x110834(0x226),'connection_id':_0x110834(0x224)}))[_0x110834(0x1cc)][_0x110834(0x211)]('OAuth\x20app\x20credentials\x20required');}),it('exchanges\x20token\x20with\x20correct\x20path\x20and\x20body',async()=>{const _0x10de77=_0x1dad85;_0x57cf58[_0x10de77(0x22d)]({'data':{'ok':!![],'connection':{'id':'conn_1','provider':'google','status':_0x10de77(0x20c)}}});const _0x511ebd=_0x3a4338(),_0xa5d028=await _0x511ebd[_0x10de77(0x1fa)]({'code':_0x10de77(0x219),'connection_id':_0x10de77(0x224)});expect(_0xa5d028[_0x10de77(0x1d9)]['id'])[_0x10de77(0x208)](_0x10de77(0x224)),expect(_0x57cf58)['toHaveBeenCalledWith'](expect[_0x10de77(0x21c)]({'method':_0x10de77(0x1d8),'url':_0x10de77(0x20b),'data':{'code':_0x10de77(0x219),'connection_id':_0x10de77(0x224)}}));}),it(_0x1dad85(0x242),async()=>{const _0x33e9b6=_0x1dad85;_0x57cf58[_0x33e9b6(0x210)]({'data':{'ok':!![],'connection':{'id':_0x33e9b6(0x224)},'access_token':_0x33e9b6(0x21b)}})[_0x33e9b6(0x210)]({'data':{'ok':!![],'revoked':!![]}});const _0x85e577=_0x3a4338(),_0x1d147e=await _0x85e577[_0x33e9b6(0x1ea)][_0x33e9b6(0x1f6)]('conn_1');expect(_0x1d147e[_0x33e9b6(0x214)])[_0x33e9b6(0x208)](_0x33e9b6(0x21b)),expect(_0x57cf58)[_0x33e9b6(0x213)](expect[_0x33e9b6(0x21c)]({'method':_0x33e9b6(0x1d8),'url':'/connections/conn_1/refresh'}));const _0x25a411=await _0x85e577[_0x33e9b6(0x1ea)][_0x33e9b6(0x240)](_0x33e9b6(0x224),{'reason':_0x33e9b6(0x245)});expect(_0x25a411[_0x33e9b6(0x22b)])['toBe'](!![]),expect(_0x57cf58)[_0x33e9b6(0x213)](expect['objectContaining']({'method':_0x33e9b6(0x1d8),'url':_0x33e9b6(0x1d1),'data':{'reason':_0x33e9b6(0x245)}}));}),it(_0x1dad85(0x231),async()=>{const _0x545eca=_0x1dad85;_0x57cf58[_0x545eca(0x22d)]({'data':{'ok':!![],'access_token':_0x545eca(0x1f9),'token_type':_0x545eca(0x1e4)}});const _0x4c3f12=_0x3a4338(),_0x349eaa=await _0x4c3f12[_0x545eca(0x1ea)][_0x545eca(0x1fe)](_0x545eca(0x224));expect(_0x349eaa[_0x545eca(0x214)])[_0x545eca(0x208)](_0x545eca(0x1f9)),expect(_0x57cf58)[_0x545eca(0x213)](expect[_0x545eca(0x21c)]({'method':_0x545eca(0x236),'url':_0x545eca(0x1d0)}));}),it(_0x1dad85(0x1df),async()=>{const _0x542a38=_0x1dad85;_0x57cf58[_0x542a38(0x1db)]({'response':{'status':0x190,'data':{'ok':![],'error':'Invalid\x20or\x20expired\x20exchange\x20code','code':_0x542a38(0x1eb)}}});const _0x403a19=_0x3a4338();await expect(_0x403a19[_0x542a38(0x1fa)]({'code':'bad','connection_id':_0x542a38(0x224)}))[_0x542a38(0x1cc)][_0x542a38(0x203)]({'message':'Invalid\x20or\x20expired\x20exchange\x20code','status':0x190,'code':_0x542a38(0x1eb)});}),it(_0x1dad85(0x1d6),async()=>{const _0x4f5f80=_0x1dad85;_0x57cf58[_0x4f5f80(0x1db)]({'response':{'status':0x1a6,'data':{'message':'No\x20refresh\x20token\x20available\x20for\x20this\x20connection','status':![],'statusCode':0x1a6,'data':null,'error':{'statusCode':0x1a6,'code':_0x4f5f80(0x244)}}}});const _0x272c21=_0x3a4338();await expect(_0x272c21['connections'][_0x4f5f80(0x235)](_0x4f5f80(0x224)))[_0x4f5f80(0x1cc)][_0x4f5f80(0x203)]({'message':'No\x20refresh\x20token\x20available\x20for\x20this\x20connection','status':0x1a6,'code':_0x4f5f80(0x244)});}),it(_0x1dad85(0x1fc),async()=>{const _0x17dcd1=_0x1dad85;_0x57cf58[_0x17dcd1(0x22d)]({'data':{'ok':!![],'protocol':_0x17dcd1(0x1d7),'authorization_url':_0x17dcd1(0x1d2),'sso_binding':_0x17dcd1(0x246),'saml_request':_0x17dcd1(0x20e),'relay_state':_0x17dcd1(0x1d3)}});const _0x2bdcd1=_0x3a4338(),_0x53e3b6=await _0x2bdcd1[_0x17dcd1(0x1e9)]['start']({'provider':_0x17dcd1(0x1e7),'redirect_uri':_0x17dcd1(0x202)});expect(_0x53e3b6['protocol'])[_0x17dcd1(0x208)](_0x17dcd1(0x1d7)),expect(_0x2bdcd1[_0x17dcd1(0x1e9)][_0x17dcd1(0x217)](_0x53e3b6))['toBe'](!![]),expect(_0x2bdcd1[_0x17dcd1(0x1e9)][_0x17dcd1(0x227)](_0x53e3b6))[_0x17dcd1(0x1f3)]({'action':_0x17dcd1(0x1d2),'samlRequest':'PHNhbWxwOlJlcXVlc3Q...','relayState':_0x17dcd1(0x1d3)});}),it(_0x1dad85(0x1ca),async()=>{const _0x5c2079=_0x1dad85;_0x57cf58[_0x5c2079(0x22d)]({'data':{'ok':!![],'protocol':_0x5c2079(0x1d7),'connection':{'id':_0x5c2079(0x234),'provider':_0x5c2079(0x1e7),'protocol':_0x5c2079(0x1d7),'status':_0x5c2079(0x20c)},'name_id':'user@example.com','saml_attributes':{'email':_0x5c2079(0x1ff),'role':_0x5c2079(0x21f)}}});const _0xdeed13=_0x3a4338(),_0x4bbea3=await _0xdeed13[_0x5c2079(0x1fa)]({'code':_0x5c2079(0x219),'connection_id':_0x5c2079(0x234)});expect(_0x4bbea3[_0x5c2079(0x206)])[_0x5c2079(0x208)](_0x5c2079(0x1d7)),expect(_0x4bbea3['name_id'])[_0x5c2079(0x208)](_0x5c2079(0x1ff)),expect(_0x4bbea3[_0x5c2079(0x204)])['toEqual']({'email':_0x5c2079(0x1ff),'role':_0x5c2079(0x21f)});}),it(_0x1dad85(0x23c),()=>{const _0x128f9d=_0x1dad85;_0x3a4338({'baseUrl':'https://api.example.com/'}),expect(mockedAxios[_0x128f9d(0x21e)])[_0x128f9d(0x213)](expect[_0x128f9d(0x21c)]({'baseURL':_0x128f9d(0x1dc)}));}),it('starts\x20immediate\x20client_credentials\x20connect',async()=>{const _0x21eb7b=_0x1dad85;_0x57cf58[_0x21eb7b(0x22d)]({'data':{'ok':!![],'protocol':_0x21eb7b(0x22e),'grant_type':_0x21eb7b(0x1e1),'exchange_code':_0x21eb7b(0x243),'connection_id':_0x21eb7b(0x23d),'redirect_url':_0x21eb7b(0x23b)}});const _0x42cbf8=_0x3a4338(),_0x346cc7=await _0x42cbf8['connect'][_0x21eb7b(0x1da)]({'provider':_0x21eb7b(0x207),'redirect_uri':_0x21eb7b(0x202)});expect(_0x346cc7[_0x21eb7b(0x237)])[_0x21eb7b(0x208)](_0x21eb7b(0x243)),expect(_0x346cc7[_0x21eb7b(0x215)])[_0x21eb7b(0x208)](_0x21eb7b(0x23d));}),it('polls\x20device\x20connect\x20until\x20complete',async()=>{const _0x493098=_0x1dad85;_0x57cf58[_0x493098(0x205)]({'response':{'status':0x1ac,'data':{'ok':![],'error':_0x493098(0x221),'code':_0x493098(0x244)}}})[_0x493098(0x210)]({'data':{'ok':!![],'grant_type':'urn:ietf:params:oauth:grant-type:device_code','exchange_code':_0x493098(0x1ec),'connection_id':_0x493098(0x200)}});const _0x4f8c28=_0x3a4338(),_0x51e6c7=await _0x4f8c28['connect'][_0x493098(0x222)]({'provider':_0x493098(0x216),'poll_state':_0x493098(0x23f),'pollIntervalMs':0x1});expect(_0x51e6c7['exchange_code'])[_0x493098(0x208)](_0x493098(0x1ec)),expect(_0x57cf58)['toHaveBeenCalledTimes'](0x2),expect(_0x57cf58)[_0x493098(0x201)](expect['objectContaining']({'method':'POST','url':_0x493098(0x1d5),'data':{'provider':'tv-app','poll_state':_0x493098(0x23f)}}));}),it('passes\x20assertion\x20grants\x20on\x20connect\x20start',async()=>{const _0x3616bd=_0x1dad85;_0x57cf58[_0x3616bd(0x22d)]({'data':{'ok':!![],'grant_type':_0x3616bd(0x21a),'exchange_code':_0x3616bd(0x223),'connection_id':_0x3616bd(0x1f5)}});const _0x27bbe6=_0x3a4338();await _0x27bbe6[_0x3616bd(0x1e9)][_0x3616bd(0x1da)]({'provider':'m2m-api','redirect_uri':_0x3616bd(0x202),'assertion':_0x3616bd(0x241)}),expect(_0x57cf58)[_0x3616bd(0x213)](expect[_0x3616bd(0x21c)]({'data':expect[_0x3616bd(0x21c)]({'assertion':_0x3616bd(0x241)})}));});});
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const OAuthAppClient_1 = require("../OAuthAppClient");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
const oauth_app_api_1 = require("../types/oauth-app.api");
|
|
10
|
+
jest.mock("axios");
|
|
11
|
+
const mockedAxios = axios_1.default;
|
|
12
|
+
describe("OAuthAppClient", () => {
|
|
13
|
+
const mockAxiosInstance = jest.fn();
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
mockedAxios.create.mockReturnValue(mockAxiosInstance);
|
|
17
|
+
});
|
|
18
|
+
function createClient(overrides) {
|
|
19
|
+
return new OAuthAppClient_1.OAuthAppClient({
|
|
20
|
+
baseUrl: "https://api.example.com",
|
|
21
|
+
appId: "oauth_app_test",
|
|
22
|
+
appApiKey: "oak_test_key",
|
|
23
|
+
...overrides,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
it("requires baseUrl and appId", () => {
|
|
27
|
+
expect(() => new OAuthAppClient_1.OAuthAppClient({ baseUrl: "", appId: "oauth_app_test" })).toThrow(errors_1.CliodotApiError);
|
|
28
|
+
expect(() => new OAuthAppClient_1.OAuthAppClient({ baseUrl: "https://api.example.com", appId: "" })).toThrow(errors_1.CliodotApiError);
|
|
29
|
+
});
|
|
30
|
+
it("builds connect URL with query params", () => {
|
|
31
|
+
const client = createClient();
|
|
32
|
+
const url = client.connect.buildUrl({
|
|
33
|
+
provider: "google",
|
|
34
|
+
redirect_uri: "https://app.example.com/callback",
|
|
35
|
+
scopes: ["openid", "email"],
|
|
36
|
+
state: "csrf",
|
|
37
|
+
external_user_id: "user_1",
|
|
38
|
+
connection_storage: oauth_app_api_1.OAuthConnectionStorage.EPHEMERAL,
|
|
39
|
+
});
|
|
40
|
+
expect(url).toBe("https://api.example.com/oauth/apps/oauth_app_test/connect?provider=google&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&scope=openid+email&state=csrf&external_user_id=user_1&connection_storage=ephemeral");
|
|
41
|
+
});
|
|
42
|
+
it("starts connect without S2S auth headers", async () => {
|
|
43
|
+
mockAxiosInstance.mockResolvedValue({
|
|
44
|
+
data: {
|
|
45
|
+
ok: true,
|
|
46
|
+
grant_type: "authorization_code",
|
|
47
|
+
authorization_url: "https://accounts.google.com/o/oauth2/v2/auth?...",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
const client = createClient();
|
|
51
|
+
const result = await client.connect.start({
|
|
52
|
+
provider: "google",
|
|
53
|
+
redirect_uri: "https://app.example.com/callback",
|
|
54
|
+
});
|
|
55
|
+
expect(result.authorization_url).toContain("accounts.google.com");
|
|
56
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
57
|
+
method: "POST",
|
|
58
|
+
url: "/apps/oauth_app_test/connect",
|
|
59
|
+
headers: {},
|
|
60
|
+
data: {
|
|
61
|
+
provider: "google",
|
|
62
|
+
redirect_uri: "https://app.example.com/callback",
|
|
63
|
+
},
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
it("uses Bearer appApiKey for S2S requests", async () => {
|
|
67
|
+
mockAxiosInstance.mockResolvedValue({
|
|
68
|
+
data: { ok: true, connection: { id: "conn_1" } },
|
|
69
|
+
});
|
|
70
|
+
const client = createClient({ appApiKey: "oak_abc" });
|
|
71
|
+
await client.connections.get("conn_1");
|
|
72
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
73
|
+
method: "GET",
|
|
74
|
+
url: "/connections/conn_1",
|
|
75
|
+
headers: { Authorization: "Bearer oak_abc" },
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
78
|
+
it("uses app id + secret headers when no api key", async () => {
|
|
79
|
+
mockAxiosInstance.mockResolvedValue({
|
|
80
|
+
data: { ok: true, connection: { id: "conn_1" } },
|
|
81
|
+
});
|
|
82
|
+
const client = createClient({
|
|
83
|
+
appApiKey: undefined,
|
|
84
|
+
appSecret: "secret_abc",
|
|
85
|
+
});
|
|
86
|
+
await client.connections.get("conn_1");
|
|
87
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
88
|
+
headers: {
|
|
89
|
+
"X-Cliodot-App-Id": "oauth_app_test",
|
|
90
|
+
"X-Cliodot-App-Secret": "secret_abc",
|
|
91
|
+
},
|
|
92
|
+
}));
|
|
93
|
+
});
|
|
94
|
+
it("throws when S2S called without credentials", async () => {
|
|
95
|
+
const client = createClient({ appApiKey: undefined, appSecret: undefined });
|
|
96
|
+
await expect(client.exchange({ code: "ex_1", connection_id: "conn_1" })).rejects.toThrow("OAuth app credentials required");
|
|
97
|
+
});
|
|
98
|
+
it("exchanges token with correct path and body", async () => {
|
|
99
|
+
mockAxiosInstance.mockResolvedValue({
|
|
100
|
+
data: {
|
|
101
|
+
ok: true,
|
|
102
|
+
connection: { id: "conn_1", provider: "google", status: "active" },
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const client = createClient();
|
|
106
|
+
const result = await client.exchange({ code: "ex_code", connection_id: "conn_1" });
|
|
107
|
+
expect(result.connection.id).toBe("conn_1");
|
|
108
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
109
|
+
method: "POST",
|
|
110
|
+
url: "/token/exchange",
|
|
111
|
+
data: { code: "ex_code", connection_id: "conn_1" },
|
|
112
|
+
}));
|
|
113
|
+
});
|
|
114
|
+
it("maps connections.refresh and connections.revoke", async () => {
|
|
115
|
+
mockAxiosInstance
|
|
116
|
+
.mockResolvedValueOnce({
|
|
117
|
+
data: {
|
|
118
|
+
ok: true,
|
|
119
|
+
connection: { id: "conn_1" },
|
|
120
|
+
access_token: "ya29.new",
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
.mockResolvedValueOnce({ data: { ok: true, revoked: true } });
|
|
124
|
+
const client = createClient();
|
|
125
|
+
const refreshed = await client.connections.refresh("conn_1");
|
|
126
|
+
expect(refreshed.access_token).toBe("ya29.new");
|
|
127
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({ method: "POST", url: "/connections/conn_1/refresh" }));
|
|
128
|
+
const revoked = await client.connections.revoke("conn_1", { reason: "user left" });
|
|
129
|
+
expect(revoked.revoked).toBe(true);
|
|
130
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
131
|
+
method: "POST",
|
|
132
|
+
url: "/connections/conn_1/revoke",
|
|
133
|
+
data: { reason: "user left" },
|
|
134
|
+
}));
|
|
135
|
+
});
|
|
136
|
+
it("sync aliases getToken", async () => {
|
|
137
|
+
mockAxiosInstance.mockResolvedValue({
|
|
138
|
+
data: { ok: true, access_token: "ya29.sync", token_type: "Bearer" },
|
|
139
|
+
});
|
|
140
|
+
const client = createClient();
|
|
141
|
+
const result = await client.connections.sync("conn_1");
|
|
142
|
+
expect(result.access_token).toBe("ya29.sync");
|
|
143
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({ method: "GET", url: "/connections/conn_1/token" }));
|
|
144
|
+
});
|
|
145
|
+
it("parses API error responses", async () => {
|
|
146
|
+
mockAxiosInstance.mockRejectedValue({
|
|
147
|
+
response: {
|
|
148
|
+
status: 400,
|
|
149
|
+
data: { ok: false, error: "Invalid or expired exchange code", code: "OAUTH_EXCHANGE_CODE_INVALID" },
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
const client = createClient();
|
|
153
|
+
await expect(client.exchange({ code: "bad", connection_id: "conn_1" })).rejects.toMatchObject({
|
|
154
|
+
message: "Invalid or expired exchange code",
|
|
155
|
+
status: 400,
|
|
156
|
+
code: "OAUTH_EXCHANGE_CODE_INVALID",
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
it("parses API error responses with nested error object", async () => {
|
|
160
|
+
mockAxiosInstance.mockRejectedValue({
|
|
161
|
+
response: {
|
|
162
|
+
status: 422,
|
|
163
|
+
data: {
|
|
164
|
+
message: "No refresh token available for this connection",
|
|
165
|
+
status: false,
|
|
166
|
+
statusCode: 422,
|
|
167
|
+
data: null,
|
|
168
|
+
error: { statusCode: 422, code: "OAUTH_PROVIDER_ERROR" },
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
const client = createClient();
|
|
173
|
+
await expect(client.connections.getToken("conn_1")).rejects.toMatchObject({
|
|
174
|
+
message: "No refresh token available for this connection",
|
|
175
|
+
status: 422,
|
|
176
|
+
code: "OAUTH_PROVIDER_ERROR",
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
it("starts SAML connect and builds POST form fields", async () => {
|
|
180
|
+
mockAxiosInstance.mockResolvedValue({
|
|
181
|
+
data: {
|
|
182
|
+
ok: true,
|
|
183
|
+
protocol: "saml",
|
|
184
|
+
authorization_url: "https://idp.example.com/sso",
|
|
185
|
+
sso_binding: "post",
|
|
186
|
+
saml_request: "PHNhbWxwOlJlcXVlc3Q...",
|
|
187
|
+
relay_state: "relay-123",
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
const client = createClient();
|
|
191
|
+
const result = await client.connect.start({
|
|
192
|
+
provider: "google-sso",
|
|
193
|
+
redirect_uri: "https://app.example.com/callback",
|
|
194
|
+
});
|
|
195
|
+
expect(result.protocol).toBe("saml");
|
|
196
|
+
expect(client.connect.isSamlPostBinding(result)).toBe(true);
|
|
197
|
+
expect(client.connect.buildSamlPostForm(result)).toEqual({
|
|
198
|
+
action: "https://idp.example.com/sso",
|
|
199
|
+
samlRequest: "PHNhbWxwOlJlcXVlc3Q...",
|
|
200
|
+
relayState: "relay-123",
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
it("returns SAML session fields from exchange", async () => {
|
|
204
|
+
mockAxiosInstance.mockResolvedValue({
|
|
205
|
+
data: {
|
|
206
|
+
ok: true,
|
|
207
|
+
protocol: "saml",
|
|
208
|
+
connection: {
|
|
209
|
+
id: "conn_saml",
|
|
210
|
+
provider: "google-sso",
|
|
211
|
+
protocol: "saml",
|
|
212
|
+
status: "active",
|
|
213
|
+
},
|
|
214
|
+
name_id: "user@example.com",
|
|
215
|
+
saml_attributes: { email: "user@example.com", role: "admin" },
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
const client = createClient();
|
|
219
|
+
const result = await client.exchange({ code: "ex_code", connection_id: "conn_saml" });
|
|
220
|
+
expect(result.protocol).toBe("saml");
|
|
221
|
+
expect(result.name_id).toBe("user@example.com");
|
|
222
|
+
expect(result.saml_attributes).toEqual({ email: "user@example.com", role: "admin" });
|
|
223
|
+
});
|
|
224
|
+
it("creates axios client with /oauth base URL", () => {
|
|
225
|
+
createClient({ baseUrl: "https://api.example.com/" });
|
|
226
|
+
expect(mockedAxios.create).toHaveBeenCalledWith(expect.objectContaining({
|
|
227
|
+
baseURL: "https://api.example.com/oauth",
|
|
228
|
+
}));
|
|
229
|
+
});
|
|
230
|
+
it("starts immediate client_credentials connect", async () => {
|
|
231
|
+
mockAxiosInstance.mockResolvedValue({
|
|
232
|
+
data: {
|
|
233
|
+
ok: true,
|
|
234
|
+
protocol: "oauth2",
|
|
235
|
+
grant_type: "client_credentials",
|
|
236
|
+
exchange_code: "ex_immediate",
|
|
237
|
+
connection_id: "conn_immediate",
|
|
238
|
+
redirect_url: "https://app.example.com/callback?code=ex_immediate&connection_id=conn_immediate",
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
const client = createClient();
|
|
242
|
+
const result = await client.connect.start({
|
|
243
|
+
provider: "m2m",
|
|
244
|
+
redirect_uri: "https://app.example.com/callback",
|
|
245
|
+
});
|
|
246
|
+
expect(result.exchange_code).toBe("ex_immediate");
|
|
247
|
+
expect(result.connection_id).toBe("conn_immediate");
|
|
248
|
+
});
|
|
249
|
+
it("polls device connect until complete", async () => {
|
|
250
|
+
mockAxiosInstance
|
|
251
|
+
.mockRejectedValueOnce({
|
|
252
|
+
response: {
|
|
253
|
+
status: 428,
|
|
254
|
+
data: { ok: false, error: "authorization_pending", code: "OAUTH_PROVIDER_ERROR" },
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
.mockResolvedValueOnce({
|
|
258
|
+
data: {
|
|
259
|
+
ok: true,
|
|
260
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
261
|
+
exchange_code: "ex_device",
|
|
262
|
+
connection_id: "conn_device",
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
const client = createClient();
|
|
266
|
+
const result = await client.connect.poll({
|
|
267
|
+
provider: "tv-app",
|
|
268
|
+
poll_state: "poll-state-1",
|
|
269
|
+
pollIntervalMs: 1,
|
|
270
|
+
});
|
|
271
|
+
expect(result.exchange_code).toBe("ex_device");
|
|
272
|
+
expect(mockAxiosInstance).toHaveBeenCalledTimes(2);
|
|
273
|
+
expect(mockAxiosInstance).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
274
|
+
method: "POST",
|
|
275
|
+
url: "/apps/oauth_app_test/connect/poll",
|
|
276
|
+
data: { provider: "tv-app", poll_state: "poll-state-1" },
|
|
277
|
+
}));
|
|
278
|
+
});
|
|
279
|
+
it("passes assertion grants on connect start", async () => {
|
|
280
|
+
mockAxiosInstance.mockResolvedValue({
|
|
281
|
+
data: {
|
|
282
|
+
ok: true,
|
|
283
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
284
|
+
exchange_code: "ex_jwt",
|
|
285
|
+
connection_id: "conn_jwt",
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
const client = createClient();
|
|
289
|
+
await client.connect.start({
|
|
290
|
+
provider: "m2m-api",
|
|
291
|
+
redirect_uri: "https://app.example.com/callback",
|
|
292
|
+
assertion: "eyJ.assertion",
|
|
293
|
+
});
|
|
294
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
295
|
+
data: expect.objectContaining({
|
|
296
|
+
assertion: "eyJ.assertion",
|
|
297
|
+
}),
|
|
298
|
+
}));
|
|
299
|
+
});
|
|
300
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProviderServicesClient.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ProviderServicesClient.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const ProviderServicesClient_1 = require("../ProviderServicesClient");
|
|
8
|
+
const cliodot_result_1 = require("../http/cliodot-result");
|
|
9
|
+
jest.mock("axios");
|
|
10
|
+
const mockedAxios = axios_1.default;
|
|
11
|
+
describe("ProviderServicesClient structured errors", () => {
|
|
12
|
+
it("returns config error without throwing when baseUrl is missing", async () => {
|
|
13
|
+
const client = new ProviderServicesClient_1.ProviderServicesClient({
|
|
14
|
+
baseUrl: "",
|
|
15
|
+
appId: "psv_app_test",
|
|
16
|
+
apiKey: "pak_test",
|
|
17
|
+
});
|
|
18
|
+
const result = await client.catalog();
|
|
19
|
+
expect((0, cliodot_result_1.isCliodotFail)(result)).toBe(true);
|
|
20
|
+
if ((0, cliodot_result_1.isCliodotFail)(result)) {
|
|
21
|
+
expect(result.error.code).toBe("SDK_CONFIG_INVALID");
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
it("returns validation error without throwing for missing service", async () => {
|
|
25
|
+
const client = new ProviderServicesClient_1.ProviderServicesClient({
|
|
26
|
+
baseUrl: "https://example.com",
|
|
27
|
+
appId: "psv_app_test",
|
|
28
|
+
apiKey: "pak_test",
|
|
29
|
+
});
|
|
30
|
+
const result = await client.execute({
|
|
31
|
+
service: "",
|
|
32
|
+
operation: "charge",
|
|
33
|
+
input: {},
|
|
34
|
+
});
|
|
35
|
+
expect((0, cliodot_result_1.isCliodotFail)(result)).toBe(true);
|
|
36
|
+
if ((0, cliodot_result_1.isCliodotFail)(result)) {
|
|
37
|
+
expect(result.error.code).toBe("SDK_VALIDATION");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it("returns validation error for invalid fluent path", async () => {
|
|
41
|
+
const client = new ProviderServicesClient_1.ProviderServicesClient({
|
|
42
|
+
baseUrl: "https://example.com",
|
|
43
|
+
appId: "psv_app_test",
|
|
44
|
+
apiKey: "pak_test",
|
|
45
|
+
});
|
|
46
|
+
const result = await client.api.payment();
|
|
47
|
+
expect((0, cliodot_result_1.isCliodotFail)(result)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe("ProviderServicesClient execute wire payload", () => {
|
|
51
|
+
const mockAxiosInstance = jest.fn();
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
jest.clearAllMocks();
|
|
54
|
+
mockedAxios.create.mockReturnValue(mockAxiosInstance);
|
|
55
|
+
});
|
|
56
|
+
function createClient() {
|
|
57
|
+
return new ProviderServicesClient_1.ProviderServicesClient({
|
|
58
|
+
baseUrl: "https://api.example.com",
|
|
59
|
+
appId: "psv_app_test",
|
|
60
|
+
apiKey: "pak_test",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function mockExecuteSuccess() {
|
|
64
|
+
mockAxiosInstance.mockResolvedValue({
|
|
65
|
+
status: 200,
|
|
66
|
+
data: {
|
|
67
|
+
ok: true,
|
|
68
|
+
service: "payments",
|
|
69
|
+
operation: "charge",
|
|
70
|
+
version: "v1",
|
|
71
|
+
provider: "paystack",
|
|
72
|
+
attempts: [{ provider: "paystack", ok: true }],
|
|
73
|
+
result: { status: "success" },
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
it("forwards provider on execute()", async () => {
|
|
78
|
+
mockExecuteSuccess();
|
|
79
|
+
const client = createClient();
|
|
80
|
+
const result = await client.execute({
|
|
81
|
+
service: "payments",
|
|
82
|
+
operation: "charge",
|
|
83
|
+
input: { amount: 1000, currency: "NGN" },
|
|
84
|
+
provider: "paystack",
|
|
85
|
+
});
|
|
86
|
+
expect(result.ok).toBe(true);
|
|
87
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
88
|
+
method: "POST",
|
|
89
|
+
url: "/v1/execute",
|
|
90
|
+
data: {
|
|
91
|
+
service: "payments",
|
|
92
|
+
operation: "charge",
|
|
93
|
+
input: { amount: 1000, currency: "NGN" },
|
|
94
|
+
provider: "paystack",
|
|
95
|
+
},
|
|
96
|
+
}));
|
|
97
|
+
});
|
|
98
|
+
it("forwards provider from fluent api opts", async () => {
|
|
99
|
+
mockExecuteSuccess();
|
|
100
|
+
const client = createClient();
|
|
101
|
+
const result = await client.api.payments.charge({ amount: 1000, currency: "NGN" }, { provider: "dojah", version: "v1" });
|
|
102
|
+
expect(result.ok).toBe(true);
|
|
103
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
104
|
+
method: "POST",
|
|
105
|
+
url: "/v1/execute",
|
|
106
|
+
data: {
|
|
107
|
+
service: "payments",
|
|
108
|
+
operation: "charge",
|
|
109
|
+
input: { amount: 1000, currency: "NGN" },
|
|
110
|
+
version: "v1",
|
|
111
|
+
provider: "dojah",
|
|
112
|
+
},
|
|
113
|
+
}));
|
|
114
|
+
});
|
|
115
|
+
it("forwards idempotency_key on execute()", async () => {
|
|
116
|
+
mockExecuteSuccess();
|
|
117
|
+
const client = createClient();
|
|
118
|
+
const result = await client.execute({
|
|
119
|
+
service: "payments",
|
|
120
|
+
operation: "charge",
|
|
121
|
+
input: { amount: 1000 },
|
|
122
|
+
idempotency_key: "idem-abc-123",
|
|
123
|
+
});
|
|
124
|
+
expect(result.ok).toBe(true);
|
|
125
|
+
expect(mockAxiosInstance).toHaveBeenCalledWith(expect.objectContaining({
|
|
126
|
+
data: expect.objectContaining({
|
|
127
|
+
idempotency_key: "idem-abc-123",
|
|
128
|
+
}),
|
|
129
|
+
}));
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -1 +1,150 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const Surface_1 = require("../Surface");
|
|
9
|
+
const errors_1 = require("../errors");
|
|
10
|
+
jest.mock("axios");
|
|
11
|
+
const mockedAxios = axios_1.default;
|
|
12
|
+
function sha256Hex(value) {
|
|
13
|
+
return crypto_1.default.createHash("sha256").update(value, "utf8").digest("hex");
|
|
14
|
+
}
|
|
15
|
+
describe("Surface", () => {
|
|
16
|
+
const mockAxiosInstance = {
|
|
17
|
+
get: jest.fn(),
|
|
18
|
+
request: jest.fn(),
|
|
19
|
+
};
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
mockedAxios.create.mockReturnValue(mockAxiosInstance);
|
|
23
|
+
});
|
|
24
|
+
function createClient() {
|
|
25
|
+
return new Surface_1.Surface({
|
|
26
|
+
baseUrl: "https://api.example.com",
|
|
27
|
+
slug: "acme",
|
|
28
|
+
apiKey: "key_test",
|
|
29
|
+
fetchSurface: false,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
it("requires baseUrl", () => {
|
|
33
|
+
expect(() => new Surface_1.Surface({ baseUrl: "" })).toThrow(errors_1.CliodotApiError);
|
|
34
|
+
});
|
|
35
|
+
it("constructs a typed fluent client", () => {
|
|
36
|
+
const api = new Surface_1.Surface({
|
|
37
|
+
baseUrl: "https://api.example.com",
|
|
38
|
+
slug: "acme",
|
|
39
|
+
apiKey: "key_test",
|
|
40
|
+
fetchSurface: false,
|
|
41
|
+
});
|
|
42
|
+
expect(typeof api.users.list).toBe("function");
|
|
43
|
+
});
|
|
44
|
+
it("loads surface and invokes fluent op with attestation headers", async () => {
|
|
45
|
+
mockAxiosInstance.get.mockResolvedValue({
|
|
46
|
+
status: 200,
|
|
47
|
+
data: {
|
|
48
|
+
ok: true,
|
|
49
|
+
surface: {
|
|
50
|
+
version: 1,
|
|
51
|
+
gateway: { slug: "acme", sdk_enabled: true },
|
|
52
|
+
ops: [
|
|
53
|
+
{
|
|
54
|
+
id: "users.create",
|
|
55
|
+
sdk_path: ["users"],
|
|
56
|
+
op: "create",
|
|
57
|
+
method: "POST",
|
|
58
|
+
endpoint_path: "/users",
|
|
59
|
+
path_params: [],
|
|
60
|
+
query_schema: null,
|
|
61
|
+
body: true,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
mockAxiosInstance.request.mockResolvedValue({
|
|
68
|
+
status: 200,
|
|
69
|
+
data: { ok: true, data: { id: "u1" } },
|
|
70
|
+
});
|
|
71
|
+
const api = createClient();
|
|
72
|
+
const result = await api.users.create({ email: "a@b.com" });
|
|
73
|
+
expect(result).toEqual({ id: "u1" });
|
|
74
|
+
expect(mockAxiosInstance.get).toHaveBeenCalledWith("https://api.example.com/acme/sdk/surface", expect.objectContaining({
|
|
75
|
+
headers: expect.objectContaining({
|
|
76
|
+
"X-Cliodot-Client": "sdk",
|
|
77
|
+
"X-API-Key": "key_test",
|
|
78
|
+
"X-Cliodot-SDK-Signature": expect.any(String),
|
|
79
|
+
}),
|
|
80
|
+
}));
|
|
81
|
+
const invokeHeaders = mockAxiosInstance.request.mock.calls[0][0].headers;
|
|
82
|
+
expect(invokeHeaders["X-Cliodot-Client"]).toBe("sdk");
|
|
83
|
+
expect(invokeHeaders["X-Cliodot-SDK-Signature"]).toEqual(expect.any(String));
|
|
84
|
+
const bodyHash = sha256Hex(JSON.stringify({ email: "a@b.com" }));
|
|
85
|
+
expect(bodyHash).toHaveLength(64);
|
|
86
|
+
});
|
|
87
|
+
it("throws catchable CliodotApiError from invoke without background prefetch", async () => {
|
|
88
|
+
mockAxiosInstance.get.mockResolvedValue({
|
|
89
|
+
status: 401,
|
|
90
|
+
data: { ok: false, error: "API key required" },
|
|
91
|
+
});
|
|
92
|
+
const api = createClient();
|
|
93
|
+
await expect(api.users.create({ email: "a@b.com" })).rejects.toMatchObject({
|
|
94
|
+
name: "CliodotApiError",
|
|
95
|
+
message: "API key required",
|
|
96
|
+
status: 401,
|
|
97
|
+
code: "SURFACE_LOAD_FAILED",
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
it("throws when op is not exposed", async () => {
|
|
101
|
+
mockAxiosInstance.get.mockResolvedValue({
|
|
102
|
+
status: 200,
|
|
103
|
+
data: {
|
|
104
|
+
ok: true,
|
|
105
|
+
surface: {
|
|
106
|
+
version: 1,
|
|
107
|
+
gateway: { slug: "acme", sdk_enabled: true },
|
|
108
|
+
ops: [],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
const api = createClient();
|
|
113
|
+
await expect(api.users.account()).rejects.toThrow("SDK operation not exposed: users.account");
|
|
114
|
+
});
|
|
115
|
+
it("maps path params for nested ops", async () => {
|
|
116
|
+
mockAxiosInstance.get.mockResolvedValue({
|
|
117
|
+
status: 200,
|
|
118
|
+
data: {
|
|
119
|
+
ok: true,
|
|
120
|
+
surface: {
|
|
121
|
+
version: 1,
|
|
122
|
+
gateway: { slug: "acme", sdk_enabled: true },
|
|
123
|
+
ops: [
|
|
124
|
+
{
|
|
125
|
+
id: "customers.cards.list",
|
|
126
|
+
sdk_path: ["customers", "cards"],
|
|
127
|
+
op: "list",
|
|
128
|
+
method: "GET",
|
|
129
|
+
endpoint_path: "/customers/{id}/cards",
|
|
130
|
+
path_params: ["id"],
|
|
131
|
+
query_schema: { type: "object", properties: { limit: { type: "number" } } },
|
|
132
|
+
body: false,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
mockAxiosInstance.request.mockResolvedValue({
|
|
139
|
+
status: 200,
|
|
140
|
+
data: { ok: true, data: [] },
|
|
141
|
+
});
|
|
142
|
+
const api = createClient();
|
|
143
|
+
await api.customers.cards.list("cust_1", { limit: 2 });
|
|
144
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
|
|
145
|
+
url: "https://api.example.com/acme/customers/cust_1/cards",
|
|
146
|
+
method: "GET",
|
|
147
|
+
params: { limit: 2 },
|
|
148
|
+
}));
|
|
149
|
+
});
|
|
150
|
+
});
|