{"_id":"@botmatic/js-contact","_rev":"2-9f5ccf480e880f50aaaca6b798cc7d07","name":"@botmatic/js-contact","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@botmatic/js-contact","version":"1.0.0","description":"Implement Botmatic Integrations with ease.   Allows you to easily create integrations to synchronize Botmatic contacts with an external service.","main":"src/index.js","scripts":{"start":"node src/index.js","test":"nyc mocha"},"keywords":[],"author":"","license":"ISC","dependencies":{"@botmatic/js-integration":"^0.4.3","dotenv":"^5.0.0","js-api-client":"git+ssh://git@github.com/botmatic/js-api-client.git","js-mapper":"git+ssh://git@github.com/botmatic/js-mapper.git","randomstring":"^1.1.5","request":"^2.83.0"},"devDependencies":{"chai":"^4.1.2","chai-http":"git+ssh://git@github.com/chaijs/chai-http.git","express":"^4.16.2","nock":"^9.1.6","nyc":"^11.4.1","redis":"^2.8.0"},"gitHead":"e98c7d4652973dc13a809c1c4ec17b29e42930fe","_id":"@botmatic/js-contact@1.0.0","_npmVersion":"5.6.0","_nodeVersion":"8.3.0","_npmUser":{"name":"botmatic","email":"kevin+botmatic-npm@botmatic.ai"},"dist":{"integrity":"sha512-Bmy5fr+LeGIN2/kiniV614lTLgwEw/93LVDfIeEsjPfxeFTqSwPVqJUiDe1subhZXipt5MlkJZf582cZ5nJY/Q==","shasum":"d154d0fb06609d568d77ee5c2c285add517c5cd2","tarball":"https://registry.npmjs.org/@botmatic/js-contact/-/js-contact-1.0.0.tgz","fileCount":12,"unpackedSize":123378,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAMQLkgZu0QPEviG2QnCFcXfEacrjSC+b+8kVBla3YReAiEA30rn0pAHPURnTWPYPKruL9X1Auir451wQ/Pq7XQSNYk="}]},"maintainers":[{"name":"botmatic","email":"kevin+botmatic-npm@botmatic.ai"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/js-contact_1.0.0_1521739809624_0.8526569073956167"},"_hasShrinkwrap":false}},"time":{"created":"2018-03-22T17:30:09.568Z","1.0.0":"2018-03-22T17:30:09.701Z","modified":"2022-04-04T20:14:09.402Z"},"maintainers":[{"name":"botmatic","email":"kevin+botmatic-npm@botmatic.ai"}],"description":"Implement Botmatic Integrations with ease.   Allows you to easily create integrations to synchronize Botmatic contacts with an external service.","keywords":[],"license":"ISC","readme":"# JS Contact\nImplement Botmatic Integrations with ease.  \nAllows you to easily create integrations to synchronize Botmatic contacts with an external service.\n\n## Table of contents\n\n1. Install\n2. Basic usage\n3. Implementations steps\n4. Configuration\n  - Parameters descripiton\n  - Using the default express instance\n  - Using an existing express instance\n  - Authenticating requests from Botmatic\n5. Botmatic Events\n  - Handling INSTALL\n  - Handling UNINSTALL\n  - Handling other events\n  - Handling actions\n6. Reference\n  - Properties\n  - Methods\n  - Interfaces\n\n## Install\n```bash\nnpm install @botmatic/js-contact\n```\n\n## Basic usage\nUpon initialization, `js-contact` takes a configuration object as follow:\n```javascript\nconst app = express()\n\nconst botmaticConfig = {\n   consumer: yourConsumer, // REQUIRED // API consumer for your external API. Implements the ExternalAPIConsumer Interface\n   mappings: yourMappings, // REQUIRED // An array of Mapping. See js_mapper module\n   keyStore: keyStore, // REQUIRED // The storage to map ids. Implements the KeyStorage Interface\n   endpoint: \"/\", // OPTIONAL // Botmatic will send events and actions to this path\n   server:   app, // OPTIONAL // The express server to add those routes\n   port:   3456, // OPTIONAL // If using the default express instance, the port the app listens\n   auth: () => true // OPTIONAL // Authorization function to authorize requests sent by Botmatic\n}\n\nconst botmatic = require('@botmatic/js-contact')(botmaticConfig)\n```\n\nOnce the module is initialized, it will start listening to events from Botmatic.\n\n## Implementation steps\nIn order to have a complete contact integration you should:\n\n1. Create mappings (See @botmatic/js-mapper)\n2. Implement an ExternalAPIConsumer for your external service\n3. Optional: if choose not to use @botmatic/js-redis-key-store, implement your own\n4. Add routes to your express server (or to the default one) to handle events\n  from your external service.\n5. Optional: add listeners for other events or actions\n\n## Configuration\n\n### Parameters descripiton\n\nproperties | type                                 | attributes                       | description\n---------- | ------------------------------------ | -------------------------------- | -----------\nconsumer   | ExternalAPIConsumer                  | required                         | Used to create, updated and delete contacts on your external service\nmappings   | Mapping[]                            | required                         | Used to convert contacts to and from your external service\nkeyStore   | KeyStore                             | required                         | Used to store botmatic/external id pairs\nendpoint   | string                               | optional, default = `\"/\"`        | The route to which Botmatic will send events\nserver     | Express                              | optional                         | An existing Express server instance\nauth       | `function(token) => {token, client}` | optional, default = `() => true` | A function to validate request sent to Botmatic. Not called on Install\n\n### Using the default express instance\nYou can start using @botmatic/js-contact without having to setup an express server,\n@botmatic/js-contact includes a default one.  \nIt will listen on port `3000` by default, by you can specify a custom one using the `port`\nparameter. \n```javascript\nconst botmaticConfig = {\n   consumer: yourConsumer, // your API consumer\n   mappings: yourMappings, // your mappings\n   keyStore: keyStore, // a key store\n   endpoint: \"/\", // your endpoint path\n   port:   3456, // port to listen \n   auth: authorizeBotmatic // an auth function\n}\n\nconst botmatic = require('@botmatic/js-contact')(botmaticConfig)\n// botmatic.app is the running express server\n```\n\n### Using an existing express instance\nIf you wish to add @botmatic/js-contact to an existing express server, pass it\nas the `server` parameter. Routes will be added to the specified `endpoint` path.\n```javascript\nconst app = express()\n\nconst botmaticConfig = {\n   consumer: yourConsumer, // your API consumer\n   mappings: yourMappings, // your mappings\n   keyStore: keyStore, // a key store\n   endpoint: \"/\", // your endpoint path\n   server:   app, // your existing express app\n   auth: authorizeBotmatic // an auth function\n}\n\nconst botmatic = require('@botmatic/js-contact')(botmaticConfig)\n\napp.listen(PORT)\n```\n\n### Authenticating requests from Botmatic\nThe `auth` function passed on initialization is called on every received event, except INSTALL.\nIt takes the token from the request headers as first and only parameter, and *must* return a Promise resolving\nto an object as follow:\n\n```json\n{\n  \"token\": \"THE_BOTMATIC_TOKEN\",\n  \"client\": { // OPTIONAL // contains any information required about the client related this integration token\n    \"id\": \"deadbeef\"\n  }\n}\n```\nThis object will be the `auth` property of the object passed as parameter to all events listeners.\n\n##### Example `auth` function\n```javascript\nconst authorization = token => {\n  return new Promise((resolve, reject) => {\n    // you should check you received this token upon installation\n    if (validate(token)) {\n      clientsService.getForToken(token)\n      .then(client => {\n        resolve({token, client})\n      })\n      .catch(error => {\n        reject(`error get client for token ${error}`)\n      })\n    }\n    else {\n      // Returning false will stop propagating the event and return a 401 status code\n      reject(\"invalid token\")\n    }\n  })\n}\n\n// in Botmatic configuration\nconst botmaticConfig = {\n   consumer: yourConsumer, // REQUIRED // API consumer for your external API. Implements the ExternalAPIConsumer Interface\n   mappings: yourMappings, // REQUIRED // An array of Mapping. See js_mapper module\n   keyStore: keyStore, // REQUIRED // The storage to map ids. Implements the KeyStorage Interface\n   endpoint: \"/\", // OPTIONAL // Botmatic will send events and actions to this path\n   server:   app, // OPTIONAL // The express server to add those routes\n   auth: authorization // Authorization function to authorize requests sent by Botmatic\n}\n\n// initialize @botmatic/js-contact\nconst botmatic = require('@botmatic/js-contact')(botmaticConfig)\n```\n\n## Botmatic Events\n`@botmatic/js-contact` takes care of handling CONTACT_CREATED, CONTACT_UPDATED and CONTACT_DELETED events.  \nYou will still have to implement listeners for INSTALL, UNINSTALL, BOT_REPLY and USER_REPLY events,\nand any action you wish to integrate in Botmatic.  \n\n### Handling the INSTALL event\nWhen an integration is installed on Botmatic, an `INSTALL` event is sent to your implementation.  \nThe token is then verified and the properties defined in your mappings are created.  \nThis is when you should store the Botmatic authorization token, and import your contacts\nto Botmatic.\n\n```javascript\nbotmatic.onInstall(async ({auth: token}) => {\n  // Store your token\n  redis.set('mytoken', token)\n  \n  // Import your contacts\n  botmatic.importContacts(token)\n  \n  // return the default format\n  return {data: {success: true}, type: \"data\"}\n})\n```\n\n### Handling the UNINSTALL event\nWhen an integration is uninstalled from Botmatic, an `UNINSTALL` event is sent to your implementation.\nThe botmatic/external id pairs will have been removed from your storage.  \nYou should remove your Botmatic authorization from your storage.\n\n```javascript\nbotmatic.onUninstall(async ({auth: token}) => {\n  // remove the token\n  redis.del('mytoken')\n  \n  // return the default format\n  return {data: {success: true}, type: \"data\"}\n})\n```\n\n### Handling other events\n`@botmatic/js-contact` provides an `onEvent` function to allow you to respond to BOT_REPLY and USER_REPLY events\n\n```javascript\nbotmatic.onEvent(botmatic.events.BOT_REPLY, ({auth: {token, client}, data}) => {\n  // data.event == \"bot_reply\"\n  // data.data contains the event data\n  \n  return {data: {success: true}, type: \"data\"}\n})\n```\n\n### Handling actions\n\n```javascript\nbotmatic.onAction(\"your_action_name\", ({auth: {token, client}, data}) => {\n  // handle your action\n  \n  return {data: your_action_result, type: \"data\"}\n})\n```\n\n## Reference\n\n### Properties\n\n#### .app\n - *type :* Express\n - *description : * An express server instance. \n\n#### .events (read only)\n - *type :* Object\n - *description :* Constants for events names\n\nproperties | description\n--- | ---\nCONTACT_CREATED | event sent when a contact is created on botmatic\nCONTACT_UPDATED | event sent when a contact is updated on botmatic\nCONTACT_DELETED | event sent when a contact is deleted on botmatic\nBOT_REPLY | event sent when bot sent a message to a user on botmatic\nUSER_REPLY | event sent when a user sent a message to a bot on botmatic\nINSTALL | event sent when your integration is installed on a workspace\nUNINSTALL | event sent when your integration is uninstalled on a workspace\n\n### Methods\n\n##### `createContact(contact, token) -> Promise<{success, id, error}>`\nTransforms the contact according to the `mappings` and creates it on Botmatic.  \nSaves the botmatic/external ids pair in the keyStore.\n\nParameters:\n - **contact**, *object*: a contact in your API format\n - **token**, *string*: the botmatic integration token\n\nThe response `success` is `true` when:\n - The contact has successfully been created\n - The new id is returned\n\nIt is `false` when:\n - The creation failed\n - No id has been returned\n - An error occurred\n \n```javascript\nconst {success, id, error} = await botmatic.createContact({\"first_name\": \"Patrick\", \"last_name\": \"Chen\"}, token)\n// success == true\n// id is the id returned by your external service\n```\n\n##### `importContacts(contacts, token) -> Promise<{success, error}>`\nTransforms all the contact according to the `mappings` and creates them on Botmatic.  \nSaves all botmatic/external id pairs in the keyStore\n\nParameters:\n - **contacts**, *array*: an array of contact in your API format\n - **token**, *string*: the botmatic integration token\n\nThe response `success` is `true` when:\n - The contacts have successfully been imported\n\nIt is `false` when:\n - The import failed\n - An error occurred\n \n```javascript\nconst {success, error} = await botmatic.importContacts(contacts, token)\n// success == true\n// error == undefined\n```\n\n##### `updateContact(contact, token) -> Promise<{success, error}>`\nTransforms the contact according to the `mappings` and updates it on Botmatic.  \nThe contact object must have its external id in the identifying field specified in `mappings` configuration.\n\nParameters:\n  - **contact**, *object*: a contact in your API format\n  - **token**, *string*: the botmatic integration token\n\nThe response `success` is `true` when the contact has successfully been updated.  \nIt is `false` when:\n - the contact was not found on the remote service. `error` == `\"resource not found\"`\n - an error occurred\n\n```javascript\nconst {success, error} = await botmatic.updateContact(contact, token)\n// success == true\n// error == undefined\n```\n\n##### `deleteContact(contact_id, token) -> Promise<{success, error}>`\nDeletes a contact on Botmatic. `contact_id` must be the contact's external id.\n\nParameters:\n  - **contact_id**, *string*: the contact's external id\n  - **token**, *string*: the botmatic integration token\n \nThe response `success` is `true` when the contact has successfully been deleted.  \nIt is `false` when:\n - the contact was not found on the remote service. `error` = `\"resrouce not found\"`\n - an error occured\n\n```javascript\nconst {success, error} = await botmatic.deleteContact(contact_id, token)\n// success == true\n// error == undefiend\n```\n\n##### `createProperty(property, token) -> Promise<{success, error}>`\nAdds a property on Botmatic.\n\nParameters:\n  - **property**, *{name: string [, type: string]}*: the property\n  - **token**, *string*: the botmatic integration token\n \nThe response `success` is `true` when the property has successfully been created.  \nIt is `false` otherwise, check the response `error`.\n\n```javascript\nconst {success, error} = await botmatic.createProperty(property, token)\n// success == true\n// error == undefiend\n```\n\n##### `createProperties(properties, token) -> Promise<{success, error}>`\nAdds many properties on Botmatic.\n\nParameters:\n  - **properties**, *array*: the properties to add\n  - **token**, *string*: the botmatic integration token\n \nThe response `success` is `true` when the properties have successfully been created.  \nIt is `false` otherwise, check the response `error`.\n\n```javascript\nconst {success, error} = await botmatic.createProperty(property, token)\n// success == true\n// error == undefiend\n```\n\n##### `onInstall(callback)`\nAdds a listener for the INSTALL event. \nThe callback's signature is:  \n```callback({auth: {token}}) -> Promise<{data: object, type: \"data\"}>```\nWhere `token` is the integration token to be used when creating, updating or deleting\ncontacts or properties.  \nSee \"Handling INSTALL\" for an example. \n\n##### `onUninstall(callback)`\nAdds a listener for the UNINSTALL event. \nThe callback's signature is:  \n```callback({auth: {token, client}}) -> Promise<{data: object, type: \"data\"}>```\nWhere `token` is the integration token to be used when creating, updating or deleting\ncontacts or properties, `client` is the client data return by the `auth` function\nin configuration.  \nSee \"Authenticating requests from Botmatic\"  \nSee \"Handling UNINSTALL\"\n\n##### `onEvent(eventName, callback)`\nAdds a listener for any event in `js-contact.events`\nThe callback's signature is:  \n```callback({auth: {token, client}}) -> Promise<{data: object, type: \"data\"}>```\nWhere `token` is the integration token to be used when creating, updating or deleting\ncontacts or properties, `client` is the client data return by the `auth` function\nin configuration.  \nSee \"Authenticating requests from Botmatic\" \nSee \"Handling other events\"\n\n##### `onAction(callback)`\nAdds a listener for an action.\nThe callback's signature is:  \n```callback({auth: {token, client}}) -> Promise<{data: object, type: \"data\"}>```\nWhere `token` is the integration token to be used when creating, updating or deleting\ncontacts or properties, `client` is the client data return by the `auth` function\nin configuration.  \nSee \"Authenticating requests from Botmatic\" \nSee \"Handling other actions\"\n\n##### `close([callback])`\nProperly shuts down the express server. If provided, calls `callback` when done.\n\n### Interfaces\n\n#### ExternalAPIConsumer Interface\nAn ExternalAPIConsumer is used to communicate with your service's API. It should\nimplement the set of methods descibed bellow, and handle authentication to your service.\n\n\n##### `createContact(contact) -> Promise<{success, id, error}>`\nCreates a contact.  \nThe response `success` is `true` when:\n - The contact has successfully been created\n - The new id is returned\n\nIt is `false` when:\n - The creation failed\n - No id has been returned\n - An error occurred\n \n```javascript\nconst {success, id, error} = await consumer.createContact({\"first_name\": \"Patrick\", \"last_name\": \"Chen\"})\n// success == true\n// id is the id returned by your external service\n```\n\n##### `getContact(id) -> Promise<{success, contact, error}>`\nFetches a contact by its id.   \nThe response `success` is `true` when:\n - A contact with the given `id` is found\n\nIt is `false` when:\n - No contact was found. `error` == `\"resource not found\"`\n - An error occurred\n \n```javascript\nconst {success, contact, error} = await cosumer.getContact(23)\n// success == true\n// contact == { first_name: \"Patrick\", last_name: \"Chen\" }\n```\n\n##### `getContactByEmail(email) -> Promise<{success, contact, error}>`\nFetches a contact by its email.  \nThe response `success` is `true` when:\n - A contact with a matching `email` is found\n\nIt is `false` when:\n - No matching contact was found. `error` == `\"resource not found\"`\n - An error occured\n \n```javascript\nconst {success, contact, error} = await consumer.getContactByEmail(\"patrick.chen@fake.org\")\n// success == true\n// contact == { first_name: \"Patrick\", last_name: \"Chen\", email: \"patrick.chen@fake.org\" }\n```\n\n##### `listContacts([page=1], [limit=30]) -> Promise<{success, contacts, error}>`\nFetches a paginated list of contacts.  \nThe response `success` is `true` when:\n - The request is successful\n - If no contacts are found, `contacts` is an empty array\n\nThe response `success` is `false` when:\n - An error occured\n\n```javascript\nconst {success, contacts, error} = await consumer.listContacts(2, 30)\n// success == true\n// contacts is an array containing contacts from 31 to 61\n```\n\n##### `listAllContacts([page_size=30], callback) -> Promise<{success, contacts, error}>`\nFetches all contacts, page by page, and calls `callback` with each page. `callback` may be called with an empty array.  \nActually calls `listContact()` until it returns an empty array.  \n\nThe response `success` is always `true`.\nThe response `contacts` will only contains the result of successful calls to `listContacts()`\n\n```javascript\nconst {success, contacts, error} = await consumer.listAllContacts(30, (contacts) => {\n  console.log(`Fetched ${contacts.length} contacts`)\n})\n\nconsole.log(`Fetched a total of ${contacts.length} contacts`)\n// success == true\n// contains all contacts\n// Console shows : \n//  Fetched 30 contacts\n//  Fetched 13 contacts\n//  Fetched a total of 43 contacts\n```\n##### `updateContact(contact) -> Promise<{success, error}>`\nUpdates a contact.  \n\nThe response `success` is `true` when the contact has successfully been updated.  \nIt is `false` when:\n - the contact was not found on the remote service. `error` == `\"resource not found\"`\n - an error occurred\n\n```javascript\nconst {success, error} = await consumer.updateContact(contact)\n// success == true\n```\n\n##### `deleteContact(contact_id) -> Promise<{success, error}>`\nDeletes a contact.  \n \nThe response `success` is `true` when the contact has successfully been deleted.  \nIt is `false` when:\n - the contact was not found on the remote service. `error` = `\"resrouce not found\"`\n - an error occured\n\n```javascript\nconst {success, error} = await consumer.deleteContact(29)\n// success == true\n```\n\n##### Implementation example\n```javascript\nconst API_BASE = \"http://dummy.api.com\"\nconst CONTACT_ENDPOINT = `${API_BASE}/contacts`\n\nconst createContact = (contact) => {\n  return Promise(resolve => {\n    request(CONTACT_ENPOINT, {\n      method: 'post',\n      headers: {'content-type': 'application/json'},\n      json: contact\n    }, (err, responseBody) => {\n      if (!err) {\n        resolve({success: true, id: responseBody.id})\n      }\n      else {\n        resolve({success: false, error: err})\n      }\n    })\n  })\n}\n\n// Implement the other functions\n\nmodule.exports = {\n  createContact\n}\n```\n\n#### KeyStore Interface\nBecause ids on Botmatic won't necessarily match ids on your external service, a KeyStore is used to store botmatic/external id pairs.\nAlthough there is a `@botmatic/js-redis-key-store` implementing this interface with redis,\nyou can implement your own for any type storage your want.    \nAll functions take an `integrationId` as first argument. It is recommended to use the token received upon installation as it unique per integration.\n\n##### `saveIds(integrationId, botmaticId, externalId) -> Promise<boolean>`\nSaves a botmatic/external id pair.\nThe returned `Promise` resolves to true in case of success, false in case of error.\n\n##### `getBotmaticId(integrationId, externalId) -> Promise<string | null>`\nGets the botmatic id associated with the given external id.  \nThe returned `Promise` resolves to the botmatic id, or `null` if none is found.\n\n##### `getExternalId(integrationId, botmaticId) -> Promise<string | null>`\nGets the external id associated with the given botmatic id.  \nThe returned `Promise` resolves to the external id, or `null` if none is found.\n\n##### `deleteIds(integrationId, botmaticId, externalId) -> Promise<boolean>`\nDeletes a botmatic/external id pair.  \nThe returned `Promise` resolves to true in case of success, false in case of error.\n\n##### `deleteAllIds(integrationId) -> Promise<boolean>`\nDeletes all botmatic/external id pairs for a given integration.\nThe returned `Promise` resolves to true in case of success, false in case of error.","readmeFilename":"README.md"}