nango 0.24.13 → 0.24.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sync.js CHANGED
@@ -14,31 +14,214 @@ import parser from '@babel/parser';
14
14
  import traverse from '@babel/traverse';
15
15
  import promptly from 'promptly';
16
16
  import { loadSimplifiedConfig, cloudHost, stagingHost, SyncType, syncRunService, nangoConfigFile, checkForIntegrationFile } from '@nangohq/shared';
17
- import { hostport, port, httpsAgent, verifyNecessaryFiles, getConnection, NANGO_INTEGRATIONS_LOCATION, buildInterfaces, enrichHeaders, getNangoRootPath, printDebug } from './utils.js';
17
+ import { hostport, port, httpsAgent, verifyNecessaryFiles, getConnection, NANGO_INTEGRATIONS_LOCATION, NANGO_INTEGRATIONS_NAME, buildInterfaces, enrichHeaders, getNangoRootPath, printDebug } from './utils.js';
18
18
  const __filename = fileURLToPath(import.meta.url);
19
19
  const __dirname = dirname(__filename);
20
20
  dotenv.config();
21
21
  const TYPES_FILE_NAME = 'models.ts';
22
22
  const NangoSyncTypesFileLocation = 'dist/nango-sync.d.ts';
23
23
  const exampleSyncName = 'github-issue-example';
24
- const createModelFile = (notify = false) => {
24
+ export const version = (debug) => {
25
+ if (debug) {
26
+ printDebug('Looking up the version first for a local path first then globally');
27
+ }
28
+ const packageJson = JSON.parse(fs.readFileSync(path.resolve(getNangoRootPath(debug), 'package.json'), 'utf8'));
29
+ const dockerComposeYaml = fs.readFileSync(path.resolve(getNangoRootPath(), 'docker/docker-compose.yaml'), 'utf8');
30
+ const dockerCompose = yaml.load(dockerComposeYaml);
31
+ const nangoServerImage = dockerCompose.services['nango-server'].image;
32
+ const nangoWorkerImage = dockerCompose.services['nango-worker'].image;
33
+ const nangoServerVersion = nangoServerImage.split(':').pop();
34
+ const nangoWorkerVersion = nangoWorkerImage.split(':').pop();
35
+ console.log(chalk.green('Nango Server version:'), nangoServerVersion);
36
+ console.log(chalk.green('Nango Worker version:'), nangoWorkerVersion);
37
+ console.log(chalk.green('Nango CLI version:'), packageJson.version);
38
+ };
39
+ /**
40
+ * Init
41
+ * If we're not currently in the nango-integrations directory create one
42
+ * and create an example nango.yaml file
43
+ */
44
+ export const init = (debug = false) => {
45
+ const data = {
46
+ integrations: {
47
+ 'demo-github-integration': {
48
+ [exampleSyncName]: {
49
+ runs: 'every half hour',
50
+ returns: ['GithubIssue']
51
+ }
52
+ }
53
+ },
54
+ models: {
55
+ GithubIssue: {
56
+ id: 'integer',
57
+ owner: 'string',
58
+ repo: 'string',
59
+ issue_number: 'number',
60
+ title: 'string',
61
+ author: 'string',
62
+ author_id: 'string',
63
+ state: 'string',
64
+ date_created: 'date',
65
+ date_last_modified: 'date',
66
+ body: 'string'
67
+ }
68
+ }
69
+ };
70
+ const yamlData = yaml.dump(data);
71
+ // if currently in the nango-integrations directory then don't create another one
25
72
  const cwd = process.cwd();
26
- const configContents = fs.readFileSync(path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`), 'utf8');
73
+ const currentDirectorySplit = cwd.split('/');
74
+ const currentDirectory = currentDirectorySplit[currentDirectorySplit.length - 1];
75
+ let dirExists = false;
76
+ let inParentDirectory = true;
77
+ if (currentDirectory === NANGO_INTEGRATIONS_NAME) {
78
+ dirExists = true;
79
+ inParentDirectory = false;
80
+ if (debug) {
81
+ printDebug(`Currently in the ${NANGO_INTEGRATIONS_NAME} directory so the directory will not be created`);
82
+ }
83
+ }
84
+ if (fs.existsSync(`./${NANGO_INTEGRATIONS_NAME}`)) {
85
+ dirExists = true;
86
+ console.log(chalk.red(`The ${NANGO_INTEGRATIONS_NAME} directory already exists. You should run commands from within this directory`));
87
+ }
88
+ if (!dirExists) {
89
+ if (debug) {
90
+ printDebug(`Creating the nango integrations directory at ./${NANGO_INTEGRATIONS_NAME}`);
91
+ }
92
+ fs.mkdirSync(`./${NANGO_INTEGRATIONS_NAME}`);
93
+ }
94
+ const configFileLocation = inParentDirectory ? `./${NANGO_INTEGRATIONS_NAME}/${nangoConfigFile}` : `./${nangoConfigFile}`;
95
+ if (!fs.existsSync(configFileLocation)) {
96
+ if (debug) {
97
+ printDebug(`Creating the ${nangoConfigFile} file at ${configFileLocation}`);
98
+ }
99
+ fs.writeFileSync(configFileLocation, yamlData);
100
+ }
101
+ else {
102
+ if (debug) {
103
+ printDebug(`Nango config file already exists at ${configFileLocation} so not creating a new one`);
104
+ }
105
+ }
106
+ const envFileLocation = inParentDirectory ? `./${NANGO_INTEGRATIONS_NAME}/.env` : './.env';
107
+ if (!fs.existsSync(envFileLocation)) {
108
+ if (debug) {
109
+ printDebug(`Creating the .env file at ${envFileLocation}`);
110
+ }
111
+ fs.writeFileSync(envFileLocation, `# Authenticates the CLI (get the keys in the dashboard's Projects Settings).
112
+ #NANGO_SECRET_KEY_DEV=xxxx-xxx-xxxx
113
+ #NANGO_SECRET_KEY_PROD=xxxx-xxx-xxxx
114
+
115
+ # Nango's instance URL (OSS: change to http://localhost:3003 or your instance URL).
116
+ NANGO_HOSTPORT=https://api.nango.dev # Default value
117
+
118
+ # How to handle CLI upgrades ("prompt", "auto" or "ignore").
119
+ NANGO_CLI_UPGRADE_MODE=prompt # Default value
120
+
121
+ # Whether to prompt before deployments.
122
+ NANGO_DEPLOY_AUTO_CONFIRM=false # Default value`);
123
+ }
124
+ else {
125
+ if (debug) {
126
+ printDebug(`.env file already exists at ${envFileLocation} so not creating a new one`);
127
+ }
128
+ }
129
+ console.log(chalk.green(`Nango integrations initialized!`));
130
+ };
131
+ export const generate = async (debug = false) => {
132
+ const templateContents = fs.readFileSync(path.resolve(__dirname, './integration.ejs'), 'utf8');
133
+ const githubExampleTemplateContents = fs.readFileSync(path.resolve(__dirname, './integration.github.ejs'), 'utf8');
134
+ const configContents = fs.readFileSync(`./${nangoConfigFile}`, 'utf8');
27
135
  const configData = yaml.load(configContents);
136
+ const { integrations } = configData;
28
137
  const { models } = configData;
29
- const interfaceDefinitions = buildInterfaces(models);
138
+ const interfaceDefinitions = buildInterfaces(models, debug);
30
139
  fs.writeFileSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`, interfaceDefinitions.join('\n'));
140
+ if (debug) {
141
+ printDebug(`Interfaces from the ${nangoConfigFile} file written to ${TYPES_FILE_NAME}`);
142
+ }
31
143
  // insert NangoSync types to the bottom of the file
32
144
  const typesContent = fs.readFileSync(`${getNangoRootPath()}/${NangoSyncTypesFileLocation}`, 'utf8');
33
145
  fs.writeFileSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`, typesContent, { flag: 'a' });
146
+ if (debug) {
147
+ printDebug(`NangoSync types written to ${TYPES_FILE_NAME}`);
148
+ }
149
+ const allSyncNames = {};
150
+ for (let i = 0; i < Object.keys(integrations).length; i++) {
151
+ const providerConfigKey = Object.keys(integrations)[i];
152
+ if (debug) {
153
+ printDebug(`Generating ${providerConfigKey} integrations`);
154
+ }
155
+ const syncObject = integrations[providerConfigKey];
156
+ const syncNames = Object.keys(syncObject);
157
+ for (let k = 0; k < syncNames.length; k++) {
158
+ const syncName = syncNames[k];
159
+ if (allSyncNames[syncName] === undefined) {
160
+ allSyncNames[syncName] = true;
161
+ }
162
+ else {
163
+ console.log(chalk.red(`The sync name ${syncName} is duplicated in the ${nangoConfigFile} file. All sync names must be unique.`));
164
+ process.exit(1);
165
+ }
166
+ if (debug) {
167
+ printDebug(`Generating ${syncName} integration`);
168
+ }
169
+ const syncData = syncObject[syncName];
170
+ if (!syncData.returns) {
171
+ console.log(chalk.red(`The ${syncName} integration is missing a returns property for what models the sync returns. Make sure you have "returns" instead of "return"`));
172
+ process.exit(1);
173
+ }
174
+ const { returns: models } = syncData;
175
+ const syncNameCamel = syncName
176
+ .split('-')
177
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
178
+ .join('');
179
+ const ejsTeamplateContents = syncName === exampleSyncName ? githubExampleTemplateContents : templateContents;
180
+ const rendered = ejs.render(ejsTeamplateContents, {
181
+ syncName: syncNameCamel,
182
+ interfaceFileName: TYPES_FILE_NAME.replace('.ts', ''),
183
+ interfaceNames: models.map((model) => {
184
+ const singularModel = model?.charAt(model.length - 1) === 's' ? model.slice(0, -1) : model;
185
+ return `${singularModel.charAt(0).toUpperCase()}${singularModel.slice(1)}`;
186
+ }),
187
+ mappings: models.map((model) => {
188
+ const singularModel = model.charAt(model.length - 1) === 's' ? model.slice(0, -1) : model;
189
+ return {
190
+ name: model,
191
+ type: `${singularModel.charAt(0).toUpperCase()}${singularModel.slice(1)}`
192
+ };
193
+ })
194
+ });
195
+ if (!fs.existsSync(`${NANGO_INTEGRATIONS_LOCATION}/${syncName}.ts`)) {
196
+ fs.writeFileSync(`${NANGO_INTEGRATIONS_LOCATION}/${syncName}.ts`, rendered);
197
+ if (debug) {
198
+ printDebug(`Created ${syncName}.ts file`);
199
+ }
200
+ }
201
+ else {
202
+ if (debug) {
203
+ printDebug(`${syncName}.ts file already exists, so will not overwrite it.`);
204
+ }
205
+ }
206
+ }
207
+ }
208
+ console.log(chalk.green(`Integration files have been created`));
209
+ };
210
+ const createModelFile = (notify = false) => {
211
+ const configContents = fs.readFileSync(`./${nangoConfigFile}`, 'utf8');
212
+ const configData = yaml.load(configContents);
213
+ const { models } = configData;
214
+ const interfaceDefinitions = buildInterfaces(models);
215
+ fs.writeFileSync(`./${TYPES_FILE_NAME}`, interfaceDefinitions.join('\n'));
216
+ // insert NangoSync types to the bottom of the file
217
+ const typesContent = fs.readFileSync(`${getNangoRootPath()}/${NangoSyncTypesFileLocation}`, 'utf8');
218
+ fs.writeFileSync(`./${TYPES_FILE_NAME}`, typesContent, { flag: 'a' });
34
219
  if (notify) {
35
- const rawNangoIntegrationLocation = NANGO_INTEGRATIONS_LOCATION.replace('./', '');
36
- console.log(chalk.green(`${rawNangoIntegrationLocation}/${nangoConfigFile} was updated. The interface file (${rawNangoIntegrationLocation}/${TYPES_FILE_NAME}) was updated to reflect the updated config`));
220
+ console.log(chalk.green(`The ${nangoConfigFile} was updated. The interface file (${TYPES_FILE_NAME}) was updated to reflect the updated config`));
37
221
  }
38
222
  };
39
223
  const getConfig = async (debug = false) => {
40
- const cwd = process.cwd();
41
- const config = await loadSimplifiedConfig(path.resolve(cwd, NANGO_INTEGRATIONS_LOCATION));
224
+ const config = await loadSimplifiedConfig('./');
42
225
  if (!config) {
43
226
  throw new Error(`Error loading the ${nangoConfigFile} file`);
44
227
  }
@@ -47,21 +230,40 @@ const getConfig = async (debug = false) => {
47
230
  }
48
231
  return config;
49
232
  };
50
- export const deploy = async (options, debug = false) => {
51
- const { env, version, sync: optionalSyncName, secretKey, host, autoConfirm } = options;
52
- await verifyNecessaryFiles(autoConfirm);
53
- if (host) {
233
+ async function parseSecretKey(environment, debug = false) {
234
+ if (process.env['NANGO_SECRET_KEY_PROD'] && environment === 'prod') {
54
235
  if (debug) {
55
- printDebug(`Global host flag is set, setting NANGO_HOSTPORT to ${host}.`);
236
+ printDebug(`Environment is set to prod, setting NANGO_SECRET_KEY to NANGO_SECRET_KEY_PROD.`);
56
237
  }
57
- process.env['NANGO_HOSTPORT'] = host;
238
+ process.env['NANGO_SECRET_KEY'] = process.env['NANGO_SECRET_KEY_PROD'];
58
239
  }
59
- if (secretKey) {
240
+ if (process.env['NANGO_SECRET_KEY_DEV'] && environment === 'dev') {
60
241
  if (debug) {
61
- printDebug(`Global secretKey flag is set, setting NANGO_SECRET_KEY.`);
242
+ printDebug(`Environment is set to dev, setting NANGO_SECRET_KEY to NANGO_SECRET_KEY_DEV.`);
62
243
  }
63
- process.env['NANGO_SECRET_KEY'] = secretKey;
244
+ process.env['NANGO_SECRET_KEY'] = process.env['NANGO_SECRET_KEY_DEV'];
64
245
  }
246
+ if (!process.env['NANGO_SECRET_KEY']) {
247
+ console.log(chalk.red(`NANGO_SECRET_KEY environment variable is not set. Please set it now`));
248
+ try {
249
+ const secretKey = await promptly.prompt('Secret Key: ');
250
+ if (secretKey) {
251
+ process.env['NANGO_SECRET_KEY'] = secretKey;
252
+ }
253
+ else {
254
+ return;
255
+ }
256
+ }
257
+ catch (error) {
258
+ console.log('Error occurred while trying to prompt for secret key:', error);
259
+ process.exit(1);
260
+ }
261
+ }
262
+ }
263
+ export const deploy = async (options, environment, debug = false) => {
264
+ const { env, version, sync: optionalSyncName, autoConfirm } = options;
265
+ await verifyNecessaryFiles(autoConfirm);
266
+ await parseSecretKey(environment, debug);
65
267
  if (!process.env['NANGO_HOSTPORT']) {
66
268
  switch (env) {
67
269
  case 'local':
@@ -77,26 +279,10 @@ export const deploy = async (options, debug = false) => {
77
279
  }
78
280
  if (debug) {
79
281
  printDebug(`NANGO_HOSTPORT is set to ${process.env['NANGO_HOSTPORT']}.`);
80
- }
81
- if (process.env['NANGO_HOSTPORT'] !== `http://localhost:${port}` && !process.env['NANGO_SECRET_KEY']) {
82
- console.log(chalk.red(`NANGO_SECRET_KEY environment variable is not set. Please set it now`));
83
- try {
84
- const secretKey = await promptly.prompt('Secret Key: ');
85
- if (secretKey) {
86
- process.env['NANGO_SECRET_KEY'] = secretKey;
87
- }
88
- else {
89
- return;
90
- }
91
- }
92
- catch (error) {
93
- console.log('Error occurred while trying to prompt for secret key:', error);
94
- process.exit(1);
95
- }
282
+ printDebug(`Environment is set to ${environment}`);
96
283
  }
97
284
  tsc(debug);
98
285
  const config = await getConfig(debug);
99
- const cwd = process.cwd();
100
286
  const postData = [];
101
287
  for (const integration of config) {
102
288
  const { providerConfigKey } = integration;
@@ -106,7 +292,7 @@ export const deploy = async (options, debug = false) => {
106
292
  }
107
293
  for (const sync of syncs) {
108
294
  const { name: syncName, runs, returns: models, models: model_schema } = sync;
109
- const { path: integrationFilePath, result: integrationFileResult } = checkForIntegrationFile(syncName, path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}`));
295
+ const { path: integrationFilePath, result: integrationFileResult } = checkForIntegrationFile(syncName, './');
110
296
  if (!integrationFileResult) {
111
297
  console.log(chalk.red(`No integration file found for ${syncName} at ${integrationFilePath}. Skipping...`));
112
298
  continue;
@@ -127,11 +313,7 @@ export const deploy = async (options, debug = false) => {
127
313
  }
128
314
  }
129
315
  const url = process.env['NANGO_HOSTPORT'] + `/sync/deploy`;
130
- if (postData.length === 0) {
131
- console.log(chalk.red(`No syncs found to deploy. Please make sure your integration files compiled successfully and exist in your dist directory`));
132
- return;
133
- }
134
- if (!process.env['NANGO_DEPLOY_AUTO_CONFIRM'] && !autoConfirm) {
316
+ if (process.env['NANGO_DEPLOY_AUTO_CONFIRM'] !== 'true' && !autoConfirm) {
135
317
  const confirmationUrl = process.env['NANGO_HOSTPORT'] + `/sync/deploy/confirmation`;
136
318
  try {
137
319
  const response = await axios.post(confirmationUrl, { syncs: postData, reconcile: false, debug }, { headers: enrichHeaders(), httpsAgent: httpsAgent() });
@@ -152,8 +334,13 @@ export const deploy = async (options, debug = false) => {
152
334
  .post(process.env['NANGO_HOSTPORT'] + `/sync/deploy`, { syncs: postData, reconcile: true, debug }, { headers: enrichHeaders(), httpsAgent: httpsAgent() })
153
335
  .then((response) => {
154
336
  const results = response.data;
155
- const nameAndVersions = results.map((result) => `${result.sync_name}@v${result.version}`);
156
- console.log(chalk.green(`Successfully deployed the syncs: ${nameAndVersions.join(', ')}!`));
337
+ if (results.length === 0) {
338
+ console.log(chalk.green(`Successfully removed the syncs.`));
339
+ }
340
+ else {
341
+ const nameAndVersions = results.map((result) => `${result.sync_name}@v${result.version}`);
342
+ console.log(chalk.green(`Successfully deployed the syncs: ${nameAndVersions.join(', ')}!`));
343
+ }
157
344
  })
158
345
  .catch((err) => {
159
346
  const errorMessage = JSON.stringify(err.response.data, null, 2);
@@ -192,8 +379,13 @@ async function deploySyncs(url, body) {
192
379
  .post(url, body, { headers: enrichHeaders(), httpsAgent: httpsAgent() })
193
380
  .then((response) => {
194
381
  const results = response.data;
195
- const nameAndVersions = results.map((result) => `${result.sync_name}@v${result.version}`);
196
- console.log(chalk.green(`Successfully deployed the syncs: ${nameAndVersions.join(', ')}!`));
382
+ if (results.length === 0) {
383
+ console.log(chalk.green(`Successfully removed the syncs.`));
384
+ }
385
+ else {
386
+ const nameAndVersions = results.map((result) => `${result.sync_name}@v${result.version}`);
387
+ console.log(chalk.green(`Successfully deployed the syncs: ${nameAndVersions.join(', ')}!`));
388
+ }
197
389
  })
198
390
  .catch((err) => {
199
391
  let errorMessage;
@@ -209,198 +401,38 @@ async function deploySyncs(url, body) {
209
401
  process.exit(1);
210
402
  });
211
403
  }
212
- export const version = (debug) => {
213
- if (debug) {
214
- printDebug('Looking up the version first for a local path first then globally');
215
- }
216
- const packageJson = JSON.parse(fs.readFileSync(path.resolve(getNangoRootPath(debug), 'package.json'), 'utf8'));
217
- const dockerComposeYaml = fs.readFileSync(path.resolve(getNangoRootPath(), 'docker/docker-compose.yaml'), 'utf8');
218
- const dockerCompose = yaml.load(dockerComposeYaml);
219
- const nangoServerImage = dockerCompose.services['nango-server'].image;
220
- const nangoWorkerImage = dockerCompose.services['nango-worker'].image;
221
- const nangoServerVersion = nangoServerImage.split(':').pop();
222
- const nangoWorkerVersion = nangoWorkerImage.split(':').pop();
223
- console.log(chalk.green('Nango Server version:'), nangoServerVersion);
224
- console.log(chalk.green('Nango Worker version:'), nangoWorkerVersion);
225
- console.log(chalk.green('Nango CLI version:'), packageJson.version);
226
- };
227
- export const init = (debug = false) => {
228
- const data = {
229
- integrations: {
230
- github: {
231
- [exampleSyncName]: {
232
- runs: 'every half hour',
233
- returns: ['GithubIssue']
234
- }
235
- }
236
- },
237
- models: {
238
- GithubIssue: {
239
- id: 'integer',
240
- owner: 'string',
241
- repo: 'string',
242
- issue_number: 'number',
243
- title: 'string',
244
- author: 'string',
245
- author_id: 'string',
246
- state: 'string',
247
- date_created: 'date',
248
- date_last_modified: 'date',
249
- body: 'string'
250
- }
251
- }
252
- };
253
- const yamlData = yaml.dump(data);
254
- if (!fs.existsSync(NANGO_INTEGRATIONS_LOCATION)) {
255
- if (debug) {
256
- printDebug('Creating the nango integrations directory');
257
- }
258
- fs.mkdirSync(NANGO_INTEGRATIONS_LOCATION);
259
- }
260
- else {
261
- if (debug) {
262
- printDebug('Nango integrations directory already exists');
263
- }
264
- }
265
- if (!fs.existsSync(`${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`)) {
266
- if (debug) {
267
- printDebug(`Creating the ${nangoConfigFile} file}`);
268
- }
269
- fs.writeFileSync(`${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`, yamlData);
270
- }
271
- else {
272
- if (debug) {
273
- printDebug('Nango config file already exists');
274
- }
275
- }
276
- // check if a .env file exists and if not create it with some default content
277
- if (!fs.existsSync('.env')) {
278
- if (debug) {
279
- printDebug('Creating the .env file');
280
- }
281
- fs.writeFileSync('.env', `#NANGO_HOSTPORT=https://api-staging.nango.dev
282
- #NANGO_AUTO_UPGRADE=true # set to true to automatically upgrade to the latest version of nango
283
- #NANGO_NO_PROMPT_FOR_UPGRADE=true # set to true to not prompt for upgrade
284
- #NANGO_DEPLOY_AUTO_CONFIRM=true # set to true to automatically confirm deployment without prompting
285
- #NANGO_SECRET_KEY=xxxx-xxx-xxxx # required if deploying to cloud
286
- #NANGO_INTEGRATIONS_LOCATION=use-this-to-override-where-the-nango-integrations-directory-goes
287
- #NANGO_PORT=use-this-to-override-the-default-3003
288
- #NANGO_DB_PORT=use-this-to-override-the-default-5432`);
289
- }
290
- else {
404
+ export const dryRun = async (options, environment, debug = false) => {
405
+ let syncName = '';
406
+ let connectionId, suppliedLastSyncDate;
407
+ await parseSecretKey(environment, debug);
408
+ if (!process.env['NANGO_HOSTPORT']) {
291
409
  if (debug) {
292
- printDebug('.env file already exists');
410
+ printDebug(`NANGO_HOSTPORT is not set. Setting the default to ${hostport}`);
293
411
  }
412
+ process.env['NANGO_HOSTPORT'] = hostport;
294
413
  }
295
- console.log(chalk.green(`Nango integrations initialized!`));
296
- };
297
- export const generate = async (debug = false) => {
298
- const templateContents = fs.readFileSync(path.resolve(__dirname, './integration.ejs'), 'utf8');
299
- const githubExampleTemplateContents = fs.readFileSync(path.resolve(__dirname, './integration.github.ejs'), 'utf8');
300
- const cwd = process.cwd();
301
- const configContents = fs.readFileSync(path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`), 'utf8');
302
- const configData = yaml.load(configContents);
303
- const { integrations } = configData;
304
- const { models } = configData;
305
- const interfaceDefinitions = buildInterfaces(models);
306
- fs.writeFileSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`, interfaceDefinitions.join('\n'));
307
- if (debug) {
308
- printDebug(`Interfaces from the ${nangoConfigFile} file written to ${TYPES_FILE_NAME}`);
309
- }
310
- // insert NangoSync types to the bottom of the file
311
- const typesContent = fs.readFileSync(`${getNangoRootPath()}/${NangoSyncTypesFileLocation}`, 'utf8');
312
- fs.writeFileSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`, typesContent, { flag: 'a' });
313
414
  if (debug) {
314
- printDebug(`NangoSync types written to ${TYPES_FILE_NAME}`);
315
- }
316
- for (let i = 0; i < Object.keys(integrations).length; i++) {
317
- const providerConfigKey = Object.keys(integrations)[i];
318
- if (debug) {
319
- printDebug(`Generating ${providerConfigKey} integrations`);
320
- }
321
- const syncObject = integrations[providerConfigKey];
322
- const syncNames = Object.keys(syncObject);
323
- for (let k = 0; k < syncNames.length; k++) {
324
- const syncName = syncNames[k];
325
- if (debug) {
326
- printDebug(`Generating ${syncName} integration`);
327
- }
328
- const syncData = syncObject[syncName];
329
- const { returns: models } = syncData;
330
- const syncNameCamel = syncName
331
- .split('-')
332
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
333
- .join('');
334
- const ejsTeamplateContents = syncName === exampleSyncName ? githubExampleTemplateContents : templateContents;
335
- const rendered = ejs.render(ejsTeamplateContents, {
336
- syncName: syncNameCamel,
337
- interfaceFileName: TYPES_FILE_NAME.replace('.ts', ''),
338
- interfaceNames: models.map((model) => {
339
- const singularModel = model?.charAt(model.length - 1) === 's' ? model.slice(0, -1) : model;
340
- return `${singularModel.charAt(0).toUpperCase()}${singularModel.slice(1)}`;
341
- }),
342
- mappings: models.map((model) => {
343
- const singularModel = model.charAt(model.length - 1) === 's' ? model.slice(0, -1) : model;
344
- return {
345
- name: model,
346
- type: `${singularModel.charAt(0).toUpperCase()}${singularModel.slice(1)}`
347
- };
348
- })
349
- });
350
- if (!fs.existsSync(`${NANGO_INTEGRATIONS_LOCATION}/${syncName}.ts`)) {
351
- fs.writeFileSync(`${NANGO_INTEGRATIONS_LOCATION}/${syncName}.ts`, rendered);
352
- if (debug) {
353
- printDebug(`Created ${syncName}.ts file`);
354
- }
355
- }
356
- else {
357
- if (debug) {
358
- printDebug(`${syncName}.ts file already exists, so will not overwrite it.`);
359
- }
360
- }
361
- }
362
- }
363
- console.log(chalk.green(`Integration files have been created`));
364
- };
365
- export const run = async (args, options, debug = false) => {
366
- let syncName, providerConfigKey, connectionId, suppliedLastSyncDate, host, secretKey;
367
- if (args.length > 0) {
368
- [syncName, providerConfigKey, connectionId, suppliedLastSyncDate] = args;
415
+ printDebug(`NANGO_HOSTPORT is set to ${process.env['NANGO_HOSTPORT']}`);
369
416
  }
370
417
  if (Object.keys(options).length > 0) {
371
- ({ sync: syncName, provider: providerConfigKey, connection: connectionId, lastSyncDate: suppliedLastSyncDate, host, secretKey } = options);
418
+ ({ sync: syncName, connectionId, lastSyncDate: suppliedLastSyncDate } = options);
372
419
  }
373
420
  if (!syncName) {
374
421
  console.log(chalk.red('Sync name is required'));
375
422
  return;
376
423
  }
377
- if (!providerConfigKey) {
378
- console.log(chalk.red('Provider config key is required'));
379
- return;
380
- }
381
424
  if (!connectionId) {
382
425
  console.log(chalk.red('Connection id is required'));
383
426
  return;
384
427
  }
385
- if (debug) {
386
- if (host) {
387
- printDebug(`Host value is set to ${host}. This will override the value in the .env file`);
388
- }
389
- }
390
- if (host) {
391
- process.env['NANGO_HOSTPORT'] = host;
392
- }
393
- if (secretKey) {
394
- process.env['NANGO_SECRET_KEY'] = secretKey;
395
- }
396
- if (!process.env['NANGO_HOSTPORT']) {
397
- if (debug) {
398
- printDebug(`NANGO_HOSTPORT is not set. Setting the default to ${hostport}`);
399
- }
400
- process.env['NANGO_HOSTPORT'] = hostport;
428
+ const config = await getConfig(debug);
429
+ const providerConfigKey = config.find((config) => config.syncs.find((sync) => sync.name === syncName))?.providerConfigKey;
430
+ if (!providerConfigKey) {
431
+ console.log(chalk.red(`Provider config key not found, please check that the provider exists for this sync name: ${syncName}`));
432
+ return;
401
433
  }
402
434
  if (debug) {
403
- printDebug(`NANGO_HOSTPORT is set to ${process.env['NANGO_HOSTPORT']}`);
435
+ printDebug(`Provider config key found to be ${providerConfigKey}`);
404
436
  }
405
437
  const nangoConnection = (await getConnection(providerConfigKey, connectionId, {
406
438
  'Nango-Is-Sync': true,
@@ -410,6 +442,9 @@ export const run = async (args, options, debug = false) => {
410
442
  console.log(chalk.red('Connection not found'));
411
443
  return;
412
444
  }
445
+ if (debug) {
446
+ printDebug(`Connection found with ${JSON.stringify(nangoConnection, null, 2)}`);
447
+ }
413
448
  if (process.env['NANGO_HOSTPORT'] === cloudHost || process.env['NANGO_HOSTPORT'] === stagingHost) {
414
449
  process.env['NANGO_CLOUD'] = 'true';
415
450
  }
@@ -420,13 +455,12 @@ export const run = async (args, options, debug = false) => {
420
455
  }
421
456
  lastSyncDate = new Date(suppliedLastSyncDate);
422
457
  }
423
- const cwd = process.cwd();
424
458
  const syncRun = new syncRunService({
425
459
  writeToDb: false,
426
460
  nangoConnection,
427
461
  syncName,
428
462
  syncType: SyncType.INITIAL,
429
- loadLocation: path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}`),
463
+ loadLocation: './',
430
464
  debug
431
465
  });
432
466
  try {
@@ -440,38 +474,35 @@ export const run = async (args, options, debug = false) => {
440
474
  }
441
475
  };
442
476
  export const tsc = (debug = false) => {
443
- const cwd = process.cwd();
444
477
  const tsconfig = fs.readFileSync(`${getNangoRootPath()}/tsconfig.dev.json`, 'utf8');
445
- const distDir = path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/dist`);
478
+ const distDir = './dist';
446
479
  if (!fs.existsSync(distDir)) {
447
480
  if (debug) {
448
481
  printDebug(`Creating ${distDir} directory`);
449
482
  }
450
483
  fs.mkdirSync(distDir);
451
484
  }
452
- if (!fs.existsSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`)) {
485
+ if (!fs.existsSync(`./${TYPES_FILE_NAME}`)) {
453
486
  if (debug) {
454
487
  printDebug(`Creating ${TYPES_FILE_NAME} file`);
455
488
  }
456
489
  createModelFile();
457
490
  }
458
- const rawNangoIntegrationLocation = NANGO_INTEGRATIONS_LOCATION.replace('./', '');
459
491
  const compiler = tsNode.create({
460
492
  compilerOptions: JSON.parse(tsconfig).compilerOptions
461
493
  });
462
494
  if (debug) {
463
495
  printDebug(`Compiler options: ${JSON.stringify(JSON.parse(tsconfig).compilerOptions, null, 2)}`);
464
496
  }
465
- const integrationFiles = glob.sync(path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/*.ts`));
497
+ const integrationFiles = glob.sync(`./*.ts`);
466
498
  for (const filePath of integrationFiles) {
467
499
  try {
468
500
  if (!nangoCallsAreAwaited(filePath)) {
469
501
  return;
470
502
  }
471
503
  const result = compiler.compile(fs.readFileSync(filePath, 'utf8'), filePath);
472
- const jsFilePath = path.join(path.dirname(filePath), path.basename(filePath, '.ts') + '.js');
473
- const distJSFilePath = jsFilePath.replace(rawNangoIntegrationLocation, `${rawNangoIntegrationLocation}/dist`);
474
- fs.writeFileSync(distJSFilePath, result);
504
+ const jsFilePath = filePath.replace(/\/[^\/]*$/, `/dist/${path.basename(filePath.replace('.ts', '.js'))}`);
505
+ fs.writeFileSync(jsFilePath, result);
475
506
  console.log(chalk.green(`Compiled "${filePath}" successfully`));
476
507
  }
477
508
  catch (error) {
@@ -504,52 +535,51 @@ const nangoCallsAreAwaited = (filePath) => {
504
535
  return areAwaited;
505
536
  };
506
537
  export const tscWatch = (debug = false) => {
507
- const cwd = process.cwd();
508
538
  const tsconfig = fs.readFileSync(`${getNangoRootPath()}/tsconfig.dev.json`, 'utf8');
509
- const watchPath = [`${NANGO_INTEGRATIONS_LOCATION}/*.ts`, `${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`];
539
+ const watchPath = [`./*.ts`, `./${nangoConfigFile}`];
510
540
  if (debug) {
511
541
  printDebug(`Watching ${watchPath.join(', ')}`);
512
542
  }
513
- const rawNangoIntegrationLocation = NANGO_INTEGRATIONS_LOCATION.replace('./', '');
514
543
  const watcher = chokidar.watch(watchPath, {
515
544
  ignoreInitial: false,
516
545
  ignored: (filePath) => {
517
- return filePath === `${rawNangoIntegrationLocation}/${TYPES_FILE_NAME}`;
546
+ return filePath === TYPES_FILE_NAME;
518
547
  }
519
548
  });
520
- const distDir = path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/dist`);
549
+ const distDir = './dist';
521
550
  if (!fs.existsSync(distDir)) {
522
551
  if (debug) {
523
552
  printDebug(`Creating ${distDir} directory`);
524
553
  }
525
554
  fs.mkdirSync(distDir);
526
555
  }
527
- if (!fs.existsSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`)) {
556
+ if (!fs.existsSync(`./${TYPES_FILE_NAME}`)) {
528
557
  if (debug) {
529
558
  printDebug(`Creating ${TYPES_FILE_NAME} file`);
530
559
  }
531
560
  createModelFile();
532
561
  }
533
562
  watcher.on('add', (filePath) => {
534
- if (filePath === `${rawNangoIntegrationLocation}/${nangoConfigFile}`) {
563
+ if (filePath === nangoConfigFile) {
535
564
  return;
536
565
  }
537
566
  compileFile(filePath);
538
567
  });
539
568
  watcher.on('unlink', (filePath) => {
540
- if (filePath === `${rawNangoIntegrationLocation}/${nangoConfigFile}`) {
569
+ if (filePath === nangoConfigFile) {
541
570
  return;
542
571
  }
543
- const jsFilePath = path.join(path.dirname(filePath), path.basename(filePath, '.ts') + '.js');
544
- const distJSFilePath = jsFilePath.replace(rawNangoIntegrationLocation, `${rawNangoIntegrationLocation}/dist`);
545
- fs.unlinkSync(distJSFilePath);
572
+ const jsFilePath = `./dist/${path.basename(filePath.replace('.ts', '.js'))}`;
573
+ fs.unlinkSync(jsFilePath);
546
574
  });
547
575
  watcher.on('change', (filePath) => {
548
- if (filePath === `${rawNangoIntegrationLocation}/${nangoConfigFile}`) {
576
+ if (filePath === nangoConfigFile) {
549
577
  // config file changed, re-compile each ts file
550
- const integrationFiles = glob.sync(path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/*.ts`));
578
+ const integrationFiles = glob.sync(`./*.ts`);
551
579
  for (const file of integrationFiles) {
552
- compileFile(file);
580
+ // strip the file to just the last part
581
+ const strippedFile = file.replace(/^.*[\\\/]/, '');
582
+ compileFile(strippedFile);
553
583
  }
554
584
  return;
555
585
  }
@@ -564,9 +594,8 @@ export const tscWatch = (debug = false) => {
564
594
  return;
565
595
  }
566
596
  const result = compiler.compile(fs.readFileSync(filePath, 'utf8'), filePath);
567
- const jsFilePath = path.join(path.dirname(filePath), path.basename(filePath, '.ts') + '.js');
568
- const distJSFilePath = jsFilePath.replace(rawNangoIntegrationLocation, `${rawNangoIntegrationLocation}/dist`);
569
- fs.writeFileSync(distJSFilePath, result);
597
+ const jsFilePath = `./dist/${path.basename(filePath.replace('.ts', '.js'))}`;
598
+ fs.writeFileSync(jsFilePath, result);
570
599
  console.log(chalk.green(`Compiled ${filePath} successfully`));
571
600
  }
572
601
  catch (error) {
@@ -577,7 +606,7 @@ export const tscWatch = (debug = false) => {
577
606
  }
578
607
  };
579
608
  export const configWatch = (debug = false) => {
580
- const watchPath = `${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`;
609
+ const watchPath = `./${nangoConfigFile}`;
581
610
  if (debug) {
582
611
  printDebug(`Watching ${watchPath}`);
583
612
  }