nango 0.24.12 → 0.24.13

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,214 +14,31 @@ 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, NANGO_INTEGRATIONS_NAME, buildInterfaces, enrichHeaders, getNangoRootPath, printDebug } from './utils.js';
17
+ import { hostport, port, httpsAgent, verifyNecessaryFiles, getConnection, NANGO_INTEGRATIONS_LOCATION, 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
- 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
24
+ const createModelFile = (notify = false) => {
72
25
  const cwd = process.cwd();
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');
26
+ const configContents = fs.readFileSync(path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`), 'utf8');
135
27
  const configData = yaml.load(configContents);
136
- const { integrations } = configData;
137
28
  const { models } = configData;
138
- const interfaceDefinitions = buildInterfaces(models, debug);
29
+ const interfaceDefinitions = buildInterfaces(models);
139
30
  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
- }
143
31
  // insert NangoSync types to the bottom of the file
144
32
  const typesContent = fs.readFileSync(`${getNangoRootPath()}/${NangoSyncTypesFileLocation}`, 'utf8');
145
33
  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' });
219
34
  if (notify) {
220
- console.log(chalk.green(`The ${nangoConfigFile} was updated. The interface file (${TYPES_FILE_NAME}) was updated to reflect the updated config`));
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`));
221
37
  }
222
38
  };
223
39
  const getConfig = async (debug = false) => {
224
- const config = await loadSimplifiedConfig('./');
40
+ const cwd = process.cwd();
41
+ const config = await loadSimplifiedConfig(path.resolve(cwd, NANGO_INTEGRATIONS_LOCATION));
225
42
  if (!config) {
226
43
  throw new Error(`Error loading the ${nangoConfigFile} file`);
227
44
  }
@@ -230,40 +47,21 @@ const getConfig = async (debug = false) => {
230
47
  }
231
48
  return config;
232
49
  };
233
- async function parseSecretKey(environment, debug = false) {
234
- if (process.env['NANGO_SECRET_KEY_PROD'] && environment === 'prod') {
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) {
235
54
  if (debug) {
236
- printDebug(`Environment is set to prod, setting NANGO_SECRET_KEY to NANGO_SECRET_KEY_PROD.`);
55
+ printDebug(`Global host flag is set, setting NANGO_HOSTPORT to ${host}.`);
237
56
  }
238
- process.env['NANGO_SECRET_KEY'] = process.env['NANGO_SECRET_KEY_PROD'];
57
+ process.env['NANGO_HOSTPORT'] = host;
239
58
  }
240
- if (process.env['NANGO_SECRET_KEY_DEV'] && environment === 'dev') {
59
+ if (secretKey) {
241
60
  if (debug) {
242
- printDebug(`Environment is set to dev, setting NANGO_SECRET_KEY to NANGO_SECRET_KEY_DEV.`);
61
+ printDebug(`Global secretKey flag is set, setting NANGO_SECRET_KEY.`);
243
62
  }
244
- process.env['NANGO_SECRET_KEY'] = process.env['NANGO_SECRET_KEY_DEV'];
63
+ process.env['NANGO_SECRET_KEY'] = secretKey;
245
64
  }
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);
267
65
  if (!process.env['NANGO_HOSTPORT']) {
268
66
  switch (env) {
269
67
  case 'local':
@@ -279,10 +77,26 @@ export const deploy = async (options, environment, debug = false) => {
279
77
  }
280
78
  if (debug) {
281
79
  printDebug(`NANGO_HOSTPORT is set to ${process.env['NANGO_HOSTPORT']}.`);
282
- printDebug(`Environment is set to ${environment}`);
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
+ }
283
96
  }
284
97
  tsc(debug);
285
98
  const config = await getConfig(debug);
99
+ const cwd = process.cwd();
286
100
  const postData = [];
287
101
  for (const integration of config) {
288
102
  const { providerConfigKey } = integration;
@@ -292,7 +106,7 @@ export const deploy = async (options, environment, debug = false) => {
292
106
  }
293
107
  for (const sync of syncs) {
294
108
  const { name: syncName, runs, returns: models, models: model_schema } = sync;
295
- const { path: integrationFilePath, result: integrationFileResult } = checkForIntegrationFile(syncName, './');
109
+ const { path: integrationFilePath, result: integrationFileResult } = checkForIntegrationFile(syncName, path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}`));
296
110
  if (!integrationFileResult) {
297
111
  console.log(chalk.red(`No integration file found for ${syncName} at ${integrationFilePath}. Skipping...`));
298
112
  continue;
@@ -313,7 +127,11 @@ export const deploy = async (options, environment, debug = false) => {
313
127
  }
314
128
  }
315
129
  const url = process.env['NANGO_HOSTPORT'] + `/sync/deploy`;
316
- if (process.env['NANGO_DEPLOY_AUTO_CONFIRM'] !== 'true' && !autoConfirm) {
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) {
317
135
  const confirmationUrl = process.env['NANGO_HOSTPORT'] + `/sync/deploy/confirmation`;
318
136
  try {
319
137
  const response = await axios.post(confirmationUrl, { syncs: postData, reconcile: false, debug }, { headers: enrichHeaders(), httpsAgent: httpsAgent() });
@@ -334,13 +152,8 @@ export const deploy = async (options, environment, debug = false) => {
334
152
  .post(process.env['NANGO_HOSTPORT'] + `/sync/deploy`, { syncs: postData, reconcile: true, debug }, { headers: enrichHeaders(), httpsAgent: httpsAgent() })
335
153
  .then((response) => {
336
154
  const results = response.data;
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
- }
155
+ const nameAndVersions = results.map((result) => `${result.sync_name}@v${result.version}`);
156
+ console.log(chalk.green(`Successfully deployed the syncs: ${nameAndVersions.join(', ')}!`));
344
157
  })
345
158
  .catch((err) => {
346
159
  const errorMessage = JSON.stringify(err.response.data, null, 2);
@@ -379,13 +192,8 @@ async function deploySyncs(url, body) {
379
192
  .post(url, body, { headers: enrichHeaders(), httpsAgent: httpsAgent() })
380
193
  .then((response) => {
381
194
  const results = response.data;
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
- }
195
+ const nameAndVersions = results.map((result) => `${result.sync_name}@v${result.version}`);
196
+ console.log(chalk.green(`Successfully deployed the syncs: ${nameAndVersions.join(', ')}!`));
389
197
  })
390
198
  .catch((err) => {
391
199
  let errorMessage;
@@ -401,38 +209,198 @@ async function deploySyncs(url, body) {
401
209
  process.exit(1);
402
210
  });
403
211
  }
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']) {
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)) {
409
255
  if (debug) {
410
- printDebug(`NANGO_HOSTPORT is not set. Setting the default to ${hostport}`);
256
+ printDebug('Creating the nango integrations directory');
411
257
  }
412
- process.env['NANGO_HOSTPORT'] = hostport;
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);
413
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 {
291
+ if (debug) {
292
+ printDebug('.env file already exists');
293
+ }
294
+ }
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'));
414
307
  if (debug) {
415
- printDebug(`NANGO_HOSTPORT is set to ${process.env['NANGO_HOSTPORT']}`);
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
+ 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;
416
369
  }
417
370
  if (Object.keys(options).length > 0) {
418
- ({ sync: syncName, connectionId, lastSyncDate: suppliedLastSyncDate } = options);
371
+ ({ sync: syncName, provider: providerConfigKey, connection: connectionId, lastSyncDate: suppliedLastSyncDate, host, secretKey } = options);
419
372
  }
420
373
  if (!syncName) {
421
374
  console.log(chalk.red('Sync name is required'));
422
375
  return;
423
376
  }
377
+ if (!providerConfigKey) {
378
+ console.log(chalk.red('Provider config key is required'));
379
+ return;
380
+ }
424
381
  if (!connectionId) {
425
382
  console.log(chalk.red('Connection id is required'));
426
383
  return;
427
384
  }
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;
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;
433
401
  }
434
402
  if (debug) {
435
- printDebug(`Provider config key found to be ${providerConfigKey}`);
403
+ printDebug(`NANGO_HOSTPORT is set to ${process.env['NANGO_HOSTPORT']}`);
436
404
  }
437
405
  const nangoConnection = (await getConnection(providerConfigKey, connectionId, {
438
406
  'Nango-Is-Sync': true,
@@ -442,9 +410,6 @@ export const dryRun = async (options, environment, debug = false) => {
442
410
  console.log(chalk.red('Connection not found'));
443
411
  return;
444
412
  }
445
- if (debug) {
446
- printDebug(`Connection found with ${JSON.stringify(nangoConnection, null, 2)}`);
447
- }
448
413
  if (process.env['NANGO_HOSTPORT'] === cloudHost || process.env['NANGO_HOSTPORT'] === stagingHost) {
449
414
  process.env['NANGO_CLOUD'] = 'true';
450
415
  }
@@ -455,12 +420,13 @@ export const dryRun = async (options, environment, debug = false) => {
455
420
  }
456
421
  lastSyncDate = new Date(suppliedLastSyncDate);
457
422
  }
423
+ const cwd = process.cwd();
458
424
  const syncRun = new syncRunService({
459
425
  writeToDb: false,
460
426
  nangoConnection,
461
427
  syncName,
462
428
  syncType: SyncType.INITIAL,
463
- loadLocation: './',
429
+ loadLocation: path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}`),
464
430
  debug
465
431
  });
466
432
  try {
@@ -474,35 +440,38 @@ export const dryRun = async (options, environment, debug = false) => {
474
440
  }
475
441
  };
476
442
  export const tsc = (debug = false) => {
443
+ const cwd = process.cwd();
477
444
  const tsconfig = fs.readFileSync(`${getNangoRootPath()}/tsconfig.dev.json`, 'utf8');
478
- const distDir = './dist';
445
+ const distDir = path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/dist`);
479
446
  if (!fs.existsSync(distDir)) {
480
447
  if (debug) {
481
448
  printDebug(`Creating ${distDir} directory`);
482
449
  }
483
450
  fs.mkdirSync(distDir);
484
451
  }
485
- if (!fs.existsSync(`./${TYPES_FILE_NAME}`)) {
452
+ if (!fs.existsSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`)) {
486
453
  if (debug) {
487
454
  printDebug(`Creating ${TYPES_FILE_NAME} file`);
488
455
  }
489
456
  createModelFile();
490
457
  }
458
+ const rawNangoIntegrationLocation = NANGO_INTEGRATIONS_LOCATION.replace('./', '');
491
459
  const compiler = tsNode.create({
492
460
  compilerOptions: JSON.parse(tsconfig).compilerOptions
493
461
  });
494
462
  if (debug) {
495
463
  printDebug(`Compiler options: ${JSON.stringify(JSON.parse(tsconfig).compilerOptions, null, 2)}`);
496
464
  }
497
- const integrationFiles = glob.sync(`./*.ts`);
465
+ const integrationFiles = glob.sync(path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/*.ts`));
498
466
  for (const filePath of integrationFiles) {
499
467
  try {
500
468
  if (!nangoCallsAreAwaited(filePath)) {
501
469
  return;
502
470
  }
503
471
  const result = compiler.compile(fs.readFileSync(filePath, 'utf8'), filePath);
504
- const jsFilePath = filePath.replace(/\/[^\/]*$/, `/dist/${path.basename(filePath.replace('.ts', '.js'))}`);
505
- fs.writeFileSync(jsFilePath, result);
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);
506
475
  console.log(chalk.green(`Compiled "${filePath}" successfully`));
507
476
  }
508
477
  catch (error) {
@@ -535,51 +504,52 @@ const nangoCallsAreAwaited = (filePath) => {
535
504
  return areAwaited;
536
505
  };
537
506
  export const tscWatch = (debug = false) => {
507
+ const cwd = process.cwd();
538
508
  const tsconfig = fs.readFileSync(`${getNangoRootPath()}/tsconfig.dev.json`, 'utf8');
539
- const watchPath = [`./*.ts`, `./${nangoConfigFile}`];
509
+ const watchPath = [`${NANGO_INTEGRATIONS_LOCATION}/*.ts`, `${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`];
540
510
  if (debug) {
541
511
  printDebug(`Watching ${watchPath.join(', ')}`);
542
512
  }
513
+ const rawNangoIntegrationLocation = NANGO_INTEGRATIONS_LOCATION.replace('./', '');
543
514
  const watcher = chokidar.watch(watchPath, {
544
515
  ignoreInitial: false,
545
516
  ignored: (filePath) => {
546
- return filePath === TYPES_FILE_NAME;
517
+ return filePath === `${rawNangoIntegrationLocation}/${TYPES_FILE_NAME}`;
547
518
  }
548
519
  });
549
- const distDir = './dist';
520
+ const distDir = path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/dist`);
550
521
  if (!fs.existsSync(distDir)) {
551
522
  if (debug) {
552
523
  printDebug(`Creating ${distDir} directory`);
553
524
  }
554
525
  fs.mkdirSync(distDir);
555
526
  }
556
- if (!fs.existsSync(`./${TYPES_FILE_NAME}`)) {
527
+ if (!fs.existsSync(`${NANGO_INTEGRATIONS_LOCATION}/${TYPES_FILE_NAME}`)) {
557
528
  if (debug) {
558
529
  printDebug(`Creating ${TYPES_FILE_NAME} file`);
559
530
  }
560
531
  createModelFile();
561
532
  }
562
533
  watcher.on('add', (filePath) => {
563
- if (filePath === nangoConfigFile) {
534
+ if (filePath === `${rawNangoIntegrationLocation}/${nangoConfigFile}`) {
564
535
  return;
565
536
  }
566
537
  compileFile(filePath);
567
538
  });
568
539
  watcher.on('unlink', (filePath) => {
569
- if (filePath === nangoConfigFile) {
540
+ if (filePath === `${rawNangoIntegrationLocation}/${nangoConfigFile}`) {
570
541
  return;
571
542
  }
572
- const jsFilePath = `./dist/${path.basename(filePath.replace('.ts', '.js'))}`;
573
- fs.unlinkSync(jsFilePath);
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);
574
546
  });
575
547
  watcher.on('change', (filePath) => {
576
- if (filePath === nangoConfigFile) {
548
+ if (filePath === `${rawNangoIntegrationLocation}/${nangoConfigFile}`) {
577
549
  // config file changed, re-compile each ts file
578
- const integrationFiles = glob.sync(`./*.ts`);
550
+ const integrationFiles = glob.sync(path.resolve(cwd, `${NANGO_INTEGRATIONS_LOCATION}/*.ts`));
579
551
  for (const file of integrationFiles) {
580
- // strip the file to just the last part
581
- const strippedFile = file.replace(/^.*[\\\/]/, '');
582
- compileFile(strippedFile);
552
+ compileFile(file);
583
553
  }
584
554
  return;
585
555
  }
@@ -594,8 +564,9 @@ export const tscWatch = (debug = false) => {
594
564
  return;
595
565
  }
596
566
  const result = compiler.compile(fs.readFileSync(filePath, 'utf8'), filePath);
597
- const jsFilePath = `./dist/${path.basename(filePath.replace('.ts', '.js'))}`;
598
- fs.writeFileSync(jsFilePath, result);
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);
599
570
  console.log(chalk.green(`Compiled ${filePath} successfully`));
600
571
  }
601
572
  catch (error) {
@@ -606,7 +577,7 @@ export const tscWatch = (debug = false) => {
606
577
  }
607
578
  };
608
579
  export const configWatch = (debug = false) => {
609
- const watchPath = `./${nangoConfigFile}`;
580
+ const watchPath = `${NANGO_INTEGRATIONS_LOCATION}/${nangoConfigFile}`;
610
581
  if (debug) {
611
582
  printDebug(`Watching ${watchPath}`);
612
583
  }