UNPKG

datafire

Version:

[![Travis][travis-image]][travis-link] [![Downloads][downloads-image]][npm-link] [![NPM version][npm-image]][npm-link] [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://www.npmjs.com/package/datafire) <!--[![Dependency status][deps-i

393 lines (352 loc) 1.35 MB
webpackJsonp(["polyfills"],{ /***/ "./config.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; module.exports = { "favicon": "assets/logo-no-text.png", "routes": { "/": { "ui": "documentation", "title": "DataFire", "navigation": [{ "title": "Introduction", "markdownFile": "../README.md", "markdownSection": ["DataFire", "Installation", "Sample Projects"], "noSubsections": true, "expand": true, "children": [{ "markdownFile": "markdown/Basic Concepts.md", "title": "Basic Concepts", "contents": "## Basic Concepts\n\nThere are three basic concepts in DataFire: **Integrations**, **Actions**, and **Triggers**\n\n### Integrations\n\nIntegrations are third-party apps you can connect to your project. Often integrations involve a REST API,\nsuch as the GitHub or Slack integrations. However, nearly any type of service can be wrapped with a\nDataFire integration; for example, we have integrations for making HTTP requests, connecting to MongoDB,\nand serving websites.\n\nMost integrations will ask for some form of credentials or configuration. Use the `datafire authenticate`\ncommand to add credentials to your project, or visit the *Integrations* tab in your project on DataFire.io\n\nEach integration comes with a set of actions (see below). For instance, the Slack integration has one action\nfor listing all available channels, and another action for posting a new message.\n\n[Read more about integrations](/Integrations)\n\n### Actions\n\nActions contain the logic that runs your DataFire project. Each integration provides a set of actions,\nand you can build new actions using NodeJS.\n\nEach action needs, at minimum, a `handler`. This contains the logic that runs when the action is called.\nAdditionally, each action can specify an `inputSchema` and `outputSchema` to tell the caller exactly what\nto expect. Any input will be validated against the schema before the handler is called.\n\n[Read more about actions](/Actions)\n\n### Triggers\n\nTriggers tell DataFire when and how to run your actions. There are three types of triggers:\n\n* **tasks** run your actions on a schedule, like *every ten minutes* or *every sunday at 11AM*\n* **paths** put your actions behind a URL, so it can be triggered over the web. For example, `GET /pets` or `POST /payment`\n* **tests** allow you to run your action manually\n\nThe trigger can also specify any input for the action and which accounts to use.\n\n[Read more about triggers](/Triggers)\n" }, { "markdownFile": "markdown/Hello World.md", "title": "Hello World", "contents": "# DataFire \"Hello World\" Project\nThis is a small sample project to demonstrate DataFire's features. It will create\na single Action, which takes in a name, and outputs a greeting. We'll also link\nthat action to an HTTP endpoint and a scheduled task.\n\n## The Basics\n\n### Action\nWe'll create our action in `hello.js`.\n\n#### hello.js\n```js\nvar datafire = require('datafire');\nmodule.exports = new datafire.Action({\n handler: input => \"Hello, world\",\n})\n```\n\n### Trigger\nNow let's create a `GET /hello` API endpoint in `DataFire.yml` that will trigger the Action:\n\n#### DataFire.yml\n```yaml\npaths:\n /hello:\n get:\n action: ./hello.js\n```\n\n### Running\n\nWe can try it out with `datafire serve`\n```bash\ndatafire serve --port 3000 &\n# DataFire listening on port 3000\n\ncurl http://localhost:3000/hello\n# \"Hello, world\"\n\nkill $! # stop the server\n```\n\n## Adding Inputs\nYou can add inputs with [JSON Schema](http://json-schema.org/).\n\n```js\nvar datafire = require('datafire');\nmodule.exports = new datafire.Action({\n handler: input => 'Hello, ' + input.name,\n inputs: [{\n title: 'name',\n type: 'string',\n maxLength: 20,\n pattern: '\\\\w+',\n }],\n})\n```\n\nThen we can run:\n```bash\ndatafire serve --port 3000 &\n# DataFire listening on port 3000\n\ncurl http://localhost:3000/hello?name=world\n# \"Hello, world\"\n\ncurl http://localhost:3000/hello\n# {\"error\": \"Missing required query parameter 'name'\"}\n```\n\n## HTTP Responses\nBy default, DataFire will return your handler's output as JSON with a 200 status\ncode (as well as 404/400/500 errors when appropriate). However, you can specify\ncustom response codes, content types, and bodies in your Action.\n\n```js\nmodule.exports = new datafire.Action({\n inputs: [{title: 'name'}],\n handler: input => {\n if (input.name === 'Voldemort') {\n return new datafire.Response({\n statusCode: 401,\n headers: {'Content-Type': 'text/html'},\n body: \"<h1>Nope.</h1>\",\n });\n } else {\n return \"Hello, \" + input.name;\n }\n }\n})\n```\n\n" }], "contents": "# DataFire\n\n[![Travis][travis-image]][travis-link]\n[![Downloads][downloads-image]][npm-link]\n[![NPM version][npm-image]][npm-link]\n[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://www.npmjs.com/package/datafire)\n<!--[![Dependency status][deps-image]][deps-link]\n[![devDependency status][devdeps-image]][devdeps-link]-->\n<!--[![Code Climate][climate-image]][climate-link]-->\n\n[downloads-image]: https://img.shields.io/npm/dm/datafire.svg\n[twitter-image]: https://img.shields.io/badge/Share-on%20Twitter-blue.svg\n[twitter-link]: https://twitter.com/intent/tweet?text=DataFire%20-%20open+source+integration+framework:&url=http%3A%2F%2Fgithub.com%2FDataFire%2FDataFire\n[gitter-image]: https://img.shields.io/badge/Chat-on%20Gitter-blue.svg\n[gitter-link]: https://gitter.im/DataFire/Lobby\n[npm-image]: https://img.shields.io/npm/v/datafire.svg\n[npm-link]: https://npmjs.org/package/datafire\n[travis-image]: https://travis-ci.org/DataFire/DataFire.svg?branch=master\n[travis-link]: https://travis-ci.org/DataFire/DataFire\n[climate-image]: https://codeclimate.com/github/DataFire/DataFire.png\n[climate-link]: https://codeclimate.com/github/DataFire/DataFire\n[deps-image]: https://img.shields.io/david/DataFire/DataFire.svg\n[deps-link]: https://david-dm.org/DataFire/DataFire\n[devdeps-image]: https://img.shields.io/david/dev/DataFire/DataFire.svg\n[devdeps-link]: https://david-dm.org/DataFire/DataFire#info=devDependencies\n[blog-image]: https://img.shields.io/badge/Read-on%20Medium-blue.svg\n[blog-link]: https://medium.com/datafire-io\n[mail-image]: https://img.shields.io/badge/Subscribe-on%20MailChimp-blue.svg\n[mail-link]: https://eepurl.com/c3t10T\n\nDataFire is an [open source](https://github.com/DataFire/DataFire/blob/master/LICENSE) framework for building and integrating APIs. It\nprovides over [1000 integrations](https://github.com/DataFire/Integrations), including:\n\nAWS &bull; Azure &bull; MongoDB &bull; Slack &bull; GitHub &bull;\nTwilio &bull; Trello &bull; Square &bull;\nGoogle Sheets &bull; Gmail &bull; Heroku\n\nEach integration provides a set of [composable actions](https://docs.datafire.io/Actions). New actions [can be built](https://docs.datafire.io/Introduction/Hello_World) by\ncombining existing actions, JavaScript, and external libraries. They are driven by [JavaScript Promises](https://developers.google.com/web/fundamentals/primers/promises),\nand can be triggered by a URL, on a schedule, or manually.\n\nWant more? [DataFire.io](https://datafire.io) provides a simple interface for building,\nmanaging, and hosting DataFire projects.\n\n[![Share on Twitter][twitter-image]][twitter-link]\n[![Read on Medium][blog-image]][blog-link]\n[![Chat on Gitter][gitter-image]][gitter-link]\n[![Subscribe on MailChimp][mail-image]][mail-link]\n\n\n\n## Installation\n> Be sure to install DataFire both globally and as a project dependency.\n\n```\nnpm install -g datafire\nnpm install --save datafire\n```\n\n\n\n## Sample Projects\n| | | |\n|--|--|--|\n| Create an API backed by Google Sheets | [Repo](https://github.com/DataFire-repos/spreadsheet-base) | [Run on DataFire.io](https://app.datafire.io/projects?baseRepo=https:%2F%2Fgithub.com%2FDataFire-repos%2Fspreadsheet-base) |\n| E-mail yourself news headlines | [Repo](https://github.com/DataFire-flows/headlines) | [Run on DataFire.io](https://app.datafire.io/projects?baseRepo=https:%2F%2Fgithub.com%2FDataFire-flows%2Fheadlines)|\n| Backend for a \"Contact Us\" form | [Repo](https://github.com/DataFire-repos/contact-us-base) | [Run on DataFire.io](https://app.datafire.io/projects?baseRepo=https:%2F%2Fgithub.com%2FDataFire-repos%2Fcontact-us-base) |\n| Sync GitHub issues to a Trello board | [Repo](https://github.com/DataFire-flows/github-issues-to-trello) | [Run on DataFire.io](https://app.datafire.io/projects?baseRepo=https:%2F%2Fgithub.com%2FDataFire-flows%2Fgithub-issues-to-trello) |\n| Create a Spotify playlist from r/listentothis | [Repo](https://github.com/DataFire-flows/listen-to-this) | [Run on DataFire.io](https://app.datafire.io/projects?baseRepo=https:%2F%2Fgithub.com%2FDataFire-flows%2Flisten-to-this) |\n\n" }, { "markdownFile": "markdown/DataFire_yml.md", "title": "DataFire.yml Configuration", "contents": "# DataFire.yml\n\nHere's a sample DataFire.yml that shows all the available fields.\n\n```yaml\noptions:\n cors: true # enable cross-origin requests\n cache: 100 # number of millseconds to cache requests\n bodyLimit: 100kb # maximum size of JSON body for HTTP requests\n\n# Store credentials for different APIs and services.\n# You can also put this (and other fields) in DataFire-accounts.yml, which can be added to your .gitignore\naccounts:\n mongodb_readonly:\n url: https://readonly@database.example.com\n\n# Authorizers will before each of your path triggers (unless disabled),\n# and will populate context.accounts.AUTHORIZER_ID\nauthorizers:\n user:\n action: ./actions/get-user-from-auth-header.js\n\nevents:\n # This action will be called whenever one of your path triggers runs\n http:\n action: ./actions/log-http.js\n\n # This action will be called whenever one of your task triggers runs\n task:\n action: ./actions/log-task.js\n\n # This action will be called whenever one of your path or task triggers fails unexpectedly.\n error:\n action: ./actions/send-alert.js\n\n # This action will be called whenever one of your OAuth tokens is refreshed\n oauth_refresh:\n action: ./actions/update-refresh-token.js\n\n# paths are the URLs served by your project\n# E.g. the first path here will be served at GET http://localhost/hello\npaths:\n # The minimum needed for a path trigger is an action\n /hello:\n get:\n action: ./actions/hello.js\n\n # You can also use actions from an installed integration\n /profile:\n get:\n action: github/users.username.get\n input:\n username: torvalds\n\n# tasks will run on a regular schedule\ntasks:\n send_database_report:\n action: ./actions/send_db_report.js\n schedule: rate(1 day) # You can use 'rate' or 'cron'\n\n# tests can be run manually on the command line\ntests:\n generate_database_report:\n action: ./actions/send_db_report.js\n\n# Use openapi to control fields in the openapi.json generated for your project\nopenapi:\n host: www.example.com\n schemes:\n - https\n info:\n description: An API built with DataFire\n version: 2.0.0-beta\n\n```\n\n## Triggers\n`paths`, `tests`, and `tasks` all represent triggers for your actions. Triggers can have the following fields:\n\n* `action` (required) - the action to call, either local (e.g. `./actions/do_something.js`) or from an integration (e.g. `xkcd/getLatestComic`)\n* `accounts` - Accounts to use for this trigger, overriding project-level accounts\n* `input` - Input to use for this trigger. If not set for a `path` trigger, the `path` will pass query parameters and JSON/form data as input.\n* `errorHandler` - An action to run whenever an unknown error occurs.\n\n`path` triggers also have these fields:\n\n* `cache` - how long to cache the result of this action (server-side), overriding project-level cache\n* `authorizers` - actions to run before this path is called, overriding project-level authorizers\n\n`task` triggers also have these fields:\n\n* `schedule` (required) - When to run the task, using `rate` or `cron`. Rate may be in minutes, hours, days, or months. Cron syntax [can be found here](https://en.wikipedia.org/wiki/Cron)\n* `monitor` - Poll a resource for new data. Your action will only be run when new data appears.\n* `monitor.action` - The action being polled\n* `monitor.array` - The location of an array in the action's output to monitor, e.g. `feed.entries`\n* `monitor.trackBy` - A field in each item of the array to use as an identifier, e.g. `link` or `info.title`\n* `monitor.input` - input to `monitor.action`\n* `monitor.accounts` - accounts for `monitor.action`\n" }, { "markdownFile": "markdown/Integrations.md", "contents": "# Integrations\nOver 800 integrations are available on npm, under the `@datafire` scope.\nYou can view a list of available integrations on [DataFire.io](https://app.datafire.io/integrations)\n\nEach integration comes with a set of actions. For example, the `hacker_news` integration\ncontains the `getStories`, `getItem`, and `getUser` actions.\n\nTo add an integration to your project, run:\n```\nnpm install @datafire/$integration\n```\nFor example, to install the `hacker_news` integration:\n```bash\nnpm install @datafire/hacker_news\n```\n\n## Using Integrations\nAdd an integration to your NodeJS project using `create()`. You can then\ncall its actions using Promises or async/await.\n\n```js\nlet hn = require('@datafire/hacker_news').create();\n```\n\n### Authentication\n> See [Authentication](/Authentication) for the full documentation\n\nYou can pass an account to `create()`:\n\n```js\nlet datafire = require('datafire');\nlet project = datafire.Project.main();\n\nlet github = require('@datafire/github').create(project.accounts.github_alice);\n// or\ngithub = require('@datafire/github').create({\n access_token: \"abcde\",\n});\n\n(async () => {\n\n let user = await github.user.get();\n console.log('Logged in user is ' + user.login);\n\n})();\n```\n\n\n\n### Actions\nEach integration offers a set of actions - each action returns a Promise.\n\n#### With async/await\nWe recommend using NodeJS 7.10 or above, which includes support for `await`.\n\n```js\nlet hn = require('@datafire/hacker_news').create();\n\n(async () => {\n\n let storyIDs = await hn.getStories({storyType: 'top'});\n for (let itemID of storyIDs) {\n let story = await hn.getItem({itemID});\n console.log(story.title, story.url);\n }\n\n})();\n```\n\n#### With Promises\nIf you're using an older version of Node, you can use Promises:\n```js\nlet hn = require('@datafire/hacker_news').create();\n\nhn.getStories({storyType: 'top'})\n .then(storyIDs => {\n return Promise.all(storyIDs.map(itemID => {\n return hn.getItem({itemID});\n }))\n })\n .then(stories => {\n stories.forEach(story => {\n console.log(story.title, story.url);\n })\n })\n\n```\n\n## Versioning\nDataFire integrations use [semver](http://semver.org/) after version 0.1.0. Specifically:\n* PATCH changes will occur for backward-compatible fixes, or documentation changes\n* MINOR changes will occur when new functionality is added\n* MAJOR changes will occur if breaking changes are made\n\n\n## Custom Integrations\n\n> If you'd like to add your API to the DataFire registry, submit a\n> [pull request](https://github.com/DataFire/Integrations).\n\n### Add Integrations by URL\nNew integrations can be added by the URL of an Open API (Swagger) specification or an RSS feed:\n```\ndatafire integrate --rss https://www.reddit.com/.rss\ndatafire integrate --openapi https://api.acme.com/openapi.json --name acme\n```\n\nThis will create the directory `./integrations/$name` with the required information. You can\nreference this integration in NodeJS with:\n\n```js\nvar acme = require('./integrations/acme');\n```\n\n#### API Specification Formats\nYou can also specify `--raml`, `--io_docs`, `--wadl`, or `--api_blueprint`, though you'll need to install\napi-spec-converter:\n```\nnpm install -g api-spec-converter\ndatafire integrate --raml https://raw.githubusercontent.com/raml-apis/Bufferapp/master/api.raml\n```\n\n### Write Integrations in JavaScript\nYou can greate custom integrations using the `datafire.Integration` class.\nHere's an example that creates a filesystem integration:\n\n```js\nvar datafire = require('datafire');\nvar fs = require('fs');\nvar filesystem = module.exports = new Integration({\n id: \"filesystem\",\n title: \"Filesystem\",\n description: \"Gives read access to the filesystem\",\n});\n\nfilesystem.addAction('readFile', new datafire.Action({\n inputs: [{\n name: \"filename\",\n type: \"string\",\n maxLength: 100,\n }],\n handler: input => {\n return new Promise((resolve, reject) => {\n fs.readFile(input.filename, (err, contents) => {\n if (err) reject(err);\n else resolve(contents)\n });\n });\n }\n}));\n```\n\n", "title": "Integrations" }, { "markdownFile": "markdown/Actions.md", "contents": "## Actions\nActions contain the logic that runs your DataFire project. Actions come in two varieties:\n* actions you build yourself in JavaScript, e.g. `./actions/hello.js`\n* actions that are part of an integration e.g. `hacker_news/getUser`\n\nYou can run actions on the command line:\n```bash\ndatafire run hacker_news/getUser -i.username norvig\n```\n\nOr create triggers for them:\n```yaml\npaths:\n /my_profile:\n get:\n action: hacker_news/getUser\n input:\n username: 'norvig'\n```\n\nOr run them in JavaScript:\n```js\nvar hackerNews = require('@datafire/hacker_news').create();\n\n// Using await (requires NodeJS >= v7.10):\n(async function() {\n\n var user = await hackerNews.getUser({username: 'norvig'});\n console.log(user);\n\n})();\n\n// Or with Promises:\nhackerNews.getUser({\n username: 'norvig',\n}).then(user => {\n console.log(user);\n});\n```\n\n### Building Actions\n> [Learn more about building actions](/Introduction/Hello_World)\n\nEvery action has a `handler`, which must return a value or a Promise. Actions can also\nspecify their inputs and outputs (using JSON schema).\nAny inputs will be validated each time the action is run before the handler is called.\n\n", "title": "Actions" }, { "markdownFile": "markdown/Triggers.md", "contents": "## Triggers\n\nTriggers tell DataFire how and when to run your actions. There are three different types of triggers:\n\n* `paths` - URLs like `GET /hello` or `POST /pets/{id}`\n* `tasks` - Jobs that run on a schedule, like \"every hour\", or \"every tuesday at 3pm\"\n* `tests` - Jobs that can be run manually using the `datafire` command line tool\n\nEach trigger must have an `action`, and can also specify the `input` and `accounts` to pass\nto that action.\n\n### Paths\nPaths create URLs that trigger your actions. For example, you can create a URL that returns\nyour GitHub profile:\n```yaml\npaths:\n /github_profile:\n get:\n action: github/users.username.get\n input:\n username: 'torvalds'\n```\n\nIf you don't specify the `input` field, DataFire will automatically pass either query parameters\n(for GET/DELETE/HEAD/OPTIONS) or the JSON body (for POST/PATCH/PUT) from the request to the\naction.\n\nStart serving your paths with:\n```bash\ndatafire serve --port 3000\n```\n\n### Tasks\nYou can schedule tasks in DataFire.yml by specifying a\n[rate or cron expression](http://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html#RateExpressions).\n```yaml\ntasks:\n send_database_report:\n action: ./send-db-report.js\n schedule: rate(1 day) // or cron(0 0 * * * *)\n accounts:\n google_gmail: lucy\n mongodb: mongo_read_only\n```\n\nStart running tasks with:\n```\ndatafire serve --tasks\n```\n\n#### Monitors\nA monitor will poll a particular resource for new items,\nand only run your action if a new item is found. For instance, we can\ncheck for new items on Reddit every 5 minutes:\n\n```yaml\ntasks:\n watch_reddit:\n schedule: rate(5 minutes)\n monitor:\n action: reddit_rss/frontPage\n array: feed.entries\n trackBy: link\n input:\n subreddit: sports\n action: ./post-story-to-slack.js\n```\n\nIn the above example, the action `reddit_rss/frontPage` returns a response like this:\n```json\n{\n \"feed\": {\n \"entries\": [{\n \"link\": \"https://reddit.com/foo/bar\",\n \"title\": \"FooBar\"\n }, {\n \"link\": \"https://reddit.com/baz/quux\",\n \"title\": \"BazQuxx\"\n }]\n }\n}\n```\n\nIn order to track the items in the `entries` array, we have to set two fields:\n* `monitor.array` is set to `feed.entries` - the path we need to take from the top of the JSON response to get at the array.\n* `monitor.trackBy` is set to `link` - this is the field we will use as a unique identifier for each entry.\nOnly entries with a link we haven't seen before will trigger the `post-story-to-slack.js` action.\n\n", "title": "Triggers" }, { "markdownFile": "markdown/Authentication.md", "contents": "# Authentication\n> If you want to try these examples, you can generate a GitHub\n> access token [on the settings page](https://github.com/settings/tokens)\n\n## Passing Credentials to Integrations\nYou can populate accounts programmatically, or using YAML configurations.\n\n### YAML\n\n#### Project-level credentials\nYou can specify the project-level credentials in DataFire.yml or DataFire-accounts.yml.\nWe suggest adding DataFire-accounts.yml to your .gitignore.\n\nYou can also use the `datafire authenticate` command to populate DataFire-accounts.yml.\n\n```yml\naccounts:\n github: # the default account\n access_token: \"abcde\"\n github_alice:\n access_token: \"fghij\"\n github_bob:\n access_token: \"klmno\"\n```\n\nThese accounts will be available in `context.accounts` in all of this project's actions.\n\n#### Trigger-level credentials\nYou can override or add accounts for any trigger:\n\n```yml\npaths:\n /github_profile:\n get:\n action: github/user.get\n accounts:\n github:\n access_token: \"12345\"\n```\n\n### Programmatically\nWhen using an integration, you can `.create()` an instance that will always use the same credentials,\nor change accounts on the fly using `.actions`;\n\n#### .create()\nWhen you use `.create()`, you create an instance of the integration using the given account.\n\n```js\nlet datafire = require('datafire');\nlet project = datafire.Project.main();\nlet github = require('@datafire/github').create(project.accounts.github_alice);\n// or\ngithub = require('@datafire/github').create({\n access_token: \"abcde\",\n});\n\n(async () => {\n\n let user = await github.user.get();\n console.log('Logged in user is ' + user.login);\n\n})();\n```\n\n#### .actions\nWhen you use `.actions`, you can specify the context each time an action is run.\n\n```js\nlet datafire = require('datafire');\nlet project = datafire.Project.main();\nlet github = require('@datafire/github').actions;\n\nlet aliceContext = new datafire.Context({\n accounts: {\n github: project.accounts.github_alice,\n }\n});\n\nlet bobContext = new datafire.Context({\n accounts: {\n github: project.accounts.github_bob,\n }\n});\n\n(async () => {\n\n let alice = await github.user.get(null, aliceContext);\n let bob = await github.user.get(null, bobContext);\n console.log(alice, bob);\n\n})();\n```\n\n## OAuth Clients\nIf you want to add an OAuth client to your project (e.g. to allow users\nto log in with GitHub or Instagram), you can use the `oauthCallback`\naction for that integration. For example:\n\n```yaml\npaths:\n /oauth_callback:\n get:\n action: ./github_callback.js\n accounts:\n github_oauth_provider:\n client_id: abcd\n client_secret: xyz\n```\n\n```js\nlet datafire = require('datafire');\nlet project = datafire.Project.main();\nlet github = require('@datafire/github').actions;\nlet mongodb = require('@datafire/mongodb').create(project.accounts.mongodb);\n\nmodule.exports = new datafire.Action({\n inputSchema: github.oauthCallback.inputSchema,\n handler: async (input, context) => {\n context.accounts.github = project.accounts.github_oauth_provider;\n let authData = await github.oauthCallback.run({code: input.code}, context);\n\n context.accounts.github = authData;\n let githubProfile = await github.user.get({}, context);\n\n let update = await mongodb.update({\n table: 'users',\n query: {\n id: {$eq: context.user.id},\n },\n document: {\n github_access_token: authData.access_token,\n email: githubProfile.email,\n }\n });\n\n return \"Success\";\n }\n});\n```\n\n## Authorizers\nPath triggers can use authorizers to populate `context.accounts` with the results of an action.\n\nFor example:\n```yaml\nauthorizers:\n user:\n action: ./getUserByAPIKey.js\n```\n\n#### ./getUserByAPIKey.js\n```js\nmodule.exports = new datafire.Action({\n handler: (input, context) => {\n let auth = context.request.headers.authorization;\n if (!auth) return new datafire.Response({statusCode: 401});\n return mongodb.findOne({\n query: {\n apiKey: {$eq: auth}\n }\n });\n }\n});\n```\n\nAuthorizers in the top level will be run for every request. You can also override\nauthorizers for individual paths, or disable them by setting them to `null`.\n```yaml\nauthorizers:\n user:\n action: ./getUserByAPIKey.js\npaths:\n /public/status:\n get:\n action: ./getStatus.js\n authorizers:\n user: null\n```\n\n## Require Credentials\nYou can declare a set of credentials that your Action or Integration expects using the\n`security` field. Each security item should specify an integration, or\na set of expected fields.\n\n```js\nlet scrape = new datafire.Action({\n security: {\n github_account: {\n description: \"The github account to read from\",\n integration: 'github'\n },\n database: {\n description: \"Credentials for the database to write to\"\n fields: {\n url: \"The database URL\",\n username: \"Database user\",\n password: \"User's password\"\n }\n }\n },\n handler: (input, context) => {\n // ...\n }\n});\n\nlet context = new datafire.Context({\n accounts: {\n github_account_to_scrape: {\n api_key: 'abcde'\n },\n database: {\n url: '...',\n username: 'foo',\n password: 'bar',\n }\n }\n})\n\nscrape.run({}, context);\n```\n\n## Add Credentials using the CLI\n\nTo add a new account, run\n```\ndatafire authenticate <integration>\n```\nDataFire will prompt you for your credentials, as well as an alias for the account.\n\n### OAuth 2.0\nMany integrations, such as GitHub and Gmail, offer OAuth 2.0\nauthentication. OAuth tokens are more secure than using\nAPI keys or passwords, but are a bit more complicated.\n\n### Genrating OAuth tokens\n\nThe easiest way to generate an OAuth token is on [datafire.io](https://datafire.io).\nHowever, you can also register your own OAuth client with the API provider\nin order to generate tokens yourself.\n\nFirst create a new OAuth\nclient on the integration's website, for example,\n[github.com/settings/developers](https://github.com/settings/developers)\nor\n[console.developers.google.com](https://console.developers.google.com).\n\nIn your application's settings, set `redirect_uri`\nto `http://localhost:3333`.\n\nFinally, run\n```\ndatafire authenticate <integration>\n```\n\nDataFire will prompt you for your `client_id` and `client_secret`,\nthen provide you with a URL to visit to log into your account.\n\nOnce you've logged in, you'll be redirected to localhost:3333, where\nyou should see your `access_token` and, if applicable, `refresh_token`.\n\n", "title": "Authentication" }, { "markdownFile": "markdown/CLI.md", "title": "Command-line Interface", "contents": "> Run `datafire --help` or `datafire <command> --help` for more info\n\n```bash\ndatafire serve --port 3000 # Start API server\ndatafire serve --tasks # Start API server and start running tasks\n\ndatafire list # View installed integrations\ndatafire list -a # View all available integrations\ndatafire list -a -q news # Search for integrations by keyword\n\ndatafire integrate --name petstore --openapi http://petstore.swagger.io/v2/swagger.json\ndatafire integrate --name reddit --rss http://www.reddit.com/.rss\n\ndatafire describe hacker_news # Show info and actions\ndatafire describe hacker_news/getItem # Show action details\n\ndatafire authenticate google_gmail # Store credentials in DataFire-auth.yml\n\n# Run an action\ndatafire run ./sendMessage.js\n\n# Run integration actions with [integration]/[action]\ndatafire run github/repositories.get\n\n# Pass parameters with --input\ndatafire run github/search.repositories.get --input.q java\n\n# Use credentials with --accounts\ndatafire run github/user.get --accounts.github.access_token \"abcde\"\n```\n\n\n" }, { "title": "Cookbook", "autoselect": true, "children": [{ "markdownFile": "markdown/Cookbook.md", "markdownSection": "Server Options", "contents": "## Server Options\nYou can set the following options in DataFire.yml:\n\n* `cors` - enable cross-origin requests\n* `cache` - time to cache requests (in millisecons)\n\n```yaml\noptions:\n cors: true\n cache: 5000\npaths:\n /hello:\n get:\n action: ./hello.js\n```\n\n", "title": "Server Options" }, { "markdownFile": "markdown/Cookbook.md", "markdownSection": "HTTP Requests", "contents": "## HTTP Requests\nYou can use `context.request` to access the original request\n\n```js\nmodule.exports = new datafire.Action({\n handler: (input, context) => {\n if (context.type === 'http') {\n console.log(context.request.method, context.request.path);\n console.log(context.request.query.name);\n console.log(context.request.headers.authorization);\n }\n }\n})\n```\n\n", "title": "HTTP Requests" }, { "markdownFile": "markdown/Cookbook.md", "markdownSection": "NodeJS Express", "contents": "## NodeJS Express\nDataFire uses [Express](https://github.com/expressjs/express) with\n[Swagger Middleware](https://github.com/BigstickCarpet/swagger-express-middleware)\nto handle and validate requests. You can incorporate DataFire into your current Express\nserver using `ProjectServer.getRouter()`:\n\n```js\nlet datafire = require('datafire');\nlet project = datafire.Project.fromDirectory(__dirname); // Dir containing DataFire.yml\nlet projectServer = new datafire.ProjectServer(project);\n\nlet express = require('express');\nlet app = express();\n\napp.get('/hello', (req, res) => {\n res.send('hello world');\n})\n\nprojectServer.getRouter().then(router => {\n app.use('/api', router);\n});\n```\n\n", "title": "NodeJS Express" }, { "markdownFile": "markdown/Cookbook.md", "markdownSection": "Custom Routing and Other Frameworks", "contents": "### Custom Routing and Other Frameworks\nIf you want to handle routing yourself or via another framework, you can still use DataFire actions.\nYou can skip creating a DataFire project (i.e. no need for DataFire.yml), and use actions for input\nvalidation and promise-based integrations:\n\n```js\nlet datafire = require('datafire');\nlet express = require('express');\n\nlet action = new datafire.Action({\n inputs: [{\n title: 'name',\n type: 'string',\n default: 'world',\n minLength: 3,\n }],\n handler: input => \"Hello, \" + input.name,\n})\n\napp.get('/hello', (req, res) => {\n action.run(req.query)\n .then(result => res.json(result));\n})\n```\n\nYou can access an Open API specification for your API at `/openapi.json`. For example:\n\n```bash\ndatafire serve --port 3000 &\ncurl \"http://localhost:3000/openapi.json\" > openapi.json\n```\n\nYou can also use DataFire.yml to specify top-level fields in your Open API,\nsuch as `host`, `schemes`, and `info`:\n\n```yaml\nopenapi:\n host: api.example.com:3000\n schemes:\n -https\n info:\n version: 2.0\n```\n\nThe GitHub action `repos.owner.repo.issues.get` returns an array of\nissues for a particular repository, but it only returns 30 issues at\na time.\n\nTo iterate over all issues, we pass the `page` input, incrementing it\nuntil GitHub returns an empty array.\n\n```js\nlet datafire = require('datafire');\nlet github = require('@datafire/github').actions;\n\nmodule.exports = new datafire.Action({\n handler: input => {\n let allIssues = [];\n let page = 1;\n\n function nextPage(issues) {\n // If we get back an empty array, we've gotten all the issues available.\n if (issues && !issues.length) return allIssues;\n\n // Otherwise, add the latest page of issues to allIssues,\n // and get the next page.\n allIssues = allIssues.concat(issues || []);\n return github.repos.owner.repo.issues.get({\n page: page++,\n owner: 'npm',\n repo: 'npm',\n }).then(nextPage);\n }\n\n return nextPage();\n }\n});\n```\n\nYou can create actions that use multiple accounts for the same integration.\nFor example, you could copy GitHub issues from one repository to another.\n\n```js\nvar datafire = require('datafire');\nvar github = require('@datafire/github').actions;\nvar action = new datafire.Action({\n security: {\n from_account: {\n description: \"Account to use when retrieving issues\",\n integration: 'github',\n },\n to_account: {\n description: \"Account to use when creating issues\",\n integration: 'github',\n },\n },\n inputs: [{\n title: 'fromRepo',\n type: 'string',\n description: \"Repo to copy issues from, in the form `username/repo`\",\n }, {\n title: 'toRepo',\n type: 'string',\n description: \"Repo to copy issues from, in the form `username/repo`\",\n }],\n handler: (input, context) => {\n return datafire.flow(context)\n .then(_ => {\n context.accounts.github = context.accounts.from_account;\n [owner, repo] = input.fromRepo.split('/');\n return github.repos.owner.repo.issues.get({owner, repo}, context)\n })\n .then(issues => {\n context.accounts.github = context.accounts.to_account;\n [owner, repo] = input.toRepo.split('/');\n return Promise.all(issues.map(issue => {\n return github.repos.owner.repo.issues.post({\n owner,\n repo,\n title: issue.title,\n body: issue.body,\n }, context);\n }))\n })\n }\n});\n```\n\nWhen testing your actions, you can mock any integration by calling `mockAll()`.\nDataFire will use [json-schema-faker](https://github.com/json-schema-faker/json-schema-faker)\nto mock the response for all of that integration's actions.\n\nFor example, say the `createUser` action sends a welcome message via Gmail.\nWe want to test that `createUser` runs successfully, but don't want to send\nthe welcome message.\n\n```js\nlet gmailIntegration = require('@datafire/gmail');\ngmailIntegration.mockAll();\n\nlet createUser = require('../actions/createUser.js');\n\ndescribe(\"createUser\", () => {\n it('should succeed', () => {\n return createUser.run({\n email: 'foo@bar.com',\n password: '12345678',\n });\n })\n})\n```\n\n\n", "title": "Custom Routing and Other Frameworks" }, { "markdownFile": "markdown/Cookbook.md", "markdownSection": "Open API", "contents": "## Open API\nYou can access an Open API specification for your API at `/openapi.json`. For example:\n\n```bash\ndatafire serve --port 3000 &\ncurl \"http://localhost:3000/openapi.json\" > openapi.json\n```\n\nYou can also use DataFire.yml to specify top-level fields in your Open API,\nsuch as `host`, `schemes`, and `info`:\n\n```yaml\nopenapi:\n host: api.example.com:3000\n schemes:\n -https\n info:\n version: 2.0\n```\n\n", "title": "Open API" }, { "markdownFile": "markdown/Cookbook.md", "markdownSection": "Pagination", "contents": "## Pagination\nThe GitHub action `repos.owner.repo.issues.get` returns an array of\nissues for a particular repository, but it only returns 30 issues at\na time.\n\nTo iterate over all issues, we pass the `page` input, incrementing it\nuntil GitHub returns an empty array.\n\n```js\nlet datafire = require('datafire');\nlet github = require('@datafire/github').actions;\n\nmodule.exports = new datafire.Action({\n handler: input => {\n let allIssues = [];\n let page = 1;\n\n function nextPage(issues) {\n // If we get back an empty array, we've gotten all the issues available.\n if (issues && !issues.length) return allIssues;\n\n // Otherwise, add the latest page of issues to allIssues,\n // and get the next page.\n allIssues = allIssues.concat(issues || []);\n return github.repos.owner.repo.issues.get({\n page: page++,\n owner: 'npm',\n repo: 'npm',\n }).then(nextPage);\n }\n\n return nextPage();\n }\n});\n```\n\n", "title": "Pagination" }, { "markdownFile": "markdown/Cookbook.md", "markdownSection": "Testing", "contents": "## Testing\nWhen testing your actions, you can mock any integration by calling `mockAll()`.\nDataFire will use [json-schema-faker](https://github.com/json-schema-faker/json-schema-faker)\nto mock the response for all of that integration's actions.\n\nFor example, say the `createUser` action sends a welcome message via Gmail.\nWe want to test that `createUser` runs successfully, but don't want to send\nthe welcome message.\n\n```js\nlet gmailIntegration = require('@datafire/gmail');\ngmailIntegration.mockAll();\n\nlet createUser = require('../actions/createUser.js');\n\ndescribe(\"createUser\", () => {\n it('should succeed', () => {\n return createUser.run({\n email: 'foo@bar.com',\n password: '12345678',\n });\n })\n})\n```\n\n\n", "title": "Testing" }] }], "meta": { "title": "API Documentation" } } }, "basePath": "/", "sitemap": false, "oauth": false, "workflows": {}, "codegen": { "disabled": true, "languages": [], "templates": {}, "setupTemplates": {} }, "bootstrap": { "bootstrapVersion": 3, "styleLoaders": ["style-loader", "css-loader", "sass-loader"], "styles": true }, "uiOptions": { "discussionTitle": "Discussion", "optionalizeHost": false, "embedConsoleInDocumentation": false, "redirectHashURLs": false, "messages": { "deprecated": "This operation has been deprecated.", "beta": "This operation is still in beta.", "console": "Use the API Console to make live calls to the API. Select an operation from the menu, fill out the parameters, and hit \"Send Request\" when you're ready." } }, "prismjsTheme": "", "javascript": [], "css": [], "github": { "redirect_uri": "", "client_id": "", "repo": "", "workflowDirectory": "workflows" }, "templates": { "navbar": "<nav class=\"navbar navbar-default navbar-fixed-top\">\n <div class=\"container-fluid\">\n <div class=\"navbar-header\">\n <img src=\"https://app.datafire.io/assets/img/logo-no-text.png\">\n <a class=\"navbar-brand\" routerlink=\"/\" href=\"/\">DataFire</a>\n </div>\n <div class=\"navbar-header pull-right\">\n\t <ul class=\"nav navbar-nav\">\n <li><a class=\"nav-link\" href=\"https://github.com/DataFire/DataFire\">Open Source Framework</a></li>\n <li><a class=\"nav-link\" href=\"https://app.datafire.io\">DataFire.io Platform</a></li>\n\t </ul>\n </div>\n </div>\n</nav>\n", "loading": "<style>\n .loading-screen {\n min-height: 500px;\n text-align: center;\n padding-top: 100px;\n }\n</style>\n<div id=\"LoadingScreen\" class=\"loading-screen\">\n <h1><i class=\"fa fa-spin fa-refresh\"></i><h1>\n</div>\n\n", "head": "", "footer": "" }, "env": {}, "externalCSS": true }; /***/ }), /***/ "./node_modules/clipboard/lib/clipboard-action.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__; var _typeof2 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; (function (global, factory) { if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, __webpack_require__("./node_modules/select/src/select.js")], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else if (typeof exports !== "undefined") { factory(module, require('select')); } else { var mod = { exports: {} }; factory(mod, global.select); global.clipboardAction = mod.exports; } })(undefined, function (module, _select) { 'use strict'; var _select2 = _interopRequireDefault(_select); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) { return typeof obj === 'undefined' ? 'undefined' : _typeof2(obj); } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj === 'undefined' ? 'undefined' : _typeof2(obj); }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var ClipboardAction = function () { /** * @param {Object} options */ function ClipboardAction(options) { _classCallCheck(this, ClipboardAction); this.resolveOptions(options); this.initSelection(); } /** * Defines base properties passed from constructor. * @param {Object} options */ _createClass(ClipboardAction, [{ key: 'resolveOptions', value: function resolveOptions() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; this.action = options.action; this.container = options.container; this.emitter = options.emitter; this.target = options.target; this.text = options.text; this.trigger = options.trigger; this.selectedText = ''; } }, { key: 'initSelection', value: function initSelection() { if (this.text) { this.selectFake(); } else if (this.target) { this.selectTarget(); } } }, { key: 'selectFake', value: function selectFake() { var _this = this; var isRTL = document.documentElement.getAttribute('dir') == 'rtl'; this.removeFake(); this.fakeHandlerCallback = function () { return _this.removeFake(); }; this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true; this.fakeElem = document.createElement('textarea'); // Prevent zooming on iOS this.fakeElem.style.fontSize = '12pt'; // Reset box model this.fakeElem.style.border = '0'; this.fakeElem.style.padding = '0'; this.fakeElem.style.margin = '0'; // Move element out of screen horizontally this.fakeElem.style.position = 'absolute'; this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically var yPosition = window.pageYOffset || document.documentElement.scrollTop; this.fakeElem.style.top = yPosition + 'px'; this.fakeElem.setAttribute('readonly', ''); this.fakeElem.value = this.text; this.container.appendChild(this.fakeElem); this.selectedText = (0, _select2.default)(this.fakeElem); this.copyText(); } }, { key: 'removeFake', value: function removeFake() { if (this.fakeHandler) { this.container.removeEventListener('click', this.fakeHandlerCallback); this.fakeHandler = null; this.fakeHandlerCallback = null; } if (this.fakeElem) { this.container.removeChild(this.fakeElem); this.fakeElem = null; } } }, { key: 'selectTarget', value: function selectTarget() { this.selectedText = (0, _select2.default)(this.target); this.copyText(); } }, { key: 'copyText', value: function copyText() { var succeeded = void 0; try { succeeded = document.execCommand(this.action); } catch (err) { succeeded = false; } this.handleResult(succeeded); } }, { key: 'handleResult', value: function handleResult(succeeded) { this.emitter.emit(succeeded ? 'success' : 'error', { action: this.action, text: this.selectedText, trigger: this.trigger, clearSelection: this.clearSelection.bind(this) }); } }, { key: 'clearSelection', value: function clearSelection() { if (this.trigger) { this.trigger.focus(); } window.getSelection().removeAllRanges(); } }, { key: 'destroy', value: function destroy() { this.removeFake(); } }, { key: 'action', set: function set() { var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy'; this._action = action; if (this._action !== 'copy' && this._action !== 'cut') { throw new Error('Invalid "action" value, use either "copy" or "cut"'); } }, get: function get() { return this._action; } }, { key: 'target', set: function set(target) { if (target !== undefined) { if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) { if (this.action === 'copy' && target.hasAttribute('disabled')) { throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); } if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) { throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); } this._target = target; } else { throw new Error('Invalid "target" value, use a valid Element'); } } }, get: function get() { return this._target; } }]); return ClipboardAction; }(); module.exports = ClipboardAction; }); /***/ }), /***/ "./node_modules/clipboard/lib/clipboard.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; var __W