Skip to content

Commit

Permalink
Merge pull request #26 from domapic/release-1.0.0-alpha.5
Browse files Browse the repository at this point in the history
Release 1.0.0 alpha.5
  • Loading branch information
javierbrea authored Dec 17, 2018
2 parents ad98105 + da76325 commit bed2517
Show file tree
Hide file tree
Showing 22 changed files with 354 additions and 31 deletions.
4 changes: 4 additions & 0 deletions .narval.yml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ suites:
specs:
- test/end-to-end/specs/service-connection.specs.js
- test/end-to-end/specs/console-registered.specs.js
- test/end-to-end/specs/console-plugin-config-registered.specs.js
- test/end-to-end/specs/controller-action-handler.specs.js
- test/end-to-end/specs/controller-state-handler.specs.js
coverage: *disable-coverage
Expand All @@ -503,6 +504,7 @@ suites:
specs:
- test/end-to-end/specs/service-connection.specs.js
- test/end-to-end/specs/console-registered.specs.js
- test/end-to-end/specs/console-plugin-config-registered.specs.js
- test/end-to-end/specs/controller-action-handler.specs.js
- test/end-to-end/specs/controller-state-handler.specs.js
coverage: *disable-coverage
Expand All @@ -523,6 +525,7 @@ suites:
specs:
- test/end-to-end/specs/service-connection.specs.js
- test/end-to-end/specs/console-registered.specs.js
- test/end-to-end/specs/console-plugin-config-registered.specs.js
- test/end-to-end/specs/controller-action-handler.specs.js
- test/end-to-end/specs/controller-state-handler.specs.js
coverage: *disable-coverage
Expand Down Expand Up @@ -578,6 +581,7 @@ suites:
specs:
- test/end-to-end/specs/service-connection.specs.js
- test/end-to-end/specs/console-changed.specs.js
- test/end-to-end/specs/console-plugin-config-changed.specs.js
- test/end-to-end/specs/controller-action-handler.specs.js
- test/end-to-end/specs/controller-state-handler.specs.js
coverage: *disable-coverage
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
### Removed

## [1.0.0-alpha.5] - 2018-12-17
### Added
- Add servicePluginConfigs client
- Add servicePluginConfigs events tests
- Add "addPluginConfig" method. Register plugin configurations on service connection.

### Changed
- Upgrade domapic-controller version in end-to-end tests.

## [1.0.0-alpha.4] - 2018-12-09
### Added
- Expose errors constructors to services
Expand Down
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@

* [Modules](#modules)
* [Creating a module](#creating-a-module)
* [Abilities](#registering-abilities)
* [Abilities](#abilities)
* [Action](#action)
* [State](#state)
* [Event](#event)
* [Plugins configurations](#plugins-configurations)
* [Plugins](#plugins)
* [Creating a plugin](#creating-a-plugin)
* [Controller events listeners](#controller-events-listeners)
Expand Down Expand Up @@ -61,7 +62,7 @@ Modules can be created with few lines of code. Here is an example of a module co
"version": "1.0.0",
"description": "Domapic module controlling a relay",
"dependencies": {
"domapic-service": "1.0.0-alpha.3"
"domapic-service": "1.0.0-alpha.5"
}
}
```
Expand Down Expand Up @@ -123,6 +124,7 @@ Returns a module instance, containing:
* `set(key [, value])` - Sets `value` for provided `key` into module storage. Returns a promise.
* `api` - Object containing methods for [extending the built-in api](#extending-api).
* `register(abilitiesData)` - Register provided abilities into the module. Read the [abilities](#abilities) chapter for further info.
* `addPluginConfig(pluginConfigs)` - Add module default configurations for domapic plugins. Read the [Plugins configurations](#plugins-configurations) chapter for further info.
* `start` - Starts the server.
* `events`- [Node.js emitter object][nodejs-events-url]. Used to emit abilities events to the controller.
* `errors` - Domapic errors constructors. Useful for rejecting abilities handlers with specific http errors. For further info read the [errors chapter in the domapic-base documentation][domapic-base-url]
Expand Down Expand Up @@ -190,10 +192,31 @@ In this example, the action's `handler` method returned `Promise.resolve(true)`.
When [ability](#abilities) has an `event` property defined, the `emit` method of the module's `events` object can be used to emit the ability event, passing the correspondant data. This will produce module calling to controller api to inform about the trigered event.

```js
module.events.emit('switch', true)
dmpcModule.events.emit('switch', true)
```
In this example, the module will call to the correspondant controller api resource, passing `true` as data.

### Plugins configurations

Some Domapic Plugins require extra configurations for modules _(as an example, the `homebridge-domapic-plugin` needs to map the module abilities to an specific HomeKit accesory type, in order to be controlled by Siri)_. The modules can define a default configuration for certain types of Domapic Plugins. This configurations can be modified afterwards by users through the Controller UI in order to allow more customization, _(for example, a `contact-sensor-domapic-module` sometimes needs to be mapped to a `Door` accesory, and sometimes to a `Window` accesory)_

For defining default plugins configurations, the `addPluginConfig` method is available in the module instances:

#### `addPluginConfig(pluginConfigs)`

Add plugin configuration or configurations (can receive an array of configurations too). Each configuration must have the next properties:
* `pluginPackageName` `<string>` Package name of the plugin for which the configuration is destined.
* `config` `<object>` Object containing the plugin configuration. Its format depends of each specific plugin configuration requisites.

```js
dmpcModule.addPluginConfig({
pluginPackageName: 'homebridge-domapic-plugin',
config: {
foo: 'foo-plugin-config'
}
})
```

## Plugins

A Domapic Plugin is an action-event-state based REST API Micro-service that can "extend" the Domapic Controller functionality. Once it is connected with the Controller, it will receive events each time an entity is created, modified or deleted in the Controller. Plugins will be informed too about all module events or actions received by the Controller. Plugins also have an interface to the Controller, that allows to perform actions such as consult module states, dispatch module actions, create users, etc.
Expand Down Expand Up @@ -281,6 +304,9 @@ All available events are:
* service:created
* service:updated
* service:deleted
* servicePluginConfig:created
* servicePluginConfig:updated
* servicePluginConfig:deleted
* user:created
* user:updated
* user:deleted
Expand Down Expand Up @@ -308,9 +334,13 @@ A Domapic Plugin provides an interface that allows to perform operations into th
* `users` - Interface for Controller's "user" entities:
* `me()` - Returns data about plugin user
* `get([id][,filter])` - Returns users data. Because of security reasons, only "operator" users will be returned. Request can be filtered providing an specific user id as \<String\>, or an \<Object\> containing any other api supported filter (such as `{name:'foo-name'}`).
* `create(userData)` - Creates and user, and returns the new `id`. Only creating users with "operator" role is supported.
* `create(userData)` - Creates an user, and returns the new `id`. Only creating users with "operator" role is supported.
* `services`- Interface for Controller's "service" entities:
* `get([id][,filter])` - Returns services data. Request can be filtered providing an specific service id as \<String\>, or an \<Object\> containing any other api supported filter (such as `{type:'module'}`).
* `servicePluginConfigs`- Interface for Controller's "servicePluginConfigs" entities:
* `get([id][,filter])` - Returns servicePluginConfigs data. Request can be filtered providing an specific servicePluginConfig id as \<String\>, or an \<Object\> containing any other api supported filter (such as `{service:'service-id', 'plugin-package-name': 'foo-plugin-package-name'}`).
* `create(configData)` - Creates a service plugin configuratin, and returns the new `id`.
* `update(id, configData)` - Updates an specific service plugin configuration.
* `abilities`- Interface for Controller's "ability" entities:
* `get([id][,filter])` - Returns abilities data. Request can be filtered providing an specific ability id as \<String\>, or an \<Object\> containing any other api supported filter (such as `{service:'foo-module-id'}`).
* `state(id)` - Returns state of provided ability.
Expand Down
31 changes: 31 additions & 0 deletions lib/ApiClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ const ApiClient = function (service, url, apiKey) {

const getCreatedId = response => Promise.resolve(response.headers.location.split('/').pop())

// auth tokens
const getAuthTokens = filter => client.get(uris.query(uris.authTokens(), filter))

const createApiKey = data => client.post(uris.authApiKey(), data)

// users
const getUsers = filter => {
if (filter) {
return client.get(uris.query(uris.users(), filter))
Expand Down Expand Up @@ -42,6 +44,7 @@ const ApiClient = function (service, url, apiKey) {

const getUserMe = () => client.get(uris.usersMe())

// services
const getService = id => client.get(uris.service(id))

const getServices = filter => {
Expand All @@ -59,6 +62,25 @@ const ApiClient = function (service, url, apiKey) {

const updateService = (id, data) => client.patch(uris.service(id), data)

// servicePluginConfigs
const getServicePluginConfig = id => client.get(uris.servicePluginConfig(id))

const getServicePluginConfigs = filter => {
if (_.isString(filter)) {
return getServicePluginConfig(filter)
}
if (filter) {
return client.get(uris.query(uris.servicePluginConfigs(), filter))
}
return client.get(uris.servicePluginConfigs())
}

const createServicePluginConfig = data => client.post(uris.servicePluginConfigs(), data)
.then(getCreatedId)

const updateServicePluginConfig = (id, data) => client.patch(uris.servicePluginConfig(id), data)

// abilities
const getAbility = id => client.get(uris.ability(id))

const getAbilities = filter => {
Expand All @@ -84,26 +106,35 @@ const ApiClient = function (service, url, apiKey) {

const sendAbilityAction = (id, data) => client.post(uris.abilityAction(id), data)

// logs
const getLogs = () => client.get(uris.logs())

return {
getAuthTokens,
createApiKey,

getUsers,
getOperatorUsers,
createUser,
createOperatorUser,
getUserMe,

getServices,
createService,
updateService,

getServicePluginConfigs,
createServicePluginConfig,
updateServicePluginConfig,

getAbilities,
createAbility,
updateAbility,
deleteAbility,
getAbilityState,
sendAbilityEvent,
sendAbilityAction,

getLogs
}
}
Expand Down
25 changes: 24 additions & 1 deletion lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const OMIT_IN_ABILITIES_COMPARATION = [
'updatedAt'
]

const Client = function (service, controllerData, serviceData, serviceAbilities) {
const Client = function (service, controllerData, serviceData, serviceAbilities, pluginConfigs) {
const abilities = _.cloneDeep(serviceAbilities)

const state = {
Expand Down Expand Up @@ -144,6 +144,28 @@ const Client = function (service, controllerData, serviceData, serviceAbilities)
})
}

const addPluginConfig = pluginConfig => {
return apiClient.getServicePluginConfigs({
service: controllerData.serviceId,
'plugin-package-name': pluginConfig.pluginPackageName
}).then(pluginConfigs => {
if (pluginConfigs.length) {
return Promise.resolve()
}
return apiClient.createServicePluginConfig({
_service: controllerData.serviceId,
...pluginConfig
})
})
}

const addPluginConfigs = () => {
if (!pluginConfigs.length) {
return Promise.resolve()
}
return Promise.map(pluginConfigs, addPluginConfig)
}

const checkAbilityCorrespondence = function (remoteAbility) {
if (remoteAbility._service !== controllerData.serviceId) {
return Promise.resolve()
Expand Down Expand Up @@ -316,6 +338,7 @@ const Client = function (service, controllerData, serviceData, serviceAbilities)
.then(doLogin)
.then(storeControllerData)
.then(registerService)
.then(addPluginConfigs)
.then(registerAbilities)
.catch(service.errors.Forbidden, () => {
return Promise.reject(new service.errors.Forbidden(templates.compiled.controllerAuthError()))
Expand Down
17 changes: 15 additions & 2 deletions lib/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const Connection = function (service, security) {
let _serviceIdPromise
let _client
let _abilities = []
let _pluginConfigs = []

const getConfigurationProperty = function (key, errorMessage) {
return service.config.get(key)
Expand Down Expand Up @@ -89,7 +90,7 @@ const Connection = function (service, security) {
}

const openConnection = function (controllerData, serviceData) {
_client = new Client(service, controllerData, serviceData, _abilities)
_client = new Client(service, controllerData, serviceData, _abilities, _pluginConfigs)
return _client.connect()
.then(() => {
return service.tracer.info(templates.compiled.connected(controllerData))
Expand Down Expand Up @@ -225,6 +226,11 @@ const Connection = function (service, security) {
services: {
get: new ControllerClient('getServices')
},
servicePluginConfigs: {
get: new ControllerClient('getServicePluginConfigs'),
create: new ControllerClient('createServicePluginConfig'),
update: new ControllerClient('updateServicePluginConfig')
},
abilities: {
get: new ControllerClient('getAbilities'),
state: new ControllerClient('getAbilityState'),
Expand All @@ -235,13 +241,20 @@ const Connection = function (service, security) {
}
}

const addPluginConfig = pluginConfig => {
const configs = _.isArray(pluginConfig) ? pluginConfig : [pluginConfig]
_pluginConfigs = [..._pluginConfigs, ...configs]
return Promise.resolve()
}

return {
addApi,
connect,
setType,
sendAbilityEvent,
addAbility,
controllerClient
controllerClient,
addPluginConfig
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/api/events.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"entity": {
"description": "Controller entity to which event is related",
"type": "string",
"enum": ["ability", "service", "user"]
"enum": ["ability", "service", "user", "servicePluginConfig"]
},
"operation": {
"description": "Operation applied to entity",
Expand Down
13 changes: 11 additions & 2 deletions lib/serviceHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ class ServiceHandler {

this.start = this.start.bind(this)
this.connect = this.connect.bind(this)
this.addPluginConfig = this.addPluginConfig.bind(this)
}

addPluginConfig (pluginConfig) {
if (this.started) {
return Promise.reject(templates.compiled.addPluginConfigServerStarted())
}
return this.connection.addPluginConfig(pluginConfig)
}

getControllerStorageData () {
Expand All @@ -35,7 +43,7 @@ class ServiceHandler {
controllerApiKey: storage.controllerData.apiKey
})
}
return Promise.reject(this.service.errors.NotFound(templates.compiled.noControllerFoundInStorage))
return Promise.reject(this.service.errors.NotFound(templates.compiled.noControllerFoundInStorage()))
})
}

Expand Down Expand Up @@ -96,7 +104,8 @@ class ServiceHandler {
addOperations: this.service.server.addOperations
},
start: this.start,
events
events,
addPluginConfig: this.addPluginConfig
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion lib/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ const templates = {

connectingUsingStorage: 'Trying to connect with Controller using last connection data',
connectingUsingUserAndConfig: 'Trying to connect with Controller using user data from storage and configuration data',
connectingIgnoringStorage: 'Trying to connect with Controller only with configuration. Ignoring storage'
connectingIgnoringStorage: 'Trying to connect with Controller only with configuration. Ignoring storage',

addPluginConfigServerStarted: 'Adding plugin configurations once the server is started is not allowed'
}

module.exports = {
Expand Down
6 changes: 6 additions & 0 deletions lib/uris.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const EVENT_HANDLER_PATH = 'event'
// Controller
const AUTH = 'auth'
const SERVICES = 'services'
const SERVICE_PLUGIN_CONFIGS = 'service-plugin-configs'
const USERS = 'users'
const LOGS = 'logs'

Expand All @@ -33,6 +34,9 @@ const authApiKey = () => resolveUri(AUTH, 'apiKey')
const services = () => SERVICES
const serviceUri = id => resolveUri(SERVICES, id)

const servicePluginConfigs = () => SERVICE_PLUGIN_CONFIGS
const servicePluginConfig = id => resolveUri(SERVICE_PLUGIN_CONFIGS, id)

const abilities = () => ABILITIES
const ability = id => resolveUri(ABILITIES, id)
const abilityEvent = id => resolveUri(ABILITIES, id, EVENT_HANDLER_PATH)
Expand All @@ -57,6 +61,8 @@ module.exports = {
authApiKey,
services,
service: serviceUri,
servicePluginConfigs,
servicePluginConfig,
abilities,
ability,
abilityEvent,
Expand Down
Loading

0 comments on commit bed2517

Please sign in to comment.