@botmatic/js-contact v1.0.0
JS Contact
Implement Botmatic Integrations with ease.
Allows you to easily create integrations to synchronize Botmatic contacts with an external service.
Table of contents
- Install
- Basic usage
- Implementations steps
- Configuration
- Parameters descripiton
- Using the default express instance
- Using an existing express instance
- Authenticating requests from Botmatic
- Botmatic Events
- Handling INSTALL
- Handling UNINSTALL
- Handling other events
- Handling actions
- Reference
- Properties
- Methods
- Interfaces
Install
npm install @botmatic/js-contactBasic usage
Upon initialization, js-contact takes a configuration object as follow:
const app = express()
const botmaticConfig = {
consumer: yourConsumer, // REQUIRED // API consumer for your external API. Implements the ExternalAPIConsumer Interface
mappings: yourMappings, // REQUIRED // An array of Mapping. See js_mapper module
keyStore: keyStore, // REQUIRED // The storage to map ids. Implements the KeyStorage Interface
endpoint: "/", // OPTIONAL // Botmatic will send events and actions to this path
server: app, // OPTIONAL // The express server to add those routes
port: 3456, // OPTIONAL // If using the default express instance, the port the app listens
auth: () => true // OPTIONAL // Authorization function to authorize requests sent by Botmatic
}
const botmatic = require('@botmatic/js-contact')(botmaticConfig)Once the module is initialized, it will start listening to events from Botmatic.
Implementation steps
In order to have a complete contact integration you should:
- Create mappings (See @botmatic/js-mapper)
- Implement an ExternalAPIConsumer for your external service
- Optional: if choose not to use @botmatic/js-redis-key-store, implement your own
- Add routes to your express server (or to the default one) to handle events from your external service.
- Optional: add listeners for other events or actions
Configuration
Parameters descripiton
| properties | type | attributes | description |
|---|---|---|---|
| consumer | ExternalAPIConsumer | required | Used to create, updated and delete contacts on your external service |
| mappings | Mapping[] | required | Used to convert contacts to and from your external service |
| keyStore | KeyStore | required | Used to store botmatic/external id pairs |
| endpoint | string | optional, default = "/" | The route to which Botmatic will send events |
| server | Express | optional | An existing Express server instance |
| auth | function(token) => {token, client} | optional, default = () => true | A function to validate request sent to Botmatic. Not called on Install |
Using the default express instance
You can start using @botmatic/js-contact without having to setup an express server,
@botmatic/js-contact includes a default one.
It will listen on port 3000 by default, by you can specify a custom one using the port
parameter.
const botmaticConfig = {
consumer: yourConsumer, // your API consumer
mappings: yourMappings, // your mappings
keyStore: keyStore, // a key store
endpoint: "/", // your endpoint path
port: 3456, // port to listen
auth: authorizeBotmatic // an auth function
}
const botmatic = require('@botmatic/js-contact')(botmaticConfig)
// botmatic.app is the running express serverUsing an existing express instance
If you wish to add @botmatic/js-contact to an existing express server, pass it
as the server parameter. Routes will be added to the specified endpoint path.
const app = express()
const botmaticConfig = {
consumer: yourConsumer, // your API consumer
mappings: yourMappings, // your mappings
keyStore: keyStore, // a key store
endpoint: "/", // your endpoint path
server: app, // your existing express app
auth: authorizeBotmatic // an auth function
}
const botmatic = require('@botmatic/js-contact')(botmaticConfig)
app.listen(PORT)Authenticating requests from Botmatic
The auth function passed on initialization is called on every received event, except INSTALL.
It takes the token from the request headers as first and only parameter, and must return a Promise resolving
to an object as follow:
{
"token": "THE_BOTMATIC_TOKEN",
"client": { // OPTIONAL // contains any information required about the client related this integration token
"id": "deadbeef"
}
}This object will be the auth property of the object passed as parameter to all events listeners.
Example auth function
const authorization = token => {
return new Promise((resolve, reject) => {
// you should check you received this token upon installation
if (validate(token)) {
clientsService.getForToken(token)
.then(client => {
resolve({token, client})
})
.catch(error => {
reject(`error get client for token ${error}`)
})
}
else {
// Returning false will stop propagating the event and return a 401 status code
reject("invalid token")
}
})
}
// in Botmatic configuration
const botmaticConfig = {
consumer: yourConsumer, // REQUIRED // API consumer for your external API. Implements the ExternalAPIConsumer Interface
mappings: yourMappings, // REQUIRED // An array of Mapping. See js_mapper module
keyStore: keyStore, // REQUIRED // The storage to map ids. Implements the KeyStorage Interface
endpoint: "/", // OPTIONAL // Botmatic will send events and actions to this path
server: app, // OPTIONAL // The express server to add those routes
auth: authorization // Authorization function to authorize requests sent by Botmatic
}
// initialize @botmatic/js-contact
const botmatic = require('@botmatic/js-contact')(botmaticConfig)Botmatic Events
@botmatic/js-contact takes care of handling CONTACT_CREATED, CONTACT_UPDATED and CONTACT_DELETED events.
You will still have to implement listeners for INSTALL, UNINSTALL, BOT_REPLY and USER_REPLY events,
and any action you wish to integrate in Botmatic.
Handling the INSTALL event
When an integration is installed on Botmatic, an INSTALL event is sent to your implementation.
The token is then verified and the properties defined in your mappings are created.
This is when you should store the Botmatic authorization token, and import your contacts
to Botmatic.
botmatic.onInstall(async ({auth: token}) => {
// Store your token
redis.set('mytoken', token)
// Import your contacts
botmatic.importContacts(token)
// return the default format
return {data: {success: true}, type: "data"}
})Handling the UNINSTALL event
When an integration is uninstalled from Botmatic, an UNINSTALL event is sent to your implementation.
The botmatic/external id pairs will have been removed from your storage.
You should remove your Botmatic authorization from your storage.
botmatic.onUninstall(async ({auth: token}) => {
// remove the token
redis.del('mytoken')
// return the default format
return {data: {success: true}, type: "data"}
})Handling other events
@botmatic/js-contact provides an onEvent function to allow you to respond to BOT_REPLY and USER_REPLY events
botmatic.onEvent(botmatic.events.BOT_REPLY, ({auth: {token, client}, data}) => {
// data.event == "bot_reply"
// data.data contains the event data
return {data: {success: true}, type: "data"}
})Handling actions
botmatic.onAction("your_action_name", ({auth: {token, client}, data}) => {
// handle your action
return {data: your_action_result, type: "data"}
})Reference
Properties
.app
- type : Express
- description : An express server instance.
.events (read only)
- type : Object
- description : Constants for events names
| properties | description |
|---|---|
| CONTACT_CREATED | event sent when a contact is created on botmatic |
| CONTACT_UPDATED | event sent when a contact is updated on botmatic |
| CONTACT_DELETED | event sent when a contact is deleted on botmatic |
| BOT_REPLY | event sent when bot sent a message to a user on botmatic |
| USER_REPLY | event sent when a user sent a message to a bot on botmatic |
| INSTALL | event sent when your integration is installed on a workspace |
| UNINSTALL | event sent when your integration is uninstalled on a workspace |
Methods
createContact(contact, token) -> Promise<{success, id, error}>
Transforms the contact according to the mappings and creates it on Botmatic.
Saves the botmatic/external ids pair in the keyStore.
Parameters:
- contact, object: a contact in your API format
- token, string: the botmatic integration token
The response success is true when:
- The contact has successfully been created
- The new id is returned
It is false when:
- The creation failed
- No id has been returned
An error occurred
const {success, id, error} = await botmatic.createContact({"first_name": "Patrick", "last_name": "Chen"}, token)
// success == true
// id is the id returned by your external serviceimportContacts(contacts, token) -> Promise<{success, error}>
Transforms all the contact according to the mappings and creates them on Botmatic.
Saves all botmatic/external id pairs in the keyStore
Parameters:
- contacts, array: an array of contact in your API format
- token, string: the botmatic integration token
The response success is true when:
- The contacts have successfully been imported
It is false when:
- The import failed
An error occurred
const {success, error} = await botmatic.importContacts(contacts, token)
// success == true
// error == undefinedupdateContact(contact, token) -> Promise<{success, error}>
Transforms the contact according to the mappings and updates it on Botmatic.
The contact object must have its external id in the identifying field specified in mappings configuration.
Parameters:
- contact, object: a contact in your API format
- token, string: the botmatic integration token
The response success is true when the contact has successfully been updated.
It is false when:
- the contact was not found on the remote service.
error=="resource not found" - an error occurred
const {success, error} = await botmatic.updateContact(contact, token)
// success == true
// error == undefineddeleteContact(contact_id, token) -> Promise<{success, error}>
Deletes a contact on Botmatic. contact_id must be the contact's external id.
Parameters:
- contact_id, string: the contact's external id
token, string: the botmatic integration token
The response success is true when the contact has successfully been deleted.
It is false when:
- the contact was not found on the remote service.
error="resrouce not found" - an error occured
const {success, error} = await botmatic.deleteContact(contact_id, token)
// success == true
// error == undefiendcreateProperty(property, token) -> Promise<{success, error}>
Adds a property on Botmatic.
Parameters:
- property, {name: string , type: string}: the property
token, string: the botmatic integration token
The response success is true when the property has successfully been created.
It is false otherwise, check the response error.
const {success, error} = await botmatic.createProperty(property, token)
// success == true
// error == undefiendcreateProperties(properties, token) -> Promise<{success, error}>
Adds many properties on Botmatic.
Parameters:
- properties, array: the properties to add
token, string: the botmatic integration token
The response success is true when the properties have successfully been created.
It is false otherwise, check the response error.
const {success, error} = await botmatic.createProperty(property, token)
// success == true
// error == undefiendonInstall(callback)
Adds a listener for the INSTALL event.
The callback's signature is:callback({auth: {token}}) -> Promise<{data: object, type: "data"}>
Where token is the integration token to be used when creating, updating or deleting
contacts or properties.
See "Handling INSTALL" for an example.
onUninstall(callback)
Adds a listener for the UNINSTALL event.
The callback's signature is:callback({auth: {token, client}}) -> Promise<{data: object, type: "data"}>
Where token is the integration token to be used when creating, updating or deleting
contacts or properties, client is the client data return by the auth function
in configuration.
See "Authenticating requests from Botmatic"
See "Handling UNINSTALL"
onEvent(eventName, callback)
Adds a listener for any event in js-contact.events
The callback's signature is:callback({auth: {token, client}}) -> Promise<{data: object, type: "data"}>
Where token is the integration token to be used when creating, updating or deleting
contacts or properties, client is the client data return by the auth function
in configuration.
See "Authenticating requests from Botmatic"
See "Handling other events"
onAction(callback)
Adds a listener for an action.
The callback's signature is:callback({auth: {token, client}}) -> Promise<{data: object, type: "data"}>
Where token is the integration token to be used when creating, updating or deleting
contacts or properties, client is the client data return by the auth function
in configuration.
See "Authenticating requests from Botmatic"
See "Handling other actions"
close([callback])
Properly shuts down the express server. If provided, calls callback when done.
Interfaces
ExternalAPIConsumer Interface
An ExternalAPIConsumer is used to communicate with your service's API. It should implement the set of methods descibed bellow, and handle authentication to your service.
createContact(contact) -> Promise<{success, id, error}>
Creates a contact.
The response success is true when:
- The contact has successfully been created
- The new id is returned
It is false when:
- The creation failed
- No id has been returned
An error occurred
const {success, id, error} = await consumer.createContact({"first_name": "Patrick", "last_name": "Chen"})
// success == true
// id is the id returned by your external servicegetContact(id) -> Promise<{success, contact, error}>
Fetches a contact by its id.
The response success is true when:
- A contact with the given
idis found
It is false when:
- No contact was found.
error=="resource not found" An error occurred
const {success, contact, error} = await cosumer.getContact(23)
// success == true
// contact == { first_name: "Patrick", last_name: "Chen" }getContactByEmail(email) -> Promise<{success, contact, error}>
Fetches a contact by its email.
The response success is true when:
- A contact with a matching
emailis found
It is false when:
- No matching contact was found.
error=="resource not found" An error occured
const {success, contact, error} = await consumer.getContactByEmail("patrick.chen@fake.org")
// success == true
// contact == { first_name: "Patrick", last_name: "Chen", email: "patrick.chen@fake.org" }listContacts([page=1], [limit=30]) -> Promise<{success, contacts, error}>
Fetches a paginated list of contacts.
The response success is true when:
- The request is successful
- If no contacts are found,
contactsis an empty array
The response success is false when:
- An error occured
const {success, contacts, error} = await consumer.listContacts(2, 30)
// success == true
// contacts is an array containing contacts from 31 to 61listAllContacts([page_size=30], callback) -> Promise<{success, contacts, error}>
Fetches all contacts, page by page, and calls callback with each page. callback may be called with an empty array.
Actually calls listContact() until it returns an empty array.
The response success is always true.
The response contacts will only contains the result of successful calls to listContacts()
const {success, contacts, error} = await consumer.listAllContacts(30, (contacts) => {
console.log(`Fetched ${contacts.length} contacts`)
})
console.log(`Fetched a total of ${contacts.length} contacts`)
// success == true
// contains all contacts
// Console shows :
// Fetched 30 contacts
// Fetched 13 contacts
// Fetched a total of 43 contactsupdateContact(contact) -> Promise<{success, error}>
Updates a contact.
The response success is true when the contact has successfully been updated.
It is false when:
- the contact was not found on the remote service.
error=="resource not found" - an error occurred
const {success, error} = await consumer.updateContact(contact)
// success == truedeleteContact(contact_id) -> Promise<{success, error}>
Deletes a contact.
The response success is true when the contact has successfully been deleted.
It is false when:
- the contact was not found on the remote service.
error="resrouce not found" - an error occured
const {success, error} = await consumer.deleteContact(29)
// success == trueImplementation example
const API_BASE = "http://dummy.api.com"
const CONTACT_ENDPOINT = `${API_BASE}/contacts`
const createContact = (contact) => {
return Promise(resolve => {
request(CONTACT_ENPOINT, {
method: 'post',
headers: {'content-type': 'application/json'},
json: contact
}, (err, responseBody) => {
if (!err) {
resolve({success: true, id: responseBody.id})
}
else {
resolve({success: false, error: err})
}
})
})
}
// Implement the other functions
module.exports = {
createContact
}KeyStore Interface
Because ids on Botmatic won't necessarily match ids on your external service, a KeyStore is used to store botmatic/external id pairs.
Although there is a @botmatic/js-redis-key-store implementing this interface with redis,
you can implement your own for any type storage your want.
All functions take an integrationId as first argument. It is recommended to use the token received upon installation as it unique per integration.
saveIds(integrationId, botmaticId, externalId) -> Promise<boolean>
Saves a botmatic/external id pair.
The returned Promise resolves to true in case of success, false in case of error.
getBotmaticId(integrationId, externalId) -> Promise<string | null>
Gets the botmatic id associated with the given external id.
The returned Promise resolves to the botmatic id, or null if none is found.
getExternalId(integrationId, botmaticId) -> Promise<string | null>
Gets the external id associated with the given botmatic id.
The returned Promise resolves to the external id, or null if none is found.
deleteIds(integrationId, botmaticId, externalId) -> Promise<boolean>
Deletes a botmatic/external id pair.
The returned Promise resolves to true in case of success, false in case of error.
deleteAllIds(integrationId) -> Promise<boolean>
Deletes all botmatic/external id pairs for a given integration.
The returned Promise resolves to true in case of success, false in case of error.
8 years ago