Skip to content

Commit

Permalink
Merge pull request #50 from domapic/release-1.0.0-beta.2
Browse files Browse the repository at this point in the history
Release 1.0.0 beta.2
  • Loading branch information
javierbrea committed Mar 3, 2019
2 parents 3eac587 + 6053dc9 commit 4b2aa17
Show file tree
Hide file tree
Showing 44 changed files with 1,536 additions and 284 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ npm-debug.log
/.test
/.narval
/.coverage
/.domapic

2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ addons:
script:
- npm test
- npm run coveralls
- sonar-scanner
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then sonar-scanner; fi'
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
### Removed

## [1.0.0-beta.2] - 2019-03-02
### Added
- Add web ui
- Add socket.io server
- Emit events to socket authenticated users connected using sockets
- Add page and ability filters to logs api
- Add logs/stats api

### Fixed
- Add "anonymous" role to swagger. Now anomnymous user has anonymous role.
- Fix logs capped collection max size

### Changed
- Use validator library for emails and uri validations

## [1.0.0-beta.1] - 2019-01-08
### Added
- Add DELETE method to servicePluginConfigs api
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ Controller server for Domapic domotic systems.
Connect all your Domapic Modules and control them with a single application. <!-- and program them to interact automatically using the provided web interface
Install Domapic plugins to connect Domapic with other domotic systems or online services. -->

__NOTE: The next schema includes some Domapic pieces that are still not released. The web ui for the Controller, Domapic Cloud, mobile apps and Alexa plugins will be available soon.__
__NOTE: The next schema includes some Domapic pieces that are still not released. The Domapic Cloud plugin, mobile apps and Alexa plugin will be available soon.__

![Domapic system example][domapic-example-image]

> Above, an example of two modules in a [Domapic System][website-url]. Now, the relay can be controlled using the web or mobile applications, or interacting with ["Alexa"][alexa-url] or ["HomeKit"][homekit-url]. Automatisms can be configured in the [Domapic Controller Web UI][domapic-controller-url] to make the [_Phillips Hue_][hue-url] bulb be switched off automatically when the relay bulb is switched on, for example.
> Above, an example of two modules in a [Domapic System][website-url]. Now, the relay can be controlled using the web or mobile applications, or interacting with ["Alexa"][alexa-url] or ["HomeKit"][homekit-url]. _Automatisms can be configured in the [Domapic Controller Web UI][domapic-controller-url] to make the [_Phillips Hue_][hue-url] bulb be switched off automatically when the relay bulb is switched on, for example. (Automatisms feature is still in development)_
## Prerequisites

Expand All @@ -62,9 +62,11 @@ npm i domapic-controller -g --production
domapic-controller start
```

The controller process will be started at background (using [PM2][pm2-url] as manager). Now you can browse to [http://localhost:3000](http://localhost:3000) to check that the server has started successfully. A __Swagger UI__ describing the server api will be available at that url.
The controller process will be started at background (using [PM2][pm2-url] as manager). Now you can browse to [http://localhost:3000](http://localhost:3000) to check that the server has started successfully.

![Swagger example][swagger-example-image]
The Domapic Controller Web User interface will be available at that url. If you are loading the web interface from localhost, login is not necessary because authentication is disabled by default for the 172.0.0.1 IP. If not, use the "admin" user with "admin" password. Remember to add your own administrator user and delete the default one.

![Swagger example][domapic-controller-ui-example-image]

> Note that, with basic options, server will be started over `http` protocol, and security will be disabled for localhost requests. Read about all [options](#options) and [security](#security) to start the controller with stricter security options.
Expand Down Expand Up @@ -161,7 +163,9 @@ Follow the next steps to securize your Controller before exposing it to the inte

* __Setup an administrator user:__

> The Controller is distributed with a default administrator user, which name is "admin", and password is "admin". Delete it and setup your own administrator user:
> The Controller is distributed with a default administrator user, which name is "admin", and password is "admin". Delete it and setup your own administrator user.

You can use both the web user interface or the command line interface in order to remove the user:

```
domapic-controller user remove admin
Expand All @@ -188,7 +192,7 @@ Follow the next steps to securize your Controller before exposing it to the inte

[domapic-logo-image]: http://domapic.com/assets/domapic-logo.png
[domapic-example-image]: http://domapic.com/assets/domapic-schema-example_01.png
[swagger-example-image]: http://domapic.com/assets/swagger-example.jpg
[domapic-controller-ui-example-image]: http://domapic.com/assets/domapic-controller-ui-screenshots.jpg

[coveralls-image]: https://coveralls.io/repos/github/domapic/domapic-controller/badge.svg
[coveralls-url]: https://coveralls.io/github/domapic/domapic-controller
Expand Down
13 changes: 7 additions & 6 deletions lib/api/abilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const Operations = (service, commands) => {
handler: (params, body, res) => commands.ability.update(params.path.id, body).then(abilityData => {
res.status(204)
res.header(LOCATION, `${LOCATION_ROOT}${abilityData._id}`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.UPDATE, abilityData)
events.all(EVENT_ENTITY, events.OPERATIONS.UPDATE, abilityData)
return Promise.resolve()
})
},
Expand All @@ -47,15 +47,15 @@ const Operations = (service, commands) => {
.then(abilityOwner => commands.ability.add(abilityOwner, body).then(abilityData => {
res.status(201)
res.header(LOCATION, `${LOCATION_ROOT}${abilityData._id}`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.CREATE, abilityData)
events.all(EVENT_ENTITY, events.OPERATIONS.CREATE, abilityData)
return Promise.resolve()
}))
},
deleteAbility: {
auth: onlyOwner,
handler: (params, body, res, userData) => commands.ability.remove(params.path.id).then(() => {
res.status(204)
events.plugin(EVENT_ENTITY, events.OPERATIONS.DELETE, {
events.all(EVENT_ENTITY, events.OPERATIONS.DELETE, {
_id: params.path.id
})
return Promise.resolve()
Expand All @@ -65,7 +65,7 @@ const Operations = (service, commands) => {
handler: (params, body, res) => commands.composed.dispatchAbilityAction(params.path.id, body).then(serviceResponse => {
res.status(201)
res.header(LOCATION, `${LOCATION_ROOT}${params.path.id}/state`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.ACTION, {
events.all(EVENT_ENTITY, events.OPERATIONS.ACTION, {
_id: params.path.id,
...body
})
Expand All @@ -80,10 +80,11 @@ const Operations = (service, commands) => {
handler: (params, body, res) => commands.composed.triggerAbilityEvent(params.path.id, body).then(() => {
res.status(201)
res.header(LOCATION, `${LOCATION_ROOT}${params.path.id}/state`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.EVENT, {
const eventData = {
_id: params.path.id,
...body
})
}
events.all(EVENT_ENTITY, events.OPERATIONS.EVENT, eventData)
return Promise.resolve()
})
}
Expand Down
4 changes: 2 additions & 2 deletions lib/api/abilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"type": {
"description": "Type of ability data",
"type": "string",
"enum": ["string", "boolean", "number", "integer", "float"]
"enum": ["string", "boolean", "number"]
},
"format": {
"description": "Format of ability data",
Expand Down Expand Up @@ -191,7 +191,7 @@
"type": {
"description": "Type of ability data",
"type": "string",
"enum": ["string", "boolean", "number", "integer", "float"]
"enum": ["string", "boolean", "number"]
},
"format": {
"description": "Format of ability data",
Expand Down
25 changes: 24 additions & 1 deletion lib/api/logs.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
'use strict'

const { omitBy, isUndefined } = require('lodash')

const definition = require('./logs.json')

const abilityFilter = params => omitBy({_ability: params.query.ability}, isUndefined)

const parse = {
params: {
page: page => Number(page)
}
}

const Operations = (service, commands) => ({
getLogs: {
handler: () => commands.log.getAll()
handler: params => {
const filter = abilityFilter(params)
if (params.query.page) {
return commands.log.getPaginated(params.query.page, filter)
}
return commands.log.getAll(filter)
},
parse
},
getLogsStats: {
handler: params => {
return commands.log.getStats(abilityFilter(params))
},
parse
}
})

Expand Down
70 changes: 70 additions & 0 deletions lib/api/logs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"tags": [{
"name": "log",
"description": "Controller events and actions logs"
},{
"name": "stats",
"description": "Stats"
}],
"components": {
"schemas": {
Expand Down Expand Up @@ -50,12 +53,44 @@
"items": {
"$ref": "#/components/schemas/Log"
}
},
"LogsStats": {
"description": "Log stats data",
"type": "object",
"properties": {
"total": {
"description": "Total number of registered logs",
"type": "number"
}
},
"additionalProperties": false,
"example": {
"total": "1547"
}
}
}
},
"paths": {
"/logs": {
"get": {
"parameters": [
{
"in": "query",
"name": "ability",
"schema": {
"type": "string"
},
"description": "Filter logs by ability"
},
{
"in": "query",
"name": "page",
"schema": {
"type": "string"
},
"description": "Returns only logs of received page. Each page contains 10 logs"
}
],
"tags": ["log", "action", "event", "ability"],
"summary": "Return logs",
"description": "Returns an array with all events and actions logs",
Expand All @@ -78,6 +113,41 @@
"apiKey": []
}]
}
},
"/logs/stats": {
"get": {
"parameters": [
{
"in": "query",
"name": "ability",
"schema": {
"type": "string"
},
"description": "Filter stats by ability"
}
],
"tags": ["log", "action", "event", "ability", "stats"],
"summary": "Returns logs stats",
"description": "Returns logs stats",
"operationId": "getLogsStats",
"responses": {
"200": {
"description": "Logs stats response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LogsStats"
}
}
}
}
},
"security": [{
"jwt": []
}, {
"apiKey": []
}]
}
}
}
}
6 changes: 3 additions & 3 deletions lib/api/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const Operations = (service, commands) => ({
handler: (params, body, res) => commands.service.update(params.path.id, body).then(serviceData => {
res.status(204)
res.header('location', `/api/services/${serviceData._id}`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.UPDATE, serviceData)
events.all(EVENT_ENTITY, events.OPERATIONS.UPDATE, serviceData)
return Promise.resolve()
})
},
Expand All @@ -41,15 +41,15 @@ const Operations = (service, commands) => ({
.then(serviceData => {
res.status(201)
res.header('location', `/api/services/${serviceData._id}`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.CREATE, serviceData)
events.all(EVENT_ENTITY, events.OPERATIONS.CREATE, serviceData)
return Promise.resolve()
}))
},
deleteService: {
auth: onlyAdmin,
handler: (params, body, res) => commands.composed.removeService(params.path.id).then(() => {
res.status(204)
events.plugin(EVENT_ENTITY, events.OPERATIONS.DELETE, {
events.all(EVENT_ENTITY, events.OPERATIONS.DELETE, {
_id: params.path.id
})
return Promise.resolve()
Expand Down
8 changes: 4 additions & 4 deletions lib/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const Operations = (service, commands) => {
.then(user => {
res.status(201)
res.header(LOCATION, `${LOCATION_ROOT}${user._id}`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.CREATE, user)
events.all(EVENT_ENTITY, events.OPERATIONS.CREATE, user, [roles.ADMIN, roles.ANONYMOUS])
return Promise.resolve()
})
},
Expand Down Expand Up @@ -68,17 +68,17 @@ const Operations = (service, commands) => {
handler: (params, body, res) => commands.user.update(params.path.id, body).then(user => {
res.status(204)
res.header(LOCATION, `${LOCATION_ROOT}${user._id}`)
events.plugin(EVENT_ENTITY, events.OPERATIONS.UPDATE, user)
events.all(EVENT_ENTITY, events.OPERATIONS.UPDATE, user, [roles.ADMIN, roles.ANONYMOUS])
return Promise.resolve()
})
},
deleteUser: {
auth: onlyAdmin,
handler: (params, body, res) => commands.composed.removeUser(params.path.id).then(() => {
res.status(204)
events.plugin(EVENT_ENTITY, events.OPERATIONS.DELETE, {
events.all(EVENT_ENTITY, events.OPERATIONS.DELETE, {
_id: params.path.id
})
}, [roles.ADMIN, roles.ANONYMOUS])
return Promise.resolve()
})
},
Expand Down
2 changes: 1 addition & 1 deletion lib/api/users.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"role": {
"description": "Role assigned to the user",
"type": "string",
"enum": ["admin", "operator", "module", "plugin", "service-registerer"]
"enum": ["admin", "operator", "module", "plugin", "service-registerer", "anonymous"]
},
"createdAt": {
"description": "Creation date timestamp",
Expand Down
28 changes: 25 additions & 3 deletions lib/commands/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,40 @@

const PUBLIC_FIELDS = '_ability type data createdAt'

const PAGE_SIZE = 10

const events = require('../events')
const EVENT_ENTITY = 'log'

const Commands = (service, models, client) => {
const add = logData => {
const newLog = new models.Log(logData)
return newLog.save()
.then(() => Promise.resolve(newLog))
.then(() => {
events.socket(EVENT_ENTITY, events.OPERATIONS.CREATE, newLog)
return Promise.resolve(newLog)
})
}

const getAll = () => models.Log.find({}, PUBLIC_FIELDS)
const getAll = (filter = {}) => models.Log.find(filter, PUBLIC_FIELDS).sort([['createdAt', -1]])

const getPaginated = (page, filter = {}) => models.Log.find(filter, PUBLIC_FIELDS, {
skip: (page - 1) * PAGE_SIZE,
limit: PAGE_SIZE
}).sort([['createdAt', -1]])

const getStats = filter => {
const countPromise = filter ? models.Log.countDocuments(filter) : Promise.resolve(models.Log.estimatedDocumentCount())
return countPromise.then(total => ({
total
}))
}

return {
add,
getAll
getAll,
getPaginated,
getStats
}
}

Expand Down
Loading

0 comments on commit 4b2aa17

Please sign in to comment.