{orderedFields.map(([name]) => {
const [type, key] = name.split('.') as ['credentials' | 'params', string];
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+
const definition = provider[type === 'credentials' ? 'credentials' : 'connection_config']?.[key];
// Not all fields have a definition in providers.yaml so we fallback to default
const base = name in defaultConfiguration ? defaultConfiguration[name] : undefined;
diff --git a/packages/database/lib/getConfig.ts b/packages/database/lib/getConfig.ts
index 96206a9cec4..77567655f3e 100644
--- a/packages/database/lib/getConfig.ts
+++ b/packages/database/lib/getConfig.ts
@@ -15,7 +15,8 @@ export function getDbConfig({ timeoutMs }: { timeoutMs: number }): Knex.Config {
database: process.env['NANGO_DB_NAME'] || 'nango',
password: process.env['NANGO_DB_PASSWORD'] || 'nango',
ssl: process.env['NANGO_DB_SSL'] != null && process.env['NANGO_DB_SSL'].toLowerCase() === 'true' ? { rejectUnauthorized: false } : undefined,
- statement_timeout: timeoutMs
+ statement_timeout: timeoutMs,
+ application_name: process.env['NANGO_DB_APPLICATION_NAME'] || '[unknown]'
},
pool: {
min: parseInt(process.env['NANGO_DB_POOL_MIN'] || '0'),
diff --git a/packages/database/lib/knexfile.cjs b/packages/database/lib/knexfile.cjs
index fc5cab26182..b0333df9c97 100644
--- a/packages/database/lib/knexfile.cjs
+++ b/packages/database/lib/knexfile.cjs
@@ -1,6 +1,5 @@
// Knex CLI for migration needs this Knexfile but doesn't play well with ESM modules.
// That's why the content of the Knex config is moved to ./config.ts to be imported by the app in ESM-fashion, and the Knexfile is only used for the Knex CLI in CommonJS-fashion.
-// eslint-disable-next-line @typescript-eslint/no-var-requires
const { config } = require('../dist/config.js');
module.exports = config;
diff --git a/packages/database/lib/migrations/20221026075018_create_connection.cjs b/packages/database/lib/migrations/20221026075018_create_connection.cjs
index a6e172e51e7..49dfa162191 100644
--- a/packages/database/lib/migrations/20221026075018_create_connection.cjs
+++ b/packages/database/lib/migrations/20221026075018_create_connection.cjs
@@ -1,4 +1,4 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable('_nango_connections', function (table) {
table.increments('id').primary();
table.timestamps(true, true);
@@ -9,6 +9,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable('_nango_connections');
};
diff --git a/packages/database/lib/migrations/20221026075019_create_config.cjs b/packages/database/lib/migrations/20221026075019_create_config.cjs
index 8dfe0450e3a..fd25c56b143 100644
--- a/packages/database/lib/migrations/20221026075019_create_config.cjs
+++ b/packages/database/lib/migrations/20221026075019_create_config.cjs
@@ -1,4 +1,4 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable('_nango_configs', function (table) {
table.increments('id').primary();
table.timestamps(true, true);
@@ -11,6 +11,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable('_nango_configs');
};
diff --git a/packages/database/lib/migrations/20221223094334_add_connection_config.cjs b/packages/database/lib/migrations/20221223094334_add_connection_config.cjs
index 5dc3801cf9d..3b69a8daefe 100644
--- a/packages/database/lib/migrations/20221223094334_add_connection_config.cjs
+++ b/packages/database/lib/migrations/20221223094334_add_connection_config.cjs
@@ -1,10 +1,10 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.jsonb('connection_config');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.dropColumn('connection_config');
});
diff --git a/packages/database/lib/migrations/20230121211135_alter_scope_type_in_config.cjs b/packages/database/lib/migrations/20230121211135_alter_scope_type_in_config.cjs
index 01d17177bfa..89da01b3373 100644
--- a/packages/database/lib/migrations/20230121211135_alter_scope_type_in_config.cjs
+++ b/packages/database/lib/migrations/20230121211135_alter_scope_type_in_config.cjs
@@ -1,10 +1,10 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable('_nango_configs', function (table) {
table.text('oauth_scopes').alter({ alterType: true });
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_configs', function (table) {
table.string('oauth_scopes').alter({ alterType: true });
});
diff --git a/packages/database/lib/migrations/20230208124114_create_account.cjs b/packages/database/lib/migrations/20230208124114_create_account.cjs
index 3d0cd294bf4..5faaf060e02 100644
--- a/packages/database/lib/migrations/20230208124114_create_account.cjs
+++ b/packages/database/lib/migrations/20230208124114_create_account.cjs
@@ -1,4 +1,4 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"');
return knex.schema.createTable('_nango_accounts', function (table) {
table.increments('id').primary();
@@ -12,6 +12,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable('_nango_accounts');
};
diff --git a/packages/database/lib/migrations/20230208124526_add_account_id_to_config.cjs b/packages/database/lib/migrations/20230208124526_add_account_id_to_config.cjs
index 1ab7574a349..dd7f9b84d9d 100644
--- a/packages/database/lib/migrations/20230208124526_add_account_id_to_config.cjs
+++ b/packages/database/lib/migrations/20230208124526_add_account_id_to_config.cjs
@@ -1,4 +1,4 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.from(`_nango_accounts`).insert({ email: 'self-hosted', id: 0 }).onConflict(['id']).merge();
return knex.schema.alterTable('_nango_configs', function (table) {
table.integer('account_id').references('id').inTable('_nango_accounts').defaultTo(0).notNullable();
@@ -7,7 +7,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_configs', function (table) {
table.dropUnique(['unique_key', 'account_id']);
table.unique('unique_key');
diff --git a/packages/database/lib/migrations/20230208124533_add_account_id_to_connection.cjs b/packages/database/lib/migrations/20230208124533_add_account_id_to_connection.cjs
index db7b5c0252b..2c1d0f4e46b 100644
--- a/packages/database/lib/migrations/20230208124533_add_account_id_to_connection.cjs
+++ b/packages/database/lib/migrations/20230208124533_add_account_id_to_connection.cjs
@@ -1,4 +1,4 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.integer('account_id').references('id').inTable('_nango_accounts').defaultTo(0).notNullable();
table.dropUnique(['provider_config_key', 'connection_id']);
@@ -6,7 +6,7 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.dropColumn('account_id');
table.dropUnique(['provider_config_key', 'connection_id', 'account_id']);
diff --git a/packages/database/lib/migrations/20230213163753_add_callback_to_account.cjs b/packages/database/lib/migrations/20230213163753_add_callback_to_account.cjs
index 5d2c42473b3..df8376fb5fd 100644
--- a/packages/database/lib/migrations/20230213163753_add_callback_to_account.cjs
+++ b/packages/database/lib/migrations/20230213163753_add_callback_to_account.cjs
@@ -1,10 +1,10 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.text('callback_url');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.dropColumn('callback_url');
});
diff --git a/packages/database/lib/migrations/20230217123640_add_metadata_to_connection.cjs b/packages/database/lib/migrations/20230217123640_add_metadata_to_connection.cjs
index d0fbfd33a56..7cc236aefe4 100644
--- a/packages/database/lib/migrations/20230217123640_add_metadata_to_connection.cjs
+++ b/packages/database/lib/migrations/20230217123640_add_metadata_to_connection.cjs
@@ -1,10 +1,10 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.jsonb('metadata');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.dropColumn('metadata');
});
diff --git a/packages/database/lib/migrations/20230225211916_create_user.cjs b/packages/database/lib/migrations/20230225211916_create_user.cjs
index 3b7a86b3b4e..e085f0a66d0 100644
--- a/packages/database/lib/migrations/20230225211916_create_user.cjs
+++ b/packages/database/lib/migrations/20230225211916_create_user.cjs
@@ -1,4 +1,4 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.createTable('_nango_users', function (table) {
table.increments('id').primary();
table.timestamps(true, true);
@@ -11,6 +11,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable('_nango_users');
};
diff --git a/packages/database/lib/migrations/20230225211941_add_name_and_owner_to_account.cjs b/packages/database/lib/migrations/20230225211941_add_name_and_owner_to_account.cjs
index b787eda54dd..e768247ab81 100644
--- a/packages/database/lib/migrations/20230225211941_add_name_and_owner_to_account.cjs
+++ b/packages/database/lib/migrations/20230225211941_add_name_and_owner_to_account.cjs
@@ -1,4 +1,4 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex
.from(`_nango_users`)
.insert({ id: 0, name: 'unknown', email: 'unknown@example.com', hashed_password: 'unkown', salt: 'unknown', account_id: 0 })
@@ -11,7 +11,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.dropColumn('owner_id');
table.dropColumn('name');
diff --git a/packages/database/lib/migrations/20230307152421_add_reset_link_to_users.cjs b/packages/database/lib/migrations/20230307152421_add_reset_link_to_users.cjs
index 002eba07f22..918598b03b7 100644
--- a/packages/database/lib/migrations/20230307152421_add_reset_link_to_users.cjs
+++ b/packages/database/lib/migrations/20230307152421_add_reset_link_to_users.cjs
@@ -1,10 +1,10 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_users', function (table) {
table.string('reset_password_token');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_users', function (table) {
table.dropColumn('reset_password_token');
});
diff --git a/packages/database/lib/migrations/20230322233031_add_encryption_to_config.cjs b/packages/database/lib/migrations/20230322233031_add_encryption_to_config.cjs
index 314553dcc4f..81cf9618ebc 100644
--- a/packages/database/lib/migrations/20230322233031_add_encryption_to_config.cjs
+++ b/packages/database/lib/migrations/20230322233031_add_encryption_to_config.cjs
@@ -1,11 +1,11 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_configs', function (table) {
table.string('oauth_client_secret_iv');
table.string('oauth_client_secret_tag');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_configs', function (table) {
table.dropColumn('oauth_client_secret_iv');
table.dropColumn('oauth_client_secret_tag');
diff --git a/packages/database/lib/migrations/20230322233042_add_encryption_to_connection.cjs b/packages/database/lib/migrations/20230322233042_add_encryption_to_connection.cjs
index 4c0daab9e33..601605f8366 100644
--- a/packages/database/lib/migrations/20230322233042_add_encryption_to_connection.cjs
+++ b/packages/database/lib/migrations/20230322233042_add_encryption_to_connection.cjs
@@ -1,11 +1,11 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.string('credentials_iv');
table.string('credentials_tag');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_connections', function (table) {
table.dropColumn('credentials_iv');
table.dropColumn('credentials_tag');
diff --git a/packages/database/lib/migrations/20230322233049_add_encryption_to_account.cjs b/packages/database/lib/migrations/20230322233049_add_encryption_to_account.cjs
index 62e1d3c8d2f..4e0993d6dd4 100644
--- a/packages/database/lib/migrations/20230322233049_add_encryption_to_account.cjs
+++ b/packages/database/lib/migrations/20230322233049_add_encryption_to_account.cjs
@@ -1,11 +1,11 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.string('secret_key_iv');
table.string('secret_key_tag');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.dropColumn('secret_key_iv');
table.dropColumn('secret_key_tag');
diff --git a/packages/database/lib/migrations/20230323011937_create_db_config.cjs b/packages/database/lib/migrations/20230323011937_create_db_config.cjs
index 444b0858039..994dd7ac4fb 100644
--- a/packages/database/lib/migrations/20230323011937_create_db_config.cjs
+++ b/packages/database/lib/migrations/20230323011937_create_db_config.cjs
@@ -1,10 +1,10 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable('_nango_db_config', function (table) {
table.string('encryption_key_hash');
table.boolean('encryption_complete').defaultTo(false);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable('_nango_db_config');
};
diff --git a/packages/database/lib/migrations/20230324135032_alter_secret_from_account.cjs b/packages/database/lib/migrations/20230324135032_alter_secret_from_account.cjs
index d98e23c7cc3..8c85a3c49d0 100644
--- a/packages/database/lib/migrations/20230324135032_alter_secret_from_account.cjs
+++ b/packages/database/lib/migrations/20230324135032_alter_secret_from_account.cjs
@@ -1,10 +1,10 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.string('secret_key').defaultTo(knex.raw('uuid_generate_v4()')).alter({ alterType: true });
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.uuid('secret_key').defaultTo(knex.raw('uuid_generate_v4()')).alter({ alterType: true });
});
diff --git a/packages/database/lib/migrations/20230326083713_create_oauth_session.cjs b/packages/database/lib/migrations/20230326083713_create_oauth_session.cjs
index eca03c1f9eb..efab102e3a0 100644
--- a/packages/database/lib/migrations/20230326083713_create_oauth_session.cjs
+++ b/packages/database/lib/migrations/20230326083713_create_oauth_session.cjs
@@ -1,4 +1,4 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable('_nango_oauth_sessions', function (table) {
table.uuid('id').notNullable();
table.string('provider_config_key').notNullable();
@@ -17,6 +17,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable('_nango_oauth_sessions');
};
diff --git a/packages/database/lib/migrations/20230510083713_activity_logging_table.cjs b/packages/database/lib/migrations/20230510083713_activity_logging_table.cjs
index 294dbdbf081..3964466706b 100644
--- a/packages/database/lib/migrations/20230510083713_activity_logging_table.cjs
+++ b/packages/database/lib/migrations/20230510083713_activity_logging_table.cjs
@@ -1,7 +1,7 @@
const tableName = '_nango_activity_logs';
const messagesTableName = '_nango_activity_log_messages';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema
.createTable(tableName, function (table) {
table.increments('id').primary();
@@ -40,7 +40,7 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(messagesTableName).then(() => {
return knex.schema.dropTable(tableName);
});
diff --git a/packages/database/lib/migrations/20230511090816_tickets.cjs b/packages/database/lib/migrations/20230511090816_tickets.cjs
index 472e61e2fa9..c6608e31fb2 100644
--- a/packages/database/lib/migrations/20230511090816_tickets.cjs
+++ b/packages/database/lib/migrations/20230511090816_tickets.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_unified_tickets';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.uuid('id').notNullable();
table.string('external_id').notNullable();
@@ -25,6 +25,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20230512090816_add_sync.cjs b/packages/database/lib/migrations/20230512090816_add_sync.cjs
index e70fe9b8fa7..ee4a14eb0ea 100644
--- a/packages/database/lib/migrations/20230512090816_add_sync.cjs
+++ b/packages/database/lib/migrations/20230512090816_add_sync.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_jobs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.increments('id').primary();
table.integer('nango_connection_id').unsigned().notNullable();
@@ -13,6 +13,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20230513090816_add_sync_schedule.cjs b/packages/database/lib/migrations/20230513090816_add_sync_schedule.cjs
index 71340be8579..d6b8f0b5dbe 100644
--- a/packages/database/lib/migrations/20230513090816_add_sync_schedule.cjs
+++ b/packages/database/lib/migrations/20230513090816_add_sync_schedule.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_schedules';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.increments('id').primary();
table.integer('nango_connection_id').references('id').inTable('_nango_connections').defaultTo(0).notNullable().onDelete('CASCADE');
@@ -11,6 +11,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20230516090816_add_sync_config.cjs b/packages/database/lib/migrations/20230516090816_add_sync_config.cjs
index b524abb28b7..361c3b3d4fe 100644
--- a/packages/database/lib/migrations/20230516090816_add_sync_config.cjs
+++ b/packages/database/lib/migrations/20230516090816_add_sync_config.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.increments('id').primary();
table.integer('account_id').references('id').inTable('_nango_accounts').defaultTo(0).notNullable();
@@ -11,6 +11,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20230519080408_add_sync_data.cjs b/packages/database/lib/migrations/20230519080408_add_sync_data.cjs
index c7756f83d6c..55d6495c4b8 100644
--- a/packages/database/lib/migrations/20230519080408_add_sync_data.cjs
+++ b/packages/database/lib/migrations/20230519080408_add_sync_data.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.uuid('id').notNullable();
table.string('external_id').notNullable();
@@ -38,6 +38,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20230525185131_update_data_records_uniqueness.cjs b/packages/database/lib/migrations/20230525185131_update_data_records_uniqueness.cjs
index 38144594608..1ce8dfa15c2 100644
--- a/packages/database/lib/migrations/20230525185131_update_data_records_uniqueness.cjs
+++ b/packages/database/lib/migrations/20230525185131_update_data_records_uniqueness.cjs
@@ -1,13 +1,13 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropUnique(['nango_connection_id', 'external_id']);
table.unique(['nango_connection_id', 'external_id', 'model']);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropUnique(['nango_connection_id', 'external_id', 'model']);
table.unique(['nango_connection_id', 'external_id']);
diff --git a/packages/database/lib/migrations/20230529080846_field_mappings_per_connection.cjs b/packages/database/lib/migrations/20230529080846_field_mappings_per_connection.cjs
index 73ec8dca45e..e911adced62 100644
--- a/packages/database/lib/migrations/20230529080846_field_mappings_per_connection.cjs
+++ b/packages/database/lib/migrations/20230529080846_field_mappings_per_connection.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_connections';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.jsonb('field_mappings').defaultTo('{}');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.dropColumn('field_mappings');
});
diff --git a/packages/database/lib/migrations/20230529110419_sync_config_and_schedule_adjustments.cjs b/packages/database/lib/migrations/20230529110419_sync_config_and_schedule_adjustments.cjs
index 5b76458e98f..0cae98c6a2c 100644
--- a/packages/database/lib/migrations/20230529110419_sync_config_and_schedule_adjustments.cjs
+++ b/packages/database/lib/migrations/20230529110419_sync_config_and_schedule_adjustments.cjs
@@ -1,7 +1,7 @@
const syncJobs = '_nango_sync_jobs';
const syncSchedules = '_nango_sync_schedules';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(syncJobs, function (table) {
table.specificType('models', 'text ARRAY');
table.string('frequency');
@@ -13,7 +13,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(syncJobs, function (table) {
table.dropColumn('models');
table.dropColumn('frequency');
diff --git a/packages/database/lib/migrations/20230529110450_sync_changes.cjs b/packages/database/lib/migrations/20230529110450_sync_changes.cjs
index e4ab5ee8f39..e230319d859 100644
--- a/packages/database/lib/migrations/20230529110450_sync_changes.cjs
+++ b/packages/database/lib/migrations/20230529110450_sync_changes.cjs
@@ -3,7 +3,7 @@ const syncJobs = '_nango_sync_jobs';
const syncSchedules = '_nango_sync_schedules';
const syncDataRecords = '_nango_sync_data_records';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.createTable(syncs, function (table) {
table.uuid('id').primary().notNullable();
table.integer('nango_connection_id').unsigned().notNullable();
@@ -36,7 +36,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(syncJobs, function (table) {
table.dropColumn('sync_id');
table.integer('nango_connection_id').unsigned().notNullable();
diff --git a/packages/database/lib/migrations/20230529164653_index_on_model_for_sync_data_records.cjs b/packages/database/lib/migrations/20230529164653_index_on_model_for_sync_data_records.cjs
index 26bef9fc514..60fbae703a6 100644
--- a/packages/database/lib/migrations/20230529164653_index_on_model_for_sync_data_records.cjs
+++ b/packages/database/lib/migrations/20230529164653_index_on_model_for_sync_data_records.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.table(tableName, function (table) {
table.index('model');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex('model');
});
diff --git a/packages/database/lib/migrations/20230531185120_operation_name_for_activity_log.cjs b/packages/database/lib/migrations/20230531185120_operation_name_for_activity_log.cjs
index 797b683387c..38878a2e64e 100644
--- a/packages/database/lib/migrations/20230531185120_operation_name_for_activity_log.cjs
+++ b/packages/database/lib/migrations/20230531185120_operation_name_for_activity_log.cjs
@@ -1,12 +1,12 @@
const TABLE_NAME = '_nango_activity_logs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.string('operation_name');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.dropColumn('operation_name');
});
diff --git a/packages/database/lib/migrations/20230601081230_activity_log_id_for_sync_job.cjs b/packages/database/lib/migrations/20230601081230_activity_log_id_for_sync_job.cjs
index b7d0c9f5d44..00f3c1acc07 100644
--- a/packages/database/lib/migrations/20230601081230_activity_log_id_for_sync_job.cjs
+++ b/packages/database/lib/migrations/20230601081230_activity_log_id_for_sync_job.cjs
@@ -1,12 +1,12 @@
const TABLE_NAME = '_nango_sync_jobs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.integer('activity_log_id').references('id').inTable('_nango_activity_logs').onDelete('SET NULL');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.dropColumn('activity_log_id');
});
diff --git a/packages/database/lib/migrations/20230602172423_add_offset_to_schedule.cjs b/packages/database/lib/migrations/20230602172423_add_offset_to_schedule.cjs
index 4b7a8c2d20b..8333fad3ca7 100644
--- a/packages/database/lib/migrations/20230602172423_add_offset_to_schedule.cjs
+++ b/packages/database/lib/migrations/20230602172423_add_offset_to_schedule.cjs
@@ -1,12 +1,12 @@
const TABLE_NAME = '_nango_sync_schedules';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.integer('offset').defaultTo(0);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.dropColumn('offset');
});
diff --git a/packages/database/lib/migrations/20230605073948_store_webhook_url_in_accounts.cjs b/packages/database/lib/migrations/20230605073948_store_webhook_url_in_accounts.cjs
index 9dba0728e49..d988bdf0500 100644
--- a/packages/database/lib/migrations/20230605073948_store_webhook_url_in_accounts.cjs
+++ b/packages/database/lib/migrations/20230605073948_store_webhook_url_in_accounts.cjs
@@ -1,10 +1,10 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.text('webhook_url');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_accounts', function (table) {
table.dropColumn('webhook_url');
});
diff --git a/packages/database/lib/migrations/20230605101533_sync_cloud_updates.cjs b/packages/database/lib/migrations/20230605101533_sync_cloud_updates.cjs
index cf3b5759414..1175e90ed81 100644
--- a/packages/database/lib/migrations/20230605101533_sync_cloud_updates.cjs
+++ b/packages/database/lib/migrations/20230605101533_sync_cloud_updates.cjs
@@ -1,6 +1,6 @@
const syncConfigs = '_nango_sync_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(syncConfigs, function (table) {
table.dropColumn('provider');
table.dropColumn('snippet');
@@ -12,7 +12,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(syncConfigs, function (table) {
table.string('provider');
table.text('snippet');
diff --git a/packages/database/lib/migrations/20230605161639_sync_cloud_model_and_version.cjs b/packages/database/lib/migrations/20230605161639_sync_cloud_model_and_version.cjs
index 134f01cea7c..195963aa3b0 100644
--- a/packages/database/lib/migrations/20230605161639_sync_cloud_model_and_version.cjs
+++ b/packages/database/lib/migrations/20230605161639_sync_cloud_model_and_version.cjs
@@ -1,6 +1,6 @@
const TABLE = '_nango_sync_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(TABLE, function (table) {
table.dropColumn('integration_name');
table.string('version').notNullable();
@@ -8,7 +8,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(TABLE, function (table) {
table.string('integration_name');
table.dropColumn('version');
diff --git a/packages/database/lib/migrations/20230606103939_sync_cloud_is_active_and_metadata.cjs b/packages/database/lib/migrations/20230606103939_sync_cloud_is_active_and_metadata.cjs
index a406ac47b3a..c16028ae3f2 100644
--- a/packages/database/lib/migrations/20230606103939_sync_cloud_is_active_and_metadata.cjs
+++ b/packages/database/lib/migrations/20230606103939_sync_cloud_is_active_and_metadata.cjs
@@ -1,13 +1,13 @@
const TABLE = '_nango_sync_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(TABLE, function (table) {
table.boolean('active').defaultTo(false);
table.string('runs');
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(TABLE, function (table) {
table.dropColumn('active');
table.dropColumn('runs');
diff --git a/packages/database/lib/migrations/20230612060608_store_models_in_sync_configs.cjs b/packages/database/lib/migrations/20230612060608_store_models_in_sync_configs.cjs
index c97e03bdb60..545164c870c 100644
--- a/packages/database/lib/migrations/20230612060608_store_models_in_sync_configs.cjs
+++ b/packages/database/lib/migrations/20230612060608_store_models_in_sync_configs.cjs
@@ -1,12 +1,12 @@
const TABLE = '_nango_sync_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(TABLE, function (table) {
table.jsonb('model_schema').defaultTo('{}');
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(TABLE, function (table) {
table.dropColumn('model_schema');
});
diff --git a/packages/database/lib/migrations/20230613080214_link_sync_configs.cjs b/packages/database/lib/migrations/20230613080214_link_sync_configs.cjs
index 7bebfc6f0b8..73ae09e7deb 100644
--- a/packages/database/lib/migrations/20230613080214_link_sync_configs.cjs
+++ b/packages/database/lib/migrations/20230613080214_link_sync_configs.cjs
@@ -2,7 +2,7 @@ const JOBS_TABLE = '_nango_sync_jobs';
const RECORDS_TABLE = '_nango_sync_data_records';
const CONFIGS_TABLE = '_nango_sync_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(RECORDS_TABLE, function (table) {
table.integer('sync_job_id').references('id').inTable(JOBS_TABLE).onDelete('CASCADE').index();
});
@@ -12,7 +12,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(RECORDS_TABLE, function (table) {
table.dropColumn('sync_job_id');
});
diff --git a/packages/database/lib/migrations/20230614080012_drop_unified_tickets_table.cjs b/packages/database/lib/migrations/20230614080012_drop_unified_tickets_table.cjs
index 0bd1ed6f38f..ba50636af2c 100644
--- a/packages/database/lib/migrations/20230614080012_drop_unified_tickets_table.cjs
+++ b/packages/database/lib/migrations/20230614080012_drop_unified_tickets_table.cjs
@@ -1,10 +1,10 @@
const tableName = '_nango_unified_tickets';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.dropTable(tableName);
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.uuid('id').notNullable();
table.string('external_id').notNullable();
diff --git a/packages/database/lib/migrations/20230614091738_link_sync_id_to_sync_config.cjs b/packages/database/lib/migrations/20230614091738_link_sync_id_to_sync_config.cjs
index 0e7b868eb37..39d3e46ab7f 100644
--- a/packages/database/lib/migrations/20230614091738_link_sync_id_to_sync_config.cjs
+++ b/packages/database/lib/migrations/20230614091738_link_sync_id_to_sync_config.cjs
@@ -1,13 +1,13 @@
const tableName = '_nango_sync_configs';
const syncs = '_nango_syncs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.uuid('sync_id').references('id').inTable(syncs).onDelete('CASCADE');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.dropColumn('sync_id');
});
diff --git a/packages/database/lib/migrations/20230615183358_update_indices_on_data_records.cjs b/packages/database/lib/migrations/20230615183358_update_indices_on_data_records.cjs
index 0b1f490f405..aa175f6b564 100644
--- a/packages/database/lib/migrations/20230615183358_update_indices_on_data_records.cjs
+++ b/packages/database/lib/migrations/20230615183358_update_indices_on_data_records.cjs
@@ -1,13 +1,13 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.table(tableName, function (table) {
table.index('data_hash');
table.index('external_id');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex('data_hash');
table.dropIndex('external_id');
diff --git a/packages/database/lib/migrations/20230629054518_add_sync_deploy_type.cjs b/packages/database/lib/migrations/20230629054518_add_sync_deploy_type.cjs
index 1fd3e37a66b..ae43fb801d6 100644
--- a/packages/database/lib/migrations/20230629054518_add_sync_deploy_type.cjs
+++ b/packages/database/lib/migrations/20230629054518_add_sync_deploy_type.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_activity_logs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema
.alterTable(tableName, function (table) {
table.string('action_new').defaultTo('oauth').notNullable();
@@ -22,7 +22,7 @@ exports.up = function (knex, _) {
);
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema
.alterTable(tableName, function (table) {
table.enu('action_new', ['oauth', 'proxy', 'token', 'sync']).defaultTo('oauth').notNullable();
diff --git a/packages/database/lib/migrations/20230703041405_update_sync_models.cjs b/packages/database/lib/migrations/20230703041405_update_sync_models.cjs
index 919335f008e..98ef5a05f93 100644
--- a/packages/database/lib/migrations/20230703041405_update_sync_models.cjs
+++ b/packages/database/lib/migrations/20230703041405_update_sync_models.cjs
@@ -1,7 +1,7 @@
const SYNC_TABLE = '_nango_syncs';
const SYNC_CONFIG = '_nango_sync_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(SYNC_TABLE, function (table) {
table.dropColumn('models');
});
@@ -11,7 +11,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(SYNC_TABLE, function (table) {
table.specificType('models', 'text ARRAY');
});
diff --git a/packages/database/lib/migrations/20230704175816_nango_environments.cjs b/packages/database/lib/migrations/20230704175816_nango_environments.cjs
index 86345bd34d1..679e47cfc86 100644
--- a/packages/database/lib/migrations/20230704175816_nango_environments.cjs
+++ b/packages/database/lib/migrations/20230704175816_nango_environments.cjs
@@ -1,4 +1,4 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.createTable('_nango_environments', function (table) {
table.increments('id').primary();
table.string('name').notNullable();
@@ -20,6 +20,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable('_nango_environments');
};
diff --git a/packages/database/lib/migrations/20230704175820_link_environment_accounts.cjs b/packages/database/lib/migrations/20230704175820_link_environment_accounts.cjs
index d500286cf94..8be4c90021b 100644
--- a/packages/database/lib/migrations/20230704175820_link_environment_accounts.cjs
+++ b/packages/database/lib/migrations/20230704175820_link_environment_accounts.cjs
@@ -6,7 +6,7 @@
const ENVIRONMENTS_TABLE = '_nango_environments';
const ACCOUNTS_TABLE = '_nango_accounts';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
const existingAccounts = await knex.table(ACCOUNTS_TABLE).select('*');
for (const account of existingAccounts) {
@@ -37,7 +37,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.table('_nango_environments').truncate();
return knex.schema.alterTable(ACCOUNTS_TABLE, function (table) {
diff --git a/packages/database/lib/migrations/20230707103352_use_environment_id_instead_of_account_id.cjs b/packages/database/lib/migrations/20230707103352_use_environment_id_instead_of_account_id.cjs
index 479b68e3ea5..a2eeafe3bd8 100644
--- a/packages/database/lib/migrations/20230707103352_use_environment_id_instead_of_account_id.cjs
+++ b/packages/database/lib/migrations/20230707103352_use_environment_id_instead_of_account_id.cjs
@@ -8,7 +8,7 @@ const ACCOUNTS_TABLE = '_nango_accounts';
const TABLE_PREFIX = '_nango_';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
const tablesToReplace = [
{ name: `${TABLE_PREFIX}activity_logs`, dropForeign: true, truncate: true },
{ name: `${TABLE_PREFIX}configs`, dropForeign: true, truncate: false },
@@ -50,7 +50,7 @@ exports.up = async function (knex, _) {
}
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(ACCOUNTS_TABLE, function (table) {
table.uuid('secret_key').defaultTo(knex.raw('uuid_generate_v4()')).notNullable();
table.uuid('public_key').defaultTo(knex.raw('uuid_generate_v4()')).notNullable();
diff --git a/packages/database/lib/migrations/20230719102146_oauth_credentials_not_required.cjs b/packages/database/lib/migrations/20230719102146_oauth_credentials_not_required.cjs
index 9f0e9e8a419..0a4a0efedab 100644
--- a/packages/database/lib/migrations/20230719102146_oauth_credentials_not_required.cjs
+++ b/packages/database/lib/migrations/20230719102146_oauth_credentials_not_required.cjs
@@ -1,6 +1,6 @@
const TABLE_NAME = '_nango_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.string('oauth_client_id').alter().nullable().defaultTo(null);
table.string('oauth_client_secret').alter().nullable().defaultTo(null);
@@ -8,7 +8,7 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.string('oauth_client_id').alter().notNullable();
table.string('oauth_client_secret').alter().notNullable();
diff --git a/packages/database/lib/migrations/20230721105652_soft_deletes.cjs b/packages/database/lib/migrations/20230721105652_soft_deletes.cjs
index 9a9b9d25323..e128d162b4e 100644
--- a/packages/database/lib/migrations/20230721105652_soft_deletes.cjs
+++ b/packages/database/lib/migrations/20230721105652_soft_deletes.cjs
@@ -7,7 +7,7 @@ const SYNC_SCHEDULES_TABLE = '_nango_sync_schedules';
const tables = [CONNECTIONS_TABLE, SYNCS_TABLE, SYNC_CONFIGS_TABLE, CONFIGS_TABLE, SYNC_JOBS_TABLE, SYNC_SCHEDULES_TABLE];
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
for (const nangoTable of tables) {
await knex.schema.alterTable(nangoTable, function (table) {
table.boolean('deleted').defaultTo(false);
@@ -16,7 +16,7 @@ exports.up = async function (knex, _) {
}
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
for (const nangoTable of tables) {
await knex.schema.alterTable(nangoTable, function (table) {
table.dropColumn('deleted');
diff --git a/packages/database/lib/migrations/20230721105849_additional_index_nango_connections.cjs b/packages/database/lib/migrations/20230721105849_additional_index_nango_connections.cjs
index 54552eedfb3..d82aa33e686 100644
--- a/packages/database/lib/migrations/20230721105849_additional_index_nango_connections.cjs
+++ b/packages/database/lib/migrations/20230721105849_additional_index_nango_connections.cjs
@@ -1,13 +1,13 @@
const tableName = '_nango_connections';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.table(tableName, function (table) {
table.index('provider_config_key');
table.index('connection_id');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex('provider_config_key');
table.dropIndex('connection_id');
diff --git a/packages/database/lib/migrations/20230721115148_update_uniques.cjs b/packages/database/lib/migrations/20230721115148_update_uniques.cjs
index b3f6764a6df..f76530b26c1 100644
--- a/packages/database/lib/migrations/20230721115148_update_uniques.cjs
+++ b/packages/database/lib/migrations/20230721115148_update_uniques.cjs
@@ -1,7 +1,7 @@
const CONNECTIONS_TABLE = '_nango_connections';
const CONFIGS_TABLE = '_nango_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(CONNECTIONS_TABLE, function (table) {
table.dropUnique(['provider_config_key', 'connection_id', 'environment_id']);
table.unique(['provider_config_key', 'connection_id', 'environment_id', 'deleted_at']);
@@ -12,7 +12,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(CONNECTIONS_TABLE, function (table) {
table.unique(['provider_config_key', 'connection_id', 'environment_id']);
table.dropUnique(['provider_config_key', 'connection_id', 'environment_id', 'deleted_at']);
diff --git a/packages/database/lib/migrations/20230724110746_add_connection_last_fetched_date.cjs b/packages/database/lib/migrations/20230724110746_add_connection_last_fetched_date.cjs
index 78080364bf2..e4da09f5fa7 100644
--- a/packages/database/lib/migrations/20230724110746_add_connection_last_fetched_date.cjs
+++ b/packages/database/lib/migrations/20230724110746_add_connection_last_fetched_date.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_connections';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.dateTime('last_fetched_at');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('last_fetched_at');
});
diff --git a/packages/database/lib/migrations/20230725102233_unique_sync_name_per_connection.cjs b/packages/database/lib/migrations/20230725102233_unique_sync_name_per_connection.cjs
index 492e9db64af..b5dbb0fd182 100644
--- a/packages/database/lib/migrations/20230725102233_unique_sync_name_per_connection.cjs
+++ b/packages/database/lib/migrations/20230725102233_unique_sync_name_per_connection.cjs
@@ -1,12 +1,12 @@
const SYNCS_TABLE = '_nango_syncs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable(SYNCS_TABLE, function (table) {
table.unique(['name', 'nango_connection_id', 'deleted_at']);
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(SYNCS_TABLE, function (table) {
table.dropUnique(['name', 'nango_connection_id', 'deleted_at']);
});
diff --git a/packages/database/lib/migrations/20230725103142_activity_indices.cjs b/packages/database/lib/migrations/20230725103142_activity_indices.cjs
index f7574f91817..d824982814f 100644
--- a/packages/database/lib/migrations/20230725103142_activity_indices.cjs
+++ b/packages/database/lib/migrations/20230725103142_activity_indices.cjs
@@ -1,13 +1,13 @@
const LOG_MESSAGES_TABLE = '_nango_activity_log_messages';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable(LOG_MESSAGES_TABLE, function (table) {
table.index('activity_log_id', 'activity_log_id_index');
table.index('created_at', 'created_at_index');
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(LOG_MESSAGES_TABLE, function (table) {
table.dropIndex('activity_log_id', 'activity_log_id_index');
table.dropIndex('created_at', 'created_at_index');
diff --git a/packages/database/lib/migrations/20230727075437_scopes_length.cjs b/packages/database/lib/migrations/20230727075437_scopes_length.cjs
index 3f34d62396c..5d8ade65e82 100644
--- a/packages/database/lib/migrations/20230727075437_scopes_length.cjs
+++ b/packages/database/lib/migrations/20230727075437_scopes_length.cjs
@@ -1,12 +1,12 @@
const CONFIGS_TABLE = '_nango_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable(CONFIGS_TABLE, function (table) {
table.text('oauth_scopes').alter({ alterType: true });
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(CONFIGS_TABLE, function (table) {
table.string('oauth_scopes').alter({ alterType: true });
});
diff --git a/packages/database/lib/migrations/20230728114040_last_sync_date.cjs b/packages/database/lib/migrations/20230728114040_last_sync_date.cjs
index 559c0defdbf..6672bfac326 100644
--- a/packages/database/lib/migrations/20230728114040_last_sync_date.cjs
+++ b/packages/database/lib/migrations/20230728114040_last_sync_date.cjs
@@ -1,12 +1,12 @@
const SYNCS_TABLE = '_nango_syncs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable(SYNCS_TABLE, function (table) {
table.dateTime('last_sync_date').nullable();
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(SYNCS_TABLE, function (table) {
table.dropColumn('last_sync_date');
});
diff --git a/packages/database/lib/migrations/20230803113912_remove_activity_log.cjs b/packages/database/lib/migrations/20230803113912_remove_activity_log.cjs
index 5cfb480ca64..3bf6fe7b649 100644
--- a/packages/database/lib/migrations/20230803113912_remove_activity_log.cjs
+++ b/packages/database/lib/migrations/20230803113912_remove_activity_log.cjs
@@ -1,12 +1,12 @@
const JOBS_TABLE = '_nango_sync_jobs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable(JOBS_TABLE, function (table) {
table.dropColumn('activity_log_id');
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(JOBS_TABLE, function (table) {
table.integer('activity_log_id').references('id').inTable('_nango_activity_logs').onDelete('SET NULL');
});
diff --git a/packages/database/lib/migrations/20230804082618_connection_metadata_consolidation.cjs b/packages/database/lib/migrations/20230804082618_connection_metadata_consolidation.cjs
index 095f2bf955a..3e597bef4c3 100644
--- a/packages/database/lib/migrations/20230804082618_connection_metadata_consolidation.cjs
+++ b/packages/database/lib/migrations/20230804082618_connection_metadata_consolidation.cjs
@@ -5,7 +5,7 @@
*/
const DB_TABLE = '_nango_connections';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
const existingMetaData = await knex.select('id', 'metadata', 'connection_config').from(DB_TABLE).whereNotNull('metadata').andWhere('metadata', '!=', '{}');
for (const record of existingMetaData) {
@@ -33,7 +33,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(DB_TABLE, function (table) {
table.jsonb('field_mappings').defaultTo('{}');
});
diff --git a/packages/database/lib/migrations/20230804110506_remove_params_from_connection_config.cjs b/packages/database/lib/migrations/20230804110506_remove_params_from_connection_config.cjs
index c54e9f7b01e..488c8f6a395 100644
--- a/packages/database/lib/migrations/20230804110506_remove_params_from_connection_config.cjs
+++ b/packages/database/lib/migrations/20230804110506_remove_params_from_connection_config.cjs
@@ -5,7 +5,7 @@
*/
const DB_TABLE = '_nango_connections';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
const existingCC = await knex.select('id', 'connection_config').from(DB_TABLE).whereNotNull('connection_config').andWhere('connection_config', '!=', '{}');
for (const record of existingCC) {
@@ -18,7 +18,6 @@ exports.up = async function (knex, _) {
if (key.includes('connectionConfig.params.')) {
const newKey = key.replace('connectionConfig.params.', '');
connection_config[newKey] = connection_config[key];
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete connection_config[key];
await knex.update({ connection_config }).from(DB_TABLE).where({ id });
@@ -29,6 +28,6 @@ exports.up = async function (knex, _) {
return Promise.resolve();
};
-exports.down = async function (_knex, _) {
+exports.down = async function () {
return Promise.resolve();
};
diff --git a/packages/database/lib/migrations/20230804132344_update_account_and_organization.cjs b/packages/database/lib/migrations/20230804132344_update_account_and_organization.cjs
index 00511017077..6ee25a0a35e 100644
--- a/packages/database/lib/migrations/20230804132344_update_account_and_organization.cjs
+++ b/packages/database/lib/migrations/20230804132344_update_account_and_organization.cjs
@@ -1,7 +1,7 @@
const ACCOUNTS_TABLE = '_nango_accounts';
const USERS_TABLE = '_nango_users';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(USERS_TABLE, function (table) {
table.boolean('suspended').defaultTo(false).notNullable();
table.dateTime('suspended_at').defaultTo(null);
@@ -11,7 +11,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(USERS_TABLE, function (table) {
table.dropColumn('suspended');
table.dropColumn('suspended_at');
diff --git a/packages/database/lib/migrations/20230804181955_invited_users.cjs b/packages/database/lib/migrations/20230804181955_invited_users.cjs
index a268faa3715..e9799653c55 100644
--- a/packages/database/lib/migrations/20230804181955_invited_users.cjs
+++ b/packages/database/lib/migrations/20230804181955_invited_users.cjs
@@ -1,6 +1,6 @@
const DB_TABLE = '_nango_invited_users';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.createTable(DB_TABLE, function (table) {
table.increments('id').primary();
table.string('name').notNullable();
@@ -14,6 +14,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.dropTable(DB_TABLE);
};
diff --git a/packages/database/lib/migrations/20230810110751_data_records_deleted.cjs b/packages/database/lib/migrations/20230810110751_data_records_deleted.cjs
index b87d91796eb..9d462e901d5 100644
--- a/packages/database/lib/migrations/20230810110751_data_records_deleted.cjs
+++ b/packages/database/lib/migrations/20230810110751_data_records_deleted.cjs
@@ -1,13 +1,13 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('external_is_deleted').defaultTo(false).index();
table.dateTime('external_deleted_at');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('external_is_deleted');
table.dropColumn('external_deleted_at');
diff --git a/packages/database/lib/migrations/20230811084146_deleted_records_index.cjs b/packages/database/lib/migrations/20230811084146_deleted_records_index.cjs
index 175a8a50bb1..095dcc80d0d 100644
--- a/packages/database/lib/migrations/20230811084146_deleted_records_index.cjs
+++ b/packages/database/lib/migrations/20230811084146_deleted_records_index.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.index('created_at');
table.index('updated_at');
@@ -11,7 +11,7 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex('created_at');
table.dropIndex('updated_at');
diff --git a/packages/database/lib/migrations/20230811085027_deleted_records_tracking.cjs b/packages/database/lib/migrations/20230811085027_deleted_records_tracking.cjs
index e9fa2309c7b..7afc0e2d37c 100644
--- a/packages/database/lib/migrations/20230811085027_deleted_records_tracking.cjs
+++ b/packages/database/lib/migrations/20230811085027_deleted_records_tracking.cjs
@@ -2,7 +2,7 @@ const tableName = '_nango_sync_data_records_deletes';
const JOBS_TABLE = '_nango_sync_jobs';
const SYNC_TABLE = '_nango_syncs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.uuid('id').notNullable();
table.string('external_id').notNullable().index();
@@ -46,6 +46,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20230811093401_data_records_additional_index.cjs b/packages/database/lib/migrations/20230811093401_data_records_additional_index.cjs
index 8c3eebf35d6..7bf5ac2c242 100644
--- a/packages/database/lib/migrations/20230811093401_data_records_additional_index.cjs
+++ b/packages/database/lib/migrations/20230811093401_data_records_additional_index.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.index('external_deleted_at');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex('external_deleted_at');
});
diff --git a/packages/database/lib/migrations/20230811131558_track_deletes_in_nango_sync_configs.cjs b/packages/database/lib/migrations/20230811131558_track_deletes_in_nango_sync_configs.cjs
index fa3f8e2d534..5b8cc861eb3 100644
--- a/packages/database/lib/migrations/20230811131558_track_deletes_in_nango_sync_configs.cjs
+++ b/packages/database/lib/migrations/20230811131558_track_deletes_in_nango_sync_configs.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('track_deletes').defaultTo(false);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('track_deletes');
});
diff --git a/packages/database/lib/migrations/20230904183820_add_action_type.cjs b/packages/database/lib/migrations/20230904183820_add_action_type.cjs
index 39aa8fd0d82..221e806c124 100644
--- a/packages/database/lib/migrations/20230904183820_add_action_type.cjs
+++ b/packages/database/lib/migrations/20230904183820_add_action_type.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.string('type').defaultTo('sync');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('type');
});
diff --git a/packages/database/lib/migrations/20230911164150_environment_variables_in_project_settings.cjs b/packages/database/lib/migrations/20230911164150_environment_variables_in_project_settings.cjs
index ab59abd90a7..426606293d7 100644
--- a/packages/database/lib/migrations/20230911164150_environment_variables_in_project_settings.cjs
+++ b/packages/database/lib/migrations/20230911164150_environment_variables_in_project_settings.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_environment_variables';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.increments('id').primary();
table.integer('environment_id').unsigned().references('id').inTable(`_nango_environments`);
@@ -10,6 +10,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20230912181226_add_encryption_to_tables.cjs b/packages/database/lib/migrations/20230912181226_add_encryption_to_tables.cjs
index 124cb4f3661..c4b8c02820a 100644
--- a/packages/database/lib/migrations/20230912181226_add_encryption_to_tables.cjs
+++ b/packages/database/lib/migrations/20230912181226_add_encryption_to_tables.cjs
@@ -2,7 +2,7 @@ const SECRETS_TABLE = '_nango_environment_variables';
const RECORDS_TABLE = '_nango_sync_data_records';
const DELETED_RECORDS_TABLE = '_nango_sync_data_records_deletes';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(SECRETS_TABLE, function (table) {
table.string('value_iv');
table.string('value_tag');
@@ -17,7 +17,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(RECORDS_TABLE, function (table) {
table.dropColumn('json_iv');
table.dropColumn('json_tag');
diff --git a/packages/database/lib/migrations/20230914080128_add_run_in_to_job.cjs b/packages/database/lib/migrations/20230914080128_add_run_in_to_job.cjs
index f5cb2d4bfb1..93040038336 100644
--- a/packages/database/lib/migrations/20230914080128_add_run_in_to_job.cjs
+++ b/packages/database/lib/migrations/20230914080128_add_run_in_to_job.cjs
@@ -1,12 +1,12 @@
const JOBS_TABLE = '_nango_sync_jobs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable(JOBS_TABLE, function (table) {
table.string('run_id');
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.alterTable(JOBS_TABLE, function (table) {
table.dropColumn('run_id');
});
diff --git a/packages/database/lib/migrations/20230914161744_add_auto_start_to_config.cjs b/packages/database/lib/migrations/20230914161744_add_auto_start_to_config.cjs
index 6d5e5c96e3d..4ffe9a92a36 100644
--- a/packages/database/lib/migrations/20230914161744_add_auto_start_to_config.cjs
+++ b/packages/database/lib/migrations/20230914161744_add_auto_start_to_config.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('auto_start').defaultTo(true);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('auto_start');
});
diff --git a/packages/database/lib/migrations/20230918054535_sync_level_attributes.cjs b/packages/database/lib/migrations/20230918054535_sync_level_attributes.cjs
index bd3ea6a05c1..8c3f9f58012 100644
--- a/packages/database/lib/migrations/20230918054535_sync_level_attributes.cjs
+++ b/packages/database/lib/migrations/20230918054535_sync_level_attributes.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.jsonb('attributes').defaultTo('{}');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('attributes');
});
diff --git a/packages/database/lib/migrations/20230920084353_add_prebuilt_and_public_flag_to_sync_config.cjs b/packages/database/lib/migrations/20230920084353_add_prebuilt_and_public_flag_to_sync_config.cjs
index 32b5552e99e..7f849acd15c 100644
--- a/packages/database/lib/migrations/20230920084353_add_prebuilt_and_public_flag_to_sync_config.cjs
+++ b/packages/database/lib/migrations/20230920084353_add_prebuilt_and_public_flag_to_sync_config.cjs
@@ -1,13 +1,13 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('pre_built').defaultTo(false);
table.boolean('is_public').defaultTo(false);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('pre_built');
table.dropColumn('is_public');
diff --git a/packages/database/lib/migrations/20230921104322_add_uuid_to_all_accounts.cjs b/packages/database/lib/migrations/20230921104322_add_uuid_to_all_accounts.cjs
index 05127803cdc..032396caacb 100644
--- a/packages/database/lib/migrations/20230921104322_add_uuid_to_all_accounts.cjs
+++ b/packages/database/lib/migrations/20230921104322_add_uuid_to_all_accounts.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_accounts';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(tableName, function (table) {
table.uuid('uuid').defaultTo(knex.raw('uuid_generate_v4()'));
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.table(tableName, function (table) {
table.dropColumn('uuid');
});
diff --git a/packages/database/lib/migrations/20230928082622_rotate_and_activation_logic.cjs b/packages/database/lib/migrations/20230928082622_rotate_and_activation_logic.cjs
index 3ba2bcb0dd6..c4a0a8ae114 100644
--- a/packages/database/lib/migrations/20230928082622_rotate_and_activation_logic.cjs
+++ b/packages/database/lib/migrations/20230928082622_rotate_and_activation_logic.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_environments';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.string('pending_secret_key').nullable();
table.unique('pending_secret_key');
@@ -11,7 +11,7 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('pending_secret_key');
table.dropColumn('pending_secret_key_iv');
diff --git a/packages/database/lib/migrations/20230928133211_activity_log_session_id_index.cjs b/packages/database/lib/migrations/20230928133211_activity_log_session_id_index.cjs
index 40a43cb252f..fcb2799e66d 100644
--- a/packages/database/lib/migrations/20230928133211_activity_log_session_id_index.cjs
+++ b/packages/database/lib/migrations/20230928133211_activity_log_session_id_index.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_activity_logs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.table(tableName, function (table) {
table.index('session_id');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex('session_id');
});
diff --git a/packages/database/lib/migrations/20231004161359_remove_encryption_flags.cjs b/packages/database/lib/migrations/20231004161359_remove_encryption_flags.cjs
index 34e908f3115..4166703b363 100644
--- a/packages/database/lib/migrations/20231004161359_remove_encryption_flags.cjs
+++ b/packages/database/lib/migrations/20231004161359_remove_encryption_flags.cjs
@@ -1,7 +1,7 @@
const RECORDS_TABLE = '_nango_sync_data_records';
const DELETED_RECORDS_TABLE = '_nango_sync_data_records_deletes';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
//await knex.schema.alterTable(RECORDS_TABLE, function (table) {
//table.dropColumn('json_iv');
//table.dropColumn('json_tag');
@@ -12,7 +12,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(DELETED_RECORDS_TABLE, function (table) {
table.string('json_iv');
table.string('json_tag');
diff --git a/packages/database/lib/migrations/20231004235347_config_app_updates.cjs b/packages/database/lib/migrations/20231004235347_config_app_updates.cjs
index fb8538c4ef7..ac5f97a8f83 100644
--- a/packages/database/lib/migrations/20231004235347_config_app_updates.cjs
+++ b/packages/database/lib/migrations/20231004235347_config_app_updates.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_configs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(tableName, function (table) {
table.text('oauth_client_secret').alter();
});
@@ -9,7 +9,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.alterTable(tableName, function (table) {
table.string('oauth_client_secret', 255).alter();
});
diff --git a/packages/database/lib/migrations/20231016111628_sync_metadata.cjs b/packages/database/lib/migrations/20231016111628_sync_metadata.cjs
index e9f5aa92836..359a7dcf008 100644
--- a/packages/database/lib/migrations/20231016111628_sync_metadata.cjs
+++ b/packages/database/lib/migrations/20231016111628_sync_metadata.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.jsonb('metadata').defaultTo('{}');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('metadata');
});
diff --git a/packages/database/lib/migrations/20231018115215_add_pending_delete.cjs b/packages/database/lib/migrations/20231018115215_add_pending_delete.cjs
index 3eb00525a12..97d437d7e1e 100644
--- a/packages/database/lib/migrations/20231018115215_add_pending_delete.cjs
+++ b/packages/database/lib/migrations/20231018115215_add_pending_delete.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_data_records';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('pending_delete').defaultTo(false).index();
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('pending_delete');
});
diff --git a/packages/database/lib/migrations/20231018132226_add_webhook_option.cjs b/packages/database/lib/migrations/20231018132226_add_webhook_option.cjs
index 2040e944f49..fc1b9babec9 100644
--- a/packages/database/lib/migrations/20231018132226_add_webhook_option.cjs
+++ b/packages/database/lib/migrations/20231018132226_add_webhook_option.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_environments';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('always_send_webhook').defaultTo(false);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('always_send_webhook');
});
diff --git a/packages/database/lib/migrations/20231019135320_add_environment_id_to_log_messages.cjs b/packages/database/lib/migrations/20231019135320_add_environment_id_to_log_messages.cjs
index 7e4826fe5af..f9e9c235f33 100644
--- a/packages/database/lib/migrations/20231019135320_add_environment_id_to_log_messages.cjs
+++ b/packages/database/lib/migrations/20231019135320_add_environment_id_to_log_messages.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_activity_log_messages';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.integer('environment_id').unsigned().references('id').inTable(`_nango_environments`).index();
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('environment_id');
});
diff --git a/packages/database/lib/migrations/20231020102155_sync_job_table_indexes.cjs b/packages/database/lib/migrations/20231020102155_sync_job_table_indexes.cjs
index 6a4b88b3e14..7d9aa52cfdb 100644
--- a/packages/database/lib/migrations/20231020102155_sync_job_table_indexes.cjs
+++ b/packages/database/lib/migrations/20231020102155_sync_job_table_indexes.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_jobs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.index(['sync_id', 'deleted']);
table.index('deleted');
@@ -10,7 +10,7 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex(['sync_id', 'deleted']);
table.dropIndex('deleted');
diff --git a/packages/database/lib/migrations/20231027135906_slack_notifications_option.cjs b/packages/database/lib/migrations/20231027135906_slack_notifications_option.cjs
index f9f7946d690..40f8aed8d64 100644
--- a/packages/database/lib/migrations/20231027135906_slack_notifications_option.cjs
+++ b/packages/database/lib/migrations/20231027135906_slack_notifications_option.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_environments';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('slack_notifications').defaultTo(false);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('slack_notifications');
});
diff --git a/packages/database/lib/migrations/20231030112937_slack_notifications_table.cjs b/packages/database/lib/migrations/20231030112937_slack_notifications_table.cjs
index 0c7114d7377..c55371d1db1 100644
--- a/packages/database/lib/migrations/20231030112937_slack_notifications_table.cjs
+++ b/packages/database/lib/migrations/20231030112937_slack_notifications_table.cjs
@@ -1,6 +1,6 @@
const DB_TABLE = '_nango_slack_notifications';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.createTable(DB_TABLE, function (table) {
table.increments('id').primary();
table.boolean('open').defaultTo(true).index();
@@ -14,6 +14,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.dropTable(DB_TABLE);
};
diff --git a/packages/database/lib/migrations/20231030124201_add_endpoints_to_sync_configs.cjs b/packages/database/lib/migrations/20231030124201_add_endpoints_to_sync_configs.cjs
index 2b7f04b42aa..843dee73914 100644
--- a/packages/database/lib/migrations/20231030124201_add_endpoints_to_sync_configs.cjs
+++ b/packages/database/lib/migrations/20231030124201_add_endpoints_to_sync_configs.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_endpoints';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(tableName, function (table) {
table.increments('id').primary();
table.string('method').notNullable().index();
@@ -11,6 +11,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(tableName);
};
diff --git a/packages/database/lib/migrations/20231102095648_create_onboarding_demo.cjs b/packages/database/lib/migrations/20231102095648_create_onboarding_demo.cjs
index 99d90c7e235..481391f2365 100644
--- a/packages/database/lib/migrations/20231102095648_create_onboarding_demo.cjs
+++ b/packages/database/lib/migrations/20231102095648_create_onboarding_demo.cjs
@@ -1,6 +1,6 @@
const DB_TABLE = '_nango_onboarding_demo_progress';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.createTable(DB_TABLE, function (table) {
table.increments('id').primary();
table.integer('user_id').unsigned().references('id').inTable(`_nango_users`).index();
@@ -10,6 +10,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.dropTable(DB_TABLE);
};
diff --git a/packages/database/lib/migrations/20231106085744_add_sync_index.cjs b/packages/database/lib/migrations/20231106085744_add_sync_index.cjs
index 5c0005205ca..91e7b18d0ab 100644
--- a/packages/database/lib/migrations/20231106085744_add_sync_index.cjs
+++ b/packages/database/lib/migrations/20231106085744_add_sync_index.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_syncs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.index(['nango_connection_id', 'deleted'], 'nango_syncs_nango_connection_id_deleted_index');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropIndex('nango_syncs_nango_connection_id_deleted_index');
});
diff --git a/packages/database/lib/migrations/20231106121117_delete_connection_improvements.cjs b/packages/database/lib/migrations/20231106121117_delete_connection_improvements.cjs
index 7479ea4e867..a564a59cabd 100644
--- a/packages/database/lib/migrations/20231106121117_delete_connection_improvements.cjs
+++ b/packages/database/lib/migrations/20231106121117_delete_connection_improvements.cjs
@@ -3,7 +3,7 @@ const SYNC_RECORDS_TABLE = '_nango_sync_data_records';
const SYNC_RECORDS_DELETE_TABLE = '_nango_sync_data_records_deletes';
const SYNC_SCHEDULE_TABLE = '_nango_sync_schedules';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(SYNC_RECORDS_DELETE_TABLE, function (table) {
table.index('sync_id');
});
@@ -21,7 +21,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.table(SYNC_RECORDS_DELETE_TABLE, function (table) {
table.dropIndex('sync_id');
});
diff --git a/packages/database/lib/migrations/20231108201904_nango_config_v2_updates.cjs b/packages/database/lib/migrations/20231108201904_nango_config_v2_updates.cjs
index 69a9e6de738..8462e9d64fa 100644
--- a/packages/database/lib/migrations/20231108201904_nango_config_v2_updates.cjs
+++ b/packages/database/lib/migrations/20231108201904_nango_config_v2_updates.cjs
@@ -1,13 +1,13 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.string('input').nullable();
table.string('sync_type').nullable();
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('input');
table.dropColumn('sync_type');
diff --git a/packages/database/lib/migrations/20231204191116_add_webhooks_subscriptions_to_config.cjs b/packages/database/lib/migrations/20231204191116_add_webhooks_subscriptions_to_config.cjs
index 27c8e46c3b5..78cae42df3a 100644
--- a/packages/database/lib/migrations/20231204191116_add_webhooks_subscriptions_to_config.cjs
+++ b/packages/database/lib/migrations/20231204191116_add_webhooks_subscriptions_to_config.cjs
@@ -1,12 +1,12 @@
const TABLE_NAME = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.specificType('webhook_subscriptions', 'text ARRAY');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable(TABLE_NAME, function (table) {
table.dropColumn('webhook_subscriptions');
});
diff --git a/packages/database/lib/migrations/20231205151403_add_uuid_to_environment.cjs b/packages/database/lib/migrations/20231205151403_add_uuid_to_environment.cjs
index 11a85f7c528..9ed674d8cd5 100644
--- a/packages/database/lib/migrations/20231205151403_add_uuid_to_environment.cjs
+++ b/packages/database/lib/migrations/20231205151403_add_uuid_to_environment.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_environments';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(tableName, function (table) {
table.uuid('uuid').defaultTo(knex.raw('uuid_generate_v4()')).index();
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.table(tableName, function (table) {
table.dropColumn('uuid');
});
diff --git a/packages/database/lib/migrations/20231208181152_update_jobs_type_enum.cjs b/packages/database/lib/migrations/20231208181152_update_jobs_type_enum.cjs
index 2b1183438e1..5b3e43910fe 100644
--- a/packages/database/lib/migrations/20231208181152_update_jobs_type_enum.cjs
+++ b/packages/database/lib/migrations/20231208181152_update_jobs_type_enum.cjs
@@ -1,6 +1,6 @@
const tableName = '_nango_sync_jobs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema
.alterTable(tableName, function (table) {
table.string('type_new').defaultsTo('initial').notNullable();
@@ -22,7 +22,7 @@ exports.up = function (knex, _) {
);
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema
.alterTable(tableName, function (table) {
table.enu('type', ['INITIAL', 'INCREMENTAL']).defaultTo('initial').notNullable();
diff --git a/packages/database/lib/migrations/20231213124728_drop_sync_data_records_indexes.cjs b/packages/database/lib/migrations/20231213124728_drop_sync_data_records_indexes.cjs
index 00b51322da4..b93ececddb7 100644
--- a/packages/database/lib/migrations/20231213124728_drop_sync_data_records_indexes.cjs
+++ b/packages/database/lib/migrations/20231213124728_drop_sync_data_records_indexes.cjs
@@ -1,6 +1,6 @@
exports.config = { transaction: false };
-exports.up = function (_knex) {
+exports.up = function () {
return Promise.resolve();
/*
* Production only migration
@@ -17,6 +17,6 @@ exports.up = function (_knex) {
*/
};
-exports.down = function (_knex) {
+exports.down = function () {
return Promise.resolve();
};
diff --git a/packages/database/lib/migrations/20240131121245_add_auth_webhook_option.cjs b/packages/database/lib/migrations/20240131121245_add_auth_webhook_option.cjs
index d976972fef1..36d98153f76 100644
--- a/packages/database/lib/migrations/20240131121245_add_auth_webhook_option.cjs
+++ b/packages/database/lib/migrations/20240131121245_add_auth_webhook_option.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_environments';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('send_auth_webhook').defaultTo(false);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('send_auth_webhook');
});
diff --git a/packages/database/lib/migrations/20240326141028_hash-key.cjs b/packages/database/lib/migrations/20240326141028_hash-key.cjs
index ab5f0a16e2c..8c10d06376e 100644
--- a/packages/database/lib/migrations/20240326141028_hash-key.cjs
+++ b/packages/database/lib/migrations/20240326141028_hash-key.cjs
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
const utils = require('node:util');
const crypto = require('node:crypto');
diff --git a/packages/database/lib/migrations/20240403100610_add_account_status_info.cjs b/packages/database/lib/migrations/20240403100610_add_account_status_info.cjs
index 5743dd57a4f..17fbf8e0a20 100644
--- a/packages/database/lib/migrations/20240403100610_add_account_status_info.cjs
+++ b/packages/database/lib/migrations/20240403100610_add_account_status_info.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_accounts';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.alterTable(tableName, function (table) {
table.boolean('is_capped').defaultTo(true);
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.table(tableName, function (table) {
table.dropColumn('is_capped');
});
diff --git a/packages/database/lib/migrations/20240404185301_add_sync_last_fetch_at.cjs b/packages/database/lib/migrations/20240404185301_add_sync_last_fetch_at.cjs
index dc7e4ad43d4..85bf6ce3e61 100644
--- a/packages/database/lib/migrations/20240404185301_add_sync_last_fetch_at.cjs
+++ b/packages/database/lib/migrations/20240404185301_add_sync_last_fetch_at.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_syncs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.dateTime('last_fetched_at');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('last_fetched_at');
});
diff --git a/packages/database/lib/migrations/20240405171723_add_enabled_flag_on_sync_config.cjs b/packages/database/lib/migrations/20240405171723_add_enabled_flag_on_sync_config.cjs
index d1eb54b5bc3..33c8618ec69 100644
--- a/packages/database/lib/migrations/20240405171723_add_enabled_flag_on_sync_config.cjs
+++ b/packages/database/lib/migrations/20240405171723_add_enabled_flag_on_sync_config.cjs
@@ -1,12 +1,12 @@
const tableName = '_nango_sync_configs';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable(tableName, function (table) {
table.boolean('enabled').defaultTo(true);
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table(tableName, function (table) {
table.dropColumn('enabled');
});
diff --git a/packages/database/lib/migrations/20240430171723_oauth_session_activity_log_id.cjs b/packages/database/lib/migrations/20240430171723_oauth_session_activity_log_id.cjs
index c00f246c68c..a52dfd0a313 100644
--- a/packages/database/lib/migrations/20240430171723_oauth_session_activity_log_id.cjs
+++ b/packages/database/lib/migrations/20240430171723_oauth_session_activity_log_id.cjs
@@ -1,10 +1,10 @@
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.alterTable('_nango_oauth_sessions', function (table) {
table.string('activity_log_id');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.table('_nango_oauth_sessions', function (table) {
table.dropColumn('activity_log_id');
});
diff --git a/packages/database/lib/migrations/20240502171723_index_syncs.cjs b/packages/database/lib/migrations/20240502171723_index_syncs.cjs
index 088a9d735ca..0d001c90055 100644
--- a/packages/database/lib/migrations/20240502171723_index_syncs.cjs
+++ b/packages/database/lib/migrations/20240502171723_index_syncs.cjs
@@ -1,13 +1,13 @@
exports.config = { transaction: false };
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.raw(
'CREATE INDEX CONCURRENTLY "idx_connectionid_name_where_deleted" ON "_nango_syncs" USING BTREE ("nango_connection_id", "name") WHERE deleted = false'
);
await knex.schema.raw('CREATE INDEX CONCURRENTLY "idx_id_where_deleted" ON "_nango_syncs" USING BTREE ("id") WHERE deleted = false');
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.raw('DROP INDEX CONCURRENTLY idx_connectionid_name_where_deleted');
await knex.schema.raw('DROP INDEX CONCURRENTLY idx_id_where_deleted');
};
diff --git a/packages/database/lib/migrations/20240506171723_index_environments.cjs b/packages/database/lib/migrations/20240506171723_index_environments.cjs
index b91b37ae30b..e9e598546ca 100644
--- a/packages/database/lib/migrations/20240506171723_index_environments.cjs
+++ b/packages/database/lib/migrations/20240506171723_index_environments.cjs
@@ -1,6 +1,6 @@
exports.config = { transaction: false };
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.raw(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_environments_accountid_name" ON "_nango_environments" USING BTREE ("account_id","name")'
);
@@ -9,7 +9,7 @@ exports.up = async function (knex, _) {
);
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.raw('DROP INDEX CONCURRENTLY idx_environments_accountid_name');
await knex.schema.raw('DROP INDEX CONCURRENTLY idx_environments_secretkeyhashed');
};
diff --git a/packages/database/lib/migrations/20240508141028_hash-key-backfill.cjs b/packages/database/lib/migrations/20240508141028_hash-key-backfill.cjs
index defc939debb..26d1575a595 100644
--- a/packages/database/lib/migrations/20240508141028_hash-key-backfill.cjs
+++ b/packages/database/lib/migrations/20240508141028_hash-key-backfill.cjs
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
exports.config = { transaction: false };
const utils = require('node:util');
diff --git a/packages/database/lib/migrations/20240509171724_add_webhook_secondary.cjs b/packages/database/lib/migrations/20240509171724_add_webhook_secondary.cjs
index 24c8553495b..ca224104b94 100644
--- a/packages/database/lib/migrations/20240509171724_add_webhook_secondary.cjs
+++ b/packages/database/lib/migrations/20240509171724_add_webhook_secondary.cjs
@@ -1,10 +1,10 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_environments', function (table) {
table.text('webhook_url_secondary');
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_environments', function (table) {
table.dropColumn('webhook_url_secondary');
});
diff --git a/packages/database/lib/migrations/20240517171724_verified_flag.cjs b/packages/database/lib/migrations/20240517171724_verified_flag.cjs
index d6c40cdf100..aeec76d8c75 100644
--- a/packages/database/lib/migrations/20240517171724_verified_flag.cjs
+++ b/packages/database/lib/migrations/20240517171724_verified_flag.cjs
@@ -1,4 +1,4 @@
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.alterTable('_nango_users', function (table) {
table.boolean('email_verified').defaultTo(true);
table.string('email_verification_token').nullable();
@@ -7,7 +7,7 @@ exports.up = async function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.alterTable('_nango_users', function (table) {
table.dropColumn('email_verified');
table.dropColumn('email_verification_token');
diff --git a/packages/database/lib/migrations/20240527094039_add_notifications_table.cjs b/packages/database/lib/migrations/20240527094039_add_notifications_table.cjs
index 2a906cfaedd..7ab2c520fd6 100644
--- a/packages/database/lib/migrations/20240527094039_add_notifications_table.cjs
+++ b/packages/database/lib/migrations/20240527094039_add_notifications_table.cjs
@@ -1,6 +1,6 @@
const DB_TABLE = '_nango_active_logs';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.createTable(DB_TABLE, function (table) {
table.increments('id').primary();
table.string('type', 'varchar(255)').notNullable();
@@ -17,6 +17,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.dropTable(DB_TABLE);
};
diff --git a/packages/database/lib/migrations/20240529171723_add_post_connection_script.cjs b/packages/database/lib/migrations/20240529171723_add_post_connection_script.cjs
index 65242736e95..35d5abb16cf 100644
--- a/packages/database/lib/migrations/20240529171723_add_post_connection_script.cjs
+++ b/packages/database/lib/migrations/20240529171723_add_post_connection_script.cjs
@@ -1,6 +1,6 @@
const TABLE_NAME = '_nango_post_connection_scripts';
-exports.up = function (knex, _) {
+exports.up = function (knex) {
return knex.schema.createTable(TABLE_NAME, function (table) {
table.increments('id').primary();
table.integer('config_id').unsigned().notNullable();
@@ -13,6 +13,6 @@ exports.up = function (knex, _) {
});
};
-exports.down = function (knex, _) {
+exports.down = function (knex) {
return knex.schema.dropTable(TABLE_NAME);
};
diff --git a/packages/database/lib/migrations/20240529180658_add_notifications_table_index.cjs b/packages/database/lib/migrations/20240529180658_add_notifications_table_index.cjs
index 71501ac418c..c06c32f01a4 100644
--- a/packages/database/lib/migrations/20240529180658_add_notifications_table_index.cjs
+++ b/packages/database/lib/migrations/20240529180658_add_notifications_table_index.cjs
@@ -1,9 +1,9 @@
exports.config = { transaction: false };
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.raw('CREATE INDEX CONCURRENTLY "idx_sync_id_active_true" ON "_nango_active_logs" USING BTREE ("sync_id") WHERE active = true');
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.raw('DROP INDEX CONCURRENTLY idx_sync_id_active_true');
};
diff --git a/packages/database/lib/migrations/20240531122550_add_webhooks_table.cjs b/packages/database/lib/migrations/20240531122550_add_webhooks_table.cjs
index da711839253..dddccb29cf3 100644
--- a/packages/database/lib/migrations/20240531122550_add_webhooks_table.cjs
+++ b/packages/database/lib/migrations/20240531122550_add_webhooks_table.cjs
@@ -1,6 +1,6 @@
const DB_TABLE = '_nango_external_webhooks';
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
return knex.schema.createTable(DB_TABLE, function (table) {
table.increments('id').primary();
table.integer('environment_id').unsigned().notNullable();
@@ -15,6 +15,6 @@ exports.up = async function (knex, _) {
});
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
return knex.schema.dropTable(DB_TABLE);
};
diff --git a/packages/database/lib/migrations/20240603083808_post_connection_scripts_index.cjs b/packages/database/lib/migrations/20240603083808_post_connection_scripts_index.cjs
index a5d37bdd31c..ff1ac2ee723 100644
--- a/packages/database/lib/migrations/20240603083808_post_connection_scripts_index.cjs
+++ b/packages/database/lib/migrations/20240603083808_post_connection_scripts_index.cjs
@@ -1,11 +1,11 @@
exports.config = { transaction: false };
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.raw(
'CREATE INDEX CONCURRENTLY "idx_active_config_id_name" ON "_nango_post_connection_scripts" USING BTREE ("config_id", "name") WHERE active = true'
);
};
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.raw('DROP INDEX CONCURRENTLY idx_active_config_id_name');
};
diff --git a/packages/database/lib/migrations/20240708161616_index_sync_configs.cjs b/packages/database/lib/migrations/20240708161616_index_sync_configs.cjs
index 257cdf2f208..c3561c21d60 100644
--- a/packages/database/lib/migrations/20240708161616_index_sync_configs.cjs
+++ b/packages/database/lib/migrations/20240708161616_index_sync_configs.cjs
@@ -3,7 +3,7 @@ exports.config = { transaction: false };
/**
* @param {import('knex').Knex} knex
*/
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.raw(
`CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_nango_sync_configs_active
ON _nango_sync_configs USING BTREE (type, sync_name, nango_config_id)
@@ -13,6 +13,6 @@ exports.up = async function (knex, _) {
/**
* @param {import('knex').Knex} knex
*/
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.raw('DROP INDEX CONCURRENTLY IF EXISTS idx_nango_sync_configs_active');
};
diff --git a/packages/database/lib/migrations/20240912210952_index_sync_config_id_sync.cjs b/packages/database/lib/migrations/20240912210952_index_sync_config_id_sync.cjs
index 5cc5eb16100..d0433bcf5b8 100644
--- a/packages/database/lib/migrations/20240912210952_index_sync_config_id_sync.cjs
+++ b/packages/database/lib/migrations/20240912210952_index_sync_config_id_sync.cjs
@@ -3,7 +3,7 @@ exports.config = { transaction: false };
/**
* @param {import('knex').Knex} knex
*/
-exports.up = async function (knex, _) {
+exports.up = async function (knex) {
await knex.schema.raw(
`CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_sync_config_id_where_deleted"
ON "_nango_syncs" USING BTREE ("sync_config_id")
@@ -13,6 +13,6 @@ exports.up = async function (knex, _) {
/**
* @param {import('knex').Knex} knex
*/
-exports.down = async function (knex, _) {
+exports.down = async function (knex) {
await knex.schema.raw('DROP INDEX CONCURRENTLY IF EXISTS idx_sync_config_id_where_deleted');
};
diff --git a/packages/database/lib/migrations/20241122212401_integration_backfill_missing_fields.cjs b/packages/database/lib/migrations/20241122212401_integration_backfill_missing_fields.cjs
index 6191f13eb78..cb76a1ebfef 100644
--- a/packages/database/lib/migrations/20241122212401_integration_backfill_missing_fields.cjs
+++ b/packages/database/lib/migrations/20241122212401_integration_backfill_missing_fields.cjs
@@ -1,7 +1,3 @@
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-call */
-/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('node:path');
const fs = require('node:fs');
const yaml = require('js-yaml');
@@ -23,7 +19,7 @@ exports.up = async function (knex) {
const needsClientId = ['OAUTH1', 'OAUTH2', 'TBA', 'APP'];
const clientIdProviders = Object.entries(providers)
- .filter(([_, config]) => needsClientId.includes(config.auth_mode))
+ .filter(([, config]) => needsClientId.includes(config.auth_mode))
.map(([name]) => name);
await knex
.queryBuilder()
@@ -35,7 +31,7 @@ exports.up = async function (knex) {
const needsClientSecret = ['OAUTH1', 'OAUTH2', 'TBA', 'APP'];
const clientSecretProviders = Object.entries(providers)
- .filter(([_, config]) => needsClientSecret.includes(config.auth_mode))
+ .filter(([, config]) => needsClientSecret.includes(config.auth_mode))
.map(([name]) => name);
await knex
.queryBuilder()
@@ -47,7 +43,7 @@ exports.up = async function (knex) {
const needsAppLink = ['APP'];
const appLinkProviders = Object.entries(providers)
- .filter(([_, config]) => needsAppLink.includes(config.auth_mode))
+ .filter(([, config]) => needsAppLink.includes(config.auth_mode))
.map(([name]) => name);
await knex
.queryBuilder()
diff --git a/packages/database/package.json b/packages/database/package.json
index f2886dbac8e..66133d4fb17 100644
--- a/packages/database/package.json
+++ b/packages/database/package.json
@@ -22,8 +22,8 @@
"tarn": "3.0.2"
},
"devDependencies": {
- "typescript": "5.3.3",
- "vitest": "1.6.0"
+ "typescript": "5.7.3",
+ "vitest": "2.1.8"
},
"files": [
"dist/**/*"
diff --git a/packages/fleet/lib/db/client.ts b/packages/fleet/lib/db/client.ts
index 550a1b89e5c..9676d6d6df8 100644
--- a/packages/fleet/lib/db/client.ts
+++ b/packages/fleet/lib/db/client.ts
@@ -13,7 +13,7 @@ export class DatabaseClient {
public url: string;
private config: knex.Knex.Config;
- constructor({ url, schema, poolMax = 50 }: { url: string; schema: string; poolMax?: number }) {
+ constructor({ url, schema, poolMax = 15 }: { url: string; schema: string; poolMax?: number }) {
this.url = url;
this.schema = schema;
this.config = {
diff --git a/packages/fleet/lib/db/migrations/20241222195951_create_node_config_overrides.ts b/packages/fleet/lib/db/migrations/20241222195951_create_node_config_overrides.ts
index cb670bfe769..fd8224b12f3 100644
--- a/packages/fleet/lib/db/migrations/20241222195951_create_node_config_overrides.ts
+++ b/packages/fleet/lib/db/migrations/20241222195951_create_node_config_overrides.ts
@@ -16,4 +16,6 @@ export async function up(knex: Knex): Promise
{
`);
}
-export async function down(_knex: Knex): Promise {}
+export async function down(): Promise {
+ //
+}
diff --git a/packages/fleet/lib/fleet.ts b/packages/fleet/lib/fleet.ts
index 1dc0550edb2..928075065d6 100644
--- a/packages/fleet/lib/fleet.ts
+++ b/packages/fleet/lib/fleet.ts
@@ -17,7 +17,7 @@ import { noopNodeProvider } from './node-providers/noop.js';
const defaultDbUrl =
envs.NANGO_DATABASE_URL ||
- `postgres://${encodeURIComponent(envs.NANGO_DB_USER)}:${encodeURIComponent(envs.NANGO_DB_PASSWORD)}@${envs.NANGO_DB_HOST}:${envs.NANGO_DB_PORT}/${envs.NANGO_DB_NAME}${envs.NANGO_DB_SSL ? '?sslmode=no-verify' : ''}`;
+ `postgres://${encodeURIComponent(envs.NANGO_DB_USER)}:${encodeURIComponent(envs.NANGO_DB_PASSWORD)}@${envs.NANGO_DB_HOST}:${envs.NANGO_DB_PORT}/${envs.NANGO_DB_NAME}?application_name=${envs.NANGO_DB_APPLICATION_NAME}${envs.NANGO_DB_SSL ? '&sslmode=no-verify' : ''}`;
export class Fleet {
public fleetId: string;
diff --git a/packages/fleet/lib/index.ts b/packages/fleet/lib/index.ts
index 9589ad499f4..06fe30c65e3 100644
--- a/packages/fleet/lib/index.ts
+++ b/packages/fleet/lib/index.ts
@@ -1,4 +1,4 @@
export * from './fleet.js';
export * from './types.js';
-export * from './node-providers/node_provider.js';
+export type * from './node-providers/node_provider.js';
export * from './models/helpers.js';
diff --git a/packages/fleet/lib/models/deployments.ts b/packages/fleet/lib/models/deployments.ts
index 5c61ea471c2..73f8d9e1a47 100644
--- a/packages/fleet/lib/models/deployments.ts
+++ b/packages/fleet/lib/models/deployments.ts
@@ -34,7 +34,7 @@ const DBDeployment = {
export async function create(db: knex.Knex, commitId: CommitHash): Promise> {
try {
- return db.transaction(async (trx) => {
+ return await db.transaction(async (trx) => {
// do nothing if commitId is already active deployment
const active = await getActive(db);
if (active.isErr()) {
diff --git a/packages/fleet/lib/models/nodes.ts b/packages/fleet/lib/models/nodes.ts
index b4e736f868f..dbcf1b4225e 100644
--- a/packages/fleet/lib/models/nodes.ts
+++ b/packages/fleet/lib/models/nodes.ts
@@ -204,7 +204,7 @@ export async function transitionTo(
}
): Promise> {
try {
- return db.transaction(async (trx) => {
+ return await db.transaction(async (trx) => {
const getNode = await get(trx, props.nodeId, { forUpdate: true });
if (getNode.isErr()) {
return getNode;
diff --git a/packages/fleet/package.json b/packages/fleet/package.json
index 13b751595c0..eb5146cb919 100644
--- a/packages/fleet/package.json
+++ b/packages/fleet/package.json
@@ -23,6 +23,6 @@
},
"devDependencies": {
"@nangohq/types": "file:../types",
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
}
}
diff --git a/packages/frontend/lib/connectUI.ts b/packages/frontend/lib/connectUI.ts
index 7df2e90b8d3..0c27830293b 100644
--- a/packages/frontend/lib/connectUI.ts
+++ b/packages/frontend/lib/connectUI.ts
@@ -125,6 +125,8 @@ export class ConnectUI {
if (this.iframe) {
document.body.removeChild(this.iframe);
this.iframe = null;
+
+ document.body.style.overflow = '';
}
}
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 3bfd3578e3e..6526b3ee3df 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -20,6 +20,7 @@
"files": [
"dist/**/*.js",
"dist/**/*.d.ts",
+ "dist/**/*.map",
"!**/*.json",
"README.md"
]
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 7d31f7ff63b..008e8d94d72 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -3,7 +3,10 @@
"compilerOptions": {
"lib": ["es2015", "dom"],
"rootDir": "lib",
- "outDir": "dist"
+ "outDir": "dist",
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true
},
"references": [
{
diff --git a/packages/jobs/.gitignore b/packages/jobs/.gitignore
deleted file mode 100644
index 8c3aba0aad2..00000000000
--- a/packages/jobs/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-tsconfig.tsbuildinfo
-dist/*
-node_modules
diff --git a/packages/jobs/lib/crons/deleteSyncsData.ts b/packages/jobs/lib/crons/deleteSyncsData.ts
index 733ee85ed6d..722bd7e15e4 100644
--- a/packages/jobs/lib/crons/deleteSyncsData.ts
+++ b/packages/jobs/lib/crons/deleteSyncsData.ts
@@ -3,7 +3,6 @@ import db from '@nangohq/database';
import { errorManager, ErrorSourceEnum, hardDeleteJobs, findRecentlyDeletedSync, Orchestrator } from '@nangohq/shared';
import { records } from '@nangohq/records';
import { getLogger, metrics } from '@nangohq/utils';
-import tracer from 'dd-trace';
import { orchestratorClient } from '../clients.js';
const logger = getLogger('Jobs');
@@ -24,7 +23,7 @@ export function deleteSyncsData(): void {
logger.info('[deleteSyncs] ✅ done');
} catch (err) {
const e = new Error('failed_to_hard_delete_syncs_data', { cause: err instanceof Error ? err.message : err });
- errorManager.report(e, { source: ErrorSourceEnum.PLATFORM }, tracer);
+ errorManager.report(e, { source: ErrorSourceEnum.PLATFORM });
}
metrics.duration(metrics.Types.JOBS_DELETE_SYNCS_DATA, Date.now() - start);
});
diff --git a/packages/jobs/lib/crons/timeoutLogsOperations.ts b/packages/jobs/lib/crons/timeoutLogsOperations.ts
index f6a4a160304..96e3afbfc2a 100644
--- a/packages/jobs/lib/crons/timeoutLogsOperations.ts
+++ b/packages/jobs/lib/crons/timeoutLogsOperations.ts
@@ -1,6 +1,5 @@
import * as cron from 'node-cron';
import { errorManager, ErrorSourceEnum } from '@nangohq/shared';
-import tracer from 'dd-trace';
import { envs, model } from '@nangohq/logs';
import { getLogger } from '@nangohq/utils';
@@ -20,7 +19,7 @@ export function timeoutLogsOperations(): void {
await model.setTimeoutForAll();
logger.info(`✅ Timeouted`);
} catch (err) {
- errorManager.report(err, { source: ErrorSourceEnum.PLATFORM }, tracer);
+ errorManager.report(err, { source: ErrorSourceEnum.PLATFORM });
}
}
);
diff --git a/packages/jobs/lib/execution/sync.integration.test.ts b/packages/jobs/lib/execution/sync.integration.test.ts
index fe050abe22d..3e838ac2c19 100644
--- a/packages/jobs/lib/execution/sync.integration.test.ts
+++ b/packages/jobs/lib/execution/sync.integration.test.ts
@@ -3,7 +3,7 @@ import { multipleMigrations } from '@nangohq/database';
import type { UnencryptedRecordData, ReturnedRecord } from '@nangohq/records';
import { records as recordsService, format as recordsFormatter, migrate as migrateRecords, clearDbTestsOnly as clearRecordsDb } from '@nangohq/records';
import { handleSyncSuccess, startSync } from './sync.js';
-import type { TaskSync } from '@nangohq/nango-orchestrator';
+import type { TaskAction, TaskOnEvent, TaskSync, TaskSyncAbort, TaskWebhook } from '@nangohq/nango-orchestrator';
import type { Connection, Sync, SyncResult, Job as SyncJob, SyncConfig } from '@nangohq/shared';
import { isSyncJobRunning, seeders, getLatestSyncJob, updateSyncJobResult } from '@nangohq/shared';
import { Ok, stringifyError } from '@nangohq/utils';
@@ -191,11 +191,11 @@ const runJob = async (
provider_config_key: connection.provider_config_key,
connection_id: connection.connection_id
},
- isSync: () => true,
- isAction: () => false,
- isOnEvent: () => false,
- isSyncAbort: () => false,
- isWebhook: () => false
+ isSync: (): this is TaskSync => true,
+ isWebhook: (): this is TaskWebhook => false,
+ isAction: (): this is TaskAction => false,
+ isOnEvent: (): this is TaskOnEvent => false,
+ isSyncAbort: (): this is TaskSyncAbort => false
};
const nangoProps = await startSync(task, mockStartScript);
if (nangoProps.isErr()) {
@@ -324,7 +324,7 @@ async function seeds(records: UnencryptedRecordData[], trackDeletes: boolean) {
const { env } = await seeders.seedAccountEnvAndUser();
const model = 'GithubIssue';
- const connection = await seeders.createConnectionSeed(env, 'github');
+ const connection = await seeders.createConnectionSeed({ env, provider: 'github' });
if (!connection.id) {
throw new Error('Failed to create connection');
diff --git a/packages/jobs/lib/execution/sync.ts b/packages/jobs/lib/execution/sync.ts
index 1a474c7b174..46e9ede9394 100644
--- a/packages/jobs/lib/execution/sync.ts
+++ b/packages/jobs/lib/execution/sync.ts
@@ -224,7 +224,7 @@ export async function handleSyncSuccess({ nangoProps }: { nangoProps: NangoProps
for (const model of nangoProps.syncConfig.models) {
let deletedKeys: string[] = [];
if (nangoProps.syncConfig.track_deletes) {
- deletedKeys = await records.markNonCurrentGenerationRecordsAsDeleted({
+ deletedKeys = await records.markPreviousGenerationRecordsAsDeleted({
connectionId: nangoProps.nangoConnectionId,
model,
syncId: nangoProps.syncId,
diff --git a/packages/jobs/lib/routes/postIdle.ts b/packages/jobs/lib/routes/postIdle.ts
index c6721b5a9bc..c76830dc661 100644
--- a/packages/jobs/lib/routes/postIdle.ts
+++ b/packages/jobs/lib/routes/postIdle.ts
@@ -31,9 +31,11 @@ const handler = async (req: EndpointRequest, res: EndpointResponse, res: EndpointResponse, res: EndpointResponse
if (register.isErr()) {
throw register.error;
}
- return res.status(200).json({ status: 'ok' });
+ res.status(200).json({ status: 'ok' });
+ return;
} catch (err) {
- return res.status(500).json({ error: { code: 'register_failed', message: err instanceof Error ? err.message : 'failed to register runner' } });
+ res.status(500).json({ error: { code: 'register_failed', message: err instanceof Error ? err.message : 'failed to register runner' } });
+ return;
}
};
diff --git a/packages/jobs/lib/runner/local.runner.ts b/packages/jobs/lib/runner/local.runner.ts
index 5b8111cea5d..00df93312d3 100644
--- a/packages/jobs/lib/runner/local.runner.ts
+++ b/packages/jobs/lib/runner/local.runner.ts
@@ -83,7 +83,7 @@ export class LocalRunner implements Runner {
});
}
- return Promise.resolve(new LocalRunner(runnerId, `http://localhost:${port}`, childProcess.pid));
+ return await Promise.resolve(new LocalRunner(runnerId, `http://localhost:${port}`, childProcess.pid));
} catch (err) {
throw new Error(`Unable to get runner ${runnerId}: ${stringifyError(err)}`);
}
diff --git a/packages/jobs/package.json b/packages/jobs/package.json
index b30153e1919..7230ef18421 100644
--- a/packages/jobs/package.json
+++ b/packages/jobs/package.json
@@ -27,18 +27,18 @@
"@nangohq/utils": "file:../utils",
"@nangohq/webhooks": "file:../webhooks",
"@nangohq/fleet": "file:../fleet",
- "axios": "^1.7.4",
+ "axios": "^1.7.9",
"dd-trace": "5.21.0",
"get-port": "7.1.0",
"express": "4.20.0",
"node-cron": "3.0.3",
- "zod": "3.23.8"
+ "zod": "3.24.1"
},
"devDependencies": {
"@types/node": "^20.12.2",
- "nodemon": "3.1.7",
- "typescript": "5.3.3",
- "type-fest": "4.26.1",
- "vitest": "1.6.0"
+ "nodemon": "3.1.9",
+ "typescript": "5.7.3",
+ "type-fest": "4.32.0",
+ "vitest": "2.1.8"
}
}
diff --git a/packages/keystore/package.json b/packages/keystore/package.json
index 1f5d97d2dd7..30acf23123b 100644
--- a/packages/keystore/package.json
+++ b/packages/keystore/package.json
@@ -23,6 +23,6 @@
},
"devDependencies": {
"@nangohq/types": "file:../types",
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
}
}
diff --git a/packages/kvstore/package.json b/packages/kvstore/package.json
index 07b97bf8e5c..22d22c0c197 100644
--- a/packages/kvstore/package.json
+++ b/packages/kvstore/package.json
@@ -18,7 +18,7 @@
"redis": "4.6.13"
},
"devDependencies": {
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
},
"files": [
"dist/**/*"
diff --git a/packages/logs/package.json b/packages/logs/package.json
index 243d46dfe16..e57c0b9ac0d 100644
--- a/packages/logs/package.json
+++ b/packages/logs/package.json
@@ -24,12 +24,12 @@
"@opentelemetry/sdk-trace-base": "1.27.0",
"@opentelemetry/sdk-trace-node": "1.27.0",
"@opentelemetry/semantic-conventions": "1.27.0",
- "zod": "3.23.8"
+ "zod": "3.24.1"
},
"devDependencies": {
"@nangohq/types": "file:../types",
- "type-fest": "4.26.1",
- "vitest": "1.6.0"
+ "type-fest": "4.32.0",
+ "vitest": "2.1.8"
},
"files": [
"dist/**/*"
diff --git a/packages/nango-yaml/lib/modelsParser.ts b/packages/nango-yaml/lib/modelsParser.ts
index c14891147e7..51cda70f3be 100644
--- a/packages/nango-yaml/lib/modelsParser.ts
+++ b/packages/nango-yaml/lib/modelsParser.ts
@@ -70,7 +70,7 @@ export class ModelsParser {
// At this point we are sure it's a Model
stack.add(name);
- this.parseOne({ name, fields: this.raw[name]!, stack });
+ this.parseOne({ name, fields: this.raw[name], stack });
return true;
}
diff --git a/packages/nango-yaml/package.json b/packages/nango-yaml/package.json
index 92f31c934d7..4f2742dbdf6 100644
--- a/packages/nango-yaml/package.json
+++ b/packages/nango-yaml/package.json
@@ -18,7 +18,7 @@
},
"devDependencies": {
"@nangohq/types": "0.48.1",
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
},
"files": [
"dist/**/*"
diff --git a/packages/node-client/lib/index.ts b/packages/node-client/lib/index.ts
index 6572c4b0ba3..b03c47496ea 100644
--- a/packages/node-client/lib/index.ts
+++ b/packages/node-client/lib/index.ts
@@ -745,6 +745,7 @@ export class Nango {
* @param input - An optional input data for the action
* @returns A promise that resolves with an object containing the response data from the triggered action
*/
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
public async triggerAction(providerConfigKey: string, connectionId: string, actionName: string, input?: In): Promise {
const url = `${this.serverUrl}/action/trigger`;
diff --git a/packages/node-client/lib/utils.ts b/packages/node-client/lib/utils.ts
index 3f169e8c797..8961d06db64 100644
--- a/packages/node-client/lib/utils.ts
+++ b/packages/node-client/lib/utils.ts
@@ -41,7 +41,7 @@ export function getUserAgent(userAgent?: string): string {
return `nango-node-client/${NANGO_VERSION} (${osName}/${osVersion}; node.js/${nodeVersion})${userAgent ? `; ${userAgent}` : ''}`;
}
-export function addQueryParams(url: URL, queries?: Record | undefined) {
+export function addQueryParams(url: URL, queries?: Record) {
if (!queries) {
return;
}
diff --git a/packages/node-client/package.json b/packages/node-client/package.json
index 9f2b66348a8..1748b3650ec 100644
--- a/packages/node-client/package.json
+++ b/packages/node-client/package.json
@@ -25,7 +25,7 @@
},
"license": "SEE LICENSE IN LICENSE FILE IN GIT REPOSITORY",
"dependencies": {
- "axios": "^1.7.4"
+ "axios": "^1.7.9"
},
"engines": {
"node": ">=18.0"
@@ -38,6 +38,6 @@
"devDependencies": {
"@nangohq/types": "0.48.1",
"tsup": "^8.2.4",
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
}
}
diff --git a/packages/orchestrator/lib/clients/types.ts b/packages/orchestrator/lib/clients/types.ts
index b950473d393..2f49bab4f38 100644
--- a/packages/orchestrator/lib/clients/types.ts
+++ b/packages/orchestrator/lib/clients/types.ts
@@ -108,11 +108,11 @@ export function TaskSync(props: TaskCommonFields & SyncArgs): TaskSync {
debug: props.debug,
connection: props.connection,
groupKey: props.groupKey,
- isSync: () => true,
- isWebhook: () => false,
- isAction: () => false,
- isOnEvent: () => false,
- isSyncAbort: () => false
+ isSync: (): this is TaskSync => true,
+ isWebhook: (): this is TaskWebhook => false,
+ isAction: (): this is TaskAction => false,
+ isOnEvent: (): this is TaskOnEvent => false,
+ isSyncAbort: (): this is TaskSyncAbort => false
};
}
@@ -130,11 +130,11 @@ export function TaskSyncAbort(props: TaskCommonFields & SyncArgs & AbortArgs): T
connection: props.connection,
groupKey: props.groupKey,
reason: props.reason,
- isSync: () => false,
- isWebhook: () => false,
- isAction: () => false,
- isOnEvent: () => false,
- isSyncAbort: () => true
+ isSync: (): this is TaskSync => false,
+ isWebhook: (): this is TaskWebhook => false,
+ isAction: (): this is TaskAction => false,
+ isOnEvent: (): this is TaskOnEvent => false,
+ isSyncAbort: (): this is TaskSyncAbort => true
};
}
@@ -150,11 +150,11 @@ export function TaskAction(props: TaskCommonFields & ActionArgs): TaskAction {
activityLogId: props.activityLogId,
input: props.input,
groupKey: props.groupKey,
- isSync: () => false,
- isWebhook: () => false,
- isAction: () => true,
- isOnEvent: () => false,
- isSyncAbort: () => false
+ isSync: (): this is TaskSync => false,
+ isWebhook: (): this is TaskWebhook => false,
+ isAction: (): this is TaskAction => true,
+ isOnEvent: (): this is TaskOnEvent => false,
+ isSyncAbort: (): this is TaskSyncAbort => false
};
}
@@ -171,11 +171,11 @@ export function TaskWebhook(props: TaskCommonFields & WebhookArgs): TaskWebhook
activityLogId: props.activityLogId,
input: props.input,
groupKey: props.groupKey,
- isSync: () => false,
- isWebhook: () => true,
- isAction: () => false,
- isOnEvent: () => false,
- isSyncAbort: () => false
+ isSync: (): this is TaskSync => false,
+ isWebhook: (): this is TaskWebhook => true,
+ isAction: (): this is TaskAction => false,
+ isOnEvent: (): this is TaskOnEvent => false,
+ isSyncAbort: (): this is TaskSyncAbort => false
};
}
@@ -192,11 +192,11 @@ export function TaskOnEvent(props: TaskCommonFields & OnEventArgs): TaskOnEvent
fileLocation: props.fileLocation,
activityLogId: props.activityLogId,
groupKey: props.groupKey,
- isSync: () => false,
- isWebhook: () => false,
- isAction: () => false,
- isOnEvent: () => true,
- isSyncAbort: () => false
+ isSync: (): this is TaskSync => false,
+ isWebhook: (): this is TaskWebhook => false,
+ isAction: (): this is TaskAction => false,
+ isOnEvent: (): this is TaskOnEvent => true,
+ isSyncAbort: (): this is TaskSyncAbort => false
};
}
diff --git a/packages/orchestrator/lib/routes/getHealth.ts b/packages/orchestrator/lib/routes/getHealth.ts
index a179c9bf2b8..4d5dc8c30e7 100644
--- a/packages/orchestrator/lib/routes/getHealth.ts
+++ b/packages/orchestrator/lib/routes/getHealth.ts
@@ -14,5 +14,7 @@ export const routeHandler: RouteHandler = {
path,
method,
validate: (_req, _res, next) => next(), // No extra validation needed
- handler: (_req, res) => res.status(200).json({ status: 'ok' })
+ handler: (_req, res) => {
+ res.status(200).json({ status: 'ok' });
+ }
};
diff --git a/packages/orchestrator/lib/routes/v1/postImmediate.ts b/packages/orchestrator/lib/routes/v1/postImmediate.ts
index d63be164a84..c7b01cc6293 100644
--- a/packages/orchestrator/lib/routes/v1/postImmediate.ts
+++ b/packages/orchestrator/lib/routes/v1/postImmediate.ts
@@ -88,9 +88,11 @@ const handler = (scheduler: Scheduler) => {
heartbeatTimeoutSecs: req.body.timeoutSettingsInSecs.heartbeat
});
if (task.isErr()) {
- return res.status(500).json({ error: { code: 'immediate_failed', message: task.error.message } });
+ res.status(500).json({ error: { code: 'immediate_failed', message: task.error.message } });
+ return;
}
- return res.status(200).json({ taskId: task.value.id });
+ res.status(200).json({ taskId: task.value.id });
+ return;
};
};
diff --git a/packages/orchestrator/lib/routes/v1/postRecurring.ts b/packages/orchestrator/lib/routes/v1/postRecurring.ts
index 6c806a78fc0..270a3f6025e 100644
--- a/packages/orchestrator/lib/routes/v1/postRecurring.ts
+++ b/packages/orchestrator/lib/routes/v1/postRecurring.ts
@@ -73,9 +73,11 @@ const handler = (scheduler: Scheduler) => {
lastScheduledTaskId: null
});
if (schedule.isErr()) {
- return res.status(500).json({ error: { code: 'recurring_failed', message: schedule.error.message } });
+ res.status(500).json({ error: { code: 'recurring_failed', message: schedule.error.message } });
+ return;
}
- return res.status(200).json({ scheduleId: schedule.value.id });
+ res.status(200).json({ scheduleId: schedule.value.id });
+ return;
};
};
diff --git a/packages/orchestrator/lib/routes/v1/putRecurring.ts b/packages/orchestrator/lib/routes/v1/putRecurring.ts
index 311317afef2..283b92bbb0e 100644
--- a/packages/orchestrator/lib/routes/v1/putRecurring.ts
+++ b/packages/orchestrator/lib/routes/v1/putRecurring.ts
@@ -48,12 +48,15 @@ const handler = (scheduler: Scheduler) => {
updatedSchedule = await scheduler.setScheduleFrequency({ scheduleName: schedule.name, frequencyMs: schedule.frequencyMs });
}
if (!updatedSchedule) {
- return res.status(400).json({ error: { code: 'put_recurring_failed', message: `invalid parameters: ${JSON.stringify(schedule)}` } });
+ res.status(400).json({ error: { code: 'put_recurring_failed', message: `invalid parameters: ${JSON.stringify(schedule)}` } });
+ return;
}
if (updatedSchedule.isErr()) {
- return res.status(500).json({ error: { code: 'put_recurring_failed', message: updatedSchedule.error.message } });
+ res.status(500).json({ error: { code: 'put_recurring_failed', message: updatedSchedule.error.message } });
+ return;
}
- return res.status(200).json({ scheduleId: updatedSchedule.value.id });
+ res.status(200).json({ scheduleId: updatedSchedule.value.id });
+ return;
};
};
diff --git a/packages/orchestrator/lib/routes/v1/schedules/postRun.ts b/packages/orchestrator/lib/routes/v1/schedules/postRun.ts
index 1bbaa20dd0e..305d778925d 100644
--- a/packages/orchestrator/lib/routes/v1/schedules/postRun.ts
+++ b/packages/orchestrator/lib/routes/v1/schedules/postRun.ts
@@ -32,9 +32,11 @@ const handler = (scheduler: Scheduler) => {
scheduleName: req.body.scheduleName
});
if (schedule.isErr()) {
- return res.status(500).json({ error: { code: 'recurring_run_failed', message: schedule.error.message } });
+ res.status(500).json({ error: { code: 'recurring_run_failed', message: schedule.error.message } });
+ return;
}
- return res.status(200).json({ scheduleId: schedule.value.id });
+ res.status(200).json({ scheduleId: schedule.value.id });
+ return;
};
};
diff --git a/packages/orchestrator/lib/routes/v1/schedules/postSearch.ts b/packages/orchestrator/lib/routes/v1/schedules/postSearch.ts
index 9a852194d66..bb784faec74 100644
--- a/packages/orchestrator/lib/routes/v1/schedules/postSearch.ts
+++ b/packages/orchestrator/lib/routes/v1/schedules/postSearch.ts
@@ -37,9 +37,11 @@ const handler = (scheduler: Scheduler) => {
...(names ? { names } : {})
});
if (getSchedules.isErr()) {
- return res.status(500).json({ error: { code: 'search_failed', message: getSchedules.error.message } });
+ res.status(500).json({ error: { code: 'search_failed', message: getSchedules.error.message } });
+ return;
}
- return res.status(200).json(getSchedules.value);
+ res.status(200).json(getSchedules.value);
+ return;
};
};
diff --git a/packages/orchestrator/lib/routes/v1/tasks/postSearch.ts b/packages/orchestrator/lib/routes/v1/tasks/postSearch.ts
index 3480553ce88..c5b04f2a195 100644
--- a/packages/orchestrator/lib/routes/v1/tasks/postSearch.ts
+++ b/packages/orchestrator/lib/routes/v1/tasks/postSearch.ts
@@ -40,9 +40,11 @@ const handler = (scheduler: Scheduler) => {
...(limit ? { limit } : {})
});
if (getTasks.isErr()) {
- return res.status(500).json({ error: { code: 'search_failed', message: getTasks.error.message } });
+ res.status(500).json({ error: { code: 'search_failed', message: getTasks.error.message } });
+ return;
}
- return res.status(200).json(getTasks.value);
+ res.status(200).json(getTasks.value);
+ return;
};
};
diff --git a/packages/orchestrator/package.json b/packages/orchestrator/package.json
index 435ff52e28e..b32d27c230c 100644
--- a/packages/orchestrator/package.json
+++ b/packages/orchestrator/package.json
@@ -20,12 +20,12 @@
"express": "4.20.0",
"get-port": "7.1.0",
"p-queue": "8.0.1",
- "zod": "3.23.8"
+ "zod": "3.24.1"
},
"devDependencies": {
"@nangohq/types": "file:../types",
- "type-fest": "4.26.1",
+ "type-fest": "4.32.0",
"@types/node": "20.12.2",
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
}
}
diff --git a/packages/persist/lib/routes/getHealth.ts b/packages/persist/lib/routes/getHealth.ts
index f6cb7b8aa7c..bacea1bdf02 100644
--- a/packages/persist/lib/routes/getHealth.ts
+++ b/packages/persist/lib/routes/getHealth.ts
@@ -14,5 +14,7 @@ export const routeHandler: RouteHandler = {
path,
method,
validate: (_req, _res, next) => next(),
- handler: (_req, res) => res.status(200).json({ status: 'ok' })
+ handler: (_req, res) => {
+ res.status(200).json({ status: 'ok' });
+ }
};
diff --git a/packages/persist/package.json b/packages/persist/package.json
index f9e0e4c7b62..6625d16222c 100644
--- a/packages/persist/package.json
+++ b/packages/persist/package.json
@@ -26,12 +26,12 @@
"@nangohq/utils": "file:../utils",
"dd-trace": "5.21.0",
"express": "^4.20.0",
- "zod": "3.23.8"
+ "zod": "3.24.1"
},
"devDependencies": {
"@types/node": "20.12.2",
"node-fetch": "^3.3.2",
- "typescript": "5.3.3",
- "vitest": "1.6.0"
+ "typescript": "5.7.3",
+ "vitest": "2.1.8"
}
}
diff --git a/packages/records/lib/db/config.ts b/packages/records/lib/db/config.ts
index 65afe86ff01..ea12f4d5533 100644
--- a/packages/records/lib/db/config.ts
+++ b/packages/records/lib/db/config.ts
@@ -5,7 +5,7 @@ export const schema = envs.RECORDS_DATABASE_SCHEMA;
const databaseUrl =
envs.RECORDS_DATABASE_URL ||
envs.NANGO_DATABASE_URL ||
- `postgres://${encodeURIComponent(envs.NANGO_DB_USER)}:${encodeURIComponent(envs.NANGO_DB_PASSWORD)}@${envs.NANGO_DB_HOST}:${envs.NANGO_DB_PORT}/${envs.NANGO_DB_NAME}`;
+ `postgres://${encodeURIComponent(envs.NANGO_DB_USER)}:${encodeURIComponent(envs.NANGO_DB_PASSWORD)}@${envs.NANGO_DB_HOST}:${envs.NANGO_DB_PORT}/${envs.NANGO_DB_NAME}?application_name=${envs.NANGO_DB_APPLICATION_NAME}`;
const runningMigrationOnly = process.argv.some((v) => v === 'migrate:latest');
const isJS = !runningMigrationOnly;
diff --git a/packages/records/lib/index.ts b/packages/records/lib/index.ts
index 91ab31040e5..6b92acb2a4f 100644
--- a/packages/records/lib/index.ts
+++ b/packages/records/lib/index.ts
@@ -1,5 +1,5 @@
export * from './db/migrate.js';
export * as records from './models/records.js';
export * as format from './helpers/format.js';
-export * from './types.js';
+export type * from './types.js';
export { clearDb as clearDbTestsOnly } from './db/test.helpers.js';
diff --git a/packages/records/lib/models/records.integration.test.ts b/packages/records/lib/models/records.integration.test.ts
index ff5acfad7aa..c35b3a98c1f 100644
--- a/packages/records/lib/models/records.integration.test.ts
+++ b/packages/records/lib/models/records.integration.test.ts
@@ -1,4 +1,4 @@
-import { expect, describe, it, beforeAll, afterEach } from 'vitest';
+import { expect, describe, it, beforeAll, afterAll } from 'vitest';
import dayjs from 'dayjs';
import * as uuid from 'uuid';
import { migrate } from '../db/migrate.js';
@@ -13,13 +13,13 @@ describe('Records service', () => {
await migrate();
});
- afterEach(async () => {
+ afterAll(async () => {
await db(RECORDS_TABLE).truncate();
});
it('Should write records', async () => {
- const connectionId = 1;
- const environmentId = 2;
+ const connectionId = Math.floor(Math.random() * 1000000);
+ const environmentId = Math.floor(Math.random() * 1000000);
const model = 'my-model';
const syncId = '00000000-0000-0000-0000-000000000000';
const records = [
@@ -50,8 +50,8 @@ describe('Records service', () => {
});
it('Should be able to encrypt and insert 2000 records under 2 seconds', async () => {
- const connectionId = 1;
- const environmentId = 2;
+ const connectionId = Math.floor(Math.random() * 1000000);
+ const environmentId = Math.floor(Math.random() * 1000000);
const model = 'my-model';
const syncId = '00000000-0000-0000-0000-000000000000';
const records = Array.from({ length: 2000 }, (_, i) => ({
@@ -76,8 +76,8 @@ describe('Records service', () => {
});
it('Should delete records', async () => {
- const connectionId = 1;
- const environmentId = 2;
+ const connectionId = Math.floor(Math.random() * 1000000);
+ const environmentId = Math.floor(Math.random() * 1000000);
const model = 'my-model';
const syncId = '00000000-0000-0000-0000-000000000000';
const records = [
@@ -196,8 +196,8 @@ describe('Records service', () => {
});
it('Should return correct added records count when upserting concurrently', async () => {
- const connectionId = 1;
- const environmentId = 2;
+ const connectionId = Math.floor(Math.random() * 1000000);
+ const environmentId = Math.floor(Math.random() * 1000000);
const model = 'my-model';
const syncId = '00000000-0000-0000-0000-000000000000';
const syncJobId = 1;
diff --git a/packages/records/lib/models/records.ts b/packages/records/lib/models/records.ts
index be21cb9a2f4..995a1b0e656 100644
--- a/packages/records/lib/models/records.ts
+++ b/packages/records/lib/models/records.ts
@@ -447,9 +447,9 @@ export async function deleteRecordCount({ connectionId, environmentId, model }:
await db.from(RECORD_COUNTS_TABLE).where({ connection_id: connectionId, environment_id: environmentId, model }).del();
}
-// Mark all non-deleted records that don't belong to currentGeneration as deleted
+// Mark all non-deleted records from previous generations as deleted
// returns the ids of records being deleted
-export async function markNonCurrentGenerationRecordsAsDeleted({
+export async function markPreviousGenerationRecordsAsDeleted({
connectionId,
model,
syncId,
@@ -471,9 +471,7 @@ export async function markNonCurrentGenerationRecordsAsDeleted({
sync_id: syncId,
deleted_at: null
})
- .whereNot({
- sync_job_id: generation
- })
+ .where('sync_job_id', '<', generation)
.update({
deleted_at: now,
updated_at: now,
diff --git a/packages/records/package.json b/packages/records/package.json
index 524ca08022a..6afe6d663fd 100644
--- a/packages/records/package.json
+++ b/packages/records/package.json
@@ -26,6 +26,6 @@
},
"devDependencies": {
"@types/md5": "2.3.2",
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
}
}
diff --git a/packages/runner/.gitignore b/packages/runner/.gitignore
deleted file mode 100644
index 8c3aba0aad2..00000000000
--- a/packages/runner/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-tsconfig.tsbuildinfo
-dist/*
-node_modules
diff --git a/packages/runner/package.json b/packages/runner/package.json
index c999b7177a6..6d5ed9739e0 100644
--- a/packages/runner/package.json
+++ b/packages/runner/package.json
@@ -22,7 +22,7 @@
"@nangohq/utils": "file:../utils",
"@trpc/client": "^10.45.1",
"@trpc/server": "^10.45.1",
- "axios": "^1.7.4",
+ "axios": "^1.7.9",
"botbuilder": "4.23.1",
"connect-timeout": "1.9.0",
"dd-trace": "5.21.0",
@@ -30,13 +30,13 @@
"soap": "1.1.2",
"superjson": "2.2.1",
"undici": "6.12.0",
- "zod": "3.23.8"
+ "zod": "3.24.1"
},
"devDependencies": {
"@nangohq/types": "file:../types",
"@types/connect-timeout": "0.0.39",
"@types/node": "20.12.2",
- "typescript": "5.3.3",
- "vitest": "1.6.0"
+ "typescript": "5.7.3",
+ "vitest": "2.1.8"
}
}
diff --git a/packages/scheduler/lib/types.ts b/packages/scheduler/lib/types.ts
index 2edb4063158..a215d9f6cea 100644
--- a/packages/scheduler/lib/types.ts
+++ b/packages/scheduler/lib/types.ts
@@ -30,6 +30,7 @@ export interface Task {
export type ImmediateProps = Omit;
export type { ScheduleProps };
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
const scheduleStates = ['PAUSED', 'STARTED', 'DELETED'] as const;
export type ScheduleState = (typeof scheduleStates)[number];
diff --git a/packages/scheduler/package.json b/packages/scheduler/package.json
index 65442761262..a47f419421c 100644
--- a/packages/scheduler/package.json
+++ b/packages/scheduler/package.json
@@ -23,7 +23,7 @@
"uuidv7": "0.6.3"
},
"devDependencies": {
- "type-fest": "4.26.1",
- "vitest": "1.6.0"
+ "type-fest": "4.32.0",
+ "vitest": "2.1.8"
}
}
diff --git a/packages/server/lib/controllers/appAuth.controller.ts b/packages/server/lib/controllers/appAuth.controller.ts
index 9c0312da66b..9f2758d1316 100644
--- a/packages/server/lib/controllers/appAuth.controller.ts
+++ b/packages/server/lib/controllers/appAuth.controller.ts
@@ -127,7 +127,8 @@ class AppAuthController {
await logCtx.error(error.message, { connectionConfig, url: req.originalUrl });
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ return;
}
if (!installation_id) {
@@ -171,7 +172,8 @@ class AppAuthController {
logCtx
);
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error as NangoError);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error as NangoError);
+ return;
}
const [updatedConnection] = await connectionService.upsertConnection({
@@ -186,7 +188,8 @@ class AppAuthController {
if (!updatedConnection) {
await logCtx.error('Failed to create connection');
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to create connection'));
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to create connection'));
+ return;
}
let connectSession: ConnectSessionAndEndUser | undefined;
@@ -199,7 +202,8 @@ class AppAuthController {
if (connectSessionRes.isErr()) {
await logCtx.error('Failed to get session');
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to get session'));
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to get session'));
+ return;
}
connectSession = connectSessionRes.value;
@@ -233,7 +237,8 @@ class AppAuthController {
authMode: String(provider.auth_mode)
});
- return publisher.notifySuccess(res, wsClientId, providerConfigKey, connectionId);
+ await publisher.notifySuccess(res, wsClientId, providerConfigKey, connectionId);
+ return;
} catch (err) {
const prettyError = stringifyError(err, { pretty: true });
diff --git a/packages/server/lib/controllers/connect/postReconnect.integration.test.ts b/packages/server/lib/controllers/connect/postReconnect.integration.test.ts
index dfbf8acc59e..2d4f7edaca7 100644
--- a/packages/server/lib/controllers/connect/postReconnect.integration.test.ts
+++ b/packages/server/lib/controllers/connect/postReconnect.integration.test.ts
@@ -55,7 +55,7 @@ describe(`POST ${endpoint}`, () => {
// Create an initial connection
await seeders.createConfigSeed(env, 'github', 'github');
- const connection = await seeders.createConnectionSeed(env, 'github');
+ const connection = await seeders.createConnectionSeed({ env, provider: 'github' });
await linkConnection(db.knex, { endUserId: endUser.id, connection });
const res = await api.fetch(endpoint, {
@@ -84,7 +84,7 @@ describe(`POST ${endpoint}`, () => {
// Create an initial connection
await seeders.createConfigSeed(env, 'github', 'github');
- const connection = await seeders.createConnectionSeed(env, 'github');
+ const connection = await seeders.createConnectionSeed({ env, provider: 'github' });
const res = await api.fetch(endpoint, {
method: 'POST',
@@ -108,7 +108,7 @@ describe(`POST ${endpoint}`, () => {
// Create an initial connection
await seeders.createConfigSeed(env, 'github', 'github');
- const connection = await seeders.createConnectionSeed(env, 'github');
+ const connection = await seeders.createConnectionSeed({ env, provider: 'github' });
const res = await api.fetch(endpoint, {
method: 'POST',
diff --git a/packages/server/lib/controllers/connection/connectionId/getConnection.integration.test.ts b/packages/server/lib/controllers/connection/connectionId/getConnection.integration.test.ts
index e2613575263..a61013c5688 100644
--- a/packages/server/lib/controllers/connection/connectionId/getConnection.integration.test.ts
+++ b/packages/server/lib/controllers/connection/connectionId/getConnection.integration.test.ts
@@ -61,7 +61,10 @@ describe(`GET ${endpoint}`, () => {
const { env, account } = await seeders.seedAccountEnvAndUser();
await seeders.createConfigSeed(env, 'algolia', 'algolia');
const endUser = await seeders.createEndUser({ environment: env, account });
- const conn = await seeders.createConnectionSeed(env, 'algolia', endUser, {
+ const conn = await seeders.createConnectionSeed({
+ env,
+ provider: 'algolia',
+ endUser,
rawCredentials: { type: 'API_KEY', apiKey: 'test_api_key' },
connectionConfig: { APP_ID: 'TEST' }
});
@@ -83,11 +86,11 @@ describe(`GET ${endpoint}`, () => {
},
connection_config: { APP_ID: 'TEST' },
end_user: {
- displayName: null,
+ display_name: null,
email: endUser.email,
id: endUser.endUserId,
organization: {
- displayName: null,
+ display_name: null,
id: endUser.organization!.organizationId
}
},
@@ -106,13 +109,19 @@ describe(`GET ${endpoint}`, () => {
await seeders.createConfigSeed(env, 'algolia', 'algolia');
const endUser = await seeders.createEndUser({ environment: env, account });
- const conn = await seeders.createConnectionSeed(env, 'algolia', endUser, {
+ const conn = await seeders.createConnectionSeed({
+ env,
+ provider: 'algolia',
+ endUser,
rawCredentials: { type: 'API_KEY', apiKey: 'test_api_key' },
connectionConfig: { APP_ID: 'TEST' }
});
await seeders.createConfigSeed(env, 'google', 'google');
- await seeders.createConnectionSeed(env, 'google', endUser, {
+ await seeders.createConnectionSeed({
+ env,
+ provider: 'google',
+ endUser,
connectionId: conn.connection_id,
rawCredentials: { type: 'API_KEY', apiKey: 'test_api_key' },
connectionConfig: { APP_ID: 'TEST' }
@@ -135,11 +144,11 @@ describe(`GET ${endpoint}`, () => {
},
connection_config: { APP_ID: 'TEST' },
end_user: {
- displayName: null,
+ display_name: null,
email: endUser.email,
id: endUser.endUserId,
organization: {
- displayName: null,
+ display_name: null,
id: endUser.organization!.organizationId
}
},
diff --git a/packages/server/lib/controllers/connection/connectionId/metadata/patchMetadata.integration.test.ts b/packages/server/lib/controllers/connection/connectionId/metadata/patchMetadata.integration.test.ts
index f403be9dbdf..4e4635c5b93 100644
--- a/packages/server/lib/controllers/connection/connectionId/metadata/patchMetadata.integration.test.ts
+++ b/packages/server/lib/controllers/connection/connectionId/metadata/patchMetadata.integration.test.ts
@@ -136,7 +136,7 @@ describe(`PATCH ${endpoint}`, () => {
const env = await seeders.createEnvironmentSeed();
const unique_key = 'test-update';
await seeders.createConfigSeed(env, unique_key, 'google');
- const connections = await seeders.createConnectionSeed(env, unique_key);
+ const connections = await seeders.createConnectionSeed({ env, provider: unique_key });
const { connection_id, provider_config_key } = connections;
diff --git a/packages/server/lib/controllers/connection/connectionId/metadata/postMetadata.integration.test.ts b/packages/server/lib/controllers/connection/connectionId/metadata/postMetadata.integration.test.ts
index 8e0f665a9c6..ba7cbdbdeac 100644
--- a/packages/server/lib/controllers/connection/connectionId/metadata/postMetadata.integration.test.ts
+++ b/packages/server/lib/controllers/connection/connectionId/metadata/postMetadata.integration.test.ts
@@ -136,7 +136,7 @@ describe(`POST ${endpoint}`, () => {
it('Should replace existing metadata, overwriting anything existing', async () => {
const env = await seeders.createEnvironmentSeed();
await seeders.createConfigSeed(env, 'test-replace', 'google');
- const connections = await seeders.createConnectionSeed(env, 'test-replace');
+ const connections = await seeders.createConnectionSeed({ env, provider: 'test-replace' });
const { connection_id, provider_config_key } = connections;
diff --git a/packages/server/lib/controllers/connection/getConnections.integration.test.ts b/packages/server/lib/controllers/connection/getConnections.integration.test.ts
index f99f0378be6..9e4720dddea 100644
--- a/packages/server/lib/controllers/connection/getConnections.integration.test.ts
+++ b/packages/server/lib/controllers/connection/getConnections.integration.test.ts
@@ -41,7 +41,7 @@ describe(`GET ${endpoint}`, () => {
it('should list one connection', async () => {
const { env } = await seeders.seedAccountEnvAndUser();
await seeders.createConfigSeed(env, 'github', 'github');
- const conn = await seeders.createConnectionSeed(env, 'github');
+ const conn = await seeders.createConnectionSeed({ env, provider: 'github' });
const res = await api.fetch(endpoint, {
method: 'GET',
@@ -69,8 +69,8 @@ describe(`GET ${endpoint}`, () => {
it('should search connections', async () => {
const { env } = await seeders.seedAccountEnvAndUser();
await seeders.createConfigSeed(env, 'github', 'github');
- await seeders.createConnectionSeed(env, 'github');
- const conn2 = await seeders.createConnectionSeed(env, 'github');
+ await seeders.createConnectionSeed({ env, provider: 'github' });
+ const conn2 = await seeders.createConnectionSeed({ env, provider: 'github' });
const res = await api.fetch(endpoint, {
method: 'GET',
@@ -91,7 +91,7 @@ describe(`GET ${endpoint}`, () => {
const { env, account } = await seeders.seedAccountEnvAndUser();
await seeders.createConfigSeed(env, 'github', 'github');
const endUser = await seeders.createEndUser({ environment: env, account });
- const conn = await seeders.createConnectionSeed(env, 'github', endUser);
+ const conn = await seeders.createConnectionSeed({ env, provider: 'github', endUser });
const res = await api.fetch(endpoint, {
method: 'GET',
@@ -103,12 +103,14 @@ describe(`GET ${endpoint}`, () => {
expect(res.json).toMatchObject({
connections: [
{
+ id: conn.id!,
+ provider_config_key: 'github',
connection_id: conn.connection_id,
end_user: {
id: endUser.endUserId,
- displayName: null,
+ display_name: null,
email: endUser.email,
- organization: { id: endUser.organization!.organizationId, displayName: endUser.organization!.displayName! }
+ organization: { id: endUser.organization!.organizationId, display_name: endUser.organization!.displayName! }
}
}
]
@@ -120,7 +122,7 @@ describe(`GET ${endpoint}`, () => {
const { env, account } = await seeders.seedAccountEnvAndUser();
await seeders.createConfigSeed(env, 'github', 'github');
const endUser = await seeders.createEndUser({ environment: env, account });
- await seeders.createConnectionSeed(env, 'github', endUser);
+ await seeders.createConnectionSeed({ env, provider: 'github', endUser });
const res = await api.fetch(endpoint, {
method: 'GET',
@@ -140,7 +142,7 @@ describe(`GET ${endpoint}`, () => {
const { env, account } = await seeders.seedAccountEnvAndUser();
await seeders.createConfigSeed(env, 'github', 'github');
const endUser = await seeders.createEndUser({ environment: env, account });
- const conn = await seeders.createConnectionSeed(env, 'github', endUser);
+ const conn = await seeders.createConnectionSeed({ env, provider: 'github', endUser });
const res = await api.fetch(endpoint, {
method: 'GET',
@@ -160,7 +162,7 @@ describe(`GET ${endpoint}`, () => {
const { env, account } = await seeders.seedAccountEnvAndUser();
await seeders.createConfigSeed(env, 'github', 'github');
const endUser = await seeders.createEndUser({ environment: env, account });
- const conn = await seeders.createConnectionSeed(env, 'github', endUser);
+ const conn = await seeders.createConnectionSeed({ env, provider: 'github', endUser });
const res = await api.fetch(endpoint, {
method: 'GET',
diff --git a/packages/server/lib/controllers/oauth.controller.ts b/packages/server/lib/controllers/oauth.controller.ts
index 7653a3df3d7..c77b5ca2d9b 100644
--- a/packages/server/lib/controllers/oauth.controller.ts
+++ b/packages/server/lib/controllers/oauth.controller.ts
@@ -102,7 +102,8 @@ class OAuthController {
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, receivedConnectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, receivedConnectionId, error);
+ return;
}
if (environment.hmac_enabled && !isConnectSession) {
@@ -112,7 +113,8 @@ class OAuthController {
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, receivedConnectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, receivedConnectionId, error);
+ return;
}
const verified = hmacService.verify({ receivedDigest: hmac, environment, values: [providerConfigKey, receivedConnectionId] });
@@ -121,7 +123,8 @@ class OAuthController {
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, receivedConnectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, receivedConnectionId, error);
+ return;
}
}
@@ -134,7 +137,8 @@ class OAuthController {
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ return;
}
await logCtx.enrichOperation({ integrationId: config.id!, integrationName: config.unique_key, providerName: config.provider });
@@ -145,7 +149,8 @@ class OAuthController {
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ return;
}
if (!(await isIntegrationAllowed({ config, res, logCtx }))) {
@@ -230,11 +235,12 @@ class OAuthController {
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ return;
}
if (provider.auth_mode === 'OAUTH2') {
- return this.oauth2Request({
+ await this.oauth2Request({
provider: provider as ProviderOAuth2,
providerConfig: config,
session,
@@ -246,17 +252,21 @@ class OAuthController {
userScope,
logCtx
});
+ return;
} else if (provider.auth_mode === 'APP' || provider.auth_mode === 'CUSTOM') {
- return this.appRequest(provider, config, session, res, authorizationParams, logCtx);
+ await this.appRequest(provider, config, session, res, authorizationParams, logCtx);
+ return;
} else if (provider.auth_mode === 'OAUTH1') {
- return this.oauth1Request(provider, config, session, res, callbackUrl, environmentId, logCtx);
+ await this.oauth1Request(provider, config, session, res, callbackUrl, environmentId, logCtx);
+ return;
}
const error = WSErrBuilder.UnknownAuthMode(provider.auth_mode);
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, wsClientId, providerConfigKey, connectionId, error);
+ return;
} catch (err) {
const prettyError = stringifyError(err, { pretty: true });
const error = WSErrBuilder.UnknownError();
@@ -515,7 +525,8 @@ class OAuthController {
await logCtx.error(error.message, { connectionConfig });
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
if (missesInterpolationParam(tokenUrl, connectionConfig)) {
@@ -523,7 +534,8 @@ class OAuthController {
await logCtx.error(error.message, { connectionConfig });
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
if (provider.authorization_params && missesInterpolationParamInObject(provider.authorization_params, connectionConfig)) {
@@ -531,7 +543,8 @@ class OAuthController {
await logCtx.error(error.message, { connectionConfig });
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
if (provider.token_params && missesInterpolationParamInObject(provider.token_params, connectionConfig)) {
@@ -539,7 +552,8 @@ class OAuthController {
await logCtx.error(error.message, { connectionConfig });
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
if (
provider.token_params == undefined ||
@@ -648,7 +662,8 @@ class OAuthController {
});
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
} catch (err) {
const prettyError = stringifyError(err, { pretty: true });
@@ -697,7 +712,8 @@ class OAuthController {
await logCtx.error(error.message, { ...connectionConfig });
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
await oAuthSessionService.create(session);
@@ -809,24 +825,23 @@ class OAuthController {
const errorMessage = 'No state found in callback';
const e = new Error(errorMessage);
- errorManager.report(e, {
- source: ErrorSourceEnum.PLATFORM,
- operation: LogActionEnum.AUTH,
- metadata: errorManager.getExpressRequestContext(req)
- });
+ errorManager.report(e, { source: ErrorSourceEnum.PLATFORM, operation: LogActionEnum.AUTH });
return;
}
- const session = await oAuthSessionService.findById(state as string);
+ let session;
+ try {
+ session = await oAuthSessionService.findById(state as string);
+ } catch (err) {
+ errorManager.report(err, { source: ErrorSourceEnum.PLATFORM, operation: LogActionEnum.AUTH });
+ errorManager.errRes(res, 'invalid_oauth_state');
+ return;
+ }
if (session == null) {
const e = new Error(`No session found for state: ${JSON.stringify(state)}`);
- errorManager.report(e, {
- source: ErrorSourceEnum.PLATFORM,
- operation: LogActionEnum.AUTH,
- metadata: errorManager.getExpressRequestContext(req)
- });
+ errorManager.report(e, { source: ErrorSourceEnum.PLATFORM, operation: LogActionEnum.AUTH });
return;
} else {
await oAuthSessionService.delete(state as string);
@@ -846,7 +861,8 @@ class OAuthController {
const error = WSErrBuilder.UnknownProviderTemplate(session.provider);
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
const config = (await configService.getProviderConfig(session.providerConfigKey, session.environmentId))!;
@@ -860,29 +876,28 @@ class OAuthController {
await logCtx.error(error.message);
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
}
if (session.authMode === 'OAUTH2' || session.authMode === 'CUSTOM') {
- return this.oauth2Callback(provider as ProviderOAuth2, config, session, req, res, environment, account, logCtx);
+ await this.oauth2Callback(provider as ProviderOAuth2, config, session, req, res, environment, account, logCtx);
+ return;
} else if (session.authMode === 'OAUTH1') {
- return this.oauth1Callback(provider, config, session, req, res, environment, account, logCtx);
+ await this.oauth1Callback(provider, config, session, req, res, environment, account, logCtx);
+ return;
}
const error = WSErrBuilder.UnknownAuthMode(session.authMode);
await logCtx.error(error.message, { url: req.originalUrl });
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, error);
+ return;
} catch (err) {
const prettyError = stringifyError(err, { pretty: true });
- errorManager.report(err, {
- source: ErrorSourceEnum.PLATFORM,
- operation: LogActionEnum.AUTH,
- environmentId: session.environmentId,
- metadata: errorManager.getExpressRequestContext(req)
- });
+ errorManager.report(err, { source: ErrorSourceEnum.PLATFORM, operation: LogActionEnum.AUTH, environmentId: session.environmentId });
await logCtx.error('Unknown error', { error: err, url: req.originalUrl });
await logCtx.failed();
@@ -958,7 +973,8 @@ class OAuthController {
await logCtx.info('Update request has been made', { provider: session.provider, providerConfigKey, connectionId });
await logCtx.success();
- return publisher.notifySuccess(res, channel, providerConfigKey, connectionId);
+ await publisher.notifySuccess(res, channel, providerConfigKey, connectionId);
+ return;
}
// check for oauth overrides in the connection config
@@ -1069,7 +1085,8 @@ class OAuthController {
logCtx
);
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, WSErrBuilder.UnknownError());
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, WSErrBuilder.UnknownError());
+ return;
}
let connectionConfig = {
@@ -1159,7 +1176,8 @@ class OAuthController {
if (!updatedConnection) {
await logCtx.error('Failed to create connection');
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to create connection'));
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to create connection'));
+ return;
}
let connectSession: ConnectSessionAndEndUser | undefined;
@@ -1172,7 +1190,8 @@ class OAuthController {
if (connectSessionRes.isErr()) {
await logCtx.error('Failed to get session');
await logCtx.failed();
- return publisher.notifyErr(res, channel, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to get session'));
+ await publisher.notifyErr(res, channel, providerConfigKey, connectionId, WSErrBuilder.UnknownError('failed to get session'));
+ return;
}
connectSession = connectSessionRes.value;
@@ -1247,7 +1266,8 @@ class OAuthController {
await logCtx.success();
- return publisher.notifySuccess(res, channel, providerConfigKey, connectionId, pending);
+ await publisher.notifySuccess(res, channel, providerConfigKey, connectionId, pending);
+ return;
} catch (err) {
const prettyError = stringifyError(err, { pretty: true });
errorManager.report(err, {
diff --git a/packages/server/lib/controllers/proxy.controller.ts b/packages/server/lib/controllers/proxy.controller.ts
index 72c2ed25376..730bed620b2 100644
--- a/packages/server/lib/controllers/proxy.controller.ts
+++ b/packages/server/lib/controllers/proxy.controller.ts
@@ -45,8 +45,8 @@ class ProxyController {
let logCtx: LogContext | undefined;
try {
- const connectionId = req.get('Connection-Id') as string;
- const providerConfigKey = req.get('Provider-Config-Key') as string;
+ const connectionId = req.get('Connection-Id') || '';
+ const providerConfigKey = req.get('Provider-Config-Key') || '';
const retries = req.get('Retries') as string;
const baseUrlOverride = req.get('Base-Url-Override') as string;
const decompress = req.get('Decompress') as string;
diff --git a/packages/server/lib/controllers/sync.controller.ts b/packages/server/lib/controllers/sync.controller.ts
index b4eea5ef707..825ee0d185e 100644
--- a/packages/server/lib/controllers/sync.controller.ts
+++ b/packages/server/lib/controllers/sync.controller.ts
@@ -201,7 +201,7 @@ class SyncController {
return;
}
- res.sendStatus(200);
+ res.status(200).send({ success: true });
} catch (err) {
next(err);
}
@@ -422,7 +422,7 @@ class SyncController {
initiator: 'API call'
});
- res.sendStatus(200);
+ res.status(200).send({ success: true });
} catch (err) {
next(err);
}
@@ -464,7 +464,7 @@ class SyncController {
initiator: 'API call'
});
- res.sendStatus(200);
+ res.status(200).send({ success: true });
} catch (err) {
next(err);
}
@@ -596,7 +596,7 @@ class SyncController {
});
if (result.isErr()) {
- errorManager.handleGenericError(result.error, req, res, tracer);
+ errorManager.handleGenericError(result.error, req, res);
await logCtx.failed();
return;
}
diff --git a/packages/server/lib/controllers/v1/account/managed/getCallback.ts b/packages/server/lib/controllers/v1/account/managed/getCallback.ts
index dfdb2a310a4..c6c447959ce 100644
--- a/packages/server/lib/controllers/v1/account/managed/getCallback.ts
+++ b/packages/server/lib/controllers/v1/account/managed/getCallback.ts
@@ -123,7 +123,7 @@ export const getManagedCallback = asyncWrapper(async (req, r
if (invitation) {
// If we came from an invitation we need to accept it and transfer the team
await acceptInvitation(invitation.token);
- const updated = await userService.update({ id: user!.id, account_id: invitation.account_id });
+ const updated = await userService.update({ id: user.id, account_id: invitation.account_id });
if (!updated) {
res.status(500).send({ error: { code: 'server_error', message: 'failed to update user team' } });
return;
diff --git a/packages/server/lib/controllers/v1/environment/postEnvironment.integration.test.ts b/packages/server/lib/controllers/v1/environment/postEnvironment.integration.test.ts
new file mode 100644
index 00000000000..f7d917df60c
--- /dev/null
+++ b/packages/server/lib/controllers/v1/environment/postEnvironment.integration.test.ts
@@ -0,0 +1,39 @@
+import { afterAll, beforeAll, describe, it } from 'vitest';
+import { runServer, shouldBeProtected } from '../../../utils/tests.js';
+
+let api: Awaited>;
+
+const endpoint = '/api/v1/environments';
+
+describe(`POST ${endpoint}`, () => {
+ beforeAll(async () => {
+ api = await runServer();
+ });
+ afterAll(() => {
+ api.server.close();
+ });
+
+ it('should be protected', async () => {
+ const res = await api.fetch(endpoint, {
+ method: 'POST',
+ body: { name: 'test' }
+ });
+
+ shouldBeProtected(res);
+ });
+
+ // Does not work because we only have env secret key for authentication but we actually want to create an env
+ // it('should create an environment', async () => {
+ // const { account } = await seeders.seedAccountEnvAndUser();
+
+ // const res = await api.fetch(endpoint, {
+ // method: 'POST',
+ // body: { name: 'test', accountId: account.id }
+ // });
+
+ // isSuccess(res.json);
+ // expect(res.json).toStrictEqual({
+ // data: { id: expect.any(Number), name: 'test' }
+ // });
+ // });
+});
diff --git a/packages/server/lib/controllers/v1/environment/postEnvironment.ts b/packages/server/lib/controllers/v1/environment/postEnvironment.ts
new file mode 100644
index 00000000000..817846fc972
--- /dev/null
+++ b/packages/server/lib/controllers/v1/environment/postEnvironment.ts
@@ -0,0 +1,63 @@
+import { z } from 'zod';
+import { requireEmptyQuery, zodErrorToHTTP } from '@nangohq/utils';
+import { asyncWrapper } from '../../../utils/asyncWrapper.js';
+import type { PostEnvironment } from '@nangohq/types';
+import { accountService, environmentService, externalWebhookService } from '@nangohq/shared';
+import { envSchema } from '../../../helpers/validation.js';
+
+const validationBody = z
+ .object({
+ name: envSchema
+ })
+ .strict();
+
+export const postEnvironment = asyncWrapper(async (req, res) => {
+ const emptyQuery = requireEmptyQuery(req);
+ if (emptyQuery) {
+ res.status(400).send({ error: { code: 'invalid_query_params', errors: zodErrorToHTTP(emptyQuery.error) } });
+ return;
+ }
+
+ const valBody = validationBody.safeParse(req.body);
+ if (!valBody.success) {
+ res.status(400).send({ error: { code: 'invalid_body', errors: zodErrorToHTTP(valBody.error) } });
+ return;
+ }
+
+ const body: PostEnvironment['Body'] = valBody.data;
+
+ const accountId = res.locals.user.account_id;
+
+ const account = await accountService.getAccountById(accountId);
+ if (account?.is_capped) {
+ res.status(400).send({ error: { code: 'feature_disabled', message: 'Creating environment is only available for paying customer' } });
+ return;
+ }
+
+ const environments = await environmentService.getEnvironmentsByAccountId(accountId);
+ if (environments.length >= 10) {
+ res.status(400).send({ error: { code: 'resource_capped', message: "Can't create more environments" } });
+ return;
+ }
+
+ const exists = environments.some((env) => env.name === body.name);
+ if (exists) {
+ res.status(409).send({ error: { code: 'conflict', message: 'Environment already exists' } });
+ return;
+ }
+
+ const created = await environmentService.createEnvironment(accountId, body.name);
+ if (!created) {
+ res.status(500).send({ error: { code: 'server_error', message: 'Failed to create environment' } });
+ return;
+ }
+
+ await externalWebhookService.update(created.id, {
+ alwaysSendWebhook: true,
+ sendAuthWebhook: true,
+ sendRefreshFailedWebhook: true,
+ sendSyncFailedWebhook: true
+ });
+
+ res.status(200).send({ data: { id: created.id, name: created.name } });
+});
diff --git a/packages/server/lib/controllers/v1/flows/id/patchDisable.ts b/packages/server/lib/controllers/v1/flows/id/patchDisable.ts
index a4320a18018..55aff572906 100644
--- a/packages/server/lib/controllers/v1/flows/id/patchDisable.ts
+++ b/packages/server/lib/controllers/v1/flows/id/patchDisable.ts
@@ -3,7 +3,7 @@ import { asyncWrapper } from '../../../../utils/asyncWrapper.js';
import type { PatchFlowDisable } from '@nangohq/types';
import { requireEmptyQuery, zodErrorToHTTP } from '@nangohq/utils';
import { flowConfig } from '../../../sync/deploy/validation.js';
-import { configService, disableScriptConfig } from '@nangohq/shared';
+import { configService, disableScriptConfig, errorNotificationService } from '@nangohq/shared';
import { providerConfigKeySchema, providerSchema, scriptNameSchema } from '../../../../helpers/validation.js';
export const validationBody = z
@@ -53,6 +53,7 @@ export const patchFlowDisable = asyncWrapper(async (req, res)
}
const updated = await disableScriptConfig({ id: valParams.data.id, environmentId: environment.id });
+ await errorNotificationService.sync.clearBySyncConfig({ sync_config_id: valParams.data.id });
if (updated > 0) {
res.status(200).send({ data: { success: true } });
diff --git a/packages/server/lib/controllers/v1/getEnvJs.ts b/packages/server/lib/controllers/v1/getEnvJs.ts
index daa6a1562c5..9ad1f85bb4e 100644
--- a/packages/server/lib/controllers/v1/getEnvJs.ts
+++ b/packages/server/lib/controllers/v1/getEnvJs.ts
@@ -1,4 +1,4 @@
-import { basePublicUrl, baseUrl, connectUrl, flagHasAuth, flagHasManagedAuth, flagHasScripts, isCloud } from '@nangohq/utils';
+import { basePublicUrl, baseUrl, connectUrl, flagHasAuth, flagHasManagedAuth, flagHasScripts, flagHasSlack, isCloud } from '@nangohq/utils';
import { asyncWrapper } from '../../utils/asyncWrapper.js';
import type { WindowEnv } from '@nangohq/types';
import { envs } from '@nangohq/logs';
@@ -12,14 +12,16 @@ export const getEnvJs = asyncWrapper((_, res) => {
publicPosthogKey: process.env['PUBLIC_POSTHOG_KEY'] || '',
publicPosthogHost: process.env['PUBLIC_POSTHOG_HOST'] || '',
publicLogoDevKey: process.env['PUBLIC_LOGODEV_KEY'] || '',
- publicKoalaKey: process.env['PUBLIC_KOALA_KEY'] || '',
+ publicKoalaApiUrl: process.env['PUBLIC_KOALA_API_URL'] || '',
+ publicKoalaCdnUrl: process.env['PUBLIC_KOALA_CDN_URL'] || '',
isCloud,
features: {
logs: envs.NANGO_LOGS_ENABLED,
scripts: flagHasScripts,
auth: flagHasAuth,
managedAuth: flagHasManagedAuth,
- gettingStarted: true
+ gettingStarted: true,
+ slack: flagHasSlack
}
};
res.setHeader('content-type', 'text/javascript');
diff --git a/packages/server/lib/controllers/v1/integrations/providerConfigKey/flows/getFlows.integration.test.ts b/packages/server/lib/controllers/v1/integrations/providerConfigKey/flows/getFlows.integration.test.ts
index bff76ea875b..70c33148576 100644
--- a/packages/server/lib/controllers/v1/integrations/providerConfigKey/flows/getFlows.integration.test.ts
+++ b/packages/server/lib/controllers/v1/integrations/providerConfigKey/flows/getFlows.integration.test.ts
@@ -84,7 +84,7 @@ describe(`GET ${route}`, () => {
it('should create same template and deduplicate correctly', async () => {
const { env } = await seeders.seedAccountEnvAndUser();
const config = await seeders.createConfigSeed(env, 'github', 'github');
- const connection = await seeders.createConnectionSeed(env, 'github');
+ const connection = await seeders.createConnectionSeed({ env, provider: 'github' });
await seeders.createSyncSeeds({
connectionId: connection.id!,
diff --git a/packages/server/lib/controllers/v1/logs/postInsights.integration.test.ts b/packages/server/lib/controllers/v1/logs/postInsights.integration.test.ts
index 5f268b7c84b..2f3083c6487 100644
--- a/packages/server/lib/controllers/v1/logs/postInsights.integration.test.ts
+++ b/packages/server/lib/controllers/v1/logs/postInsights.integration.test.ts
@@ -69,7 +69,7 @@ describe('POST /logs/insights', () => {
method: 'POST',
query: { env: 'dev' },
token: env.secret_key,
- body: { type: 'sync' }
+ body: { type: 'sync:run' }
});
isSuccess(res.json);
diff --git a/packages/server/lib/controllers/v1/logs/postInsights.ts b/packages/server/lib/controllers/v1/logs/postInsights.ts
index 6d6e5731c52..c1b0c4628f9 100644
--- a/packages/server/lib/controllers/v1/logs/postInsights.ts
+++ b/packages/server/lib/controllers/v1/logs/postInsights.ts
@@ -6,7 +6,7 @@ import { envs, modelOperations } from '@nangohq/logs';
const validation = z
.object({
- type: z.enum(['sync', 'action', 'proxy', 'webhook:incoming'])
+ type: z.enum(['sync:run', 'action', 'proxy', 'webhook:incoming'])
})
.strict();
diff --git a/packages/server/lib/formatters/connection.ts b/packages/server/lib/formatters/connection.ts
index d2590c77148..eccb6204aaf 100644
--- a/packages/server/lib/formatters/connection.ts
+++ b/packages/server/lib/formatters/connection.ts
@@ -7,6 +7,7 @@ import type {
DBConnection,
DBEndUser
} from '@nangohq/types';
+import { endUserToApi } from './endUser.js';
export function connectionSimpleToApi({
data,
@@ -25,14 +26,7 @@ export function connectionSimpleToApi({
provider_config_key: data.provider_config_key,
provider,
errors: activeLog,
- endUser: endUser
- ? {
- id: endUser.end_user_id,
- displayName: endUser.display_name || null,
- email: endUser.email,
- organization: endUser.organization_id ? { id: endUser.organization_id, displayName: endUser.organization_display_name || null } : null
- }
- : null,
+ endUser: endUser ? endUserToApi(endUser) : null,
created_at: String(data.created_at),
updated_at: String(data.updated_at)
};
@@ -64,14 +58,7 @@ export function connectionSimpleToPublicApi({
provider_config_key: data.provider_config_key,
provider,
errors: activeLog,
- end_user: endUser
- ? {
- id: endUser.end_user_id,
- displayName: endUser.display_name || null,
- email: endUser.email,
- organization: endUser.organization_id ? { id: endUser.organization_id, displayName: endUser.organization_display_name || null } : null
- }
- : null,
+ end_user: endUser ? endUserToApi(endUser) : null,
metadata: data.metadata || null,
created: String(data.created_at)
};
@@ -94,14 +81,7 @@ export function connectionFullToPublicApi({
provider_config_key: data.provider_config_key,
provider,
errors: activeLog,
- end_user: endUser
- ? {
- id: endUser.end_user_id,
- displayName: endUser.display_name || null,
- email: endUser.email,
- organization: endUser.organization_id ? { id: endUser.organization_id, displayName: endUser.organization_display_name || null } : null
- }
- : null,
+ end_user: endUser ? endUserToApi(endUser) : null,
metadata: data.metadata || null,
connection_config: data.connection_config || {},
created_at: String(data.created_at),
diff --git a/packages/server/lib/formatters/endUser.ts b/packages/server/lib/formatters/endUser.ts
index e26bd32280d..cea19c03310 100644
--- a/packages/server/lib/formatters/endUser.ts
+++ b/packages/server/lib/formatters/endUser.ts
@@ -7,8 +7,8 @@ export function endUserToApi(endUser: DBEndUser | null): ApiEndUser | null {
return {
id: endUser.end_user_id,
- displayName: endUser.display_name || null,
+ display_name: endUser.display_name || null,
email: endUser.email,
- organization: endUser.organization_id ? { id: endUser.organization_id, displayName: endUser.organization_display_name || null } : null
+ organization: endUser.organization_id ? { id: endUser.organization_id, display_name: endUser.organization_display_name || null } : null
};
}
diff --git a/packages/server/lib/helpers/validation.ts b/packages/server/lib/helpers/validation.ts
index c17d17c5b62..7a4e9090826 100644
--- a/packages/server/lib/helpers/validation.ts
+++ b/packages/server/lib/helpers/validation.ts
@@ -22,7 +22,7 @@ export const connectionIdSchema = z
.max(255);
export const envSchema = z
.string()
- .regex(/^[a-zA-Z0-9_-]+$/)
+ .regex(/^[a-z0-9_-]+$/)
.max(255);
export const connectSessionTokenPrefix = 'nango_connect_session_';
export const connectSessionTokenSchema = z.string().regex(new RegExp(`^${connectSessionTokenPrefix}[a-f0-9]{64}$`));
diff --git a/packages/server/lib/middleware/access.middleware.ts b/packages/server/lib/middleware/access.middleware.ts
index 3fe6b7f5c2c..72376d998d8 100644
--- a/packages/server/lib/middleware/access.middleware.ts
+++ b/packages/server/lib/middleware/access.middleware.ts
@@ -14,7 +14,7 @@ import { envs } from '../env.js';
const logger = getLogger('AccessMiddleware');
const keyRegex = /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
-const ignoreEnvPaths = ['/api/v1/meta', '/api/v1/user', '/api/v1/user/name', '/api/v1/signin', '/api/v1/invite/:id'];
+const ignoreEnvPaths = ['/api/v1/environments', '/api/v1/meta', '/api/v1/user', '/api/v1/user/name', '/api/v1/signin', '/api/v1/invite/:id'];
export class AccessMiddleware {
private async validateSecretKey(secret: string): Promise<
diff --git a/packages/server/lib/middleware/security.ts b/packages/server/lib/middleware/security.ts
index d636fae89b4..2331ff2f72a 100644
--- a/packages/server/lib/middleware/security.ts
+++ b/packages/server/lib/middleware/security.ts
@@ -9,6 +9,9 @@ export function securityMiddlewares(): RequestHandler[] {
hostWs.protocol = hostApi.startsWith('https') ? 'wss' : 'ws';
const reportOnly = process.env['CSP_REPORT_ONLY'];
+ const additionalConnectSources = [process.env['PUBLIC_KOALA_API_URL'] ? new URL(process.env['PUBLIC_KOALA_API_URL']).origin : ''];
+ const additionalScriptSources = [process.env['PUBLIC_KOALA_CDN_URL'] ? new URL(process.env['PUBLIC_KOALA_CDN_URL']).origin : ''];
+
return [
helmet.xssFilter(),
helmet.noSniff(),
@@ -24,7 +27,16 @@ export function securityMiddlewares(): RequestHandler[] {
directives: {
defaultSrc: ["'self'", hostPublic, hostApi],
childSrc: "'self'",
- connectSrc: ["'self'", 'https://*.google-analytics.com', 'https://*.sentry.io', hostPublic, hostApi, hostWs.href, 'https://*.posthog.com'],
+ connectSrc: [
+ "'self'",
+ 'https://*.google-analytics.com',
+ 'https://*.sentry.io',
+ hostPublic,
+ hostApi,
+ hostWs.href,
+ 'https://*.posthog.com',
+ ...additionalConnectSources
+ ],
fontSrc: ["'self'", 'https://*.googleapis.com', 'https://*.gstatic.com'],
frameSrc: ["'self'", 'https://accounts.google.com', hostPublic, hostApi, connectUrl, 'https://www.youtube.com'],
imgSrc: [
@@ -51,7 +63,8 @@ export function securityMiddlewares(): RequestHandler[] {
'https://*.googleapis.com',
'https://apis.google.com',
'https://*.posthog.com',
- 'https://www.youtube.com'
+ 'https://www.youtube.com',
+ ...additionalScriptSources
],
styleSrc: ['blob:', "'self'", "'unsafe-inline'", 'https://*.googleapis.com', hostPublic, hostApi],
workerSrc: ['blob:', "'self'", hostPublic, hostApi, 'https://*.googleapis.com', 'https://*.posthog.com']
diff --git a/packages/server/lib/refreshConnections.ts b/packages/server/lib/refreshConnections.ts
index 6ee25c5b6c3..a8264c43eb7 100644
--- a/packages/server/lib/refreshConnections.ts
+++ b/packages/server/lib/refreshConnections.ts
@@ -24,7 +24,7 @@ export function refreshConnectionsCron(): void {
const e = new Error('failed_to_refresh_connections', {
cause: err instanceof Error ? err.message : String(err)
});
- errorManager.report(e, { source: ErrorSourceEnum.PLATFORM }, tracer);
+ errorManager.report(e, { source: ErrorSourceEnum.PLATFORM });
} finally {
metrics.duration(metrics.Types.REFRESH_CONNECTIONS, Date.now() - start);
}
diff --git a/packages/server/lib/routes.ts b/packages/server/lib/routes.ts
index 333b11c20dc..b34758b38d7 100644
--- a/packages/server/lib/routes.ts
+++ b/packages/server/lib/routes.ts
@@ -24,7 +24,6 @@ import accountController from './controllers/account.controller.js';
import type { Response, Request, RequestHandler } from 'express';
import { isCloud, isEnterprise, isBasicAuthEnabled, isTest, isLocal, basePublicUrl, baseUrl, flagHasAuth, flagHasManagedAuth } from '@nangohq/utils';
import { errorManager } from '@nangohq/shared';
-import tracer from 'dd-trace';
import { getConnection as getConnectionWeb } from './controllers/v1/connections/connectionId/getConnection.js';
import { searchOperations } from './controllers/v1/logs/searchOperations.js';
import { getOperation } from './controllers/v1/logs/getOperation.js';
@@ -110,6 +109,7 @@ import { postPublicAppStoreAuthorization } from './controllers/auth/postAppStore
import { postRollout } from './controllers/fleet/postRollout.js';
import { getPublicConnection } from './controllers/connection/connectionId/getConnection.js';
import { postWebhook } from './controllers/webhook/environmentUuid/postWebhook.js';
+import { postEnvironment } from './controllers/v1/environment/postEnvironment.js';
export const router = express.Router();
@@ -169,7 +169,7 @@ const publicAPICorsHandler = cors({
maxAge: 600,
exposedHeaders: 'Authorization, Etag, Content-Type, Content-Length, X-Nango-Signature, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset',
allowedHeaders:
- 'Authorization, Content-Type, Accept, Origin, X-Requested-With, Nango-Activity-Log-Id, Nango-Is-Dry-Run, Nango-Is-Sync, Provider-Config-Key, Connection-Id',
+ 'Authorization, Content-Type, Accept, Origin, X-Requested-With, Nango-Activity-Log-Id, Nango-Is-Dry-Run, Nango-Is-Sync, Provider-Config-Key, Connection-Id, Sentry-Trace, Baggage',
origin: '*'
});
publicAPI.use(publicAPICorsHandler);
@@ -311,6 +311,7 @@ web.route('/api/v1/invite/:id').delete(webAuth, declineInvite);
web.route('/api/v1/account/admin/switch').post(webAuth, accountController.switchAccount.bind(accountController));
web.route('/api/v1/environment').get(webAuth, environmentController.getEnvironment.bind(environmentController));
+web.route('/api/v1/environments').post(webAuth, postEnvironment);
web.route('/api/v1/environment/callback').post(webAuth, environmentController.updateCallback.bind(environmentController));
web.route('/api/v1/environment/webhook/primary-url').patch(webAuth, updatePrimaryUrl);
web.route('/api/v1/environment/webhook/secondary-url').patch(webAuth, updateSecondaryUrl);
@@ -404,5 +405,5 @@ router.use((err: any, req: Request, res: Response>, _:
return;
}
- errorManager.handleGenericError(err, req, res, tracer);
+ errorManager.handleGenericError(err, req, res);
});
diff --git a/packages/server/lib/utils/utils.ts b/packages/server/lib/utils/utils.ts
index 499c55d365a..d5020af7c45 100644
--- a/packages/server/lib/utils/utils.ts
+++ b/packages/server/lib/utils/utils.ts
@@ -218,7 +218,8 @@ export function parseCredentialParamsFromTemplate(provider: ProviderTwoStep): st
* This can be used to convert the keys of a Json to snake case
* @param payload This the json we want to convert from a camelCase a snake_case
*/
-export function convertJsonKeysToSnakeCase(payload: Record): R | null {
+// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
+export function convertJsonKeysToSnakeCase(payload: Record): TReturn | null {
if (payload == null) {
return null;
}
@@ -234,7 +235,8 @@ export function convertJsonKeysToSnakeCase(payload: Record): R |
*
* @param payload The json we want to convert its keys to camelCase
*/
-export function convertJsonKeysToCamelCase(payload: Record): R | null {
+// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
+export function convertJsonKeysToCamelCase(payload: Record): TReturn | null {
if (payload == null) {
return null;
}
diff --git a/packages/server/lib/webhook/hubspot-webhook-routing.unit.test.ts b/packages/server/lib/webhook/hubspot-webhook-routing.unit.test.ts
index e0b314bf03a..cb830672e0d 100644
--- a/packages/server/lib/webhook/hubspot-webhook-routing.unit.test.ts
+++ b/packages/server/lib/webhook/hubspot-webhook-routing.unit.test.ts
@@ -100,7 +100,7 @@ describe('Webhook route unit tests', () => {
expect(nangoMock.executeScriptForWebhooks).toHaveBeenCalledTimes(body.length);
- const firstCallFirstArgument = nangoMock.executeScriptForWebhooks.mock.calls[0][1];
+ const firstCallFirstArgument = nangoMock.executeScriptForWebhooks.mock.calls[0]?.[1];
expect(firstCallFirstArgument.eventId).toBe(4023112300);
});
@@ -153,9 +153,9 @@ describe('Webhook route unit tests', () => {
expect(nangoMock.executeScriptForWebhooks).toHaveBeenCalledTimes(body.length);
- const firstCallFirstArgument = nangoMock.executeScriptForWebhooks.mock.calls[0][1];
+ const firstCallFirstArgument = nangoMock.executeScriptForWebhooks.mock.calls[0]?.[1];
expect(firstCallFirstArgument.eventId).toBe(1234);
- const secondCallFirstArgument = nangoMock.executeScriptForWebhooks.mock.calls[1][1];
+ const secondCallFirstArgument = nangoMock.executeScriptForWebhooks.mock.calls[1]?.[1];
expect(secondCallFirstArgument.eventId).toBe(123);
});
});
diff --git a/packages/server/lib/webhook/index.ts b/packages/server/lib/webhook/index.ts
index 358eb766731..0e6173dbf3d 100644
--- a/packages/server/lib/webhook/index.ts
+++ b/packages/server/lib/webhook/index.ts
@@ -9,4 +9,4 @@ export { default as checkrWebhookRouting } from './checkr-webhook-routing.js';
export { default as microsoftTeamsWebhookRouting } from './microsoft-teams-webhook-routing.js';
export { default as unauthenticatedWebhookRouting } from './unauthenticated-webhook-routing.js';
export { default as airtableWebhookRouting } from './airtable-webhook-routing.js';
-export * from './types.js';
+export type * from './types.js';
diff --git a/packages/server/package.json b/packages/server/package.json
index ba0292e229a..13c267e8869 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -35,7 +35,7 @@
"@nangohq/keystore": "file:../keystore",
"@nangohq/fleet": "file:../fleet",
"@workos-inc/node": "6.2.0",
- "axios": "^1.7.4",
+ "axios": "^1.7.9",
"body-parser": "1.20.3",
"connect-session-knex": "4.0.0",
"cookie-parser": "1.4.6",
@@ -62,7 +62,7 @@
"simple-oauth2": "5.1.0",
"uuid": "9.0.0",
"ws": "8.18.0",
- "zod": "3.23.8"
+ "zod": "3.24.1"
},
"devDependencies": {
"@types/cookie-parser": "1.4.3",
@@ -82,9 +82,9 @@
"@types/uuid": "8.3.4",
"@types/ws": "8.5.4",
"get-port": "7.1.0",
- "nodemon": "3.1.7",
- "type-fest": "4.26.1",
- "typescript": "5.3.3",
- "vitest": "1.6.0"
+ "nodemon": "3.1.9",
+ "type-fest": "4.32.0",
+ "typescript": "5.7.3",
+ "vitest": "2.1.8"
}
}
diff --git a/packages/shared/flows.yaml b/packages/shared/flows.yaml
index 43cfcdc7ebb..6fb3833dfae 100644
--- a/packages/shared/flows.yaml
+++ b/packages/shared/flows.yaml
@@ -3804,17 +3804,6 @@ integrations:
group: Files
scopes: repo
version: 1.0.0
- issues-demo:
- runs: every 5 minutes
- auto_start: false
- sync_type: full
- output: GithubIssueDemo
- scopes: public_repo
- description: |
- Fetches GitHub issues from our showcase repository.
- endpoint:
- method: GET
- path: /github/demo-issues
actions:
write-file:
scopes: repo
@@ -3836,14 +3825,6 @@ integrations:
output: GithubRepo
description: |
List github repos from an organization.
- create-demo-issue:
- endpoint:
- method: POST
- path: /github/create-demo-issue
- input: GithubCreateIssueInput
- output: GithubCreateIssueResult
- description: |
- Create a GitHub issue in Nango's showcase repository.
models:
Issue:
id: integer
@@ -3898,15 +3879,6 @@ integrations:
url: string
status: string
sha: string
- GithubIssueDemo:
- id: integer
- title: string
- url: string
- GithubCreateIssueInput:
- title: string
- GithubCreateIssueResult:
- url: string | undefined
- status: number
github-app:
actions:
repositories:
@@ -4023,6 +3995,46 @@ integrations:
watchers: number
watchers_count: number
web_commit_signoff_required: boolean
+ gong:
+ syncs:
+ users:
+ description: |
+ Fetches the list of gong users
+ endpoint:
+ method: GET
+ path: /users
+ group: Users
+ sync_type: incremental
+ runs: every day
+ output: User
+ scopes:
+ - api:users:read
+ models:
+ User:
+ id: string
+ firstName: string
+ lastName: string
+ email: string
+ gong-oauth:
+ syncs:
+ users:
+ description: |
+ Fetches the list of gong users
+ endpoint:
+ method: GET
+ path: /users
+ group: Users
+ sync_type: incremental
+ runs: every day
+ output: User
+ scopes:
+ - api:users:read
+ models:
+ User:
+ id: string
+ firstName: string
+ lastName: string
+ email: string
google:
syncs:
workspace-org-units:
@@ -6371,6 +6383,15 @@ integrations:
group: Users
output: SuccessResponse
input: IdEntity
+ fetch-teams:
+ description: |
+ Fetch teams in an organisation in Jira
+ output: Teams
+ input: IdEntity
+ endpoint:
+ method: GET
+ path: /teams-list
+ group: Teams
syncs:
users:
runs: every day
@@ -6404,6 +6425,11 @@ integrations:
lastName: string
email: string
products?: string[]
+ Teams:
+ teams: Team[]
+ Team:
+ id: string
+ name: string
keeper-scim:
actions:
create-user:
@@ -11023,6 +11049,18 @@ integrations:
method: GET
path: /invoices
group: Invoices
+ general-ledger:
+ description: |
+ Fetch all general ledger entries in QuickBooks
+ runs: every hour
+ output: GeneralLedger
+ sync_type: incremental
+ endpoint:
+ method: GET
+ path: /general-ledger
+ group: General Ledger
+ scopes:
+ - com.intuit.quickbooks.accounting
actions:
create-customer:
description: |
@@ -11346,6 +11384,22 @@ integrations:
customer_ref: Reference
currency_ref?: Reference
project_ref?: Reference
+ GeneralLedger:
+ id: string
+ date: string
+ createdDate: string
+ updatedDate: string
+ currency: string
+ note: string
+ lines: LedgerLine[]
+ LedgerLine:
+ journalLineId: string
+ type: string
+ accountId: string
+ accountName: string
+ netAmount: number
+ postingType: Debit | Credit
+ description: string
quickbooks-sandbox:
syncs:
customers:
@@ -11408,6 +11462,18 @@ integrations:
method: GET
path: /invoices
group: Invoices
+ general-ledger:
+ description: |
+ Fetch all general ledger entries in QuickBooks
+ runs: every hour
+ output: GeneralLedger
+ sync_type: incremental
+ endpoint:
+ method: GET
+ path: /general-ledger
+ group: General Ledger
+ scopes:
+ - com.intuit.quickbooks.accounting
actions:
create-customer:
description: |
@@ -11731,6 +11797,22 @@ integrations:
customer_ref: Reference
currency_ref?: Reference
project_ref?: Reference
+ GeneralLedger:
+ id: string
+ date: string
+ createdDate: string
+ updatedDate: string
+ currency: string
+ note: string
+ lines: LedgerLine[]
+ LedgerLine:
+ journalLineId: string
+ type: string
+ accountId: string
+ accountName: string
+ netAmount: number
+ postingType: Debit | Credit
+ description: string
ramp:
syncs:
users:
@@ -13307,6 +13389,71 @@ integrations:
warning?: string | undefined
error?: string | undefined
raw_json: string
+ smartsheet:
+ syncs:
+ users:
+ runs: every 6 hours
+ description: Fetches a list of users from Smartsheet
+ output: User
+ sync_type: incremental
+ endpoint:
+ method: GET
+ path: /users
+ group: Users
+ scopes:
+ - READ_USERS
+ actions:
+ create-user:
+ description: Creates a user in Smartsheet
+ output: User
+ endpoint:
+ method: POST
+ path: /users
+ group: Users
+ input: CreateUser
+ scopes:
+ - ADMIN_USERS
+ delete-user:
+ description: >
+ Deletes a user from Smartsheet. User is transitioned to a free
+ collaborator with read-only access to owned reports, sheets, Sights,
+ workspaces, and any shared templates (unless those are optionally
+ transferred to another user).
+ endpoint:
+ method: DELETE
+ path: /users
+ group: Users
+ output: SuccessResponse
+ input: IdEntity
+ scopes:
+ - ADMIN_USERS
+ disable-user:
+ description: >
+ Disables a user in an organization account. User will no longer be
+ able to access Smartsheet in any way. User's assets will continue to
+ be owned by this user until they are transferred to another user.
+ endpoint:
+ method: POST
+ path: /users/disable
+ group: Users
+ output: SuccessResponse
+ input: IdEntity
+ scopes:
+ - ADMIN_USERS
+ models:
+ IdEntity:
+ id: string
+ SuccessResponse:
+ success: boolean
+ User:
+ id: string
+ email: string
+ firstName: string
+ lastName: string
+ CreateUser:
+ firstName: string
+ lastName: string
+ email: string
stripe-app:
syncs:
subscriptions:
diff --git a/packages/shared/lib/models/Sync.ts b/packages/shared/lib/models/Sync.ts
index 79b6b114494..ac61a31f6de 100644
--- a/packages/shared/lib/models/Sync.ts
+++ b/packages/shared/lib/models/Sync.ts
@@ -26,6 +26,8 @@ export interface SyncResult {
export type SyncResultByModel = Record;
+export type SyncWithConnectionId = Sync & { connection_id: string };
+
export interface Sync extends TimestampsAndDeleted {
id: string;
nango_connection_id: number;
@@ -60,6 +62,7 @@ export interface ReportedSyncJobStatus {
id?: string;
type: SyncType | 'INITIAL';
name?: string;
+ connection_id?: string;
status: SyncStatus;
latestResult?: SyncResultByModel | undefined;
jobStatus?: SyncStatus;
diff --git a/packages/shared/lib/models/index.ts b/packages/shared/lib/models/index.ts
index 7be37057e8e..48cba49298c 100644
--- a/packages/shared/lib/models/index.ts
+++ b/packages/shared/lib/models/index.ts
@@ -1,9 +1,9 @@
export * from './Telemetry.js';
-export * from './Connection.js';
-export * from './Generic.js';
-export * from './Provider.js';
+export type * from './Connection.js';
+export type * from './Generic.js';
+export type * from './Provider.js';
export * from './Auth.js';
export * from './Sync.js';
-export * from './Flow.js';
-export * from './NangoConfig.js';
+export type * from './Flow.js';
+export type * from './NangoConfig.js';
export * from './Proxy.js';
diff --git a/packages/shared/lib/sdk/sync.ts b/packages/shared/lib/sdk/sync.ts
index 4b22571b21d..5be6576f5f3 100644
--- a/packages/shared/lib/sdk/sync.ts
+++ b/packages/shared/lib/sdk/sync.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */
import https from 'node:https';
import { Nango, getUserAgent } from '@nangohq/node';
import type { AdminAxiosProps } from '@nangohq/node';
@@ -1290,7 +1291,6 @@ const TELEMETRY_ALLOWED_METHODS: (keyof NangoSync)[] = [
'triggerSync'
];
-/* eslint-disable no-inner-declarations */
/**
* @internal
*
@@ -1309,5 +1309,3 @@ export function instrumentSDK(rawNango: NangoAction | NangoSync) {
}
});
}
-
-/* eslint-enable no-inner-declarations */
diff --git a/packages/shared/lib/seeders/account.seeder.ts b/packages/shared/lib/seeders/account.seeder.ts
index 867d18aa503..38dcbcec4f9 100644
--- a/packages/shared/lib/seeders/account.seeder.ts
+++ b/packages/shared/lib/seeders/account.seeder.ts
@@ -3,7 +3,7 @@ import accountService from '../services/account.service.js';
import type { DBTeam } from '@nangohq/types';
export async function createAccount(): Promise {
- const acc = await accountService.getOrCreateAccount(uuid());
+ const acc = await accountService.createAccountWithoutEnvironments(uuid());
if (!acc) {
throw new Error('failed_to_create_account');
}
diff --git a/packages/shared/lib/seeders/connection.seeder.ts b/packages/shared/lib/seeders/connection.seeder.ts
index c91d812d80c..68f07448ce5 100644
--- a/packages/shared/lib/seeders/connection.seeder.ts
+++ b/packages/shared/lib/seeders/connection.seeder.ts
@@ -2,7 +2,7 @@ import db from '@nangohq/database';
import connectionService from '../services/connection.service.js';
import type { NangoConnection } from '../models/Connection.js';
import type { AuthCredentials } from '../models/Auth.js';
-import type { DBEnvironment, EndUser } from '@nangohq/types';
+import type { ConnectionConfig, DBEnvironment, EndUser } from '@nangohq/types';
import { linkConnection } from '../services/endUser.service.js';
export const createConnectionSeeds = async (env: DBEnvironment): Promise => {
@@ -19,40 +19,52 @@ export const createConnectionSeeds = async (env: DBEnvironment): Promise res.connection.id!));
+
+ for (const res of result) {
+ if (!res.connection.id) {
+ throw new Error('Could not create connection seed');
+ }
+
+ connectionIds.push(res.connection.id);
+ }
}
return connectionIds;
};
-export const createConnectionSeed = async (
- env: DBEnvironment,
- provider: string,
- endUser?: EndUser,
- rest?: {
- connectionId?: string;
- rawCredentials?: AuthCredentials;
- connectionConfig?: any;
- }
-): Promise => {
- const name = rest?.connectionId ? rest.connectionId : Math.random().toString(36).substring(7);
+export const createConnectionSeed = async ({
+ env,
+ provider,
+ endUser,
+ connectionId,
+ rawCredentials,
+ connectionConfig
+}: {
+ env: DBEnvironment;
+ provider: string;
+ endUser?: EndUser;
+ connectionId?: string;
+ rawCredentials?: AuthCredentials;
+ connectionConfig?: ConnectionConfig;
+}): Promise => {
+ const name = connectionId ? connectionId : Math.random().toString(36).substring(7);
const result = await connectionService.upsertConnection({
connectionId: name,
providerConfigKey: provider,
provider: provider,
- parsedRawCredentials: rest?.rawCredentials || ({} as AuthCredentials),
- connectionConfig: rest?.connectionConfig || {},
+ parsedRawCredentials: rawCredentials || ({} as AuthCredentials),
+ connectionConfig: connectionConfig || {},
environmentId: env.id,
accountId: 0
});
- if (!result || result[0] === undefined) {
+ if (!result || result[0] === undefined || !result[0].connection.id) {
throw new Error('Could not create connection seed');
}
if (endUser) {
await linkConnection(db.knex, { endUserId: endUser.id, connection: result[0].connection });
}
- return { id: result[0].connection.id!, connection_id: name, provider_config_key: provider, environment_id: env.id };
+ return { id: result[0].connection.id, connection_id: name, provider_config_key: provider, environment_id: env.id };
};
export const deleteAllConnectionSeeds = async (): Promise => {
diff --git a/packages/shared/lib/services/account.service.ts b/packages/shared/lib/services/account.service.ts
index 78b2543959a..e38f2c8b9fd 100644
--- a/packages/shared/lib/services/account.service.ts
+++ b/packages/shared/lib/services/account.service.ts
@@ -70,6 +70,7 @@ class AccountService {
if (!newAccount || newAccount.length == 0 || !newAccount[0]) {
throw new Error('Failed to create account');
}
+
await environmentService.createDefaultEnvironments(newAccount[0]['id']);
return newAccount[0];
@@ -94,6 +95,15 @@ class AccountService {
return null;
}
+ /**
+ * Create Account without default environments
+ * @desc create a new account and assign to the default environments
+ */
+ async createAccountWithoutEnvironments(name: string): Promise {
+ const result = await db.knex.from(`_nango_accounts`).insert({ name }).returning('*');
+ return result[0] || null;
+ }
+
async editCustomer(is_capped: boolean, accountId: number): Promise {
await db.knex.update({ is_capped }).from(`_nango_accounts`).where({ id: accountId });
}
diff --git a/packages/shared/lib/services/connection.service.integration.test.ts b/packages/shared/lib/services/connection.service.integration.test.ts
index b90b39c314b..0df0fffe3c3 100644
--- a/packages/shared/lib/services/connection.service.integration.test.ts
+++ b/packages/shared/lib/services/connection.service.integration.test.ts
@@ -79,8 +79,8 @@ describe('Connection service integration tests', () => {
await createConfigSeed(env, 'google', 'google');
await createConfigSeed(env, 'notion', 'notion');
- const google = await createConnectionSeed(env, 'google');
- const notion = await createConnectionSeed(env, 'notion');
+ const google = await createConnectionSeed({ env, provider: 'google' });
+ const notion = await createConnectionSeed({ env, provider: 'notion' });
const dbConnections = await connectionService.listConnections({
environmentId: env.id
@@ -96,10 +96,10 @@ describe('Connection service integration tests', () => {
await createConfigSeed(env, 'google', 'google');
await createConfigSeed(env, 'notion', 'notion');
- const google = await createConnectionSeed(env, 'google');
- const notion = await createConnectionSeed(env, 'notion');
- await createConnectionSeed(env, 'notion');
- await createConnectionSeed(env, 'notion');
+ const google = await createConnectionSeed({ env, provider: 'google' });
+ const notion = await createConnectionSeed({ env, provider: 'notion' });
+ await createConnectionSeed({ env, provider: 'notion' });
+ await createConnectionSeed({ env, provider: 'notion' });
const dbConnections = await connectionService.listConnections({
environmentId: env.id,
@@ -116,8 +116,8 @@ describe('Connection service integration tests', () => {
await createConfigSeed(env, 'google', 'google');
await createConfigSeed(env, 'notion', 'notion');
- const google = await createConnectionSeed(env, 'google');
- await createConnectionSeed(env, 'notion');
+ const google = await createConnectionSeed({ env, provider: 'google' });
+ await createConnectionSeed({ env, provider: 'notion' });
const dbConnections = await connectionService.listConnections({
environmentId: env.id,
@@ -133,8 +133,8 @@ describe('Connection service integration tests', () => {
await createConfigSeed(env, 'notion', 'notion');
- const notion = await createConnectionSeed(env, 'notion');
- await createConnectionSeed(env, 'notion');
+ const notion = await createConnectionSeed({ env, provider: 'notion' });
+ await createConnectionSeed({ env, provider: 'notion' });
const dbConnections = await connectionService.listConnections({
environmentId: env.id,
@@ -150,8 +150,8 @@ describe('Connection service integration tests', () => {
await createConfigSeed(env, 'notion', 'notion');
- const notion = await createConnectionSeed(env, 'notion');
- await createConnectionSeed(env, 'notion');
+ const notion = await createConnectionSeed({ env, provider: 'notion' });
+ await createConnectionSeed({ env, provider: 'notion' });
const dbConnections = await connectionService.listConnections({
environmentId: env.id,
@@ -167,7 +167,7 @@ describe('Connection service integration tests', () => {
await createConfigSeed(env, 'notion', 'notion');
- const notionError = await createConnectionSeed(env, 'notion');
+ const notionError = await createConnectionSeed({ env, provider: 'notion' });
await errorNotificationService.auth.create({
type: 'auth',
action: 'connection_test',
@@ -175,7 +175,7 @@ describe('Connection service integration tests', () => {
log_id: Math.random().toString(36).substring(7),
active: true
});
- await createConnectionSeed(env, 'notion');
+ await createConnectionSeed({ env, provider: 'notion' });
const dbConnections = await connectionService.listConnections({
environmentId: env.id,
@@ -191,7 +191,7 @@ describe('Connection service integration tests', () => {
await createConfigSeed(env, 'notion', 'notion');
- const notionError = await createConnectionSeed(env, 'notion');
+ const notionError = await createConnectionSeed({ env, provider: 'notion' });
await errorNotificationService.auth.create({
type: 'auth',
action: 'connection_test',
@@ -199,7 +199,7 @@ describe('Connection service integration tests', () => {
log_id: Math.random().toString(36).substring(7),
active: true
});
- const notionOK = await createConnectionSeed(env, 'notion');
+ const notionOK = await createConnectionSeed({ env, provider: 'notion' });
const dbConnections = await connectionService.listConnections({
environmentId: env.id,
@@ -217,9 +217,9 @@ describe('Connection service integration tests', () => {
const config = await createConfigSeed(env, 'notion', 'notion');
- await createConnectionSeed(env, 'notion');
+ await createConnectionSeed({ env, provider: 'notion' });
- const notionAuthError = await createConnectionSeed(env, 'notion');
+ const notionAuthError = await createConnectionSeed({ env, provider: 'notion' });
await errorNotificationService.auth.create({
type: 'auth',
action: 'connection_test',
@@ -228,7 +228,7 @@ describe('Connection service integration tests', () => {
active: true
});
- const notionSyncError = await createConnectionSeed(env, 'notion');
+ const notionSyncError = await createConnectionSeed({ env, provider: 'notion' });
const sync = await createSyncSeeds({
connectionId: notionSyncError.id!,
environment_id: env.id,
diff --git a/packages/shared/lib/services/connection.service.ts b/packages/shared/lib/services/connection.service.ts
index 9dce601fc0b..381a5f40e70 100644
--- a/packages/shared/lib/services/connection.service.ts
+++ b/packages/shared/lib/services/connection.service.ts
@@ -57,7 +57,8 @@ import {
parseTableauTokenExpirationDate,
interpolateObject,
extractValueByPath,
- stripCredential
+ stripCredential,
+ interpolateObjectValues
} from '../utils/utils.js';
import type { LogContext, LogContextGetter } from '@nangohq/logs';
import { CONNECTIONS_WITH_SCRIPTS_CAP_LIMIT } from '../constants.js';
@@ -1698,7 +1699,7 @@ class ConnectionService {
const bodyFormat = provider.body_format || 'json';
- const postBody: Record | string = {};
+ let postBody: Record | string = {};
if (provider.token_params) {
for (const [key, value] of Object.entries(provider.token_params)) {
@@ -1712,6 +1713,7 @@ class ConnectionService {
postBody[key] = strippedValue;
}
}
+ postBody = interpolateObjectValues(postBody, connectionConfig);
}
const headers: Record = {};
@@ -1733,7 +1735,9 @@ class ConnectionService {
attributeNamePrefix: '$',
ignoreAttributes: false
}).build(postBody)
- : JSON.stringify(postBody);
+ : bodyFormat === 'form'
+ ? new URLSearchParams(postBody).toString()
+ : JSON.stringify(postBody);
const response = await axios.post(url.toString(), bodyContent, requestOptions);
diff --git a/packages/shared/lib/services/environment.service.ts b/packages/shared/lib/services/environment.service.ts
index dfc41a020b2..fa5d9230672 100644
--- a/packages/shared/lib/services/environment.service.ts
+++ b/packages/shared/lib/services/environment.service.ts
@@ -224,27 +224,21 @@ class EnvironmentService {
return encryptionManager.decryptEnvironment(result[0]);
}
- async createEnvironment(accountId: number, environment: string): Promise {
- const result = await db.knex.from(TABLE).insert({ account_id: accountId, name: environment }).returning('id');
+ async createEnvironment(accountId: number, name: string): Promise {
+ const [environment] = await db.knex.from(TABLE).insert({ account_id: accountId, name }).returning('*');
- if (Array.isArray(result) && result.length === 1 && result[0] && 'id' in result[0]) {
- const environmentId = result[0]['id'];
- const environment = await this.getById(environmentId);
- if (!environment) {
- return null;
- }
-
- const encryptedEnvironment = await encryptionManager.encryptEnvironment({
- ...environment,
- secret_key_hashed: await hashSecretKey(environment.secret_key)
- });
- await db.knex.from(TABLE).where({ id: environmentId }).update(encryptedEnvironment);
-
- const env = encryptionManager.decryptEnvironment(encryptedEnvironment);
- return env;
+ if (!environment) {
+ return null;
}
- return null;
+ const encryptedEnvironment = await encryptionManager.encryptEnvironment({
+ ...environment,
+ secret_key_hashed: await hashSecretKey(environment.secret_key)
+ });
+ await db.knex.from(TABLE).where({ id: environment.id }).update(encryptedEnvironment);
+
+ const env = encryptionManager.decryptEnvironment(encryptedEnvironment);
+ return env;
}
async createDefaultEnvironments(accountId: number): Promise {
diff --git a/packages/shared/lib/services/file/remote.service.ts b/packages/shared/lib/services/file/remote.service.ts
index 0e5eb8bfd3b..6998b4871cc 100644
--- a/packages/shared/lib/services/file/remote.service.ts
+++ b/packages/shared/lib/services/file/remote.service.ts
@@ -117,6 +117,10 @@ class RemoteFileService {
}
}
+ async getPublicTemplateJsonSchemaFile(integrationName: string, environmentId: number): Promise {
+ return this.getFile(`${this.publicRoute}/${integrationName}/.nango/schema.json`, environmentId);
+ }
+
getFile(fileName: string, environmentId: number): Promise {
return new Promise((resolve, reject) => {
const getObjectCommand = new GetObjectCommand({
diff --git a/packages/shared/lib/services/notification/error.service.ts b/packages/shared/lib/services/notification/error.service.ts
index 0a64a793821..6e9258ff21a 100644
--- a/packages/shared/lib/services/notification/error.service.ts
+++ b/packages/shared/lib/services/notification/error.service.ts
@@ -6,6 +6,7 @@ import type { Result } from '@nangohq/utils';
import db from '@nangohq/database';
const DB_TABLE = '_nango_active_logs';
+const SYNC_TABLE = '_nango_syncs';
type ErrorNotification = Required>;
type SyncErrorNotification = ErrorNotification & Required>;
@@ -76,6 +77,21 @@ export const errorNotificationService = {
},
clearBySyncId: async ({ sync_id }: Pick): Promise => {
await db.knex.from(DB_TABLE).where({ type: 'sync', sync_id }).delete();
+ },
+ /**
+ * Clear By Sync Config Id
+ * @description Clear all sync notifications by sync config id. This is used
+ * when disabling a sync at the integration level. Any active logs are
+ * no longer relevant because the sync is disabled.
+ */
+ clearBySyncConfig: async ({ sync_config_id }: { sync_config_id: number }): Promise => {
+ const query = db.knex
+ .from(DB_TABLE)
+ .join(SYNC_TABLE, `${SYNC_TABLE}.id`, '=', `${DB_TABLE}.sync_id`)
+ .where({ type: 'sync', active: true })
+ .andWhere({ [`${SYNC_TABLE}.sync_config_id`]: sync_config_id, [`${SYNC_TABLE}.deleted`]: false });
+
+ await query.delete();
}
}
};
diff --git a/packages/shared/lib/services/paginate.service.ts b/packages/shared/lib/services/paginate.service.ts
index 8d8e9fea8ae..263c402ad83 100644
--- a/packages/shared/lib/services/paginate.service.ts
+++ b/packages/shared/lib/services/paginate.service.ts
@@ -170,7 +170,11 @@ class PaginationService {
}
private updateConfigBodyOrParams(passPaginationParamsInBody: boolean, config: UserProvidedProxyConfiguration, updatedBodyOrParams: Record) {
- passPaginationParamsInBody ? (config.data = updatedBodyOrParams) : (config.params = updatedBodyOrParams);
+ if (passPaginationParamsInBody) {
+ config.data = updatedBodyOrParams;
+ } else {
+ config.params = updatedBodyOrParams;
+ }
}
private getNextPageLinkFromBodyOrHeaders(linkPagination: LinkPagination, response: AxiosResponse, paginationConfig: Pagination) {
diff --git a/packages/shared/lib/services/sync/config/deploy.service.ts b/packages/shared/lib/services/sync/config/deploy.service.ts
index b82cb80507a..1465fc1e86f 100644
--- a/packages/shared/lib/services/sync/config/deploy.service.ts
+++ b/packages/shared/lib/services/sync/config/deploy.service.ts
@@ -416,6 +416,8 @@ export async function deployPreBuilt({
let provider_config_key: string;
// this is a public template so copy it from the public location
+ // We might not want to do this as it just overrides the root nango.yaml
+ // which means we overwrite any custom nango.yaml that the user has
await remoteFileService.copy(
firstConfig.public_route,
nangoConfigFile,
@@ -522,6 +524,12 @@ export async function deployPreBuilt({
throw new NangoError('file_upload_error');
}
+ const flowJsonSchema: JSONSchema7 = {
+ definitions: {}
+ };
+
+ const flowModels = Array.isArray(models) ? models : [models];
+
if (is_public) {
await remoteFileService.copy(
config.public_route,
@@ -530,6 +538,21 @@ export async function deployPreBuilt({
environment.id,
`${sync_name}.ts`
);
+ // fetch the json schema so we have type checking
+ const jsonSchema = await remoteFileService.getPublicTemplateJsonSchemaFile(firstConfig.public_route, environment.id);
+
+ if (jsonSchema) {
+ const parsedJsonSchema = JSON.parse(jsonSchema);
+ for (const model of flowModels) {
+ const schema = parsedJsonSchema.definitions![model];
+ if (!schema) {
+ const error = new NangoError('deploy_missing_json_schema_model', `json_schema doesn't contain model "${model}"`);
+
+ return { success: false, error, response: null };
+ }
+ flowJsonSchema.definitions![model] = schema;
+ }
+ }
} else {
if (typeof config.fileBody === 'object' && config.fileBody.ts) {
await remoteFileService.upload(
@@ -565,7 +588,7 @@ export async function deployPreBuilt({
nango_config_id,
file_location,
version,
- models: Array.isArray(models) ? models : [models],
+ models: flowModels,
active: true,
runs,
input: input && typeof input !== 'string' ? String(input.name) : input,
@@ -581,6 +604,7 @@ export async function deployPreBuilt({
is_public,
enabled: true,
webhook_subscriptions: null,
+ models_json_schema: flowJsonSchema,
updated_at: new Date()
};
diff --git a/packages/shared/lib/services/sync/config/deploy.service.unit.test.ts b/packages/shared/lib/services/sync/config/deploy.service.unit.test.ts
index db2b96da2b1..87b7faa5573 100644
--- a/packages/shared/lib/services/sync/config/deploy.service.unit.test.ts
+++ b/packages/shared/lib/services/sync/config/deploy.service.unit.test.ts
@@ -11,6 +11,7 @@ import { Orchestrator } from '../../../clients/orchestrator.js';
import type { OrchestratorClientInterface } from '../../../clients/orchestrator.js';
import type { DBTeam, DBEnvironment, CleanedIncomingFlowConfig } from '@nangohq/types';
import type { SyncConfig } from '../../../models/Sync.js';
+import db from '@nangohq/database';
const orchestratorClientNoop: OrchestratorClientInterface = {
recurring: () => Promise.resolve({}) as any,
@@ -207,6 +208,8 @@ describe('Sync config create', () => {
return Promise.resolve([]);
});
+ vi.spyOn(db.knex, 'from').mockRejectedValue(new Error());
+
await expect(
DeployConfigService.deploy({
environment,
diff --git a/packages/shared/lib/services/sync/manager.service.ts b/packages/shared/lib/services/sync/manager.service.ts
index c1f07c6d4ed..a05a49995c0 100644
--- a/packages/shared/lib/services/sync/manager.service.ts
+++ b/packages/shared/lib/services/sync/manager.service.ts
@@ -13,7 +13,7 @@ import {
import { errorNotificationService } from '../notification/error.service.js';
import configService from '../config.service.js';
import type { Connection, NangoConnection } from '../../models/Connection.js';
-import type { Sync, ReportedSyncJobStatus, SyncCommand } from '../../models/Sync.js';
+import type { SyncWithConnectionId, ReportedSyncJobStatus, SyncCommand } from '../../models/Sync.js';
import { SyncType, SyncStatus } from '../../models/Sync.js';
import { NangoError } from '../../utils/error.js';
import type { Config as ProviderConfig } from '../../models/Provider.js';
@@ -334,12 +334,24 @@ export class SyncManagerService {
continue;
}
- const reportedStatus = await this.syncStatus({ sync, environmentId, providerConfigKey, includeJobStatus, orchestrator, recordsService });
+ const syncWithConnectionId: SyncWithConnectionId = {
+ ...sync,
+ connection_id: connection.connection_id
+ };
+
+ const reportedStatus = await this.syncStatus({
+ sync: syncWithConnectionId,
+ environmentId,
+ providerConfigKey,
+ includeJobStatus,
+ orchestrator,
+ recordsService
+ });
syncsWithStatus.push(reportedStatus);
}
} else {
- const syncs =
+ const syncs: SyncWithConnectionId[] =
syncNames.length > 0
? await getSyncsByProviderConfigAndSyncNames(environmentId, providerConfigKey, syncNames)
: await getSyncsByProviderConfigKey(environmentId, providerConfigKey);
@@ -421,7 +433,7 @@ export class SyncManagerService {
orchestrator,
recordsService
}: {
- sync: Sync;
+ sync: SyncWithConnectionId;
environmentId: number;
providerConfigKey: string;
includeJobStatus: boolean;
@@ -458,6 +470,7 @@ export class SyncManagerService {
return {
id: sync.id,
+ connection_id: sync.connection_id,
type: latestJob?.type === SyncType.INCREMENTAL ? latestJob.type : 'INITIAL',
finishedAt: latestJob?.updated_at,
nextScheduledSyncAt: schedule.nextDueDate,
diff --git a/packages/shared/lib/services/sync/sync.service.ts b/packages/shared/lib/services/sync/sync.service.ts
index e8434f80b1e..7241ff72f76 100644
--- a/packages/shared/lib/services/sync/sync.service.ts
+++ b/packages/shared/lib/services/sync/sync.service.ts
@@ -1,6 +1,6 @@
import { v4 as uuidv4 } from 'uuid';
import db, { schema, dbNamespace } from '@nangohq/database';
-import type { Sync, SyncConfig, Job as SyncJob } from '../../models/Sync.js';
+import type { Sync, SyncWithConnectionId, SyncConfig, Job as SyncJob } from '../../models/Sync.js';
import { SyncStatus } from '../../models/Sync.js';
import type { Connection, NangoConnection } from '../../models/Connection.js';
import type { ActiveLog, IncomingFlowConfig, SlimAction, SlimSync, SyncAndActionDifferences, SyncTypeLiteral } from '@nangohq/types';
@@ -197,7 +197,8 @@ export const getSyncs = async (
this.on(`${SYNC_CONFIG_TABLE}.sync_name`, `${TABLE}.name`)
.andOn(`${SYNC_CONFIG_TABLE}.deleted`, '=', db.knex.raw('FALSE'))
.andOn(`${SYNC_CONFIG_TABLE}.active`, '=', db.knex.raw('TRUE'))
- .andOn(`${SYNC_CONFIG_TABLE}.type`, '=', db.knex.raw('?', 'sync'));
+ .andOn(`${SYNC_CONFIG_TABLE}.type`, '=', db.knex.raw('?', 'sync'))
+ .andOn(`${SYNC_CONFIG_TABLE}.enabled`, '=', db.knex.raw('?', 'TRUE'));
})
.where({
nango_connection_id: nangoConnection.id,
@@ -241,8 +242,6 @@ export const getSyncsByConnectionId = async (nangoConnectionId: number): Promise
return null;
};
-type SyncWithConnectionId = Sync & { connection_id: string };
-
export const getSyncsByProviderConfigKey = async (environment_id: number, providerConfigKey: string): Promise => {
const results = await db.knex
.select(`${TABLE}.*`, `${TABLE}.name`, `_nango_connections.connection_id`, `${TABLE}.created_at`, `${TABLE}.updated_at`, `${TABLE}.last_sync_date`)
@@ -284,9 +283,13 @@ export const getSyncNamesByConnectionId = async (nangoConnectionId: number): Pro
return [];
};
-export const getSyncsByProviderConfigAndSyncNames = async (environment_id: number, providerConfigKey: string, syncNames: string[]): Promise => {
+export const getSyncsByProviderConfigAndSyncNames = async (
+ environment_id: number,
+ providerConfigKey: string,
+ syncNames: string[]
+): Promise => {
const results = await db.knex
- .select(`${TABLE}.*`)
+ .select(`${TABLE}.*`, '_nango_connections.connection_id')
.from(TABLE)
.join('_nango_connections', '_nango_connections.id', `${TABLE}.nango_connection_id`)
.where({
diff --git a/packages/shared/lib/utils/analytics.ts b/packages/shared/lib/utils/analytics.ts
index 62396d9093c..4a41dd17ad5 100644
--- a/packages/shared/lib/utils/analytics.ts
+++ b/packages/shared/lib/utils/analytics.ts
@@ -1,5 +1,5 @@
import { PostHog } from 'posthog-node';
-import { localhostUrl, isCloud, isStaging, baseUrl } from '@nangohq/utils';
+import { localhostUrl, isCloud, isStaging, baseUrl, getLogger } from '@nangohq/utils';
import { UserType } from '../utils/utils.js';
import errorManager, { ErrorSourceEnum } from './error.manager.js';
import accountService from '../services/account.service.js';
@@ -8,6 +8,8 @@ import userService from '../services/user.service.js';
import { LogActionEnum } from '../models/Telemetry.js';
import { NANGO_VERSION } from '../version.js';
+const logger = getLogger('analytics');
+
export enum AnalyticsTypes {
ACCOUNT_CREATED = 'server:account_created',
ACCOUNT_JOINED = 'server:account_joined',
@@ -99,12 +101,22 @@ class Analytics {
packageVersion: string | undefined;
constructor() {
+ const hasTelemetry = process.env['TELEMETRY'] !== 'false' && !isStaging;
+ if (!hasTelemetry) {
+ return;
+ }
+
+ // hardcoded for OSS telemetry
+ const key = process.env['PUBLIC_POSTHOG_KEY'] || 'phc_4S2pWFTyPYT1i7zwC8YYQqABvGgSAzNHubUkdEFvcTl';
+ if (!key) {
+ logger.error('No PostHog key');
+ return;
+ }
+
try {
- if (process.env['TELEMETRY']?.toLowerCase() !== 'false' && !isStaging) {
- this.client = new PostHog('phc_4S2pWFTyPYT1i7zwC8YYQqABvGgSAzNHubUkdEFvcTl');
- this.client.enable();
- this.packageVersion = NANGO_VERSION;
- }
+ this.client = new PostHog(key);
+ this.client.enable();
+ this.packageVersion = NANGO_VERSION;
} catch (err) {
errorManager.report(err, {
source: ErrorSourceEnum.PLATFORM,
diff --git a/packages/shared/lib/utils/error.manager.ts b/packages/shared/lib/utils/error.manager.ts
index 70ef3d403d3..2cc9902ac71 100644
--- a/packages/shared/lib/utils/error.manager.ts
+++ b/packages/shared/lib/utils/error.manager.ts
@@ -1,15 +1,8 @@
import * as uuid from 'uuid';
-import type { EventHint } from '@sentry/node';
-import sentry from '@sentry/node';
-import type { Tracer } from 'dd-trace';
-import type { ErrorEvent } from '@sentry/types';
+import tracer from 'dd-trace';
import { NangoError } from './error.js';
import type { Response, Request } from 'express';
-import { errorToObject, getLogger, isCloud } from '@nangohq/utils';
-import environmentService from '../services/environment.service.js';
-import accountService from '../services/account.service.js';
-import userService from '../services/user.service.js';
-import { NANGO_VERSION } from '../version.js';
+import { errorToObject, getLogger } from '@nangohq/utils';
const logger = getLogger('ErrorManager');
@@ -30,95 +23,19 @@ interface ErrorOptionalConfig {
}
class ErrorManager {
- constructor() {
- try {
- if (isCloud && process.env['SENTRY_DNS']) {
- const packageVersion = NANGO_VERSION;
- sentry.init({
- dsn: process.env['SENTRY_DNS'],
- beforeSend(event: ErrorEvent, _: EventHint) {
- return event.user?.id === 'account-78' ? null : event;
- },
- environment: process.env['NODE_ENV'] as string,
- release: 'nango@' + packageVersion
- });
- }
- } catch (_) {
- return;
- }
- }
-
/**
* TODO: reuse information in res.locals when possible
*/
- public report(e: unknown, config: ErrorOptionalConfig = { source: ErrorSourceEnum.PLATFORM }, tracer?: Tracer): void {
- void sentry.withScope(async function (scope) {
- if (config.environmentId || config.accountId) {
- let accountId: number | undefined;
- if (config.environmentId) {
- const environmentName = await environmentService.getEnvironmentName(config.environmentId);
- accountId = (await environmentService.getAccountIdFromEnvironment(config.environmentId)) as number;
- sentry.setTag('environmentName', environmentName);
- }
-
- if (config.accountId && !config.environmentId) {
- accountId = config.accountId;
- }
- const account = await accountService.getAccountById(accountId as number);
-
- if (!config.userId) {
- const users = await userService.getUsersByAccountId(accountId as number);
- sentry.setUser({
- id: `account-${accountId}`,
- organization: account?.name || '',
- emails: users.map((user) => user.email).join(','),
- userIds: users.map((user) => user.id).join(',')
- });
- }
- }
+ public report(err: unknown, config: ErrorOptionalConfig = { source: ErrorSourceEnum.PLATFORM }): void {
+ logger.error('Exception caught', errorToObject(err));
- if (config.userId) {
- const user = await userService.getUserById(config.userId);
- sentry.setUser({
- id: `user-${config.userId}`,
- email: user?.email || '',
- userId: user?.id
- });
- }
-
- sentry.setTag('source', config.source);
-
- if (config.operation) {
- sentry.setTag('operation', config.operation);
- }
-
- if (config.metadata) {
- const metadata = Object.entries(config.metadata).reduce>((acc, [key, value]) => {
- if (typeof value === 'object') {
- acc[key] = JSON.stringify(value);
- } else {
- acc[key] = value;
- }
- return acc;
- }, {});
- scope.setContext('metadata', metadata);
- }
-
- if (typeof e === 'string') {
- sentry.captureException(new Error(e));
- } else {
- sentry.captureException(e);
- }
- });
-
- logger.error('Exception caught', errorToObject(e));
-
- if (e instanceof Error && tracer) {
+ if (err instanceof Error) {
// Log to datadog manually
// https://github.com/DataDog/dd-trace-js/issues/1944
const span = tracer.scope().active();
if (span) {
- span.setTag('error', e);
+ span.addTags(config);
+ span.setTag('error', err);
}
}
}
@@ -141,41 +58,14 @@ class ErrorManager {
this.errResFromNangoErr(res, err);
}
- public handleGenericError(err: any, _: Request, res: Response, tracer?: Tracer): void {
+ public handleGenericError(err: any, _: Request, res: Response): void {
const errorId = uuid.v4();
- let nangoErr: NangoError;
- if (!(err instanceof Error)) {
- nangoErr = new NangoError('generic_error_malformed', errorId);
- } else if (!(err instanceof NangoError)) {
- nangoErr = new NangoError(err.message, errorId);
- } else {
- nangoErr = err;
- }
-
- let environmentId: number | undefined;
- if ('environment' in res.locals) {
- environmentId = res.locals['environment'].id;
- }
- this.report(err, { source: ErrorSourceEnum.PLATFORM, environmentId, metadata: nangoErr.payload }, tracer);
+ this.report(err);
const supportError = new NangoError('generic_error_support', errorId);
this.errResFromNangoErr(res, supportError);
}
-
- public getExpressRequestContext(req: Request): Record {
- const metadata: Record = {};
- metadata['baseUrl'] = req.baseUrl;
- metadata['originalUrl'] = req.originalUrl;
- metadata['subdomains'] = req.subdomains;
- metadata['body'] = req.body;
- metadata['hostname'] = req.hostname;
- metadata['method'] = req.method;
- metadata['params'] = req.params;
- metadata['query'] = req.query;
-
- return metadata;
- }
}
export default new ErrorManager();
diff --git a/packages/shared/lib/utils/featureflags.ts b/packages/shared/lib/utils/featureflags.ts
index 8d9070bcfa9..3c74d749c95 100644
--- a/packages/shared/lib/utils/featureflags.ts
+++ b/packages/shared/lib/utils/featureflags.ts
@@ -23,7 +23,7 @@ export class FeatureFlags {
(r) => {
return isExcludingFlag ? !r : r;
},
- (_) => {
+ () => {
return fallback;
}
);
diff --git a/packages/shared/package.json b/packages/shared/package.json
index f52158f3d54..a2fedc54dbe 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -25,11 +25,10 @@
"@nangohq/nango-yaml": "0.48.1",
"@nangohq/node": "^0.48.1",
"@nangohq/utils": "file:../utils",
- "@sentry/node": "^7.119.2",
- "ajv": "^8.12.0",
+ "ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"archiver": "^6.0.1",
- "axios": "^1.7.4",
+ "axios": "^1.7.9",
"braintree": "^3.15.0",
"dd-trace": "5.21.0",
"exponential-backoff": "^3.1.1",
@@ -56,7 +55,6 @@
"@nangohq/logs": "file:../logs",
"@nangohq/nango-orchestrator": "file:../orchestrator",
"@nangohq/types": "0.48.1",
- "@sentry/types": "7.112.2",
"@types/braintree": "^3.3.12",
"@types/js-yaml": "^4.0.5",
"@types/json-schema": "7.0.15",
@@ -67,9 +65,9 @@
"express": "^4.20.0",
"json-schema": "0.4.0",
"knex": "3.1.0",
- "type-fest": "4.26.1",
- "typescript": "^5.3.3",
- "vitest": "1.6.0"
+ "type-fest": "4.32.0",
+ "typescript": "5.7.3",
+ "vitest": "2.1.8"
},
"files": [
"dist/**/*",
diff --git a/packages/shared/providers.yaml b/packages/shared/providers.yaml
index 4bb257f6131..89d7647ab3d 100644
--- a/packages/shared/providers.yaml
+++ b/packages/shared/providers.yaml
@@ -176,6 +176,10 @@ aircall-basic:
verification:
method: GET
endpoint: /v1/ping
+ paginate:
+ type: link
+ link_path_in_response_body: meta.next_page_link
+ response_path: results
docs_connect: https://docs.nango.dev/integrations/all/aircall-basic/connect
docs: https://docs.nango.dev/integrations/all/aircall
@@ -1602,11 +1606,13 @@ chorus:
headers:
authorization: Bearer ${apiKey}
docs: https://docs.nango.dev/integrations/all/chorus
+ docs_connect: https://docs.nango.dev/integrations/all/chorus/connect
credentials:
apiKey:
type: string
title: API Key
description: The API key for your Chorus account
+ doc_section: '#step-1-generating-your-chorus-api-key'
circle-so:
display_name: Circle.so
@@ -2046,7 +2052,7 @@ datadog:
- dev-tools
auth_mode: API_KEY
proxy:
- base_url: https://${connectionConfig.siteParameter}/api
+ base_url: https://api.${connectionConfig.siteParameter}/api
headers:
dd-api-key: ${apiKey}
dd-application-key: ${connectionConfig.applicationKey}
@@ -2689,6 +2695,24 @@ falai:
title: API Key
description: The API key for your fal.ai account
+findymail:
+ display_name: FindyMail
+ categories:
+ - marketing
+ - crm
+ auth_mode: API_KEY
+ proxy:
+ base_url: https://app.findymail.com
+ headers:
+ authorization: Bearer ${apiKey}
+ docs: https://docs.nango.dev/integrations/all/findymail
+ docs_connect: https://docs.nango.dev/integrations/all/findymail/connect
+ credentials:
+ apiKey:
+ type: string
+ title: API Key
+ description: The API key for your FindyMail account
+
firefish:
display_name: Firefish
scope_separator: ' '
@@ -2844,7 +2868,7 @@ freshdesk:
doc_section: '#step-2-finding-your-freshdesk-domain'
freshsales:
- display_name: FreshSales
+ display_name: Freshsales
categories:
- crm
auth_mode: API_KEY
@@ -2858,23 +2882,28 @@ freshsales:
retry:
after: 'retry-after'
docs: https://docs.nango.dev/integrations/all/freshsales
+ docs_connect: https://docs.nango.dev/integrations/all/freshsales/connect
connection_config:
subdomain:
type: string
- title: FreshSales Domain
- description: The subdomain of your FreshSales account
+ title: Freshsales subdomain
+ description: The subdomain of your Freshsales account
pattern: '^[a-z0-9_-]+$'
- example: domain
+ example: subdomain
suffix: .freshsales.io
prefix: https://
+ doc_section: '#step-2-finding-your-freshsales-subdomain'
credentials:
apiKey:
type: string
title: API Key
- description: The API key for your FreshSales account
+ description: The API key for your Freshsales account
+ example: 4oBqA_AzM_G3xbW3TJGvrA
+ pattern: '[A-Za-z0-9_]{22}'
+ doc_section: '#step-1-finding-your-freshsales-api-key'
freshservice:
- display_name: FreshService
+ display_name: Freshservice
categories:
- support
auth_mode: BASIC
@@ -2886,21 +2915,24 @@ freshservice:
retry:
after: 'retry-after'
docs: https://docs.nango.dev/integrations/all/freshservice
+ docs_connect: https://docs.nango.dev/integrations/all/freshservice/connect
connection_config:
subdomain:
type: string
- title: FreshService Domain
- description: The subdomain of your FreshService account
+ title: Freshservice subdomain
+ description: The subdomain of your Freshservice account
pattern: '^[a-z0-9_-]+$'
- example: domain
+ example: subdomain
suffix: .freshservice.com
prefix: https://
+ doc_section: '#step-2-finding-your-freshservice-subdomain'
credentials:
username:
type: string
title: API key
description: The API Key of your Freshservice account
secret: true
+ doc_section: '#step-1-finding-your-freshservice-api-key'
password:
type: string
title: ''
@@ -2961,7 +2993,7 @@ front:
docs: https://docs.nango.dev/integrations/all/front
gainsight-cc:
- display_name: Gainsight
+ display_name: Gainsight CC
categories:
- support
- crm
@@ -2973,13 +3005,15 @@ gainsight-cc:
proxy:
base_url: https://api2-${connectionConfig.region}.insided.com
docs: https://docs.nango.dev/integrations/all/gainsight-cc
+ docs_connect: https://docs.nango.dev/integrations/all/gainsight-cc/connect
connection_config:
region:
type: string
title: Region
description: The region of your Gainsight account
- example: eu
- pattern: '^[a-z]+$'
+ example: eu-west-1
+ pattern: '^[a-z]{2}-[a-z]+-[1-9]$'
+ doc_section: '#step-2-finding-your-region'
garmin:
display_name: Garmin
@@ -3027,16 +3061,21 @@ guru:
method: GET
endpoint: /whoami
docs: https://docs.nango.dev/integrations/all/guru
+ docs_connect: https://docs.nango.dev/integrations/all/guru/connect
credentials:
username:
type: string
title: User/Collection ID
description: The user or collection ID of your Guru account
+ doc_section: '#step-1-finding-your-user-collection-id'
password:
type: string
title: User/Collection Token
description: The user or collection token of your Guru account
secret: true
+ format: uuid
+ example: 123e4567-e89b-12d3-a456-426614174000
+ doc_section: '#step-2-generating-your-user-collection-token'
github:
display_name: GitHub
@@ -4101,6 +4140,26 @@ kustomer:
title: API Key
description: The API key for your Kustomer account
+lagrowthmachine:
+ display_name: La Growth Machine
+ categories:
+ - marketing
+ auth_mode: API_KEY
+ proxy:
+ base_url: https://apiv2.lagrowthmachine.com
+ query:
+ KEY: ${apiKey}
+ docs: https://docs.nango.dev/integrations/all/lagrowthmachine
+ docs_connect: https://docs.nango.dev/integrations/all/lagrowthmachine/connect
+ credentials:
+ apiKey:
+ type: string
+ title: API Key
+ description: The API key for your La Growth Machine account
+ example: 1bc32cba-a5d6-438a-bbcc-af312f560a3c
+ format: uuid
+ doc_section: '#step-1-finding-your-api-key'
+
lastpass:
display_name: LastPass
categories:
@@ -6022,6 +6081,26 @@ sap-success-factors:
secret: true
doc_section: '#step-4-generating-your-saml-assertion'
+scrapedo:
+ display_name: Scrape.do
+ categories:
+ - other
+ auth_mode: API_KEY
+ proxy:
+ base_url: https://api.scrape.do
+ query:
+ token: ${apiKey}
+ docs: https://docs.nango.dev/integrations/all/scrapedo
+ docs_connect: https://docs.nango.dev/integrations/all/scrapedo/connect
+ credentials:
+ apiKey:
+ type: string
+ title: API Token
+ description: The API Token for your Scrape.do account
+ example: 3c12d71308a346c41d10b19a2b2ac1ea5cacb53588d
+ pattern: '^[a-zA-Z0-9]+$'
+ doc_section: '#step-1-finding-your-api-key'
+
salesloft:
display_name: Salesloft
categories:
@@ -6034,7 +6113,7 @@ salesloft:
docs: https://docs.nango.dev/integrations/all/salesloft
sendgrid:
- display_name: Sendgrid
+ display_name: SendGrid
categories:
- marketing
auth_mode: API_KEY
@@ -6043,11 +6122,13 @@ sendgrid:
authorization: Bearer ${apiKey}
base_url: https://api.sendgrid.com
docs: https://docs.nango.dev/integrations/all/sendgrid
+ docs_connect: https://docs.nango.dev/integrations/all/sendgrid/connect
credentials:
apiKey:
type: string
title: API Key
description: The API key for your Sendgrid account
+ doc_section: '#step-1-generating-your-sendgrid-api-key'
sedna:
display_name: Sedna (Oauth2)
@@ -6171,6 +6252,63 @@ sharepoint-online:
alias: microsoft
docs: https://docs.nango.dev/integrations/all/sharepoint-online
+sharepoint-online-v1:
+ display_name: SharePoint Online (v1)
+ categories:
+ - storage
+ - communication
+ auth_mode: TWO_STEP
+ token_url: https://login.microsoftonline.com/${connectionConfig.tenantId}/oauth2/token
+ body_format: form
+ token_params:
+ client_id: ${credential.clientId}
+ grant_type: client_credentials
+ resource: https://${connectionConfig.tenantId}.sharepoint.com
+ client_assertion_type: urn:ietf:params:oauth:client-assertion-type:jwt-bearer
+ client_assertion: ${credential.assertion}
+ token_headers:
+ content-type: application/x-www-form-urlencoded
+ proxy:
+ base_url: https://${connectionConfig.tenantName}.sharepoint.com
+ headers:
+ accept: application/json;odata=verbose
+ token_response:
+ token: access_token
+ token_expiration: expires_in
+ token_expiration_strategy: expireIn
+ docs: https://docs.nango.dev/integrations/all/sharepoint-online
+ docs_connect: https://docs.nango.dev/integrations/all/sharepoint-online-v1/connect
+ connection_config:
+ tenantId:
+ type: string
+ title: Tenant ID
+ description: The unique identifier for your organization that uses Microsoft services
+ format: uuid
+ example: a1b2c3d4-e5f6-47a8-9b0c-d1234567890f
+ doc_section: '#step-1-finding-your-tenant-id'
+ order: 1
+ tenantName:
+ type: string
+ title: Tenant Name
+ description: The initial domain name for your Microsoft services tenant
+ example: mycompany
+ pattern: '^[a-zA-Z0-9]+$'
+ doc_section: '#step-2-finding-your-tenant-name'
+ order: 2
+ credentials:
+ clientId:
+ type: string
+ title: Client ID
+ description: Your application Client ID
+ secret: true
+ doc_section: '#step-3-finding-your-client-id'
+ assertion:
+ type: string
+ title: Client Assertion
+ description: Your generated client assertion
+ secret: true
+ doc_section: '#step-4-generating-your-client-assertion'
+
shipstation:
display_name: Shipstation
categories:
@@ -6211,6 +6349,33 @@ shopify:
headers:
x-shopify-access-token: ${accessToken}
docs: https://docs.nango.dev/integrations/all/shopify
+ docs_connect: https://docs.nango.dev/integrations/all/shopify/connect
+ connection_config:
+ subdomain:
+ type: string
+ title: Shopify Domain
+ description: The subdomain of your Shopify account
+ pattern: '^[a-z0-9_-]+$'
+ example: domain
+ suffix: .myshopify.com
+ prefix: https://
+ doc_section: '#step-1-finding-your-shopify-domain'
+
+shopify-api-key:
+ display_name: Shopify (api key)
+ categories:
+ - e-commerce
+ auth_mode: API_KEY
+ proxy:
+ base_url: https://${connectionConfig.subdomain}.myshopify.com
+ headers:
+ x-shopify-access-token: ${apiKey}
+ content-type: application/json
+ verification:
+ method: POST
+ endpoint: /admin/api/2024-10/graphql.json?query=%7B__schema%7Btypes%7Bname%2Ckind%2Cfields%7Bname%7D%7D%7D%7D
+ docs: https://docs.nango.dev/integrations/all/shopify
+ docs_connect: https://docs.nango.dev/integrations/all/shopify-api-key/connect
connection_config:
subdomain:
type: string
@@ -6220,6 +6385,16 @@ shopify:
example: domain
suffix: .myshopify.com
prefix: https://
+ doc_section: '#step-1-finding-your-shopify-domain'
+ order: 1
+ credentials:
+ apiKey:
+ type: string
+ title: API Access Token
+ description: The API access token generated
+ example: shpat_***************c03266f
+ pattern: '^shpat_[a-f0-9]{32}$'
+ doc_section: '#step-2-generating-your-api-access-token'
shortcut:
display_name: Shortcut
diff --git a/packages/types/lib/api.endpoints.ts b/packages/types/lib/api.endpoints.ts
index b1940d96eeb..0b1ecd3a6ce 100644
--- a/packages/types/lib/api.endpoints.ts
+++ b/packages/types/lib/api.endpoints.ts
@@ -59,6 +59,7 @@ import type {
import type { GetMeta } from './meta/api';
import type { PatchFlowDisable, PatchFlowEnable, PatchFlowFrequency, PostPreBuiltDeploy, PutUpgradePreBuiltFlow } from './flow/http.api';
import type { PostPublicWebhook } from './webhooks/http.api';
+import type { PostEnvironment } from './environment/api';
export type PublicApiEndpoints =
| SetMetadata
@@ -128,7 +129,8 @@ export type PrivateApiEndpoints =
| PutUpgradePreBuiltFlow
| PostConnectionRefresh
| PostManagedSignup
- | PostPreBuiltDeploy;
+ | PostPreBuiltDeploy
+ | PostEnvironment;
export type APIEndpoints = PrivateApiEndpoints | PublicApiEndpoints;
/**
diff --git a/packages/types/lib/api.ts b/packages/types/lib/api.ts
index 93b4824938b..9922c89013f 100644
--- a/packages/types/lib/api.ts
+++ b/packages/types/lib/api.ts
@@ -14,6 +14,7 @@ export interface ValidationError {
export type ResDefaultErrors =
| ApiError<'not_found'>
+ | ApiError<'conflict'>
| ApiError<'invalid_query_params', ValidationError[]>
| ApiError<'invalid_body', ValidationError[]>
| ApiError<'invalid_uri_params', ValidationError[]>
diff --git a/packages/types/lib/endUser/index.ts b/packages/types/lib/endUser/index.ts
index 3dc4a1799a5..04214d6ba12 100644
--- a/packages/types/lib/endUser/index.ts
+++ b/packages/types/lib/endUser/index.ts
@@ -29,10 +29,10 @@ export type DBInsertEndUser = Omit;
+ };
+}>;
diff --git a/packages/types/lib/index.ts b/packages/types/lib/index.ts
index 82e89f5d5f6..21d65fcae3d 100644
--- a/packages/types/lib/index.ts
+++ b/packages/types/lib/index.ts
@@ -25,7 +25,6 @@ export type * from './team/db.js';
export type * from './providers/api.js';
export type * from './proxy/api.js';
-export type * from './environment/db.js';
export type * from './scripts/on-events/db.js';
export type * from './scripts/on-events/api.js';
export type * from './scripts/syncs/api.js';
@@ -50,6 +49,7 @@ export type * from './runner/index.js';
export type * from './nangoYaml/index.js';
export type * from './environment/db.js';
+export type * from './environment/api/index.js';
export type * from './environment/api/webhook.js';
export type * from './environment/api/otlp.js';
export type * from './webhooks/api.js';
diff --git a/packages/types/lib/logs/api.ts b/packages/types/lib/logs/api.ts
index 9bfbbb1eeab..b0181d262bc 100644
--- a/packages/types/lib/logs/api.ts
+++ b/packages/types/lib/logs/api.ts
@@ -82,7 +82,7 @@ export type PostInsights = Endpoint<{
Path: '/api/v1/logs/insights';
Querystring: { env: string };
Body: {
- type: PickFromUnion;
+ type: PickFromUnion;
};
Success: {
data: {
diff --git a/packages/types/lib/nangoYaml/index.ts b/packages/types/lib/nangoYaml/index.ts
index d0d3b2b48a6..dcbc989cdf2 100644
--- a/packages/types/lib/nangoYaml/index.ts
+++ b/packages/types/lib/nangoYaml/index.ts
@@ -72,7 +72,7 @@ export interface NangoYamlV2IntegrationAction {
export interface NangoYamlModel {
[key: string]: NangoYamlModelFields;
}
-// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
+
export interface NangoYamlModelFields {
[key: string]: NangoYamlModelField;
}
@@ -144,9 +144,7 @@ export interface NangoModelField {
optional?: boolean | undefined;
}
-export type NangoSyncEndpointOld = {
- [key in HTTP_METHOD]?: string | undefined;
-};
+export type NangoSyncEndpointOld = Partial>;
export interface NangoSyncEndpointV2 {
method: HTTP_METHOD;
diff --git a/packages/types/lib/providers/provider.ts b/packages/types/lib/providers/provider.ts
index 0e98e456af4..4ba776e8045 100644
--- a/packages/types/lib/providers/provider.ts
+++ b/packages/types/lib/providers/provider.ts
@@ -137,7 +137,7 @@ export interface ProviderTwoStep extends Omit {
};
token_expires_in_ms?: number;
proxy_header_authorization?: string;
- body_format?: 'xml' | 'json';
+ body_format?: 'xml' | 'json' | 'form';
}
export interface ProviderSignature extends BaseProvider {
signature: {
diff --git a/packages/types/lib/web/env.ts b/packages/types/lib/web/env.ts
index 23ee29bb4ec..ba8ce8c603f 100644
--- a/packages/types/lib/web/env.ts
+++ b/packages/types/lib/web/env.ts
@@ -6,7 +6,8 @@ export interface WindowEnv {
publicPosthogKey: string;
publicPosthogHost: string;
publicLogoDevKey: string;
- publicKoalaKey: string;
+ publicKoalaApiUrl: string;
+ publicKoalaCdnUrl: string;
isCloud: boolean;
features: {
logs: boolean;
@@ -14,5 +15,6 @@ export interface WindowEnv {
auth: boolean;
managedAuth: boolean;
gettingStarted: boolean;
+ slack: boolean;
};
}
diff --git a/packages/types/package.json b/packages/types/package.json
index 7d6da92a699..8ebcea44f44 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -13,9 +13,9 @@
},
"devDependencies": {
"@types/json-schema": "7.0.15",
- "axios": "^1.7.4",
+ "axios": "^1.7.9",
"json-schema": "0.4.0",
- "type-fest": "4.26.1"
+ "type-fest": "4.32.0"
},
"license": "SEE LICENSE IN LICENSE FILE IN GIT REPOSITORY",
"files": [
diff --git a/packages/utils/lib/environment/detection.ts b/packages/utils/lib/environment/detection.ts
index e47553384c3..fe3e6d063ba 100644
--- a/packages/utils/lib/environment/detection.ts
+++ b/packages/utils/lib/environment/detection.ts
@@ -25,3 +25,4 @@ export const flagHasManagedAuth =
process.env['FLAG_MANAGED_AUTH_ENABLED'] === 'true' && Boolean(process.env['WORKOS_API_KEY'] && process.env['WORKOS_CLIENT_ID']);
export const flagHasAPIRateLimit = process.env['FLAG_API_RATE_LIMIT_ENABLED'] !== 'false';
export const flagHasBigQuery = process.env['FLAG_BIG_QUERY_EXPORT_ENABLED'] === 'true';
+export const flagHasSlack = !isHosted;
diff --git a/packages/utils/lib/environment/parse.ts b/packages/utils/lib/environment/parse.ts
index d7cd24107df..d52b3308154 100644
--- a/packages/utils/lib/environment/parse.ts
+++ b/packages/utils/lib/environment/parse.ts
@@ -144,6 +144,11 @@ export const ENVS = z.object({
.optional(),
NANGO_DB_SCHEMA: z.string().optional().default('nango'),
NANGO_DB_ADDITIONAL_SCHEMAS: z.string().optional(),
+ NANGO_DB_APPLICATION_NAME: z.string().optional().default('[unknown]'),
+
+ // PostHog
+ PUBLIC_POSTHOG_KEY: z.string().optional(),
+ PUBLIC_POSTHOG_HOST: z.string().optional(),
// Records
RECORDS_DATABASE_URL: z.string().url().optional(),
@@ -156,14 +161,14 @@ export const ENVS = z.object({
RENDER_API_KEY: z.string().optional(),
IS_RENDER: bool,
+ // Sentry
+ PUBLIC_SENTRY_KEY: z.string().optional(),
+
// Slack
NANGO_ADMIN_CONNECTION_ID: z.string().optional(),
NANGO_SLACK_INTEGRATION_KEY: z.string().optional(),
NANGO_ADMIN_UUID: z.string().uuid().optional(),
- // Sentry
- SENTRY_DNS: z.string().url().optional(),
-
// Internal API
NANGO_INTERNAL_API_KEY: z.string().optional(),
diff --git a/packages/utils/lib/express/route.ts b/packages/utils/lib/express/route.ts
index a26b748361d..4627511ba0d 100644
--- a/packages/utils/lib/express/route.ts
+++ b/packages/utils/lib/express/route.ts
@@ -12,7 +12,7 @@ export interface Route> {
export interface RouteHandler> extends Route {
validate: (req: EndpointRequest, res: EndpointResponse, next: NextFunction) => void;
- handler: (req: EndpointRequest, res: EndpointResponse, next: NextFunction) => void;
+ handler: (req: EndpointRequest, res: EndpointResponse, next: NextFunction) => void | Promise;
}
export const createRoute = >(server: Express, rh: RouteHandler): void => {
diff --git a/packages/utils/lib/result.ts b/packages/utils/lib/result.ts
index 58acd8982ff..d362d6f3297 100644
--- a/packages/utils/lib/result.ts
+++ b/packages/utils/lib/result.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */
/*
By convention Left represents a failed computation
And Right represents a successful one
@@ -26,8 +27,8 @@ export function Ok(value: T): Result {
return {
value,
unwrap: () => value,
- isErr: () => false,
- isOk: () => true,
+ isErr: (): this is Left => false,
+ isOk: (): this is Right => true,
map: (fn: (value: T) => U): Result => {
try {
return Ok(fn(value));
@@ -47,8 +48,8 @@ export function Err(error: E | string): Result {
unwrap: () => {
throw error as Error;
},
- isErr: () => true,
- isOk: () => false,
+ isErr: (): this is Left => true,
+ isOk: (): this is Right => false,
map: (_fn: (value: T) => U): Result => {
return Err(error);
},
diff --git a/packages/utils/lib/telemetry/metrics.ts b/packages/utils/lib/telemetry/metrics.ts
index 17cec816e05..ebef0100d11 100644
--- a/packages/utils/lib/telemetry/metrics.ts
+++ b/packages/utils/lib/telemetry/metrics.ts
@@ -97,7 +97,7 @@ export function time Promise>(metricName: T
computeDuration(start);
return v;
},
- (err) => {
+ (err: unknown) => {
computeDuration(start);
throw err;
}
diff --git a/packages/utils/lib/vitest.d.ts b/packages/utils/lib/vitest.d.ts
index d20460f8eb6..cd1b85ae631 100644
--- a/packages/utils/lib/vitest.d.ts
+++ b/packages/utils/lib/vitest.d.ts
@@ -1,6 +1,6 @@
+/* eslint-disable @typescript-eslint/no-empty-object-type */
export * from 'vitest';
-/* eslint-disable @typescript-eslint/no-empty-interface */
interface CustomMatchers {
toBeIsoDate: () => string;
toBeIsoDateTimezone: () => string;
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 23db45da268..4280b2c1f17 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -16,7 +16,7 @@
"license": "SEE LICENSE IN LICENSE FILE IN GIT REPOSITORY",
"dependencies": {
"@colors/colors": "1.6.0",
- "axios": "^1.7.4",
+ "axios": "^1.7.9",
"dd-trace": "5.21.0",
"exponential-backoff": "3.1.1",
"fast-safe-stringify": "2.1.1",
@@ -26,13 +26,13 @@
"serialize-error": "11.0.3",
"truncate-json": "3.0.0",
"winston": "3.13.0",
- "zod": "3.23.8"
+ "zod": "3.24.1"
},
"devDependencies": {
"@nangohq/types": "file:../types",
"express": "^4.20.0",
"ms": "3.0.0-canary.1",
- "vitest": "1.6.0"
+ "vitest": "2.1.8"
},
"files": [
"dist/**/*"
diff --git a/packages/webapp/.env.enterprise b/packages/webapp/.env.enterprise
deleted file mode 100644
index a6d9e96c690..00000000000
--- a/packages/webapp/.env.enterprise
+++ /dev/null
@@ -1,2 +0,0 @@
-REACT_APP_ENV=enterprise
-
diff --git a/packages/webapp/.env.hosted b/packages/webapp/.env.hosted
deleted file mode 100644
index 620eefa84c1..00000000000
--- a/packages/webapp/.env.hosted
+++ /dev/null
@@ -1 +0,0 @@
-REACT_APP_ENV=hosted
\ No newline at end of file
diff --git a/packages/webapp/.env.local b/packages/webapp/.env.local
deleted file mode 100644
index 42da5216624..00000000000
--- a/packages/webapp/.env.local
+++ /dev/null
@@ -1 +0,0 @@
-REACT_APP_ENV=local
diff --git a/packages/webapp/.env.prod b/packages/webapp/.env.prod
deleted file mode 100644
index 64526475ba2..00000000000
--- a/packages/webapp/.env.prod
+++ /dev/null
@@ -1,4 +0,0 @@
-REACT_APP_ENV=production
-REACT_APP_PUBLIC_POSTHOG_KEY=phc_4S2pWFTyPYT1i7zwC8YYQqABvGgSAzNHubUkdEFvcTl
-REACT_APP_PUBLIC_POSTHOG_HOST=https://app.posthog.com
-REACT_APP_PUBLIC_SENTRY_KEY=https://4b9ac52fc05cdba175ed93c515f1cbc6@o4504688952606720.ingest.sentry.io/4506314548510721
diff --git a/packages/webapp/.env.staging b/packages/webapp/.env.staging
deleted file mode 100644
index d2879444390..00000000000
--- a/packages/webapp/.env.staging
+++ /dev/null
@@ -1,2 +0,0 @@
-REACT_APP_ENV=staging
-REACT_APP_PUBLIC_SENTRY_KEY=https://4b9ac52fc05cdba175ed93c515f1cbc6@o4504688952606720.ingest.sentry.io/4506314548510721
diff --git a/packages/webapp/.gitignore b/packages/webapp/.gitignore
deleted file mode 100644
index e40b2c6a732..00000000000
--- a/packages/webapp/.gitignore
+++ /dev/null
@@ -1,22 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# production
-/build
-
-# misc
-.DS_Store
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
diff --git a/packages/webapp/package.json b/packages/webapp/package.json
index 0efe7ab4ba8..1f42957562e 100644
--- a/packages/webapp/package.json
+++ b/packages/webapp/package.json
@@ -6,16 +6,7 @@
"start": "DISABLE_ESLINT_PLUGIN=true react-scripts start",
"build": "DISABLE_ESLINT_PLUGIN=true react-scripts build",
"test": "DISABLE_ESLINT_PLUGIN=true react-scripts test",
- "eject": "DISABLE_ESLINT_PLUGIN=true react-scripts eject",
- "start:hosted": "env-cmd -f .env.hosted npm run start",
- "start:local": "env-cmd -f .env.local npm run start",
- "start:staging": "env-cmd -f .env.staging npm run start",
- "start:prod": "env-cmd -f .env.prod npm run start",
- "build:hosted": "env-cmd -f .env.hosted npm run build",
- "build:local": "env-cmd -f .env.local npm run build",
- "build:enterprise": "env-cmd -f .env.enterprise npm run build",
- "build:staging": "env-cmd -f .env.staging npm run build",
- "build:prod": "env-cmd -f .env.prod npm run build"
+ "eject": "DISABLE_ESLINT_PLUGIN=true react-scripts eject"
},
"browserslist": {
"production": [
@@ -85,7 +76,7 @@
"swr": "2.2.5",
"tailwind-merge": "2.5.4",
"tailwindcss": "3.4.14",
- "typescript": "5.3.3",
+ "typescript": "5.7.3",
"vaul": "0.9.1",
"web-vitals": "2.1.4",
"webpack": "5.94.0",
diff --git a/packages/webapp/public/images/template-logos/findymail.svg b/packages/webapp/public/images/template-logos/findymail.svg
new file mode 100644
index 00000000000..c8082aadfd7
--- /dev/null
+++ b/packages/webapp/public/images/template-logos/findymail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/webapp/public/images/template-logos/lagrowthmachine.svg b/packages/webapp/public/images/template-logos/lagrowthmachine.svg
new file mode 100644
index 00000000000..ffcba56798d
--- /dev/null
+++ b/packages/webapp/public/images/template-logos/lagrowthmachine.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/webapp/public/images/template-logos/scrapedo.svg b/packages/webapp/public/images/template-logos/scrapedo.svg
new file mode 100644
index 00000000000..1f6fd246343
--- /dev/null
+++ b/packages/webapp/public/images/template-logos/scrapedo.svg
@@ -0,0 +1,14 @@
+
+
diff --git a/packages/webapp/public/images/template-logos/sharepoint-online-v1.svg b/packages/webapp/public/images/template-logos/sharepoint-online-v1.svg
new file mode 120000
index 00000000000..fac4fe65ca9
--- /dev/null
+++ b/packages/webapp/public/images/template-logos/sharepoint-online-v1.svg
@@ -0,0 +1 @@
+sharepoint-online.svg
\ No newline at end of file
diff --git a/packages/webapp/public/images/template-logos/shopify-api-key.svg b/packages/webapp/public/images/template-logos/shopify-api-key.svg
new file mode 120000
index 00000000000..6216c3753cd
--- /dev/null
+++ b/packages/webapp/public/images/template-logos/shopify-api-key.svg
@@ -0,0 +1 @@
+shopify.svg
\ No newline at end of file
diff --git a/packages/webapp/src/App.tsx b/packages/webapp/src/App.tsx
index 1c0fb957bce..74b7088cca8 100644
--- a/packages/webapp/src/App.tsx
+++ b/packages/webapp/src/App.tsx
@@ -54,12 +54,15 @@ const App = () => {
return (
- {globalEnv.publicKoalaKey && (
+ {globalEnv.publicKoalaApiUrl && globalEnv.publicKoalaCdnUrl && (
@@ -106,7 +109,7 @@ const App = () => {
} />
} />
- {true && } />}
+ {} />}
{globalEnv.features.auth && (
<>
} />
diff --git a/packages/webapp/src/components/EnvironmentPicker.tsx b/packages/webapp/src/components/EnvironmentPicker.tsx
new file mode 100644
index 00000000000..09672c2bdd1
--- /dev/null
+++ b/packages/webapp/src/components/EnvironmentPicker.tsx
@@ -0,0 +1,163 @@
+import { Popover, PopoverContent, PopoverTrigger } from '../components/ui/Popover';
+import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../components/ui/Command';
+import { useMeta } from '../hooks/useMeta';
+import { useNavigate } from 'react-router-dom';
+import { useState } from 'react';
+import { IconCheck, IconChevronDown } from '@tabler/icons-react';
+import { Button } from './ui/button/Button';
+import { useStore } from '../store';
+import { cn } from '../utils/utils';
+import { apiPostEnvironment } from '../hooks/useEnvironment';
+import { Dialog, DialogContent, DialogFooter, DialogTitle, DialogTrigger, DialogClose } from '../components/ui/Dialog';
+import { Input } from './ui/input/Input';
+import { Info } from './Info';
+import { useToast } from '../hooks/useToast';
+
+export const EnvironmentPicker: React.FC = () => {
+ const navigate = useNavigate();
+ const { toast } = useToast();
+
+ const env = useStore((state) => state.env);
+ const setEnv = useStore((state) => state.setEnv);
+
+ const { meta, mutate } = useMeta();
+ const [open, setOpen] = useState(false);
+
+ const [openDialog, setOpenDialog] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(false);
+ const [name, setName] = useState('');
+
+ const onSelect = (selected: string) => {
+ if (selected === env) {
+ return;
+ }
+
+ setEnv(selected);
+
+ const pathSegments = window.location.pathname.split('/').filter(Boolean);
+
+ pathSegments[0] = selected;
+
+ let newPath = `/${pathSegments.join('/')}`;
+
+ // If on 'integration' or 'connections' subpages beyond the second level, redirect to their parent page
+ if (pathSegments[1] === 'integrations' && pathSegments.length > 2) {
+ newPath = `/${selected}/integrations`;
+ } else if (pathSegments[1] === 'connections' && pathSegments.length > 2) {
+ newPath = `/${selected}/connections`;
+ }
+
+ navigate(newPath);
+ };
+
+ const onCreate = async () => {
+ setLoading(true);
+
+ const res = await apiPostEnvironment({ name });
+ if ('error' in res.json) {
+ const err = res.json.error;
+ if (err.code === 'conflict') {
+ toast({ title: 'Environment name already exists', variant: 'error' });
+ } else if (err.code === 'invalid_body') {
+ setError(true);
+ } else if (err.code === 'feature_disabled' || err.code === 'resource_capped') {
+ toast({ title: err.message, variant: 'error' });
+ } else {
+ toast({ title: 'Failed to create environment', variant: 'error' });
+ }
+ } else {
+ navigate(`/${res.json.data.name}`);
+ setOpen(false);
+ setOpenDialog(false);
+ setError(false);
+ setName('');
+ void mutate();
+ }
+
+ setLoading(false);
+ };
+
+ if (!meta) {
+ return;
+ }
+
+ return (
+
+
+
+
+
+
+ {meta.environments.length > 5 && (
+
+ )}
+
+ No environment found.
+
+ {meta.environments.map((item) => (
+
+ {item.name}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/webapp/src/components/LeftNavBar.tsx b/packages/webapp/src/components/LeftNavBar.tsx
index bc0dabb3a63..fc7c998a8e2 100644
--- a/packages/webapp/src/components/LeftNavBar.tsx
+++ b/packages/webapp/src/components/LeftNavBar.tsx
@@ -15,13 +15,13 @@ import { useStore } from '../store';
import { useMeta } from '../hooks/useMeta';
import { useSignout } from '../utils/user';
import { HomeIcon, RocketIcon } from '@radix-ui/react-icons';
-import { useEnvironment } from '../hooks/useEnvironment';
import { useConnectionsCount } from '../hooks/useConnections';
import { useUser } from '../hooks/useUser';
import { globalEnv } from '../utils/env';
import { IconX } from '@tabler/icons-react';
import type { MaybePromise } from '@nangohq/types';
import { apiPatchOnboarding } from '../hooks/useOnboarding';
+import { EnvironmentPicker } from './EnvironmentPicker';
export enum LeftNavBarItems {
Homepage,
@@ -58,8 +58,6 @@ export default function LeftNavBar(props: LeftNavBarProps) {
const { user: me } = useUser();
const env = useStore((state) => state.env);
const { data } = useConnectionsCount(env);
- const setEnv = useStore((state) => state.setEnv);
- const { mutate } = useEnvironment(env);
const showGettingStarted = useStore((state) => state.showGettingStarted);
useEffect(() => {
@@ -105,63 +103,25 @@ export default function LeftNavBar(props: LeftNavBarProps) {
return list;
}, [env, showGettingStarted, meta]);
- const handleEnvChange = (e: React.ChangeEvent) => {
- const newEnv = e.target.value;
- setEnv(newEnv);
- void mutate();
-
- const pathSegments = window.location.pathname.split('/').filter(Boolean);
-
- pathSegments[0] = newEnv;
-
- let newPath = `/${pathSegments.join('/')}`;
-
- // If on 'integration' or 'connections' subpages beyond the second level, redirect to their parent page
- if (pathSegments[1] === 'integrations' && pathSegments.length > 2) {
- newPath = `/${newEnv}/integrations`;
- } else if (pathSegments[1] === 'connections' && pathSegments.length > 2) {
- newPath = `/${newEnv}/connections`;
- }
-
- navigate(newPath);
- };
-
if (!meta || !me) {
return null;
}
return (
-
+
-
+
{meta.version}
- {meta.environments.length === 0 && (
-
-
-
- )}
- {meta.environments.length > 0 && (
-
-
-
- )}
-
+
+
+
+
+
+
{items.map((item) => {
const Icon = item.icon;
return (
@@ -189,7 +149,7 @@ export default function LeftNavBar(props: LeftNavBarProps) {
})}
-
+
setShowUserSettings(!showUserSettings)}
diff --git a/packages/webapp/src/components/ui/Chart.tsx b/packages/webapp/src/components/ui/Chart.tsx
index 8a40fa71d32..3d218074a1c 100644
--- a/packages/webapp/src/components/ui/Chart.tsx
+++ b/packages/webapp/src/components/ui/Chart.tsx
@@ -6,12 +6,13 @@ import { cn } from '../../utils/utils';
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: '', dark: '.dark' } as const;
-export type ChartConfig = {
- [k in string]: {
+export type ChartConfig = Record<
+ string,
+ {
label?: React.ReactNode;
icon?: React.ComponentType;
- } & ({ color?: string; theme?: never } | { color?: never; theme: Record
});
-};
+ } & ({ color?: string; theme?: never } | { color?: never; theme: Record })
+>;
interface ChartContextProps {
config: ChartConfig;
diff --git a/packages/webapp/src/components/ui/Command.tsx b/packages/webapp/src/components/ui/Command.tsx
index 42ebbbb145d..8aaf2c1b18e 100644
--- a/packages/webapp/src/components/ui/Command.tsx
+++ b/packages/webapp/src/components/ui/Command.tsx
@@ -38,7 +38,7 @@ const CommandInput = React.forwardRef, React.ComponentPropsWithoutRef>(
- (props, ref) =>
+ (props, ref) =>
);
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
diff --git a/packages/webapp/src/components/ui/button/Button.tsx b/packages/webapp/src/components/ui/button/Button.tsx
index 1fadec6fb1b..d51c1496a8f 100644
--- a/packages/webapp/src/components/ui/button/Button.tsx
+++ b/packages/webapp/src/components/ui/button/Button.tsx
@@ -31,7 +31,8 @@ export const buttonStyles = cva(
link: 'text-grayscale-400 hover:text-white focus:text-white',
select: 'bg-grayscale-900 text-grayscale-400 border border-grayscale-900 hover:text-white focus:text-white hover:border-grayscale-600',
popoverItem: 'w-full rounded hover:bg-grayscale-900 text-grayscale-300 focus:bg-grayscale-900',
- secondary: 'bg-grayscale-900 text-grayscale-400 border border-grayscale-700 hover:text-white focus:text-white hover:border-grayscale-600'
+ secondary: 'bg-grayscale-900 text-grayscale-400 border border-grayscale-700 hover:text-white focus:text-white hover:border-grayscale-600',
+ tertiary: 'bg-grayscale-800 text-grayscale-100 border border-transparent hover:text-white focus:text-white hover:border-grayscale-600'
},
size: {
auto: '',
diff --git a/packages/webapp/src/env.d.ts b/packages/webapp/src/env.d.ts
index 8ccdb5b96d9..d6b405de728 100644
--- a/packages/webapp/src/env.d.ts
+++ b/packages/webapp/src/env.d.ts
@@ -6,9 +6,6 @@ declare global {
namespace NodeJS {
interface ProcessEnv {
PORT: string;
- REACT_APP_ENV: 'development' | 'staging' | 'production' | 'hosted' | 'enterprise';
- REACT_APP_PUBLIC_POSTHOG_KEY: string;
- REACT_APP_PUBLIC_POSTHOG_HOST: string;
}
}
}
diff --git a/packages/webapp/src/hooks/useEnvironment.tsx b/packages/webapp/src/hooks/useEnvironment.tsx
index 671336acae6..20fc4b0ef49 100644
--- a/packages/webapp/src/hooks/useEnvironment.tsx
+++ b/packages/webapp/src/hooks/useEnvironment.tsx
@@ -1,6 +1,7 @@
import useSWR from 'swr';
import type { EnvironmentAndAccount } from '@nangohq/server';
-import { swrFetcher } from '../utils/api';
+import { apiFetch, swrFetcher } from '../utils/api';
+import type { PostEnvironment } from '@nangohq/types';
export function useEnvironment(env: string) {
const { data, error, mutate } = useSWR<{ environmentAndAccount: EnvironmentAndAccount }>(`/api/v1/environment?env=${env}`, swrFetcher, {});
@@ -14,3 +15,15 @@ export function useEnvironment(env: string) {
mutate
};
}
+
+export async function apiPostEnvironment(body: PostEnvironment['Body']) {
+ const res = await apiFetch('/api/v1/environments', {
+ method: 'POST',
+ body: JSON.stringify(body)
+ });
+
+ return {
+ res,
+ json: (await res.json()) as PostEnvironment['Reply']
+ };
+}
diff --git a/packages/webapp/src/hooks/useToast.tsx b/packages/webapp/src/hooks/useToast.tsx
index 234849c98a2..2a83a5d1065 100644
--- a/packages/webapp/src/hooks/useToast.tsx
+++ b/packages/webapp/src/hooks/useToast.tsx
@@ -13,7 +13,7 @@ type ToasterToast = ToastProps & {
action?: ToastActionElement;
};
-const actionTypes = {
+export const actionTypes = {
ADD_TOAST: 'ADD_TOAST',
UPDATE_TOAST: 'UPDATE_TOAST',
DISMISS_TOAST: 'DISMISS_TOAST',
diff --git a/packages/webapp/src/index.tsx b/packages/webapp/src/index.tsx
index fe753829dcb..f796fd37235 100644
--- a/packages/webapp/src/index.tsx
+++ b/packages/webapp/src/index.tsx
@@ -1,6 +1,6 @@
import { SentryErrorBoundary } from './utils/sentry';
-import './utils/env';
+import { globalEnv } from './utils/env';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
@@ -11,7 +11,7 @@ import { PostHogProvider } from 'posthog-js/react';
import { ErrorBoundary } from './components/ErrorBoundary';
const options = {
- api_host: process.env.REACT_APP_PUBLIC_POSTHOG_HOST,
+ api_host: globalEnv.publicPosthogHost,
maskAllInputs: true
};
@@ -19,7 +19,7 @@ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
}>
-
+
diff --git a/packages/webapp/src/pages/Connection/Authorization.tsx b/packages/webapp/src/pages/Connection/Authorization.tsx
index b71af4ea561..5d7020b22b3 100644
--- a/packages/webapp/src/pages/Connection/Authorization.tsx
+++ b/packages/webapp/src/pages/Connection/Authorization.tsx
@@ -71,10 +71,10 @@ export const Authorization: React.FC = ({ connection, errorL
{endUser.id}
)}
- {endUser?.displayName && (
+ {endUser?.display_name && (
User Display Name
- {endUser.displayName}
+ {endUser.display_name}
)}
{endUser?.email && (
@@ -89,10 +89,10 @@ export const Authorization: React.FC
= ({ connection, errorL
{endUser.organization.id}
)}
- {endUser?.organization?.displayName && (
+ {endUser?.organization?.display_name && (
Organization Name
- {endUser.organization.displayName}
+ {endUser.organization.display_name}
)}
{connection.created_at && (
diff --git a/packages/webapp/src/pages/Connection/Create.tsx b/packages/webapp/src/pages/Connection/Create.tsx
index 905430d4681..6835e231835 100644
--- a/packages/webapp/src/pages/Connection/Create.tsx
+++ b/packages/webapp/src/pages/Connection/Create.tsx
@@ -177,7 +177,7 @@ export const ConnectionCreate: React.FC = () => {
className="text-white ring-0 focus:ring-0 focus-visible:outline-none"
>
- No framework found.
+ No integrations found.
{listIntegration?.integrations.map((item) => {
const checked = integration && item.uniqueKey === integration.uniqueKey;
diff --git a/packages/webapp/src/pages/Connection/CreateLegacy.tsx b/packages/webapp/src/pages/Connection/CreateLegacy.tsx
index 39d67436419..2afd965a36a 100644
--- a/packages/webapp/src/pages/Connection/CreateLegacy.tsx
+++ b/packages/webapp/src/pages/Connection/CreateLegacy.tsx
@@ -9,7 +9,7 @@ import { Tooltip } from '@geist-ui/core';
import type { Integration } from '@nangohq/server';
import useSet from '../../hooks/useSet';
-import { isHosted, isStaging, baseUrl } from '../../utils/utils';
+import { isCloudProd } from '../../utils/utils';
import { useGetIntegrationListAPI, useGetHmacAPI } from '../../utils/api';
import { useAnalyticsTrack } from '../../utils/analytics';
import DashboardLayout from '../../layout/DashboardLayout';
@@ -22,6 +22,7 @@ import type { AuthModeType } from '@nangohq/types';
import { useEnvironment } from '../../hooks/useEnvironment';
import { Helmet } from 'react-helmet';
import { useSearchParam } from 'react-use';
+import { globalEnv } from '../../utils/env';
export const ConnectionCreateLegacy: React.FC = () => {
const { mutate } = useSWRConfig();
@@ -108,7 +109,7 @@ export const ConnectionCreateLegacy: React.FC = () => {
if (environmentAndAccount) {
const { environment, host } = environmentAndAccount;
setPublicKey(environment.public_key);
- setHostUrl(host || baseUrl());
+ setHostUrl(host || globalEnv.apiUrl);
setWebsocketsPath(environment.websockets_path || '');
setIsHmacEnabled(Boolean(environment.hmac_key));
}
@@ -336,7 +337,7 @@ export const ConnectionCreateLegacy: React.FC = () => {
const snippet = () => {
const args = [];
- if (isStaging() || isHosted()) {
+ if (!isCloudProd()) {
args.push(`host: '${hostUrl}'`);
if (websocketsPath && websocketsPath !== '/') {
args.push(`websocketsPath: '${websocketsPath}'`);
diff --git a/packages/webapp/src/pages/Connection/Show.tsx b/packages/webapp/src/pages/Connection/Show.tsx
index 7963e7df815..08f9b434aa2 100644
--- a/packages/webapp/src/pages/Connection/Show.tsx
+++ b/packages/webapp/src/pages/Connection/Show.tsx
@@ -14,7 +14,6 @@ import { Button } from '../../components/ui/button/Button';
import { useEnvironment } from '../../hooks/useEnvironment';
import { Syncs } from './Syncs';
import { Authorization } from './Authorization';
-import { isHosted } from '../../utils/utils';
import { connectSlack } from '../../utils/slack-connection';
import { useStore } from '../../store';
@@ -29,6 +28,7 @@ import { useToast } from '../../hooks/useToast';
import { useListIntegration } from '../../hooks/useIntegration';
import { EndUserProfile } from './components/EndUserProfile';
import { getConnectionDisplayName } from '../../utils/endUser';
+import { globalEnv } from '../../utils/env';
export enum Tabs {
Syncs,
@@ -68,7 +68,7 @@ export const ConnectionShow: React.FC = () => {
if (location.hash === '#models' || location.hash === '#syncs') {
setActiveTab(Tabs.Syncs);
}
- if (location.hash === '#authorization' || isHosted()) {
+ if (location.hash === '#authorization' || !globalEnv.features.scripts) {
setActiveTab(Tabs.Authorization);
}
}, [location]);
@@ -94,7 +94,7 @@ export const ConnectionShow: React.FC = () => {
undefined,
{ revalidate: false }
);
- for await (const key of cache.keys()) {
+ for (const key of cache.keys()) {
if (key.startsWith('/api/v1/connections')) {
cache.delete(key);
}
@@ -237,7 +237,7 @@ export const ConnectionShow: React.FC = () => {
- {!slackIsConnected && !isHosted() && showSlackBanner && (
+ {!slackIsConnected && globalEnv.features.slack && showSlackBanner && (
setShowSlackBanner(false)}
diff --git a/packages/webapp/src/pages/Connection/components/EndUserProfile.tsx b/packages/webapp/src/pages/Connection/components/EndUserProfile.tsx
index 25631365f5d..a29237355ba 100644
--- a/packages/webapp/src/pages/Connection/components/EndUserProfile.tsx
+++ b/packages/webapp/src/pages/Connection/components/EndUserProfile.tsx
@@ -3,12 +3,12 @@ import type { ApiEndUser } from '@nangohq/types';
export const EndUserProfile: React.FC<{ endUser: ApiEndUser; connectionId: string }> = ({ endUser, connectionId }) => {
return (
-
{endUser.email ?? endUser.displayName ?? connectionId}
+
{endUser.email ?? endUser.display_name ?? connectionId}
- {endUser.email && endUser.displayName && {endUser.displayName}}
- {!endUser.email && endUser.displayName && {connectionId}}
- {endUser.organization?.displayName && ({endUser.organization?.displayName})}
+ {endUser.email && endUser.display_name && {endUser.display_name}}
+ {!endUser.email && endUser.display_name && {connectionId}}
+ {endUser.organization?.display_name && ({endUser.organization?.display_name})}
);
diff --git a/packages/webapp/src/pages/Environment/Settings.tsx b/packages/webapp/src/pages/Environment/Settings.tsx
index 9603935b29e..1b1ac57d290 100644
--- a/packages/webapp/src/pages/Environment/Settings.tsx
+++ b/packages/webapp/src/pages/Environment/Settings.tsx
@@ -16,7 +16,7 @@ import {
apiFetch
} from '../../utils/api';
import IntegrationLogo from '../../components/ui/IntegrationLogo';
-import { isHosted, defaultCallback } from '../../utils/utils';
+import { defaultCallback } from '../../utils/utils';
import DashboardLayout from '../../layout/DashboardLayout';
import { LeftNavBarItems } from '../../components/LeftNavBar';
import SecretInput from '../../components/ui/input/SecretInput';
@@ -691,10 +691,10 @@ export const EnvironmentSettings: React.FC = () => {
)}
- {!isHosted() && (
+ {globalEnv.features.slack && (
-
-
Last 14 days
+
+ Last 14 days (UTC)
+
{globalEnv.features.scripts && (
diff --git a/packages/webapp/src/pages/Homepage/components/InsightChart.tsx b/packages/webapp/src/pages/Homepage/components/InsightChart.tsx
index 3b1d68a2a7d..792e6a0a29b 100644
--- a/packages/webapp/src/pages/Homepage/components/InsightChart.tsx
+++ b/packages/webapp/src/pages/Homepage/components/InsightChart.tsx
@@ -7,7 +7,7 @@ import type { InsightsHistogramEntry, PostInsights } from '@nangohq/types';
import { Skeleton } from '../../../components/ui/Skeleton';
import { useMemo } from 'react';
import { formatQuantity } from '../../../utils/utils';
-import { addDays, format } from 'date-fns';
+import { addDays, addMinutes, format } from 'date-fns';
import { useNavigate } from 'react-router-dom';
import { getLogsUrl } from '../../../utils/logs';
@@ -84,8 +84,14 @@ export const InsightChart: React.FC<{ title: string; desc: string; type: PostIns
for (const date of dates) {
const entry = map.get(date);
total += entry?.total || 0;
+
+ // JS always transform date to local time but we receive UTC from the backend
+ // Ideally we would get the raw data points and display dates based on local TZ but we group by day so it's too late to change the timezone
+ // It fixes a display issues when you are a day in the past or in the future
+ const utc = addMinutes(new Date(date), endDate.getTimezoneOffset());
+
tmp.push({
- date: new Date(date),
+ date: utc,
total: entry?.total || 0,
success: entry?.success || 0,
failure: entry?.failure || 0
@@ -137,6 +143,7 @@ export const InsightChart: React.FC<{ title: string; desc: string; type: PostIns
-
- {message}{' '}
-
- ({operation.type}
- {'action' in operation && <>:{operation.action}>})
-
-
+ {message}
);
diff --git a/packages/webapp/src/pages/Team/components/Actions.tsx b/packages/webapp/src/pages/Team/components/Actions.tsx
index ff52347ccec..e2a79125b37 100644
--- a/packages/webapp/src/pages/Team/components/Actions.tsx
+++ b/packages/webapp/src/pages/Team/components/Actions.tsx
@@ -26,7 +26,7 @@ export const UserAction: React.FC<{ user: ApiUser }> = ({ user }) => {
setLoading(true);
const updated = await apiDeleteTeamUser(env, { id: user.id });
- if ('error' in updated) {
+ if ('error' in updated.json) {
toast({ title: 'An unexpected error occurred', variant: 'error' });
} else {
if (user.id === me!.id) {
@@ -89,7 +89,7 @@ export const InvitationAction: React.FC<{ invitation: ApiInvitation }> = ({ invi
setLoading(true);
const deleted = await apiDeleteInvite(env, { email: invitation.email });
- if ('error' in deleted) {
+ if ('error' in deleted.json) {
toast({ title: 'An unexpected error occurred', variant: 'error' });
} else {
toast({ title: `${invitation.email}'s invitation has been revoked`, variant: 'success' });
diff --git a/packages/webapp/src/pages/Team/components/Info.tsx b/packages/webapp/src/pages/Team/components/Info.tsx
index b559202de17..fc9f0545cd9 100644
--- a/packages/webapp/src/pages/Team/components/Info.tsx
+++ b/packages/webapp/src/pages/Team/components/Info.tsx
@@ -19,7 +19,7 @@ export const TeamInfo: React.FC = () => {
const onSave = async () => {
const updated = await apiPutTeam(env, { name });
- if ('error' in updated) {
+ if ('error' in updated.json) {
toast({ title: 'An unexpected error occurred', variant: 'error' });
} else {
toast({ title: 'Team updated successfully', variant: 'success' });
diff --git a/packages/webapp/src/utils/api.tsx b/packages/webapp/src/utils/api.tsx
index 9b1a0fe1274..169f059c243 100644
--- a/packages/webapp/src/utils/api.tsx
+++ b/packages/webapp/src/utils/api.tsx
@@ -1,9 +1,9 @@
import { toast } from 'react-toastify';
import { useSignout } from './user';
-import type { PostSignup } from '@nangohq/types';
+import type { ApiError, PostSignup } from '@nangohq/types';
-export async function apiFetch(input: string | URL | Request, init?: RequestInit | undefined) {
+export async function apiFetch(input: string | URL | Request, init?: RequestInit) {
return await fetch(input, {
...init,
headers: { 'Content-Type': 'application/json', ...(init?.headers || {}) },
@@ -23,7 +23,7 @@ export interface SWRError {
/**
* Default SWR fetcher does not throw on HTTP error
*/
-export async function swrFetcher(url: string, req?: RequestInit | undefined): Promise {
+export async function swrFetcher(url: string, req?: RequestInit): Promise {
const res = await apiFetch(url, req);
if (!res.ok) {
@@ -59,7 +59,7 @@ export function useSignupAPI() {
body: JSON.stringify(body)
};
- return apiFetch('/api/v1/account/signup', options);
+ return await apiFetch('/api/v1/account/signup', options);
} catch {
requestErrorToast();
}
@@ -77,7 +77,8 @@ export function useSigninAPI() {
const res = await apiFetch('/api/v1/account/signin', options);
if (res.status !== 200 && res.status !== 401 && res.status !== 400) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -93,7 +94,8 @@ export function useHostedSigninAPI() {
const res = await apiFetch('/api/v1/basic');
if (res.status !== 200 && res.status !== 401) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -116,11 +118,13 @@ export function useEditCallbackUrlAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/callback?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -143,11 +147,13 @@ export function useEditHmacEnabledAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/hmac-enabled?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -170,11 +176,13 @@ export function useEditAlwaysSendWebhookAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/webhook-send?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -197,11 +205,13 @@ export function useEditSendAuthWebhookAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/webhook-auth-send?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -224,11 +234,13 @@ export function useEditHmacKeyAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/hmac-key?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -251,11 +263,13 @@ export function useEditEnvVariablesAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/environment-variables?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -278,11 +292,13 @@ export function useEditWebhookUrlAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/webhook/primary-url?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -305,11 +321,13 @@ export function useEditWebhookSecondaryUrlAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/webhook/secondary-url?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -332,18 +350,20 @@ export function useEditOtlpSettingsAPI(env: string) {
const res = await apiFetch(`/api/v1/environment/otlp/settings?env=${env}`, options);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status === 403) {
const { error } = await res.json();
- const msg = 'message' in error ? error.message : 'Forbidden';
+ const msg = 'message' in error ? (error as ApiError['error']).message : 'Forbidden';
toast.error(msg, { position: toast.POSITION.BOTTOM_CENTER });
return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -361,11 +381,13 @@ export function useGetIntegrationListAPI(env: string) {
const res = await apiFetch(`/api/v1/integrations?env=${env}`);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
+ return;
}
return res;
@@ -383,11 +405,12 @@ export function useGetProvidersAPI(env: string) {
const res = await apiFetch(`/api/v1/provider?env=${env}`);
if (res.status === 401) {
- return signout();
+ await signout();
+ return;
}
if (res.status !== 200) {
- return serverErrorToast();
+ serverErrorToast();
}
return res;
diff --git a/packages/webapp/src/utils/endUser.ts b/packages/webapp/src/utils/endUser.ts
index d1e90e806ff..3c7f66880ad 100644
--- a/packages/webapp/src/utils/endUser.ts
+++ b/packages/webapp/src/utils/endUser.ts
@@ -1,5 +1,5 @@
import type { ApiEndUser } from '@nangohq/types';
export function getConnectionDisplayName({ endUser, connectionId }: { endUser?: ApiEndUser | null; connectionId: string }): string {
- return endUser?.displayName || endUser?.email || connectionId;
+ return endUser?.display_name || endUser?.email || connectionId;
}
diff --git a/packages/webapp/src/utils/language-snippets.tsx b/packages/webapp/src/utils/language-snippets.tsx
index 2790b002100..e6314c9f5b5 100644
--- a/packages/webapp/src/utils/language-snippets.tsx
+++ b/packages/webapp/src/utils/language-snippets.tsx
@@ -2,10 +2,11 @@ import type { NangoModel, NangoSyncEndpointV2 } from '@nangohq/types';
import type { TargetId } from 'httpsnippet-lite';
import { HTTPSnippet } from 'httpsnippet-lite';
import type { NangoSyncModel } from '../types';
-import { isProd } from './utils';
import { modelToString } from './scripts';
-const maskedKey = '';
+function maskSecret(secret: string): string {
+ return `${secret.substring(0, 4)}${'*'.repeat(secret.length - 4)}`;
+}
export function nodeSyncSnippet({
modelName,
@@ -18,10 +19,8 @@ export function nodeSyncSnippet({
connectionId: string;
providerConfigKey: string;
}) {
- const secretKeyDisplay = isProd() ? maskedKey : secretKey;
-
return `import { Nango } from '@nangohq/node';
-const nango = new Nango({ secretKey: '${secretKeyDisplay}' });
+const nango = new Nango({ secretKey: '${maskSecret(secretKey)}' });
const records = await nango.listRecords({
providerConfigKey: '${providerConfigKey}',
@@ -44,10 +43,8 @@ export function nodeActionSnippet({
providerConfigKey: string;
input?: NangoModel | NangoSyncModel;
}) {
- const secretKeyDisplay = isProd() ? maskedKey : secretKey;
-
let snippet = `import Nango from '@nangohq/node';
-const nango = new Nango({ secretKey: '${secretKeyDisplay}' });
+const nango = new Nango({ secretKey: '${maskSecret(secretKey)}' });
const response = await nango.triggerAction(
'${providerConfigKey}',
@@ -80,13 +77,11 @@ export async function httpSnippet({
language: TargetId;
input?: NangoModel | NangoSyncModel | undefined;
}) {
- const secretKeyDisplay = isProd() ? maskedKey : secretKey;
-
const snippet = new HTTPSnippet({
method: endpoint.method,
url: `${baseUrl}/v1${endpoint.path}`,
headers: [
- { name: 'Authorization', value: `Bearer ${secretKeyDisplay}` },
+ { name: 'Authorization', value: `Bearer ${maskSecret(secretKey)}` },
{ name: 'Content-Type', value: 'application/json' },
{ name: 'Connection-Id', value: connectionId },
{ name: 'Provider-Config-Key', value: providerConfigKey }
diff --git a/packages/webapp/src/utils/sentry.tsx b/packages/webapp/src/utils/sentry.tsx
index c8e4ad61322..5cc08a04c6d 100644
--- a/packages/webapp/src/utils/sentry.tsx
+++ b/packages/webapp/src/utils/sentry.tsx
@@ -1,9 +1,10 @@
import * as Sentry from '@sentry/react';
import { useEffect } from 'react';
import { Routes, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from 'react-router-dom';
+import { globalEnv } from './env';
Sentry.init({
- dsn: process.env.REACT_APP_PUBLIC_SENTRY_KEY,
+ dsn: globalEnv.publicSentryKey,
integrations: [
Sentry.reactRouterV6BrowserTracingIntegration({
useEffect,
@@ -14,7 +15,7 @@ Sentry.init({
}),
Sentry.replayIntegration()
],
- tracePropagationTargets: [/^https:\/\/app.nango\.dev\/api/],
+ tracePropagationTargets: [/^https:\/\/api.nango\.dev/],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 0.3,
diff --git a/packages/webapp/src/utils/utils.tsx b/packages/webapp/src/utils/utils.tsx
index bae0d5d70cd..fb0a9e4286b 100644
--- a/packages/webapp/src/utils/utils.tsx
+++ b/packages/webapp/src/utils/utils.tsx
@@ -3,42 +3,17 @@ import type { ClassValue } from 'clsx';
import { format } from 'date-fns';
import { twMerge } from 'tailwind-merge';
import type { SyncResult } from '../types';
+import { globalEnv } from './env';
-export const localhostUrl: string = 'http://localhost:3003';
-export const stagingUrl: string = 'https://api-staging.nango.dev';
-export const prodUrl: string = 'https://api.nango.dev';
-
-export const syncDocs = 'https://docs.nango.dev/guides/getting-started/read-from-an-api';
export const githubRepo = 'https://github.com/NangoHQ/integration-templates';
export const githubIntegrationTemplates = `${githubRepo}/tree/main/integrations`;
-export function isHosted() {
- return process.env.REACT_APP_ENV === 'hosted';
-}
-
-export function isStaging() {
- return process.env.REACT_APP_ENV === 'staging';
-}
-
-export function isProd() {
- return process.env.REACT_APP_ENV === 'production';
-}
-
-export function baseUrl() {
- switch (process.env.REACT_APP_ENV) {
- case 'hosted':
- return localhostUrl;
- case 'staging':
- return stagingUrl;
- case 'production':
- return prodUrl;
- default:
- return localhostUrl;
- }
+export function isCloudProd() {
+ return window.location.origin === 'https://app.nango.dev';
}
export function defaultCallback() {
- return baseUrl() + '/oauth/callback';
+ return globalEnv.apiUrl + '/oauth/callback';
}
export function elapsedTime(start: Date | number, end: Date | number): string {
diff --git a/packages/webhooks/lib/forward.ts b/packages/webhooks/lib/forward.ts
index 88e56a3e45c..e06e65133f7 100644
--- a/packages/webhooks/lib/forward.ts
+++ b/packages/webhooks/lib/forward.ts
@@ -60,7 +60,11 @@ export const forwardWebhook = async ({
incomingHeaders: webhookOriginalHeaders
});
- result.isOk() ? await logCtx.success() : await logCtx.failed();
+ if (result.isOk()) {
+ await logCtx.success();
+ } else {
+ await logCtx.failed();
+ }
return;
}
@@ -84,5 +88,9 @@ export const forwardWebhook = async ({
}
}
- success ? await logCtx.success() : await logCtx.failed();
+ if (success) {
+ await logCtx.success();
+ } else {
+ await logCtx.failed();
+ }
};
diff --git a/packages/webhooks/lib/utils.ts b/packages/webhooks/lib/utils.ts
index a1117efcddf..ea131b8adcc 100644
--- a/packages/webhooks/lib/utils.ts
+++ b/packages/webhooks/lib/utils.ts
@@ -25,7 +25,7 @@ export const NON_FORWARDABLE_HEADERS = [
'server'
];
-export const retry = async (logCtx?: LogContext | null | undefined, error?: AxiosError, attemptNumber?: number): Promise => {
+export const retry = async (logCtx?: LogContext | null, error?: AxiosError, attemptNumber?: number): Promise => {
if (error?.response && (error?.response?.status < 200 || error?.response?.status >= 300)) {
const content = `Webhook response received an ${
error?.response?.status || error?.code
diff --git a/packages/webhooks/package.json b/packages/webhooks/package.json
index 53a0219bb0b..b8605057981 100644
--- a/packages/webhooks/package.json
+++ b/packages/webhooks/package.json
@@ -17,14 +17,14 @@
"dependencies": {
"@nangohq/logs": "file:../logs",
"@nangohq/utils": "file:../utils",
- "axios": "^1.7.4",
+ "axios": "^1.7.9",
"dayjs": "1.11.7",
"dayjs-plugin-utc": "0.1.2"
},
"devDependencies": {
"@nangohq/types": "file:../types",
- "typescript": "5.3.3",
- "vitest": "1.6.0"
+ "typescript": "5.7.3",
+ "vitest": "2.1.8"
},
"files": [
"dist/**/*"
diff --git a/scripts/build_docker.sh b/scripts/build_docker.sh
index 0de483022f0..b24327cd98a 100755
--- a/scripts/build_docker.sh
+++ b/scripts/build_docker.sh
@@ -3,7 +3,7 @@
set -e
ACTION=$1
-ENV=$2 # enterprise | hosted | prod | staging
+ENV=$2 # enterprise | hosted | prod | staging \\ TODO: remove this, it's only needed for the frontend
GIT_HASH=$3
USAGE="./build_docker.sh GIT_HASH"
@@ -28,13 +28,6 @@ if [ -z $GIT_HASH ]; then
exit
fi
-if [ -z $SENTRY_KEY ]; then
- echo -e "${YELLOW}SENTRY_KEY is empty${NC}"
-fi
-if [ -z $POSTHOG_KEY ]; then
- echo -e "${YELLOW}POSTHOG_KEY is empty${NC}"
-fi
-
# Move to here no matter where the file was executed
cd "$(dirname "$0")"
@@ -51,10 +44,7 @@ echo -e "Building nangohq/nango:$ENV\n"
docker buildx build \
--platform linux/amd64 \
- --build-arg image_env="$ENV" \
--build-arg git_hash="$GIT_HASH" \
- --build-arg posthog_key="$SENTRY_KEY" \
- --build-arg sentry_key="$POSTHOG_KEY" \
--cache-from type=gha \
--cache-to type=gha,mode=max \
--file ../Dockerfile \
diff --git a/scripts/one-off/schedules-migration/migrate.ts b/scripts/one-off/schedules-migration/migrate.ts
index e0b926fc36f..d79f3760fd0 100755
--- a/scripts/one-off/schedules-migration/migrate.ts
+++ b/scripts/one-off/schedules-migration/migrate.ts
@@ -45,6 +45,7 @@ interface SourceSchedule {
environment_id: string;
}
+// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
type JsonObject = { [Key in string]: JsonValue } & { [Key in string]?: JsonValue | undefined };
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
diff --git a/scripts/one-off/schedules-migration/package-lock.json b/scripts/one-off/schedules-migration/package-lock.json
index 4831d504840..df0a92a4cca 100644
--- a/scripts/one-off/schedules-migration/package-lock.json
+++ b/scripts/one-off/schedules-migration/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "records-migration",
+ "name": "schedules-migration",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "records-migration",
+ "name": "schedules-migration",
"version": "1.0.0",
"dependencies": {
"knex": "^3.1.0",
diff --git a/scripts/package.json b/scripts/package.json
index faeeded655c..db6a25d4f29 100644
--- a/scripts/package.json
+++ b/scripts/package.json
@@ -3,11 +3,11 @@
"type": "module",
"devDependencies": {
"@apidevtools/swagger-cli": "4.0.4",
- "ajv": "8.12.0",
- "chalk": "5.3.0",
- "git-cliff": "2.5.0",
+ "ajv": "8.17.1",
+ "chalk": "5.4.1",
+ "git-cliff": "2.7.0",
"js-yaml": "4.1.0",
- "zx": "8.1.4",
+ "zx": "8.3.0",
"webflow-api": "3.0.1"
}
}
diff --git a/tsconfig.json b/tsconfig.json
index b0885d9b776..2b9fe4f0e59 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,10 +1,28 @@
{
- "extends": "@tsconfig/node18-strictest-esm/tsconfig.json",
- "compilerOptions": {
- "ignoreDeprecations": "5.0",
- "declaration": true,
- "sourceMap": true,
- "composite": true,
- "checkJs": false
- }
+ "compilerOptions": {
+ "lib": ["es2023"],
+ "module": "ES2022",
+ "target": "es2022",
+ "ignoreDeprecations": "5.0",
+ "declaration": true,
+ "sourceMap": true,
+ "composite": true,
+ "checkJs": false,
+ "importsNotUsedAsValues": "remove",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "moduleResolution": "node",
+ "allowUnusedLabels": false,
+ "allowUnreachableCode": false,
+ "exactOptionalPropertyTypes": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitOverride": true,
+ "noImplicitReturns": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noUncheckedIndexedAccess": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true
+ }
}
diff --git a/vite.config.ts b/vite.config.ts
index eac1fd44891..7e26ce7e7d2 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -13,7 +13,8 @@ export default defineConfig({
NANGO_LOGS_ES_URL: 'http://fake.com',
NANGO_LOGS_ES_USER: '',
NANGO_LOGS_ES_PWD: '',
- NANGO_LOGS_ENABLED: 'false'
+ NANGO_LOGS_ENABLED: 'false',
+ NANGO_DB_NAME: 'nango_test'
},
chaiConfig: {
truncateThreshold: 10000
diff --git a/vite.integration.config.ts b/vite.integration.config.ts
index c33e9943e3c..197569cf12d 100644
--- a/vite.integration.config.ts
+++ b/vite.integration.config.ts
@@ -17,6 +17,12 @@ export default defineConfig({
ORCHESTRATOR_SERVICE_URL: 'http://orchestrator'
},
fileParallelism: false,
- pool: 'forks'
+ pool: 'forks',
+
+ poolOptions: {
+ forks: {
+ singleFork: true
+ }
+ }
}
});