Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

An Impac! Bolt API is a plug-in unit of computations, comprising of multiple Widgets (and KPIs), that are made available to Impac! front-end, via Impac! API. 

Impac! v1 was essentially a big Bolt. Impac! v2 is instead a router, which forwards requests to Bolts. This allows for the engines to be modularised, and to be created & hosted by third parties. 

This doc will guide you through setting up our first Bolt, the Impac! Finance Bolt, and get you started with how to build a new widget using the frameworks currently in place. 


Table of Contents
maxLevel2
minLevel2

...

1.1. Configuring Connec! to send Webhooks to the Impac! bolt notifications router

 /wiki/spaces/DEV/pages/90439768

...

Bolts work differently than Impac! v1 widget engines when it comes to data retrieval. In Impac! v1 (caching aside), data is retrieved directly from Connec! before being run through calculations and returned to the client. What’s different with a Bolt is that it has its own database,

...

and when data is

...

updated in Connec!, the Bolt

...

can subscribe to webhooks to update its own database accordingly.

You can create an instance of Entity::Webhook in Connec! to register a service to receive webhooks when data is

...

created or updated. You can set up the Bolt with Connec! by creating an Entity::Webhook in the Connec! rails console like below (where root keys are the SystemIdentity credentials): 

Code Block
languagebash
titleCreate a webhook for your Bolt
Entity::Webhook.create!(name: 'Impac v2', endpoint: 'http://localhost:4000/api/v2/maestrano/finance/notifications', api_key: 'ROOT_KEY', api_secret: 'ROOT_SECRET', detailed_notifications: true, subscribed_entities: %w(Accounts Journals Company Invoices))


Alternatively, the Connec! webhooks can be managed via API, see doc: /wiki/spaces/DEV/pages/90439768.

Now your Bolt should be subscribed to Connec! webhooks and will receive all further data syncs.

1.2. Register the Impac! Finance Bolt with Impac!

...

Impac! API has a

...

If you take a look at your bolt in the rails console after creating it, it should have generated the api_key and api_secret credentials.

You must register the bolt with Impac! using these credentials as the IMPAC_KEY and IMPAC_SECRET in the Finance Bolt’s application.yml.

If you are having trouble accessing your pre-encrypted Impac! credentials, try using Postman to create the Bolt and retrieve its credentials:

...

model for registering Bolts, similar to the Entity::Webhook model in Connec!. A Bolt can also be created via the rails console or API.

For the rails console, the snippet below demonstrates creating the Bolt & accessing the generated credentials.

Code Block
b = Bolt.create(provider: 'maestrano', name: 'finance', protocol: 'http', host: 'localhost:4001', api_path: 'api/v1')
b.api_key
# => 'your decrypted api key'
b.api_secret
# => 'your decrypted api secret'


The credentials you have generated above are used to authenticate Impac! with the Bolt, these will be needed in the next step (1.3).

 POST request to ‘localhost:4000/api/v2/bolts/’ using Basic Auth with your root (SystemIdentity) credentials. In the request body, choose “raw” and “json”, and paste the following:

Code Block
titleCreating a bolt
{
    "bolts":         
{
     	"protocol": "http",
     	"provider": "maestrano",
     	"name": "finance",
     	“host": "localhost:4001",
     	"api_path": "api/v1"
   }
}

...

The api_key and api_secret

...

should be in the response, but note that they are only returned when the bolt is created (after which they are encrypted for the database),

...

and you would need the rails console to access them (like shown above).

1.3. Add Impac! credentials to the Impac! Finance Bolt to allow requests from Impac!

...

In the Impac! Finance Bolt’s config/application.yml

...

file, add your bolt api key & secret generated in step 1.2.

Code Block
titleImpac! Finance Bolt application.yml
ROOT_KEY = # From MnoHub: SystemIdentity.first.api_key
ROOT_SECRET = # From MnoHub: SystemIdentity.first.api_secret
IMPAC_KEY = # From Impac: the api_key generated when your bolt is created *see above'bolt-api-key'
IMPAC_SECRET = # From Impac: the api_secret generated when your bolt is created *see above
# These are the Maestrano account pusher keys for development environments
PUSHER_APP_ID: '277973'
PUSHER_KEY: '67da20b2dc7a407dc522'
PUSHER_SECRET: '7fc40a6e8a78bf9ecf1d'
OPENEXCHANGERATES_APP_ID: d8091e280ec44002ae37ede2a63167d2'bolt-api-secret'


1.4. Populating the Impac! Finance Bolt with data

...

If using a new organization, just create records with linked apps, and run a sync! Data will be pushed to the Bolt via Connec! & Impac! webhooks. 

If you are not using a new organization, note that as of right now, nothing has been implemented to make sure the Bolt is up to date with Connec!, so if the Bolt is linked up after data has already been synced with Connec!, it will miss out on the webhooks and will have nothing in its own database.

If you run into this issue, you can catch your Bolt up with your current database using the following:

With all of the components running on your machine, run the script below in the Connec! rails console, replacing the webhook_id with the id of the Entity::Webhook you just created, and the channel_id with your organization id:

Code Block
titleCatching up your Bolt's database
channel_id = 'org-fbbj'
[Entity::Account, Entity::Company, Entity::Invoice, Entity::Journal].each do |klass|
 k = klass.to_s.demodulize.pluralize
 k = 'Company' if k == Entity::Company
 klass.on_channel(channel_id).all.each do |entity|
   args = {
     messages: [
       { channel_id: channel_id, entity: k, id: entity.id.to_s }.to_json
     ],
     webhook_id: "5989cc91251fe7107052547e"
   }
   Webhooks::Notifier.notify(args)
 end
end

...

2. Building your first Bolt Widget

The dashboard service in Impac! Angular fetches the widgets from the Impac! Finance Bolt with a request to /api/v2/maestrano/finance/widgets/.

In the Bolt, your first step is to add the name of your new widget to the WIDGETS_LIST so that it may be included in the response for this endpoint.

Code Block
titleAdd new widget to bolt widgets endpoint
class Api::V1::WidgetsController < ApplicationController
	...
	WIDGETS_LIST = %w(cash_balance cash_projection your_new_widget).freeze
	...
end

The next step is to create the model for your new widget and let it inherit the base widget class. Create this file in the /app/models/widgets/ directory.

Code Block
titleNew widget class
class Widgets::YourNewWidget < Widgets::Base
end

Before you start with the calculations, think about what you want your widget to display. Will your widget hold a chart? A grouped table? Both? Your new widget’s model will use data from the Bolt’s database to manipulate and run the calculations necessary for it’s requested report. The report output, however, is based on the design of your widget.

These displays (chart, table, grouped_table, etc) have reporting layouts, which are unique data structures that determine how a widget will return the result of its calculation. Layouts are predefined, and will maintain the same data structure regardless of any requested report.

The data structures have been designed with the intentions of being directly usable by a reporting front-end or charting library with little transformation.

Every report will be output to one or several layouts. You must define a SUPPORTED_LAYOUTS constant for each individual widget (an array of the layouts for your report). The supported layouts will be listed for each widget at the public endpoint (/api/v2/maestrano/finance/widgets/). 

Code Block
languageruby
titleSupported Layouts
class Widgets::YourNewWidget < Widgets::Base
	...
	SUPPORTED_LAYOUTS = %w(chart grouped_table).freeze
	...
end

A compute method must be defined for each widget, and it must return self. It fetches the report and calculates the widget. 

Code Block
languageruby
titleCompute Method
...
	def compute
		...
		return self
	end
...

The compute method will fetch the calculated data you need for your report, but you still have to fill your layout objects for rendering. Each layout has a class defining its structure, and can be filled with your report data by defining a fill method ("fill_#{layout.name}") which adheres to that structure. 

Code Block
languageruby
titleLayout class defines structure
class Layouts::GroupedTable < Layouts::Base
  attr_accessor :title, :headers, :groups

  def initialize
    self.headers = []
    self.groups = []
    super
  end
end

...