Skip to content

Commit

Permalink
feat(webhooks): [nan-1063] webhook on refresh error and slack notific…
Browse files Browse the repository at this point in the history
…ation (#2254)

## Describe your changes
Add in webhook and slack delivery for connection refresh error and for
slack refresh resolution.

## Additional Work
- Add in a dedicated `webhooks` package which required a lot of type
movement which bloated this PR a bit. Add in additional refresh success
and error hooks to be able to consolidate logic in one place.

## Issue ticket number and link
NAN-1063

## Checklist before requesting a review (skip if just adding/editing
APIs & templates)
- [ ] I added tests, otherwise the reason is: 
- [ ] I added observability, otherwise the reason is:
- [ ] I added analytics, otherwise the reason is:
  • Loading branch information
khaliqgant authored Jun 6, 2024
1 parent 359860a commit c0eb679
Show file tree
Hide file tree
Showing 66 changed files with 1,311 additions and 947 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ COPY packages/records/package.json ./packages/records/package.json
COPY packages/types/package.json ./packages/types/package.json
COPY packages/scheduler/package.json ./packages/scheduler/package.json
COPY packages/orchestrator/package.json ./packages/orchestrator/package.json
COPY packages/webhooks/package.json ./packages/webhooks/package.json
COPY package*.json ./

# Install every dependencies
Expand Down
117 changes: 79 additions & 38 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"packages/utils",
"packages/data-ingestion",
"packages/types",
"packages/database"
"packages/database",
"packages/webhooks"
],
"scripts": {
"create:migration": "cd packages/database/lib && knex migrate:make $1 --esm --knexfile ./knexfile.cjs",
Expand Down
1 change: 1 addition & 0 deletions packages/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ COPY packages/utils/ packages/utils/
COPY packages/kvstore/ packages/kvstore/
COPY packages/logs/ packages/logs/
COPY packages/database/ packages/database/
COPY packages/webhooks/ packages/webhooks/
COPY packages/node-client/ packages/node-client/
COPY packages/records/ packages/records/
COPY packages/scheduler/ packages/scheduler/
Expand Down
9 changes: 4 additions & 5 deletions packages/server/lib/clients/oauth1.client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import oAuth1 from 'oauth';
import type { Config as ProviderConfig, TemplateOAuth1 as ProviderTemplateOAuth1, Template as ProviderTemplate } from '@nangohq/shared';
import { AuthModes } from '@nangohq/shared';
import type { IntegrationConfig, TemplateOAuth1 as ProviderTemplateOAuth1, Template as ProviderTemplate } from '@nangohq/types';

interface OAuth1RequestTokenResult {
request_token: string;
Expand All @@ -17,18 +16,18 @@ interface OAuth1RequestTokenResult {
// For reference, this is a pretty good graphic on the OAuth 1.0a flow: https://oauth.net/core/1.0/#anchor9
export class OAuth1Client {
private client: oAuth1.OAuth;
private config: ProviderConfig;
private config: IntegrationConfig;
private authConfig: ProviderTemplateOAuth1;

constructor(config: ProviderConfig, template: ProviderTemplate, callbackUrl: string) {
constructor(config: IntegrationConfig, template: ProviderTemplate, callbackUrl: string) {
this.config = config;

this.authConfig = template as ProviderTemplateOAuth1;
const headers = { 'User-Agent': 'Nango' };

this.client = new oAuth1.OAuth(
this.authConfig.request_url,
typeof this.authConfig.token_url === 'string' ? this.authConfig.token_url : (this.authConfig.token_url?.[AuthModes.OAuth1] as string),
typeof this.authConfig.token_url === 'string' ? this.authConfig.token_url : (this.authConfig.token_url?.['OAuth1'] as string),
this.config.oauth_client_id,
this.config.oauth_client_secret,
'1.0A',
Expand Down
36 changes: 20 additions & 16 deletions packages/server/lib/controllers/apiAuth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import {
errorManager,
analytics,
AnalyticsTypes,
AuthOperation,
createActivityLogMessage,
updateSuccess as updateSuccessActivityLog,
updateProvider as updateProviderActivityLog,
configService,
connectionService,
createActivityLogMessageAndEnd,
AuthModes,
getConnectionConfig,
hmacService,
ErrorSourceEnum,
Expand Down Expand Up @@ -131,7 +129,7 @@ class ApiAuthController {

const template = configService.getTemplate(config.provider);

if (template.auth_mode !== AuthModes.ApiKey) {
if (template.auth_mode !== 'API_KEY') {
await createActivityLogMessageAndEnd({
level: 'error',
environment_id: environment.id,
Expand Down Expand Up @@ -159,7 +157,7 @@ class ApiAuthController {
const { apiKey } = req.body;

const credentials: ApiKeyCredentials = {
type: AuthModes.ApiKey,
type: 'API_KEY',
apiKey
};

Expand Down Expand Up @@ -219,7 +217,7 @@ class ApiAuthController {
connection: updatedConnection.connection,
environment,
account,
auth_mode: AuthModes.ApiKey,
auth_mode: 'API_KEY',
operation: updatedConnection.operation
},
config.provider,
Expand All @@ -242,14 +240,17 @@ class ApiAuthController {
timestamp: Date.now()
});
if (logCtx) {
void connectionCreationFailedHook(
connectionCreationFailedHook(
{
connection: { connection_id: connectionId!, provider_config_key: providerConfigKey! },
environment,
account,
auth_mode: AuthModes.ApiKey,
error: `Error during API key auth: ${prettyError}`,
operation: AuthOperation.UNKNOWN
auth_mode: 'API_KEY',
error: {
type: 'unknown',
description: `Error during API key auth: ${prettyError}`
},
operation: 'unknown'
},
'unknown',
activityLogId,
Expand Down Expand Up @@ -377,7 +378,7 @@ class ApiAuthController {

const template = configService.getTemplate(config.provider);

if (template.auth_mode !== AuthModes.Basic) {
if (template.auth_mode !== 'BASIC') {
await createActivityLogMessageAndEnd({
level: 'error',
environment_id: environment.id,
Expand All @@ -394,7 +395,7 @@ class ApiAuthController {
}

const credentials: BasicApiCredentials = {
type: AuthModes.Basic,
type: 'BASIC',
username,
password
};
Expand Down Expand Up @@ -457,7 +458,7 @@ class ApiAuthController {
connection: updatedConnection.connection,
environment,
account,
auth_mode: AuthModes.Basic,
auth_mode: 'BASIC',
operation: updatedConnection.operation
},
config.provider,
Expand All @@ -480,14 +481,17 @@ class ApiAuthController {
timestamp: Date.now()
});
if (logCtx) {
void connectionCreationFailedHook(
connectionCreationFailedHook(
{
connection: { connection_id: connectionId!, provider_config_key: providerConfigKey! },
environment,
account,
auth_mode: AuthModes.ApiKey,
error: `Error during basic API key auth: ${prettyError}`,
operation: AuthOperation.UNKNOWN
auth_mode: 'API_KEY',
error: {
type: 'unknown',
description: `Error during basic API key auth: ${prettyError}`
},
operation: 'unknown'
},
'unknown',
activityLogId,
Expand Down
Loading

0 comments on commit c0eb679

Please sign in to comment.