const msgStream = new Meteor.Streamer('room-messages');
this.msgStream = msgStream;

msgStream.allowWrite('none');

msgStream.allowRead(function(eventName) {
	try {
		const room = Meteor.call('canAccessRoom', eventName, this.userId);

		if (!room) {
			return false;
		}

		if (room.t === 'c' && !RocketChat.authz.hasPermission(this.userId, 'preview-c-room') && room.usernames.indexOf(room.username) === -1) {
			return false;
		}

		return true;
	} catch (error) {
		/*error*/
		return false;
	}
});

msgStream.allowRead('__my_messages__', 'all');

msgStream.allowEmit('__my_messages__', function(eventName, msg, options) {
	try {
		const room = Meteor.call('canAccessRoom', msg.rid, this.userId);

		if (!room) {
			return false;
		}

		options.roomParticipant = room.usernames.indexOf(room.username) > -1;
		options.roomType = room.t;

		return true;
	} catch (error) {
		/*error*/
		return false;
	}
});

Meteor.startup(function() {
	function publishMessage(type, record) {
		if (record._hidden !== true && (record.imported == null)) {
			const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true;

			if (record.u && record.u._id && UI_Use_Real_Name) {
				const user = RocketChat.models.Users.findOneById(record.u._id);
				record.u.name = user && user.name;
			}

			if (record.mentions && record.mentions.length && UI_Use_Real_Name) {
				record.mentions.forEach((mention) => {
					const user = RocketChat.models.Users.findOneById(mention._id);
					mention.name = user && user.name;
				});
			}
			msgStream.emitWithoutBroadcast('__my_messages__', record, {});
			return msgStream.emitWithoutBroadcast(record.rid, record);
		}
	}

	return RocketChat.models.Messages._db.on('change', function({action, id, data/*, oplog*/}) {
		switch (action) {
			case 'insert':
				data._id = id;
				publishMessage('inserted', data);
				break;
			case 'update:record':
				publishMessage('updated', data);
				break;
			case 'update:diff':
				publishMessage('updated', RocketChat.models.Messages.findOne({
					_id: id
				}));
				break;
		}
	});
});


* Takes the uploaded file and extracts the users, channels, and messages from it.

this.logger.debug(`Constructed a new ${ info.name } Importer.`);

RocketChat.slashCommands.add('invite', undefined, {
	description: 'Invite_user_to_join_channel',
	params: '@username'
});


<template name="integrationsOutgoingHistory">

			{{> burger}}

				{{pageTitle}}

			{{#if hasPermission}}

					<button class="button danger clear-history">{{_ "clear_history"}}</button>

			{{/if}}

			{{#unless hasPermission}}

{{_ "You_are_not_authorized_to_view_this_page"}}

			{{else}}
				 {{_ "Back_to_integration_detail"}}

					{{#each history in histories}}

									{{formatDate history._createdAt}}

									<button class="button replay" data-history-id="{{ history._id }}">Replay</button>
									<button class="button primary expand">
										{{_ "Expand"}}
									</button>

									{{_ "Status"}}

										<input class="input-monitor" type="text" disabled value="{{statusI18n history.error}}">

									{{_ "Integration_Outgoing_WebHook_History_Time_Triggered"}}

										<input class="input-monitor" type="text" disabled value="{{formatDateDetail history._createdAt}}">

									{{_ "Integration_Outgoing_WebHook_History_Time_Ended_Or_Error"}}

										<input class="input-monitor" type="text" disabled value="{{formatDateDetail history._updatedAt}}">

									{{_ "Event_Trigger"}}

										<input class="input-monitor" type="text" disabled value="{{eventTypei18n history.event}}">

									{{_ "Integration_Outgoing_WebHook_History_Trigger_Step"}}

										<input class="input-monitor" type="text" disabled value="{{history.step}}">

									{{_ "Integration_Outgoing_WebHook_History_Data_Passed_To_Trigger"}}

{{{ jsonStringify history.data }}}

								{{#if hasProperty history 'prepareSentMessage'}}

										{{_ "Integration_Outgoing_WebHook_History_Messages_Sent_From_Prepare_Script"}}

{{{ jsonStringify history.prepareSentMessage }}}

								{{/if}}
								{{#if hasProperty history 'processSentMessage'}}

										{{_ "Integration_Outgoing_WebHook_History_Messages_Sent_From_Process_Script"}}

{{{ jsonStringify history.processSentMessage }}}

								{{/if}}
								{{#if hasProperty history 'url'}}

										{{_ "URL"}}

{{ history.url }}

								{{/if}}
								{{#if hasProperty history 'httpCallData'}}

										{{_ "Integration_Outgoing_WebHook_History_Data_Passed_To_URL"}}

{{{jsonStringify history.httpCallData }}}

								{{/if}}
								{{#if hasProperty history 'httpError'}}

										{{_ "Integration_Outgoing_WebHook_History_Http_Response_Error"}}

{{{ jsonStringify history.httpError }}}

								{{/if}}
								{{#if hasProperty history 'httpResult'}}

										{{_ "Integration_Outgoing_WebHook_History_Http_Response"}}

{{{ jsonStringify history.httpResult }}}

								{{/if}}
								{{#if hasProperty history 'errorStack'}}

										{{_ "Integration_Outgoing_WebHook_History_Error_Stacktrace"}}

{{{ jsonStringify history.errorStack }}}

								{{/if}}

					{{else}}

									{{_ "Integration_Outgoing_WebHook_No_History"}}

					{{/each}}
					{{#if hasMore}}

							{{> loading}}

					{{/if}}

			{{/unless}}

</template>


keep keep keep add keep keep keep keep keep

"use strict";

const fs = require("fs-extra");
const path = require("path");
const pMap = require("p-map");
const writeJsonFile = require("write-json-file");

const Command = require("@lerna/command");
const childProcess = require("@lerna/child-process");

module.exports = factory;

function factory(argv) {
  return new InitCommand(argv);
}

class InitCommand extends Command {
  get requiresGit() {
    return false;
  }

  runValidations() {
    this.logger.verbose(this.name, "skipping validations");
  }

  runPreparations() {
    this.logger.verbose(this.name, "skipping preparations");
  }

  initialize() {
    this.exact = this.options.exact;

    if (!this.gitInitialized()) {
      this.logger.info("", "Initializing Git repository");

      return childProcess.exec("git", ["init"], this.execOpts);
    }
  }

  execute() {
    let chain = Promise.resolve();

    chain = chain.then(() => this.ensurePackageJSON());
    chain = chain.then(() => this.ensureLernaConfig());
    chain = chain.then(() => this.ensurePackagesDir());

    return chain.then(() => {
      this.logger.success("", "Initialized Lerna files");
    });
  }

  ensurePackageJSON() {
    let chain = Promise.resolve();

    if (!this.project.manifest) {
      this.logger.info("", "Creating package.json");

      // initialize with default indentation so write-pkg doesn't screw it up with tabs
      chain = chain.then(() =>
        writeJsonFile(
          path.join(this.project.rootPath, "package.json"),
          {
            name: "root",
            private: true,
          },
          { indent: 2 }
        )
      );
    } else {
      this.logger.info("", "Updating package.json");
    }

    chain = chain.then(() => {
      const rootPkg = this.project.manifest;

      let targetDependencies;

      if (rootPkg.dependencies && rootPkg.dependencies.lerna) {
        // lerna is a dependency in the current project
        targetDependencies = rootPkg.dependencies;
      } else {
        // lerna is a devDependency or no dependency, yet
        if (!rootPkg.devDependencies) {
          // mutate raw JSON object
          rootPkg.set("devDependencies", {});
        }

        targetDependencies = rootPkg.devDependencies;
      }

      targetDependencies.lerna = this.exact ? this.lernaVersion : `^${this.lernaVersion}`;

      return rootPkg.serialize();
    });

    return chain;
  }

  ensureLernaConfig() {
    // config already defaulted to empty object in Project constructor
    const { config, version: projectVersion } = this.project;

    let version;

    if (this.options.independent) {
      version = "independent";
    } else if (projectVersion) {
      version = projectVersion;
    } else {
      version = "0.0.0";
    }

    if (!projectVersion) {
      this.logger.info("", "Creating lerna.json");
    } else {
      this.logger.info("", "Updating lerna.json");
    }

    delete config.lerna; // no longer relevant

    if (this.exact) {
      // ensure --exact is preserved for future init commands
      const commandConfig = config.command || (config.command = {});
      const initConfig = commandConfig.init || (commandConfig.init = {});

      initConfig.exact = true;
    }

    Object.assign(config, {
      packages: this.project.packageConfigs,
      version,
    });

    return this.project.serializeConfig();
  }

  ensurePackagesDir() {
    this.logger.info("", "Creating packages directory");

    return pMap(this.project.packageParentDirs, dir => fs.mkdirp(dir));
  }
}

module.exports.InitCommand = InitCommand;


"use strict";

const multimatch = require("multimatch");

module.exports = isHoistedPackage;

function isHoistedPackage(name, hoisting) {
  return multimatch([name], hoisting).length > 0;
}


# `@lerna/has-npm-version`

> Test if the current version of npm satisfies a given semver range

## Usage

```js
const hasNpmVersion = require('@lerna/has-npm-version');

// `npm --version` === 6.3.0
hasNpmVersion(">=6"); // => true

// `npm --version` === 5.6.0
hasNpmVersion(">=6"); // => false

// makePredicate() caches the call to `npm --version`
// useful if you're calling it repeatedly in a loop
const predicate = hasNpmVersion.makePredicate();

// `npm --version` === 6.3.0
hasNpmVersion(">=5"); // => true
```


keep keep keep keep replace replace replace replace replace replace keep keep keep

/* eslint-disable class-methods-use-this */

"use strict";

// we're actually testing integration with git
jest.unmock("@lerna/collect-updates");

const fs = require("fs-extra");
const execa = require("execa");
const log = require("npmlog");
const path = require("path");
const tempy = require("tempy");
const touch = require("touch");

// partially mocked
const ChildProcessUtilities = require("@lerna/child-process");

// helpers
const initFixture = require("@lerna-test/init-fixture")(__dirname);
const loggingOutput = require("@lerna-test/logging-output");
const gitAdd = require("@lerna-test/git-add");
const gitCommit = require("@lerna-test/git-commit");
const gitTag = require("@lerna-test/git-tag");
const updateLernaConfig = require("@lerna-test/update-lerna-config");

// file under test
const Command = require("..");

describe("core-command", () => {
  let testDir;

  beforeAll(async () => {
    testDir = await initFixture("basic");
  });

  afterEach(() => {
    // ensure common CWD is restored when individual tests
    // initialize their own fixture (which changes CWD)
    if (process.cwd() !== testDir) {
      process.chdir(testDir);
    }
  });

  ChildProcessUtilities.getChildProcessCount = jest.fn(() => 0);

  // swallow errors when passed in argv
  const onRejected = () => {};

  class OkCommand extends Command {
    initialize() {
      return true;
    }

    execute() {}
  }

  // convenience to avoid silly "not implemented errors"
  const testFactory = (argv = {}) => new OkCommand(Object.assign({ cwd: testDir }, argv));

  describe(".lernaVersion", () => {
    it("should be added to the instance", async () => {
      expect(testFactory({ lernaVersion: "__TEST__" }).lernaVersion).toBe("__TEST__");
    });
  });

  describe(".logger", () => {
    it("should be added to the instance", async () => {
      const command = testFactory();
      await command;

      expect(command.logger).toBeDefined();
    });
  });

  describe(".concurrency", () => {
    it("should be added to the instance", async () => {
      const command = testFactory({ concurrency: 6 });
      await command;

      expect(command.concurrency).toBe(6);
    });

    it("should fall back to default if concurrency given is NaN", async () => {
      const command = testFactory({ concurrency: "bla" });
      await command;

      expect(command.concurrency).toBe(4);
    });

    it("should fall back to default if concurrency given is 0", async () => {
      const command = testFactory({ concurrency: 0 });
      await command;

      expect(command.concurrency).toBe(4);
    });

    it("should fall back to 1 if concurrency given is smaller than 1", async () => {
      const command = testFactory({ concurrency: -1 });
      await command;

      expect(command.concurrency).toBe(1);
    });
  });

  describe(".toposort", () => {
    it("is enabled by default", async () => {
      const command = testFactory();
      await command;

      expect(command.toposort).toBe(true);
    });

    it("is disabled when sort config is explicitly false (--no-sort)", async () => {
      const command = testFactory({ sort: false });
      await command;

      expect(command.toposort).toBe(false);
    });
  });

  describe(".execOpts", () => {
    const ONE_HUNDRED_MEGABYTES = 1000 * 1000 * 100;

    it("has maxBuffer", async () => {
      const command = testFactory({ maxBuffer: ONE_HUNDRED_MEGABYTES });
      await command;

      expect(command.execOpts.maxBuffer).toBe(ONE_HUNDRED_MEGABYTES);
    });

    it("has repo path", async () => {
      const command = testFactory();
      await command;

      expect(command.execOpts.cwd).toBe(testDir);
    });
  });

  it("returns a Promise", async () => {
    await testFactory();
  });

  describe("when finished", () => {
    it("resolves immediately when no child processes active", async () => {
      await testFactory();

      const logMessages = loggingOutput("warn");
      expect(logMessages).toHaveLength(0);
    });

    it("waits to resolve when 1 child process active", async () => {
      ChildProcessUtilities.getChildProcessCount.mockReturnValueOnce(1);

      await testFactory();

      const [logMessage] = loggingOutput("warn");
      expect(logMessage).toMatch("Waiting for 1 child process to exit.");
    });

    it("waits to resolve when 2 child processes active", async () => {
      ChildProcessUtilities.getChildProcessCount.mockReturnValueOnce(2);

      await testFactory();

      const [logMessage] = loggingOutput("warn");
      expect(logMessage).toMatch("Waiting for 2 child processes to exit.");
    });
  });

  describe("with package error", () => {
    const originalConsoleError = console.error;

    beforeEach(() => {
      console.error = jest.fn();
    });
    afterEach(() => {
      console.error = originalConsoleError;
    });

    it("logs stdout and stderr of error from package", async () => {
      class PkgErrorCommand extends Command {
        initialize() {
          return true;
        }

        execute() {
          const err = new Error("message");

          err.cmd = "test-pkg-err";
          err.stdout = "pkg-err-stdout";
          err.stderr = "pkg-err-stderr";
          err.pkg = {
            name: "pkg-err-name",
          };

          throw err;
        }
      }

      try {
        await new PkgErrorCommand({ cwd: testDir });
      } catch (err) {
        expect(console.error.mock.calls).toHaveLength(2);
        expect(console.error.mock.calls[0]).toEqual(["pkg-err-stdout"]);
        expect(console.error.mock.calls[1]).toEqual(["pkg-err-stderr"]);

        expect(err.cmd).toEqual("test-pkg-err");
        expect(err.stdout).toEqual("pkg-err-stdout");
        expect(err.stderr).toEqual("pkg-err-stderr");
        expect(err.pkg).toEqual({
          name: "pkg-err-name",
        });
      }
    });
  });

  describe("loglevel", () => {
    afterEach(() => {
      log.level = "silent";
    });

    it("is set from lerna.json config", async () => {
      const cwd = await initFixture("basic");

      await updateLernaConfig(cwd, { loglevel: "warn" });
      await testFactory({ cwd, onRejected });

      expect(log.level).toBe("warn");
    });
  });

  describe(".packageGraph", () => {
    it("returns the graph of packages", async () => {
      const command = testFactory();
      await command;

      expect(command.packageGraph).toBeInstanceOf(Map);
    });
  });

  describe(".filteredPackages", () => {
    it("--scope should filter packages", async () => {
      const cwd = await initFixture("filtering");

      const command = testFactory({
        cwd,
        scope: ["package-2", "package-4"],
      });
      await command;

      expect(command.filteredPackages).toHaveLength(2);
      expect(command.filteredPackages[0].name).toEqual("package-2");
      expect(command.filteredPackages[1].name).toEqual("package-4");
    });

    it("--since should return all packages if no tag is found", async () => {
      const cwd = await initFixture("filtering");

      const command = testFactory({ cwd, since: "" });
      await command;

      expect(command.filteredPackages).toHaveLength(5);
    });

    it("--since should return packages updated since the last tag", async () => {
      const cwd = await initFixture("filtering");

      await gitTag(cwd, "v1.0.0");
      await touch(path.join(cwd, "packages/package-2/random-file"));
      await gitAdd(cwd, "-A");
      await gitCommit(cwd, "test");

      const command = testFactory({ cwd, since: "" });
      await command;

      expect(command.filteredPackages).toHaveLength(2);
      expect(command.filteredPackages[0].name).toEqual("package-2");
      expect(command.filteredPackages[1].name).toEqual("package-3");
    });

    it('--since "ref" should return packages updated since the specified ref', async () => {
      const cwd = await initFixture("filtering");

      // We first tag, then modify master to ensure that specifying --since will override checking against
      // the latest tag.
      await gitTag(cwd, "v1.0.0");

      await touch(path.join(cwd, "packages/package-1/random-file"));
      await gitAdd(cwd, "-A");
      await gitCommit(cwd, "test");

      // Then we can checkout a new branch, update and commit.
      await execa("git", ["checkout", "-b", "test"], { cwd });

      await touch(path.join(cwd, "packages/package-2/random-file"));
      await gitAdd(cwd, "-A");
      await gitCommit(cwd, "test");

      const command = testFactory({ cwd, since: "master" });
      await command;

      expect(command.filteredPackages).toHaveLength(2);
      expect(command.filteredPackages[0].name).toEqual("package-2");
      expect(command.filteredPackages[1].name).toEqual("package-3");
    });

    it("should respect --scope and --since when used together", async () => {
      const cwd = await initFixture("filtering");

      await execa("git", ["checkout", "-b", "test"], { cwd });
      await touch(path.join(cwd, "packages/package-4/random-file"));
      await gitAdd(cwd, "-A");
      await gitCommit(cwd, "test");

      const command = testFactory({
        cwd,
        scope: ["package-2", "package-3", "package-4"],
        since: "master",
      });
      await command;

      expect(command.filteredPackages).toHaveLength(1);
      expect(command.filteredPackages[0].name).toEqual("package-4");
    });
  });

  describe(".options", () => {
    class TestACommand extends Command {}
    class TestBCommand extends Command {}
    class TestCCommand extends Command {
      get otherCommandConfigs() {
        return ["testb"];
      }
    }

    it("should pick up global options", async () => {
      const instance = new TestACommand({ cwd: testDir, onRejected });
      await instance;

      expect(instance.options.testOption).toBe("default");
    });

    it("should override global options with command-level options", async () => {
      const instance = new TestBCommand({ cwd: testDir, onRejected });
      await instance;

      expect(instance.options.testOption).toBe("b");
    });

    it("should override global options with inherited command-level options", async () => {
      const instance = new TestCCommand({ cwd: testDir, onRejected });
      await instance;

      expect(instance.options.testOption).toBe("b");
    });

    it("should override inherited command-level options with local command-level options", async () => {
      const instance = new TestCCommand({ cwd: testDir, onRejected });
      await instance;

      expect(instance.options.testOption2).toBe("c");
    });

    it("should override everything with a CLI flag", async () => {
      const instance = new TestCCommand({
        cwd: testDir,
        onRejected,
        testOption2: "f",
      });
      await instance;

      expect(instance.options.testOption2).toBe("f");
    });

    it("should inherit durable options when a CLI flag is undefined", async () => {
      const instance = new TestCCommand({
        cwd: testDir,
        onRejected,
        testOption: undefined, // yargs does this when --test-option is not passed
      });
      await instance;

      expect(instance.options.testOption).toBe("b");
    });
  });

  describe("subclass implementation", () => {
    ["initialize", "execute"].forEach(method => {
      it(`throws if ${method}() is not overridden`, () => {
        const command = new Command({ cwd: testDir, onRejected });
        expect(() => command[method]()).toThrow();
      });
    });
  });

  describe("validations", () => {
    it("throws ENOGIT when repository is not initialized", async () => {
      expect.assertions(1);

      const cwd = tempy.directory();

      try {
        await testFactory({ cwd });
      } catch (err) {
        expect(err.prefix).toBe("ENOGIT");
      }
    });

    it("throws ENOPKG when root package.json is not found", async () => {
      expect.assertions(1);

      const cwd = await initFixture("basic");

      await fs.remove(path.join(cwd, "package.json"));

      try {
        await testFactory({ cwd });
      } catch (err) {
        expect(err.prefix).toBe("ENOPKG");
      }
    });

    it("throws JSONError when root package.json has syntax error", async () => {
      expect.assertions(1);

      const cwd = await initFixture("basic");

      await fs.writeFile(
        path.join(cwd, "package.json"), // trailing comma ...v
        '{ "name": "invalid", "lerna": { "version": "1.0.0" }, }'
      );

      try {
        await testFactory({ cwd });
      } catch (err) {
        expect(err.prefix).toBe("JSONError");
      }
    });

    it("throws ENOLERNA when lerna.json is not found", async () => {
      expect.assertions(1);

      const cwd = await initFixture("basic");

      await fs.remove(path.join(cwd, "lerna.json"));

      try {
        await testFactory({ cwd });
      } catch (err) {
        expect(err.prefix).toBe("ENOLERNA");
      }
    });
  });
});


// composed commands have already logged the lerna version

keep keep keep keep keep replace keep keep


"use strict";

const fs = require.requireActual("fs-extra");
const path = require("path");

const mockConventionalCommits = jest.genMockFromModule("@lerna/conventional-commits");

mockConventionalCommits.updateChangelog.mockImplementation(pkg => {
  const filePath = path.join(pkg.location, "CHANGELOG.md");

  // grumble grumble re-implementing the implementation
  return fs.outputFile(filePath, "changelog", "utf8").then(() => filePath);
});

function mockBumps(...bumps) {
  bumps.forEach(bump => mockConventionalCommits.recommendVersion.mockResolvedValueOnce(bump));
}

module.exports = mockConventionalCommits;
module.exports.mockBumps = mockBumps;


@lerna-test/calls-back

A local test helper


keep keep replace keep keep keep keep keep

"use strict";

const _ = require("lodash");
const dedent = require("dedent");
const execa = require("execa");
const log = require("npmlog");

const PackageGraph = require("@lerna/package-graph");
const Project = require("@lerna/project");
const writeLogFile = require("@lerna/write-log-file");
const collectUpdates = require("@lerna/collect-updates");
const filterPackages = require("@lerna/filter-packages");
const ValidationError = require("@lerna/validation-error");

const cleanStack = require("./lib/clean-stack");
const logPackageError = require("./lib/log-package-error");
const warnIfHanging = require("./lib/warn-if-hanging");

const DEFAULT_CONCURRENCY = 4;

class Command {
  constructor(argv) {
    log.pause();
    log.heading = "lerna";

    this._argv = argv;
    log.silly("argv", argv);

    this.lernaVersion = argv.lernaVersion;
    if (!argv.composed) {
      // composed commands have already logged the lerna version
      log.info("version", this.lernaVersion);
    }

    // "FooCommand" => "foo"
    this.name = this.constructor.name.replace(/Command$/, "").toLowerCase();

    // launch the command
    let runner = new Promise((resolve, reject) => {
      // run everything inside a Promise chain
      let chain = Promise.resolve();

      chain = chain.then(() => {
        this.project = new Project(argv.cwd);
      });
      chain = chain.then(() => this.configureOptions());
      chain = chain.then(() => this.configureProperties());
      chain = chain.then(() => this.configureLogging());
      chain = chain.then(() => this.runValidations());
      chain = chain.then(() => this.runPreparations());
      chain = chain.then(() => this.runCommand());

      chain.then(
        result => {
          warnIfHanging();

          resolve(result);
        },
        err => {
          if (err.pkg) {
            // Cleanly log specific package error details
            logPackageError(err);
          } else if (err.name !== "ValidationError") {
            // npmlog does some funny stuff to the stack by default,
            // so pass it directly to avoid duplication.
            log.error("", cleanStack(err, this.constructor.name));
          }

          // ValidationError does not trigger a log dump, nor do external package errors
          if (err.name !== "ValidationError" && !err.pkg) {
            writeLogFile(this.project.rootPath);
          }

          warnIfHanging();

          // error code is handled by cli.fail()
          reject(err);
        }
      );
    });

    // passed via yargs context in tests, never actual CLI
    /* istanbul ignore else */
    if (argv.onResolved || argv.onRejected) {
      runner = runner.then(argv.onResolved, argv.onRejected);

      // when nested, never resolve inner with outer callbacks
      delete argv.onResolved; // eslint-disable-line no-param-reassign
      delete argv.onRejected; // eslint-disable-line no-param-reassign
    }

    // proxy "Promise" methods to "private" instance
    this.then = (onResolved, onRejected) => runner.then(onResolved, onRejected);
    this.catch = onRejected => runner.catch(onRejected);
  }

  get requiresGit() {
    return true;
  }

  // Override this to inherit config from another command.
  // For example `changed` inherits config from `publish`.
  get otherCommandConfigs() {
    return [];
  }

  configureOptions() {
    // Command config object normalized to "command" namespace
    const commandConfig = this.project.config.command || {};

    // The current command always overrides otherCommandConfigs
    const overrides = [this.name, ...this.otherCommandConfigs].map(key => commandConfig[key]);

    this.options = _.defaults(
      {},
      // CLI flags, which if defined overrule subsequent values
      this._argv,
      // Namespaced command options from `lerna.json`
      ...overrides,
      // Global options from `lerna.json`
      this.project.config
    );
  }

  configureProperties() {
    const { concurrency, sort, maxBuffer } = this.options;

    this.concurrency = Math.max(1, +concurrency || DEFAULT_CONCURRENCY);
    this.toposort = sort === undefined || sort;
    this.execOpts = {
      cwd: this.project.rootPath,
      maxBuffer,
    };
  }

  configureLogging() {
    const { loglevel } = this.options;

    if (loglevel) {
      log.level = loglevel;
    }

    // handle log.success()
    log.addLevel("success", 3001, { fg: "green", bold: true });

    // create logger that subclasses use
    this.logger = log.newGroup(this.name);

    // emit all buffered logs at configured level and higher
    log.resume();
  }

  enableProgressBar() {
    // istanbul ignore else
    if (this.options.progress) {
      this.logger.enableProgress();
    }
  }

  gitInitialized() {
    const opts = {
      cwd: this.project.rootPath,
      // don't throw, just want boolean
      reject: false,
      // only return code, no stdio needed
      stdio: "ignore",
    };

    return execa.sync("git", ["rev-parse"], opts).code === 0;
  }

  runValidations() {
    if ((this.options.since !== undefined || this.requiresGit) && !this.gitInitialized()) {
      throw new ValidationError("ENOGIT", "The git binary was not found, or this is not a git repository.");
    }

    if (!this.project.manifest) {
      throw new ValidationError("ENOPKG", "`package.json` does not exist, have you run `lerna init`?");
    }

    if (!this.project.version) {
      throw new ValidationError("ENOLERNA", "`lerna.json` does not exist, have you run `lerna init`?");
    }

    if (this.options.independent && !this.project.isIndependent()) {
      throw new ValidationError(
        "EVERSIONMODE",
        dedent`
          You ran lerna with --independent or -i, but the repository is not set to independent mode.
          To use independent mode you need to set lerna.json's "version" property to "independent".
          Then you won't need to pass the --independent or -i flags.
        `
      );
    }
  }

  runPreparations() {
    if (!this.options.composed && this.project.isIndependent()) {
      // composed commands have already logged the independent status
      log.info("versioning", "independent");
    }

    let chain = Promise.resolve();

    chain = chain.then(() => this.project.getPackages());
    chain = chain.then(packages => {
      this.packageGraph = new PackageGraph(packages);
      this.filteredPackages = filterPackages(
        packages,
        this.options.scope,
        this.options.ignore,
        this.options.private
      );
    });

    // collectUpdates requires that filteredPackages be present prior to checking for
    // updates. That's okay because it further filters based on what's already been filtered.
    if (this.options.since !== undefined) {
      chain = chain.then(() =>
        collectUpdates(this.filteredPackages, this.packageGraph, this.execOpts, this.options)
      );
      chain = chain.then(updates => {
        const updated = new Set(updates.map(({ pkg }) => pkg.name));

        this.filteredPackages = this.filteredPackages.filter(pkg => updated.has(pkg.name));
      });
    }

    if (this.options.includeFilteredDependents) {
      chain = chain.then(() => {
        this.filteredPackages = this.packageGraph.addDependents(this.filteredPackages);
      });
    }

    if (this.options.includeFilteredDependencies) {
      chain = chain.then(() => {
        this.filteredPackages = this.packageGraph.addDependencies(this.filteredPackages);
      });
    }

    return chain;
  }

  runCommand() {
    return Promise.resolve()
      .then(() => this.initialize())
      .then(proceed => {
        if (proceed !== false) {
          return this.execute();
        }
        // early exits set their own exitCode (if non-zero)
      });
  }

  initialize() {
    throw new ValidationError(this.name, "initialize() needs to be implemented.");
  }

  execute() {
    throw new ValidationError(this.name, "execute() needs to be implemented.");
  }
}

module.exports = Command;


@lerna-test/git-tag

A local test helper


"use strict";

const ChildProcessUtilities = require("@lerna/child-process");
const Command = require("@lerna/command");
const batchPackages = require("@lerna/batch-packages");
const runParallelBatches = require("@lerna/run-parallel-batches");
const ValidationError = require("@lerna/validation-error");

module.exports = factory;

function factory(argv) {
  return new ExecCommand(argv);
}

class ExecCommand extends Command {
  get requiresGit() {
    return false;
  }

  initialize() {
    const dashedArgs = this.options["--"] || [];

    this.command = this.options.cmd || dashedArgs.shift();
    this.args = (this.options.args || []).concat(dashedArgs);

    if (!this.command) {
      throw new ValidationError("ENOCOMMAND", "A command to execute is required");
    }

    this.count = this.filteredPackages.length;

    // inverted boolean options
    this.bail = this.options.bail !== false;
    this.prefix = this.options.prefix !== false;

    // accessing properties of process.env can be expensive,
    // so cache it here to reduce churn during tighter loops
    this.env = Object.assign({}, process.env);

    this.batchedPackages = this.toposort
      ? batchPackages(this.filteredPackages, this.options.rejectCycles)
      : [this.filteredPackages];
  }

  execute() {
    if (this.options.parallel) {
      return this.runCommandInPackagesParallel();
    }

    const runner = this.options.stream
      ? pkg => this.runCommandInPackageStreaming(pkg)
      : pkg => this.runCommandInPackageCapturing(pkg);

    return runParallelBatches(this.batchedPackages, this.concurrency, runner).then(() => {
      this.logger.success(
        "exec",
        "Executed command in %d %s: %j",
        this.count,
        this.count === 1 ? "package" : "packages",
        [this.command].concat(this.args).join(" ")
      );
    });
  }

  getOpts(pkg) {
    return {
      cwd: pkg.location,
      shell: true,
      extendEnv: false,
      env: Object.assign({}, this.env, {
        LERNA_PACKAGE_NAME: pkg.name,
        LERNA_ROOT_PATH: this.project.rootPath,
      }),
      reject: this.bail,
      pkg,
    };
  }

  runCommandInPackagesParallel() {
    this.logger.info(
      "exec",
      "in %d %s: %j",
      this.count,
      this.count === 1 ? "package" : "packages",
      [this.command].concat(this.args).join(" ")
    );

    return Promise.all(this.filteredPackages.map(pkg => this.runCommandInPackageStreaming(pkg)));
  }

  runCommandInPackageStreaming(pkg) {
    return ChildProcessUtilities.spawnStreaming(
      this.command,
      this.args,
      this.getOpts(pkg),
      this.prefix && pkg.name
    );
  }

  runCommandInPackageCapturing(pkg) {
    return ChildProcessUtilities.spawn(this.command, this.args, this.getOpts(pkg));
  }
}

module.exports.ExecCommand = ExecCommand;


keep keep keep keep add keep keep keep keep keep

import Vue from 'vue';
import { parse } from 'flatted';

import axios, { AxiosRequestConfig } from 'axios';
import {
	IActivationError,
	ICredentialsDecryptedResponse,
	ICredentialsResponse,
	IExecutionsCurrentSummaryExtended,
	IExecutionDeleteFilter,
	IExecutionPushResponse,
	IExecutionResponse,
	IExecutionFlattedResponse,
	IExecutionsListResponse,
	IExecutionsStopData,
	IN8nUISettings,
	IStartRunData,
	IWorkflowDb,
	IWorkflowShortResponse,
	IRestApi,
	IWorkflowData,
	IWorkflowDataUpdate,
} from '@/Interface';
import {
	ICredentialsDecrypted,
	ICredentialType,
	IDataObject,
	INodeCredentials,
	INodePropertyOptions,
	INodeTypeDescription,
} from 'n8n-workflow';

/**
 * Unflattens the Execution data.
 *
 * @export
 * @param {IExecutionFlattedResponse} fullExecutionData The data to unflatten
 * @returns {IExecutionResponse}
 */
function unflattenExecutionData (fullExecutionData: IExecutionFlattedResponse): IExecutionResponse {
	// Unflatten the data
	const returnData: IExecutionResponse = {
		...fullExecutionData,
		workflowData: fullExecutionData.workflowData as IWorkflowDb,
		data: parse(fullExecutionData.data),
	};

	returnData.finished = returnData.finished ? returnData.finished : false;

	if (fullExecutionData.id) {
		returnData.id = fullExecutionData.id;
	}

	return returnData;
}

export class ResponseError extends Error {
	// The HTTP status code of response
	httpStatusCode?: number;

	// The error code in the resonse
	errorCode?: number;

	// The stack trace of the server
	serverStackTrace?: string;

	/**
	 * Creates an instance of ResponseError.
	 * @param {string} message The error message
	 * @param {number} [errorCode] The error code which can be used by frontend to identify the actual error
	 * @param {number} [httpStatusCode] The HTTP status code the response should have
	 * @param {string} [stack] The stack trace
	 * @memberof ResponseError
	 */
	constructor (message: string, errorCode?: number, httpStatusCode?: number, stack?: string) {
		super(message);
		this.name = 'ResponseError';

		if (errorCode) {
			this.errorCode = errorCode;
		}
		if (httpStatusCode) {
			this.httpStatusCode = httpStatusCode;
		}
		if (stack) {
			this.serverStackTrace = stack;
		}
	}
}

export const restApi = Vue.extend({
	methods: {
		restApi (): IRestApi {
			const self = this;
			return {
				async makeRestApiRequest (method: string, endpoint: string, data?: IDataObject): Promise<any> { // tslint:disable-line:no-any
					try {
						const options: AxiosRequestConfig = {
							method,
							url: endpoint,
							baseURL: self.$store.getters.getRestUrl,
							headers: {
								sessionid: self.$store.getters.sessionId,
							},
						};
						if (['PATCH', 'POST', 'PUT'].includes(method)) {
							options.data = data;
						} else {
							options.params = data;
						}

						const response = await axios.request(options);
						return response.data.data;
					} catch (error) {
						if (error.message === 'Network Error') {
							throw new ResponseError('API-Server can not be reached. It is probably down.');
						}

						const errorResponseData = error.response.data;
						if (errorResponseData !== undefined && errorResponseData.message !== undefined) {
							throw new ResponseError(errorResponseData.message, errorResponseData.code, error.response.status, errorResponseData.stack);
						}

						throw error;
					}
				},
				getActiveWorkflows: (): Promise<string[]> => {
					return self.restApi().makeRestApiRequest('GET', `/active`);
				},
				getActivationError: (id: string): Promise<IActivationError | undefined> => {
					return self.restApi().makeRestApiRequest('GET', `/active/error/${id}`);
				},
				getCurrentExecutions: (filter: object): Promise<IExecutionsCurrentSummaryExtended[]> => {
					let sendData = {};
					if (filter) {
						sendData = {
							filter,
						};
					}
					return self.restApi().makeRestApiRequest('GET', `/executions-current`, sendData);
				},
				stopCurrentExecution: (executionId: string): Promise<IExecutionsStopData> => {
					return self.restApi().makeRestApiRequest('POST', `/executions-current/${executionId}/stop`);
				},
				getSettings: (): Promise<IN8nUISettings> => {
					return self.restApi().makeRestApiRequest('GET', `/settings`);
				},

				// Returns all node-types
				getNodeTypes: (): Promise<INodeTypeDescription[]> => {
					return self.restApi().makeRestApiRequest('GET', `/node-types`);
				},

				// Returns all the parameter options from the server
				getNodeParameterOptions: (nodeType: string, methodName: string, credentials?: INodeCredentials): Promise<INodePropertyOptions[]> => {
					const sendData = {
						nodeType,
						methodName,
						credentials,
						currentNodeParameters: this.$store.getters.activeNode.parameters,
					};
					return self.restApi().makeRestApiRequest('GET', '/node-parameter-options', sendData);
				},

				// Removes a test webhook
				removeTestWebhook: (workflowId: string): Promise<boolean> => {
					return self.restApi().makeRestApiRequest('DELETE', `/test-webhook/${workflowId}`);
				},

				// Execute a workflow
				runWorkflow: async (startRunData: IStartRunData): Promise<IExecutionPushResponse> => {
					return self.restApi().makeRestApiRequest('POST', `/workflows/run`, startRunData);
				},

				// Creates new credentials
				createNewWorkflow: (sendData: IWorkflowData): Promise<IWorkflowDb> => {
					return self.restApi().makeRestApiRequest('POST', `/workflows`, sendData);
				},

				// Updates an existing workflow
				updateWorkflow: (id: string, data: IWorkflowDataUpdate): Promise<IWorkflowDb> => {
					return self.restApi().makeRestApiRequest('PATCH', `/workflows/${id}`, data);
				},

				// Deletes a workflow
				deleteWorkflow: (name: string): Promise<void> => {
					return self.restApi().makeRestApiRequest('DELETE', `/workflows/${name}`);
				},

				// Returns the workflow with the given name
				getWorkflow: (id: string): Promise<IWorkflowDb> => {
					return self.restApi().makeRestApiRequest('GET', `/workflows/${id}`);
				},

				// Returns all saved workflows
				getWorkflows: (filter?: object): Promise<IWorkflowShortResponse[]> => {
					let sendData;
					if (filter) {
						sendData = {
							filter,
						};
					}
					return self.restApi().makeRestApiRequest('GET', `/workflows`, sendData);
				},

				// Returns a workflow from a given URL
				getWorkflowFromUrl: (url: string): Promise<IWorkflowDb> => {
					return self.restApi().makeRestApiRequest('GET', `/workflows/from-url`, { url });
				},

				// Creates a new workflow
				createNewCredentials: (sendData: ICredentialsDecrypted): Promise<ICredentialsResponse> => {
					return self.restApi().makeRestApiRequest('POST', `/credentials`, sendData);
				},

				// Deletes a credentials
				deleteCredentials: (id: string): Promise<void> => {
					return self.restApi().makeRestApiRequest('DELETE', `/credentials/${id}`);
				},

				// Updates existing credentials
				updateCredentials: (id: string, data: ICredentialsDecrypted): Promise<ICredentialsResponse> => {
					return self.restApi().makeRestApiRequest('PATCH', `/credentials/${id}`, data);
				},

				// Returns the credentials with the given id
				getCredentials: (id: string, includeData?: boolean): Promise<ICredentialsDecryptedResponse | ICredentialsResponse | undefined> => {
					let sendData;
					if (includeData) {
						sendData = {
							includeData,
						};
					}
					return self.restApi().makeRestApiRequest('GET', `/credentials/${id}`, sendData);
				},

				// Returns all saved credentials
				getAllCredentials: (filter?: object): Promise<ICredentialsResponse[]> => {
					let sendData;
					if (filter) {
						sendData = {
							filter,
						};
					}

					return self.restApi().makeRestApiRequest('GET', `/credentials`, sendData);
				},

				// Returns all credential types
				getCredentialTypes: (): Promise<ICredentialType[]> => {
					return self.restApi().makeRestApiRequest('GET', `/credential-types`);
				},

				// Returns the execution with the given name
				getExecution: async (id: string): Promise<IExecutionResponse> => {
					const response = await self.restApi().makeRestApiRequest('GET', `/executions/${id}`);
					return unflattenExecutionData(response);
				},

				// Deletes executions
				deleteExecutions: (sendData: IExecutionDeleteFilter): Promise<void> => {
					return self.restApi().makeRestApiRequest('POST', `/executions/delete`, sendData);
				},

				// Returns the execution with the given name
				retryExecution: (id: string, loadWorkflow?: boolean): Promise<boolean> => {
					let sendData;
					if (loadWorkflow === true) {
						sendData = {
							loadWorkflow: true,
						};
					}
					return self.restApi().makeRestApiRequest('POST', `/executions/${id}/retry`, sendData);
				},

				// Returns all saved executions
				// TODO: For sure needs some kind of default filter like last day, with max 10 results, ...
				getPastExecutions: (filter: object, limit: number, lastId?: string | number): Promise<IExecutionsListResponse> => {
					let sendData = {};
					if (filter) {
						sendData = {
							filter,
							lastId,
							limit,
						};
					}

					return self.restApi().makeRestApiRequest('GET', `/executions`, sendData);
				},

				// Returns all the available timezones
				getTimezones: (): Promise<IDataObject> => {
					return self.restApi().makeRestApiRequest('GET', `/options/timezones`);
				},
			};
		},
	},
});


import { INodeProperties } from "n8n-workflow";

export const issueOpeations = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
			},
		},
		options: [
			{
				name: 'Create',
				value: 'create',
				description: 'Create a new issue',
			},
			{
				name: 'Update',
				value: 'update',
				description: 'Update an issue',
			},
			{
				name: 'Get',
				value: 'get',
				description: 'Get an issue',
			},
			{
				name: 'Changelog',
				value: 'changelog',
				description: 'Get issue changelog',
			},
			{
				name: 'Notify',
				value: 'notify',
				description: 'Creates an email notification for an issue and adds it to the mail queue.',
			},
			{
				name: 'Transitions',
				value: 'transitions',
				description: `Returns either all transitions or a transition that can be performed by the user on an issue, based on the issue's status.`,
			},
			{
				name: 'Delete',
				value: 'delete',
				description: 'Delete an issue',
			},
		],
		default: 'create',
		description: 'The operation to perform.',
	},
] as INodeProperties[];

export const issueFields = [

/* -------------------------------------------------------------------------- */
/*                                issue:create                                */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Project',
		name: 'project',
		type: 'options',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'create'
				]
			},
		},
		typeOptions: {
			loadOptionsMethod: 'getProjects',
		},
		description: 'Project',
	},
	{
		displayName: 'Issue Type',
		name: 'issueType',
		type: 'options',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'create'
				]
			},
		},
		typeOptions: {
			loadOptionsMethod: 'getIssueTypes',
		},
		description: 'Issue Types',
	},
	{
		displayName: 'Summary',
		name: 'summary',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'create',
				],
			},
		},
		default: '',
		description: 'Summary',
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'create',
				],
			},
		},
		options: [
			{
				displayName: 'Parent Issue Key',
				name: 'parentIssueKey',
				type: 'string',
				required: false,
				default: '',
				description: 'Parent Issue Key',
			},
			{
				displayName: 'Labels',
				name: 'labels',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getLabels',
				},
				default: [],
				required : false,
				description: 'Labels',
			},
			{
				displayName: 'Priority',
				name: 'priority',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getPriorities',
				},
				default: [],
				required : false,
				description: 'Priority',
			},
			{
				displayName: 'Assignee',
				name: 'assignee',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getUsers',
				},
				default: [],
				required : false,
				description: 'Assignee',
			},
			{
				displayName: 'Description',
				name: 'description',
				type: 'string',
				default: '',
				required : false,
				description: 'Description',
			},
			{
				displayName: 'Update History',
				name: 'updateHistory',
				type: 'boolean',
				default: false,
				required : false,
				description: `Whether the project in which the issue is created is added to the user's

				Recently viewed project list, as shown under Projects in Jira.`,
			},
		],
	},

/* -------------------------------------------------------------------------- */
/*                                issue:update                                */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Issue Key',
		name: 'issueKey',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'update',
				],
			},
		},
		default: '',
		description: 'Issue Key',
	},
	{
		displayName: 'Update Fields',
		name: 'updateFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'update',
				],
			},
		},
		options: [
			{
				displayName: 'Issue Type',
				name: 'issueType',
				type: 'options',
				required: false,
				typeOptions: {
					loadOptionsMethod: 'getIssueTypes',
				},
				default: '',
				description: 'Issue Types',
			},
			{
				displayName: 'Summary',
				name: 'summary',
				type: 'string',
				required: false,
				default: '',
				description: 'Summary',
			},
			{
				displayName: 'Parent Issue Key',
				name: 'parentIssueKey',
				type: 'string',
				required: false,
				default: '',
				description: 'Parent Issue Key',
			},
			{
				displayName: 'Labels',
				name: 'labels',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getLabels',
				},
				default: [],
				required : false,
				description: 'Labels',
			},
			{
				displayName: 'Priority',
				name: 'priority',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getPriorities',
				},
				default: [],
				required : false,
				description: 'Priority',
			},
			{
				displayName: 'Assignee',
				name: 'assignee',
				type: 'options',
				typeOptions: {
					loadOptionsMethod: 'getUsers',
				},
				default: [],
				required : false,
				description: 'Assignee',
			},
			{
				displayName: 'Description',
				name: 'description',
				type: 'string',
				default: '',
				required : false,
				description: 'Description',
			},
		],
	},

/* -------------------------------------------------------------------------- */
/*                                issue:delete                                */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Issue Key',
		name: 'issueKey',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'delete',
				],
			},
		},
		default: '',
		description: 'Issue Key',
	},
	{
		displayName: 'Delete Subtasks',
		name: 'deleteSubtasks',
		type: 'boolean',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'delete',
				],
			},
		},
		default: false,
		description: 'Delete Subtasks',
	},

/* -------------------------------------------------------------------------- */
/*                                  issue:get                                 */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Issue Key',
		name: 'issueKey',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'get',
				],
			},
		},
		default: '',
		description: 'Issue Key',
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'get',
				],
			},
		},
		options: [
			{
				displayName: 'Fields',
				name: 'fields',
				type: 'string',
				required: false,
				default: '',
				description: `A list of fields to return for the issue.

				This parameter accepts a comma-separated list.

				Use it to retrieve a subset of fields. Allowed values:

				*all Returns all fields.

				*navigable Returns navigable fields.

				Any issue field, prefixed with a minus to exclude.
`
			},
			{
				displayName: 'Fields By Key',
				name: 'fieldsByKey',
				type: 'boolean',
				required: false,
				default: false,
				description: `Indicates whether fields in fields are referenced by keys rather than IDs.

				This parameter is useful where fields have been added by a connect app and a field's key

				may differ from its ID.`,
			},
			{
				displayName: 'Expand',
				name: 'expand',
				type: 'string',
				required: false,
				default: '',
				description: `Use expand to include additional information about the issues in the response.

				This parameter accepts a comma-separated list. Expand options include:

				renderedFields Returns field values rendered in HTML format.

				names Returns the display name of each field.

				schema Returns the schema describing a field type.

				transitions Returns all possible transitions for the issue.

				editmeta Returns information about how each field can be edited.

				changelog Returns a list of recent updates to an issue, sorted by date, starting from the most recent.

				versionedRepresentations Returns a JSON array for each version of a field's value, with the highest number

				representing the most recent version. Note: When included in the request, the fields parameter is ignored.`
			},
			{
				displayName: 'Properties',
				name: 'properties',
				type: 'string',
				required: false,
				default: '',
				description: `A list of issue properties to return for the issue.

				This parameter accepts a comma-separated list. Allowed values:

				*all Returns all issue properties.

				Any issue property key, prefixed with a minus to exclude.

				Examples:

				*all Returns all properties.

				*all,-prop1 Returns all properties except prop1.

				prop1,prop2 Returns prop1 and prop2 properties.

				This parameter may be specified multiple times. For example, properties=prop1,prop2& properties=prop3.`
			},
			{
				displayName: 'Update History',
				name: 'updateHistory',
				type: 'boolean',
				required: false,
				default: false,
				description: `Whether the project in which the issue is created is added to the user's
				Recently viewed project list, as shown under Projects in Jira. This also populates the
				JQL issues search lastViewed field.`,
			},
		]
	},

/* -------------------------------------------------------------------------- */
/*                               issue:changelog                              */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Issue Key',
		name: 'issueKey',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'changelog',
				],
			},
		},
		default: '',
		description: 'Issue Key',
	},
	{
		displayName: 'Return All',
		name: 'returnAll',
		type: 'boolean',
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'changelog',
				],
			},
		},
		default: false,
		description: 'If all results should be returned or only up to a given limit.',
	},
	{
		displayName: 'Limit',
		name: 'limit',
		type: 'number',
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'changelog',
				],
				returnAll: [
					false,
				],
			},
		},
		typeOptions: {
			minValue: 1,
			maxValue: 100,
		},
		default: 50,
		description: 'How many results to return.',
	},
/* -------------------------------------------------------------------------- */
/*                                issue:notify                                */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Issue Key',
		name: 'issueKey',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'notify',
				],
			},
		},
		default: '',
		description: 'Issue Key',
	},
	{
		displayName: 'JSON Parameters',
		name: 'jsonParameters',
		type: 'boolean',
		default: false,
		description: '',
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'notify',
				],
			},
		},
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'notify',
				],
			},
		},
		options: [
			{
				displayName: 'Subject',
				name: 'subject',
				type: 'string',
				required: false,
				default: '',
				description: `The subject of the email notification for the issue. If this is not specified,
				then the subject is set to the issue key and summary.`
			},
			{
				displayName: 'Text Body',
				name: 'textBody',
				type: 'string',
				typeOptions: {
					alwaysOpenEditWindow: true,
				},
				required: false,
				default: '',
				description: `The subject of the email notification for the issue.
				If this is not specified, then the subject is set to the issue key and summary.`
			},
			{
				displayName: 'HTML Body',
				name: 'htmlBody',
				type: 'string',
				typeOptions: {
					alwaysOpenEditWindow: true,
				},
				required: false,
				default: '',
				description: 'The HTML body of the email notification for the issue.',
			},
		],
	},
	{
		displayName: 'Notification Recipients',
		name: 'notificationRecipientsUi',
		type: 'fixedCollection',
		placeholder: 'Add Recipients',
		typeOptions: {
			multipleValues: false,
		},
		description: 'The recipients of the email notification for the issue.',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'notify',
				],
				jsonParameters: [
					false,
				],
			},
		},
		options: [
			{
				name: 'notificationRecipientsValues',
				displayName: 'Recipients',
				values: [
					{
						displayName: 'Reporter',
						name: 'reporter',
						type: 'boolean',
						description: `Indicates whether the notification should be sent to the issue's reporter.`,
						default: false,
					},
					{
						displayName: 'Assignee',
						name: 'assignee',
						type: 'boolean',
						default: false,
						description: `Indicates whether the notification should be sent to the issue's assignees.`,
					},
					{
						displayName: 'Watchers',
						name: 'watchers',
						type: 'boolean',
						default: false,
						description: `Indicates whether the notification should be sent to the issue's assignees.`,
					},
					{
						displayName: 'Voters',
						name: 'voters',
						type: 'boolean',
						default: false,
						description: `Indicates whether the notification should be sent to the issue's voters.`,
					},
					{
						displayName: 'Users',
						name: 'users',
						type: 'multiOptions',
						typeOptions: {
							loadOptionsMethod: 'getUsers',
						},
						default: [],
						description: `List of users to receive the notification.`,
					},
					{
						displayName: 'Groups',
						name: 'groups',
						type: 'multiOptions',
						typeOptions: {
							loadOptionsMethod: 'getGroups',
						},
						default: [],
						description: `List of groups to receive the notification.`,
					},
				]

			}
		]
	},
	{
		displayName: 'Notification Recipients',
		name: 'notificationRecipientsJson',
		type: 'json',
		typeOptions: {
			alwaysOpenEditWindow: true,
		},
		required: false,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'notify',
				],
				jsonParameters: [
					true,
				]
			},
		},
		default: '',
		description: 'The recipients of the email notification for the issue.',
	},
	{
		displayName: 'Notification Recipients Restrictions',
		name: 'notificationRecipientsRestrictionsUi',
		type: 'fixedCollection',
		placeholder: 'Add Recipients Restriction',
		typeOptions: {
			multipleValues: false,
		},
		description: 'Restricts the notifications to users with the specified permissions.',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'notify',
				],
				jsonParameters: [
					false,
				],
			},
		},
		options: [
			{
				name: 'notificationRecipientsRestrictionsValues',
				displayName: 'Recipients Restrictions',
				values: [
					{
						displayName: 'Users',
						name: 'users',
						type: 'multiOptions',
						typeOptions: {
							loadOptionsMethod: 'getUsers',
						},
						default: [],
						description: `List of users to receive the notification.`,
					},
					{
						displayName: 'Groups',
						name: 'groups',
						type: 'multiOptions',
						typeOptions: {
							loadOptionsMethod: 'getGroups',
						},
						default: [],
						description: `List of groups to receive the notification.`,
					},
				]

			}
		]
	},
	{
		displayName: 'Notification Recipients Restrictions',
		name: 'notificationRecipientsRestrictionsJson',
		type: 'json',
		typeOptions: {
			alwaysOpenEditWindow: true,
		},
		required: false,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'notify',
				],
				jsonParameters: [
					true,
				]
			},
		},
		default: '',
		description: 'Restricts the notifications to users with the specified permissions.',
	},

/* -------------------------------------------------------------------------- */
/*                              issue:transitions                             */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'Issue Key',
		name: 'issueKey',
		type: 'string',
		required: true,
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'transitions',
				],
			},
		},
		default: '',
		description: 'Issue Key',
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'issue',
				],
				operation: [
					'transitions',
				],
			},
		},
		options: [
			{
				displayName: 'Expand',
				name: 'expand',
				type: 'string',
				required: false,
				default: '',
				description: `Use expand to include additional information about transitions in the response.

				 This parameter accepts transitions.fields, which returns information about the fields in the

				 transition screen for each transition. Fields hidden from the screen are not returned. Use this

				 information to populate the fields and update fields in Transition issue.`
			},
			{
				displayName: 'Transition ID',
				name: 'transitionId',
				type: 'string',
				required: false,
				default: '',
				description: 'The ID of the transition.',
			},
			{
				displayName: 'Skip Remote Only Condition',
				name: 'skipRemoteOnlyCondition',
				type: 'boolean',
				required: false,
				default: false,
				description: `Indicates whether transitions with the condition Hide

				From User Condition are included in the response.`,
			},
		],
	},
] as INodeProperties[];


import {
	BINARY_ENCODING,
	IExecuteFunctions,
} from 'n8n-core';
import {
	IDataObject,
	INodeTypeDescription,
	INodeExecutionData,
	INodeType,
} from 'n8n-workflow';

import { parseString } from 'xml2js';
import { OptionsWithUri } from 'request';

export class NextCloud implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'NextCloud',
		name: 'nextCloud',
		icon: 'file:nextcloud.png',
		group: ['input'],
		version: 1,
		subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
		description: 'Access data on NextCloud',
		defaults: {
			name: 'NextCloud',
			color: '#22BB44',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'nextCloudApi',
				required: true,
			}
		],
		properties: [
			{
				displayName: 'Resource',
				name: 'resource',
				type: 'options',
				options: [
					{
						name: 'File',
						value: 'file',
					},
					{
						name: 'Folder',
						value: 'folder',
					},
				],
				default: 'file',
				description: 'The resource to operate on.',
			},

			// ----------------------------------
			//         operations
			// ----------------------------------
			{
				displayName: 'Operation',
				name: 'operation',
				type: 'options',
				displayOptions: {
					show: {
						resource: [
							'file',
						],
					},
				},
				options: [
					{
						name: 'Copy',
						value: 'copy',
						description: 'Copy a file',
					},
					{
						name: 'Delete',
						value: 'delete',
						description: 'Delete a file',
					},
					{
						name: 'Download',
						value: 'download',
						description: 'Download a file',
					},
					{
						name: 'Move',
						value: 'move',
						description: 'Move a file',
					},
					{
						name: 'Upload',
						value: 'upload',
						description: 'Upload a file',
					},
				],
				default: 'upload',
				description: 'The operation to perform.',
			},

			{
				displayName: 'Operation',
				name: 'operation',
				type: 'options',
				displayOptions: {
					show: {
						resource: [
							'folder',
						],
					},
				},
				options: [
					{
						name: 'Copy',
						value: 'copy',
						description: 'Copy a folder',
					},
					{
						name: 'Create',
						value: 'create',
						description: 'Create a folder',
					},
					{
						name: 'Delete',
						value: 'delete',
						description: 'Delete a folder',
					},
					{
						name: 'List',
						value: 'list',
						description: 'Return the files and folders in a given folder',
					},
					{
						name: 'Move',
						value: 'move',
						description: 'Move a folder',
					},
				],
				default: 'create',
				description: 'The operation to perform.',
			},

			// ----------------------------------
			//         file
			// ----------------------------------

			// ----------------------------------
			//         file/folder:copy
			// ----------------------------------
			{
				displayName: 'From Path',
				name: 'path',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'copy'
						],
						resource: [
							'file',
							'folder',
						],
					},
				},
				placeholder: '/invoices/original.txt',
				description: 'The path of file or folder to copy.',
			},
			{
				displayName: 'To Path',
				name: 'toPath',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'copy'
						],
						resource: [
							'file',
							'folder',
						],
					},
				},
				placeholder: '/invoices/copy.txt',
				description: 'The destination path of file or folder.',
			},

			// ----------------------------------
			//         file/folder:delete
			// ----------------------------------
			{
				displayName: 'Delete Path',
				name: 'path',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'delete'
						],
						resource: [
							'file',
							'folder',
						],
					},
				},
				placeholder: 'invoices/2019/invoice_1.pdf',
				description: 'The path to delete. Can be a single file or a whole folder.',
			},

			// ----------------------------------
			//         file/folder:move
			// ----------------------------------
			{
				displayName: 'From Path',
				name: 'path',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'move'
						],
						resource: [
							'file',
							'folder',
						],
					},
				},
				placeholder: '/invoices/old_name.txt',
				description: 'The path of file or folder to move.',
			},
			{
				displayName: 'To Path',
				name: 'toPath',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'move'
						],
						resource: [
							'file',
							'folder',
						],
					},
				},
				placeholder: '/invoices/new_name.txt',
				description: 'The new path of file or folder.',
			},

			// ----------------------------------
			//         file:download
			// ----------------------------------
			{
				displayName: 'File Path',
				name: 'path',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'download'
						],
						resource: [
							'file',
						],
					},
				},
				placeholder: 'invoices/2019/invoice_1.pdf',
				description: 'The file path of the file to download. Has to contain the full path.',
			},
			{
				displayName: 'Binary Property',
				name: 'binaryPropertyName',
				type: 'string',
				default: 'data',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'download'
						],
						resource: [
							'file',
						],
					},
				},
				description: 'Name of the binary property to which to
write the data of the read file.',
			},

			// ----------------------------------
			//         file:upload
			// ----------------------------------
			{
				displayName: 'File Path',
				name: 'path',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'upload'
						],
						resource: [
							'file',
						],
					},
				},
				placeholder: 'invoices/2019/invoice_1.pdf',
				description: 'The file path of the file to upload. Has to contain the full path. The parent folder has to exist. Existing files get overwritten.',
			},
			{
				displayName: 'Binary Data',
				name: 'binaryDataUpload',
				type: 'boolean',
				default: false,
				required: true,
				displayOptions: {
					show: {
						operation: [
							'upload'
						],
						resource: [
							'file',
						],
					},
				},
				description: '',
			},
			{
				displayName: 'File Content',
				name: 'fileContent',
				type: 'string',
				default: '',
				displayOptions: {
					show: {
						binaryDataUpload: [
							false
						],
						operation: [
							'upload'
						],
						resource: [
							'file',
						],
					},

				},
				placeholder: '',
				description: 'The text content of the file to upload.',
			},
			{
				displayName: 'Binary Property',
				name: 'binaryPropertyName',
				type: 'string',
				default: 'data',
				required: true,
				displayOptions: {
					show: {
						binaryDataUpload: [
							true
						],
						operation: [
							'upload'
						],
						resource: [
							'file',
						],
					},

				},
				placeholder: '',
				description: 'Name of the binary property which contains
the data for the file to be uploaded.',
			},

			// ----------------------------------
			//         folder
			// ----------------------------------

			// ----------------------------------
			//         folder:create
			// ----------------------------------
			{
				displayName: 'Folder',
				name: 'path',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						operation: [
							'create'
						],
						resource: [
							'folder',
						],
					},
				},
				placeholder: 'invoices/2019',
				description: 'The folder to create. The parent folder has to exist.',
			},

			// ----------------------------------
			//         folder:list
			// ----------------------------------
			{
				displayName: 'Folder Path',
				name: 'path',
				type: 'string',
				default: '',
				displayOptions: {
					show: {
						operation: [
							'list'
						],
						resource: [
							'folder',
						],
					},
				},
				placeholder: 'invoices/2019/',
				description: 'The path of which to list the content.',
			},

		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData().slice();
		const returnData: IDataObject[] = [];

		const credentials = this.getCredentials('nextCloudApi');

		if (credentials === undefined) {
			throw new Error('No credentials got returned!');
		}

		const resource = this.getNodeParameter('resource', 0) as string;
		const operation = this.getNodeParameter('operation', 0) as string;

		let endpoint = '';
		let requestMethod = '';

		let body: string | Buffer = '';
		const headers: IDataObject = {};

		for (let i = 0; i < items.length; i++) {

			if (resource === 'file') {
				if (operation === 'download') {
					// ----------------------------------
					//         download
					// ----------------------------------

					requestMethod = 'GET';
					endpoint = this.getNodeParameter('path', i) as string;

				} else if (operation === 'upload') {
					// ----------------------------------
					//         upload
					// ----------------------------------

					requestMethod = 'PUT';
					endpoint = this.getNodeParameter('path', i) as string;

					if (this.getNodeParameter('binaryDataUpload', i) === true) {
						// Is binary file to upload
						const item = items[i];

						if (item.binary === undefined) {
							throw new Error('No binary data exists on item!');
						}

						const propertyNameUpload = this.getNodeParameter('binaryPropertyName', i) as string;

						if (item.binary[propertyNameUpload] === undefined) {
							throw new Error(`No binary data property "${propertyNameUpload}" does not exists on item!`);
						}

						body = Buffer.from(item.binary[propertyNameUpload].data, BINARY_ENCODING);
					} else {
						// Is text file
						body = this.getNodeParameter('fileContent', i) as string;
					}
				}
			} else if (resource === 'folder') {
				if (operation === 'create') {
					// ----------------------------------
					//         create
					// ----------------------------------

					requestMethod = 'MKCOL';
					endpoint = this.getNodeParameter('path', i) as string;

				} else if (operation === 'list') {
					// ----------------------------------
					//         list
					// ----------------------------------

					requestMethod = 'PROPFIND';
					endpoint = this.getNodeParameter('path', i) as string;

				}
			}

			if (['file', 'folder'].includes(resource)) {
				if (operation === 'copy') {
					// ----------------------------------
					//         copy
					// ----------------------------------

					requestMethod = 'COPY';
					endpoint = this.getNodeParameter('path', i) as string;
					const toPath = this.getNodeParameter('toPath', i) as string;
					headers.Destination = `${credentials.webDavUrl}/${encodeURI(toPath)}`;

				} else if (operation === 'delete') {
					// ----------------------------------
					//         delete
					// ----------------------------------

					requestMethod = 'DELETE';
					endpoint = this.getNodeParameter('path', i) as string;

				} else if (operation === 'move') {
					// ----------------------------------
					//         move
					// ----------------------------------

					requestMethod = 'MOVE';
					endpoint = this.getNodeParameter('path', i) as string;
					const toPath = this.getNodeParameter('toPath', i) as string;
					headers.Destination = `${credentials.webDavUrl}/${encodeURI(toPath)}`;

				}
			} else {
				throw new Error(`The resource "${resource}" is not known!`);
			}

			// Make sure that the webdav URL does never have a trailing slash because
			// one gets added always automatically
			let webDavUrl = credentials.webDavUrl as string;
			if (webDavUrl.slice(-1) === '/') {
				webDavUrl = webDavUrl.slice(0, -1);
			}

			const options: OptionsWithUri = {
				auth: {
					user: credentials.user as string,
					pass: credentials.password as string,
				},
				headers,
				method: requestMethod,
				body,
				qs: {},
				uri: `${credentials.webDavUrl}/${encodeURI(endpoint)}`,
				json: false,
			};

			if (resource === 'file' && operation === 'download') {
				// Return the data as a buffer
				options.encoding = null;
			}

			const responseData = await this.helpers.request(options);

			if (resource === 'file' && operation === 'download') {

				const newItem: INodeExecutionData = {
					json: items[i].json,
					binary: {},
				};

				if (items[i].binary !== undefined) {
					// Create a shallow copy of the binary data so that the old
					// data references which do not get changed still stay behind
					// but the incoming data does not get changed.
					Object.assign(newItem.binary, items[i].binary);
				}

				items[i] = newItem;

				const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;

				items[i].binary![binaryPropertyName] = await this.helpers.prepareBinaryData(responseData, endpoint);
			} else if (resource === 'folder' && operation === 'list') {

				const jsonResponseData: IDataObject = await new Promise((resolve, reject) => {
					parseString(responseData, { explicitArray: false }, (err, data) => {
						if (err) {
							return reject(err);
						}
						resolve(data as IDataObject);
					});
				});

				const propNames: { [key: string]: string } = {
					'd:getlastmodified': 'lastModified',
					'd:getcontentlength': 'contentLength',
					'd:getcontenttype': 'contentType',
				};

				if (jsonResponseData['d:multistatus'] !== undefined &&
					jsonResponseData['d:multistatus'] !== null &&
					(jsonResponseData['d:multistatus'] as IDataObject)['d:response'] !== undefined &&
					(jsonResponseData['d:multistatus'] as IDataObject)['d:response'] !== null) {
					let skippedFirst = false;

					// @ts-ignore
					for (const item of jsonResponseData['d:multistatus']['d:response']) {
						if (skippedFirst === false) {
							skippedFirst = true;
							continue;
						}
						const newItem: IDataObject = {};

						newItem.path = item['d:href'].slice(19);

						const props = item['d:propstat'][0]['d:prop'];

						// Get the props and save them under a proper name
						for (const propName of Object.keys(propNames)) {
							if (props[propName] !== undefined) {
								newItem[propNames[propName]] = props[propName];
							}
						}

						if (props['d:resourcetype'] === '') {
							newItem.type = 'file';
						} else {
							newItem.type = 'folder';
						}
						newItem.eTag = props['d:getetag'].slice(1, -1);

						returnData.push(newItem as IDataObject);
					}
				}
			} else {
				returnData.push(responseData as IDataObject);
			}
		}

		if (resource === 'file' && operation === 'download') {
			// For file downloads the files get attached to the existing items
			return this.prepareOutputData(items);
		} else  {
			// For all other ones does the output get replaced
			return [this.helpers.returnJsonArray(returnData)];
		}
	}
}


import {
	ICredentialType,
	NodePropertyTypes,
} from 'n8n-workflow';

export class TodoistApi implements ICredentialType {
	name = 'todoistApi';
	displayName = 'Todoist API';
	properties = [
		{
			displayName: 'API Key',
			name: 'apiKey',
			type: 'string' as NodePropertyTypes,
			default: '',
		},
	];
}


getPastExecutions(filter: object, limit: number, lastId?: string | number): Promise<IExecutionsListResponse>;

makeRestApiRequest(method: string, endpoint: string, data?: any): Promise<any>; // tslint:disable-line:no-any

getNodeParameterOptions(nodeType: string, methodName: string, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;

updateWorkflow(id: string, data: IWorkflowDataUpdate): Promise<IWorkflowDb>;

keep keep keep keep keep replace keep keep keep keep

getNodeParameterOptions(nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;