Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/add-mAttachTenant #31

Merged
merged 36 commits into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
aeb5c04
feat/add-mAttachTenant
rolljee Mar 2, 2021
5550d7e
Lint
rolljee Mar 2, 2021
573245b
Lint
rolljee Mar 2, 2021
469efd1
Remove ?. syntax
rolljee Mar 2, 2021
bd1c5c4
Add documentation
rolljee Mar 3, 2021
529e622
Update doc/1/controllers/sensor/mattach-tenant/index.md
rolljee Mar 3, 2021
b798874
Update features/SensorController.feature
rolljee Mar 3, 2021
27ba94f
Update lib/controllers/SensorController.ts
rolljee Mar 3, 2021
23fa990
Update lib/controllers/SensorController.ts
rolljee Mar 3, 2021
259b5f4
Update lib/controllers/SensorController.ts
rolljee Mar 3, 2021
b896327
Update lib/controllers/SensorController.ts
rolljee Mar 3, 2021
98410a6
Update lib/controllers/SensorController.ts
rolljee Mar 3, 2021
db06c76
Update lib/controllers/SensorController.ts
rolljee Mar 3, 2021
cf1f49c
Requested changes
rolljee Mar 3, 2021
bd42756
Add function to parse mRequest
rolljee Mar 4, 2021
a02037b
Rename a function
rolljee Mar 4, 2021
96515b9
merge 1-dev
rolljee Mar 5, 2021
4a97ad3
Requested changes
rolljee Mar 8, 2021
0d7e32d
Merge ok
rolljee Mar 8, 2021
50d4ecd
Fix issue with mAttatchSensors tests
rolljee Mar 8, 2021
ae0ac60
Remove debug leftover
rolljee Mar 8, 2021
8fb0279
Fix tests
rolljee Mar 8, 2021
10fae65
Use global.kuzzle.config.limits.documentsWriteCount to avoid issue wi…
rolljee Mar 8, 2021
6b560c2
Requested changes
rolljee Mar 8, 2021
e04e093
Make writeToDatabase function more generic
rolljee Mar 8, 2021
d442755
enhancement of writeToDatabase using kuzzle limits & Add custom test …
rolljee Mar 9, 2021
c469729
Update docs & add a part in the guide for mAttach
rolljee Mar 9, 2021
453b230
merge 1-dev
rolljee Mar 10, 2021
b666fe1
Adds laius about format, correct some spelling mistakes and modify fi…
rolljee Mar 10, 2021
14dfe31
Update docs and rename mAttach to m-attach in directory structure
rolljee Mar 10, 2021
de92bb7
Update doc/1/controllers/sensor/m-attach/index.md
rolljee Mar 10, 2021
465110f
Update doc/1/controllers/sensor/m-attach/index.md
rolljee Mar 10, 2021
7e0e2cb
Update doc/1/controllers/sensor/m-attach/index.md
rolljee Mar 10, 2021
ac9ffb1
fix left mAttachtenant to mAttach
rolljee Mar 10, 2021
eb706cd
Fix wrong identation
rolljee Mar 10, 2021
08694c0
Update doc/1/controllers/sensor/m-attach/index.md
rolljee Mar 10, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions doc/1/controllers/sensor/m-attach/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
code: true
type: page
title: mAttachTenant
rolljee marked this conversation as resolved.
Show resolved Hide resolved
description: Attach multiple sensors to multiple tenants index
---

# mAttachTenant
rolljee marked this conversation as resolved.
Show resolved Hide resolved

Attach multiple sensors to multiple tenants.

The sensor document will be duplicated inside the tenant "sensors" collection.

---

## Query Syntax

### HTTP

``` http
URL: http://kuzzle:7512/_/device-manager/sensors/_mAttach
Method: PUT
Body:
```

``` js
{
// Using JSON
"records" [{
"tenantId": "tenant-kuzzle",
"sensorId": "test-id"
}],
// Using CSV syntax
"csv": "tenant,id\ntenant-kuzzle,test-id"
}
```

### Other protocols

``` js
{
"controller": "device-manager/sensor",
"action": "mAttachTenant",
rolljee marked this conversation as resolved.
Show resolved Hide resolved
"body": {
// Using JSON
"records" [{
"tenantId": "tenant-kuzzle",
"sensorId": "test-id"
}],
// Using CSV syntax
"csv": "tenantId,sensorId\ntenant-kuzzle,test-id",
}
}
```

---

## Body properties

Body properties, must contain at least one of

- `records`: an array of object containing `tenantId` and `sensorId`
- `csv`: a csv syntax compatible containing at least this two headers `tenantId,sensorId` with their corresponding values
- `strict`: a boolean value that indicate if the process should fail at first error

---

### Optional:

* `refresh`: if set to `wait_for`, Kuzzle will not respond until the documents are indexed

---

## Response

``` js
{
"status": 200,
"error": null,
"controller": "device-manager/sensor",
"action": "mAttachTenant",
rolljee marked this conversation as resolved.
Show resolved Hide resolved
"requestId": "<unique request identifier>",
"result": {
"errors": [],
"successes": []
}
}
```
8 changes: 8 additions & 0 deletions doc/1/guides/sensors/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ Sensors can be attached to tenant by using the [device-manager/sensor:attach](/k

When attached, the sensor document is copied inside the `sensors` collection of the tenant index.

## Attach to multiple tenant

Multiple different Sensors can also be attached to multiple defferents tenant by using the [device-manager/sensor:mAttach](/kuzzle-iot-platform/device-manager/1/controllers/sensor/m-attach) API action.

The format used can be either __CSV__ in the form of a string in the format `tenantId,sensorId\nmytenantId,mysensorId` or __JSON__ in the form of an array of objects `"records": [{ "tenantId": "mytenantId", "sensorId": "mysensorId"}]`.

When attached, all sensors documents are copied inside the `sensors` collections of all different tenant index.

## Link to an asset

Sensors can be linked to an asset by using the [device-manager/sensor:link](/kuzzle-iot-platform/device-manager/1/controllers/sensor/link) API action.
Expand Down
33 changes: 32 additions & 1 deletion features/SensorController.feature
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,37 @@ Feature: Device Manager sensor controller
| tenantId | "tenant-kuzzle" |
And The document "tenant-kuzzle":"sensors":"DummyTemp_detached" exists

Scenario: Attach multiple sensors to a tenant using JSON
Given an engine on index "tenant-kuzzle"
When I successfully execute the action "device-manager/sensor":"mAttachTenant" with args:
| body.records.0.tenantId | "tenant-kuzzle" |
| body.records.0.sensorId | "DummyTemp_detached" |
| body.records.1.tenantId | "tenant-kuzzle" |
| body.records.1.sensorId | "DummyTemp_attached-ayse-unlinked" |
Then The document "device-manager":"sensors":"DummyTemp_detached" content match:
| tenantId | "tenant-kuzzle" |
Then The document "device-manager":"sensors":"DummyTemp_attached-ayse-unlinked" content match:
| tenantId | "tenant-kuzzle" |
And The document "tenant-kuzzle":"sensors":"DummyTemp_detached" exists
And The document "tenant-kuzzle":"sensors":"DummyTemp_attached-ayse-unlinked" exists

Scenario: Attach multiple sensor to a tenant using CSV
Aschen marked this conversation as resolved.
Show resolved Hide resolved
Given an engine on index "tenant-kuzzle"
When I successfully execute the action "device-manager/sensor":"mAttachTenant" with args:
| body.csv | "tenantId,sensorId\\ntenant-kuzzle,DummyTemp_detached\\ntenant-kuzzle,DummyTemp_attached-ayse-unlinked," |
Then The document "device-manager":"sensors":"DummyTemp_detached" content match:
| tenantId | "tenant-kuzzle" |
Then The document "device-manager":"sensors":"DummyTemp_attached-ayse-unlinked" content match:
| tenantId | "tenant-kuzzle" |
And The document "tenant-kuzzle":"sensors":"DummyTemp_detached" exists
And The document "tenant-kuzzle":"sensors":"DummyTemp_attached-ayse-unlinked" exists

Scenario: Attach multiple sensor to a tenant while exceeding documentsWriteCount limit
Given an engine on index "tenant-kuzzle"
When I attach multiple sensors while exeding documentsWriteCount limit
Then All attached sensors have the correct tenantId
Then All tenant sensors documents exists

Scenario: Error when assigning a sensor to a tenant
Given an engine on index "tenant-kuzzle"
When I execute the action "device-manager/sensor":"attachTenant" with args:
Expand All @@ -23,7 +54,7 @@ Feature: Device Manager sensor controller
| _id | "DummyTemp_detached" |
| index | "tenant-kuzzle" |
Then I should receive an error matching:
| message | "Sensor \"DummyTemp_detached\" is already attached to a tenant" |
| message | "These sensors \"DummyTemp_detached\" are already attached to a tenant" |

Scenario: Detach sensor from a tenant
Given an engine on index "tenant-kuzzle"
Expand Down
1 change: 1 addition & 0 deletions features/fixtures/application/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ app.hook.register('request:onError', async (request: KuzzleRequest) => {
});

app.config.set('plugins.kuzzle-plugin-logger.services.stdout.level', 'debug');
app.config.set('limits.documentsWriteCount', 20);

app.start()
.then(() => {
Expand Down
3 changes: 3 additions & 0 deletions features/fixtures/fixtures.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const sensors = require('./sensors');

module.exports = {
'device-manager': {
sensors: [
...sensors,
{ index: { _id: 'DummyTemp_detached' } },
{
reference: 'detached',
Expand Down
14 changes: 14 additions & 0 deletions features/fixtures/sensors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const sensors = [];
for (let i = 0; i < 50; i++) {
sensors.push({ index: { _id: `DummyTemp_detached-${i}` } });
sensors.push({
reference: 'detached',
model: `DummyTemp-${i}`,
measures: {},
metadata: {},
tenantId: null,
assetId: null
});
}

module.exports = sensors;
52 changes: 52 additions & 0 deletions features/step_definitions/sensor-controller-steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { When, Then } = require('cucumber');

When('I attach multiple sensors while exeding documentsWriteCount limit', async function () {
Aschen marked this conversation as resolved.
Show resolved Hide resolved
const records = [];
for (let i = 0; i < 50; i++) {
records.push({ sensorId: `DummyTemp_detached-${i}`, tenantId: 'tenant-kuzzle' });
}


await this.sdk.query({
controller: "device-manager/sensor",
action: "mAttachTenant",
body: {
records
}
});
});

Then('All attached sensors have the correct tenantId', async function () {
const sensorIds = [];
for (let i = 0; i < 50; i++) {
sensorIds.push(`DummyTemp_detached-${i}`);
}

const { successes, errors } = await this.sdk.document.mGet('device-manager', 'sensors', sensorIds);

if (errors.length > 0) {
throw new Error(errors);
}

for (let i = 0; i < successes.length; i++) {
const { _source } = successes[i];
if (_source.tenantId !== 'tenant-kuzzle') {
throw new Error('tenantId should be tenant-kuzzle but current value is: ', _source.tenantId);
}
}
});


Then('All tenant sensors documents exists', async function () {
const sensorIds = [];
for (let i = 0; i < 50; i++) {
sensorIds.push(`DummyTemp_detached-${i}`);
}

const { errors } = await this.sdk.document.mGet('tenant-kuzzle', 'sensors', sensorIds);

if (errors.length > 0) {
throw new Error(errors);
}
});

65 changes: 60 additions & 5 deletions lib/controllers/SensorController.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import csv from 'csvtojson';
import {
KuzzleRequest,
EmbeddedSDK,
JSONObject,
PluginContext,
BadRequestError
} from 'kuzzle';


import { CRUDController } from './CRUDController';
import { Decoder } from '../decoders';
import { Sensor } from '../models';
import { SensorBulkContent } from '../types';
import { SensorService } from '../services';

export class SensorController extends CRUDController {
private decoders: Map<string, Decoder>;

get sdk (): EmbeddedSDK {
get sdk(): EmbeddedSDK {
return this.context.accessors.sdk;
}

constructor (config: JSONObject, context: PluginContext, decoders: Map<string, Decoder>, sensorService: SensorService) {
constructor(config: JSONObject, context: PluginContext, decoders: Map<string, Decoder>, sensorService: SensorService) {
super(config, context, 'sensors');

this.decoders = decoders;
Expand All @@ -41,6 +45,10 @@ export class SensorController extends CRUDController {
handler: this.attachTenant.bind(this),
http: [{ verb: 'put', path: 'device-manager/:index/sensors/:_id/_attach' }]
},
mAttachTenant: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused, the action name is mAttach or mAttachTenant at the end ? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stick to mAttach, sorry about that

handler: this.mAttachTenant.bind(this),
http: [{ verb: 'put', path: 'device-manager/sensors/_mAttach' }]
},
detach: {
handler: this.detach.bind(this),
http: [{ verb: 'delete', path: 'device-manager/sensors/:_id/_detach' }]
Expand All @@ -64,11 +72,25 @@ export class SensorController extends CRUDController {
const tenantId = this.getIndex(request);
const sensorId = this.getId(request);

const sensor = await this.getSensor(sensorId);
const document = { tenantId: tenantId, sensorId: sensorId };
Aschen marked this conversation as resolved.
Show resolved Hide resolved
const sensors = await this.mGetSensor([document]);

await this.sensorService.attachTenant(sensor, tenantId);
await this.sensorService.mAttachTenant(sensors, [document], { strict: true });
}

/**
* Attach multiple sensors to multiple tenants
*/
async mAttachTenant (request: KuzzleRequest) {
const { bulkData, strict } = await this.mParseRequest(request);

const sensors = await this.mGetSensor(bulkData);

return this.sensorService.mAttachTenant(sensors, bulkData, { strict });
}



/**
* Unattach a sensor from it's tenant
*/
Expand All @@ -83,7 +105,7 @@ export class SensorController extends CRUDController {
/**
* Link a sensor to an asset.
*/
async linkAsset (request: KuzzleRequest) {
async linkAsset(request: KuzzleRequest) {
const assetId = this.getString(request, 'assetId');
const sensorId = this.getId(request);

Expand Down Expand Up @@ -111,4 +133,37 @@ export class SensorController extends CRUDController {

return new Sensor(document._source, document._id);
}

private async mGetSensor (sensors: SensorBulkContent[]): Promise<Sensor[]> {
const sensorIds = sensors.map(doc => doc.sensorId);
const result: any = await this.sdk.document.mGet(
this.config.adminIndex,
'sensors',
sensorIds
)
Aschen marked this conversation as resolved.
Show resolved Hide resolved
return result.successes.map((document: any) => new Sensor(document._source, document._id));
}

private async mParseRequest (request: KuzzleRequest) {
const { body } = request.input;

let bulkData: SensorBulkContent[];

if (body.csv) {
const lines = await csv({ delimiter: 'auto' })
.fromString(body.csv);

bulkData = lines.map(line => ({ tenantId: line.tenantId, sensorId: line.sensorId }));
Aschen marked this conversation as resolved.
Show resolved Hide resolved
}
else if (body.records) {
bulkData = body.records;
}
else {
throw new BadRequestError(`Malformed request missing property csv or records`);
}

const strict = body.strict || false;

return { strict, bulkData };
}
}
Loading