Skip to content

Commit

Permalink
feat: add tooltip model by asset model (#341)
Browse files Browse the repository at this point in the history
* add tooltip models

* fix delete asset model

* feat: unit fields for metadata & measures tooltip models

* remove unnecessary comments

* add doc & tests

* review comments

* fix tests

* fix tests

* fix tests

* delete test models

* Feat tooltipModels add locales, static & suffix

* rebase on 2-dev

* fix delete asset model

* feat: unit fields for metadata & measures tooltip models

* remove unnecessary comments

* add doc & tests

* review comments

* fix tests

* fix tests

* fix tests

* rebase on 2-dev

* Feat tooltipModels add locales, static & suffix

* rebase on 2-dev

* add update conflicts check

* doc for updateAsset model function

* review comments

* fix tests

* fix tests

* fix tests

* rebase on 2-dev

* add update conflicts check

* doc for updateAsset model function

* merge conflicts

* changed from put to patch
  • Loading branch information
afondard authored Jul 16, 2024
1 parent dadc33f commit 865ae6d
Show file tree
Hide file tree
Showing 14 changed files with 712 additions and 21 deletions.
3 changes: 2 additions & 1 deletion doc/2/concepts/models/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ An asset model contains the following information:
- `measures`: received measurements
- `metadataMappings`: (optional) metadata mappings (See [Collection Mappings](https://docs.kuzzle.io/core/2/guides/main-concepts/data-storage/#collection-mappings))
- `defaultMetadata`: (optional) default metadata values-
- `metadataDetails`: (optional) Metadata group and translations . You can use it to keep consistency on translations between your apps
- `metadataDetails`: (optional) Metadata group and translations. You can use it to keep consistency on translations between your apps
- `metadataGroups`: (optional) Groups list with translations for group name. You can use it to group metadatas by their concerns
- `tooltipModels`: (optional) Tooltip model list, each containing labels and tooltip content to be shown. You can use it to create templates that displays relevant information in dashboards

It is possible to create new models on the Kuzzle IoT Platform using either:

Expand Down
166 changes: 166 additions & 0 deletions doc/2/controllers/models/update-asset/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
---
code: true
type: page
title: updateAsset
description: Update an asset model
---

# updateAsset

Update an existing asset model.

## Query Syntax

### HTTP

```http
URL: http://kuzzle:7512/_/device-manager/models/assets/:model?engineGroup=<engine group>
Method: PUT
```

### Other protocols

```js
{
"controller": "device-manager/assets",
"action": "updateAsset",
"engineGroup": "<engine group>",
"model": "<asset model>",

"body": {

// Optional

"metadataMappings": {
// Metadata mappings
},
"defaultValues": {
// Default values for metadata
},
"metadataDetails": {
/*
Metadata details including translations and group.
[name: string]: {
group?: string;
locales: {
[locale: string]: {
friendlyName: string;
description: string;
};
};
};
*/
},
"metadataGroups": {
/*
Metadata groups list and details.
{
[groupName: string]: {
locales: {
[locale: string]: {
groupFriendlyName: string;
description: string;
};
};
};
};
*/
},
"tooltipModels": {
/*
Tooltip models for an asset model.
[key: string]: {
tooltipLabel: string;
content: [
{
category: "metadata";
label?: {
locales: {
[locale: string]: {
friendlyName: string;
description: string;
};
};
};
metadataPath: string;
suffix?: string;
},
{
category: "measure";
label?: {
locales: {
[locale: string]: {
friendlyName: string;
description: string;
};
};
};
measureSlot: string;
measureValuePath: string;
suffix?: string;
},
{
category: "static";
label?: {
locales: {
[locale: string]: {
friendlyName: string;
description: string;
};
};
};
type: "link" | "image" | "text" | "title" | "separator";
value: string;
}
];
};
*/
},
"measures": [
// Array of measure definition with type and name
]
}
}
```

---

## Arguments

- `engineGroup`: Name of the engine group
- `model`: Asset model name

---

## Body properties

- `metadataMappings`: Mappings of the metadata in Elasticsearch format
- `defaultValues`: Default values for the metadata
- `metadataDetails`: Metadata group and translations
- `metadataGroups`: Groups list with translations for group name
- `tooltipModels`: Tooltip model list, containing each labels and tooltip content to display
- `measures`: Array of measure definition. Each item defines `type` and `name` properties for the measure.

---

## Response

```js
{
"status": 200,
"error": null,
"controller": "device-manager/models",
"action": "updateAsset",
"requestId": "<unique request identifier>",
"result": {
"_id": "<modelId>",
"_source": {
// Updated asset model content
},
}
}
```

## Errors

Updating an asset with metadata mappings can cause conflicts, in this case a [ MappingsConflictsError ](../../../errors/mappings-conflicts/index.md) will be thrown with the HTTP code **409**.
73 changes: 62 additions & 11 deletions doc/2/controllers/models/write-asset/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,70 @@ Method: POST
};
*/
},
"metadataGroups"; {
/*
Metadata groups list and details.
{
[groupName: string]: {
locales: {
[locale: string]: {
groupFriendlyName: string;
description: string;
"metadataGroups": {
/*
Metadata groups list and details.
{
[groupName: string]: {
locales: {
[locale: string]: {
groupFriendlyName: string;
description: string;
};
};
};
};
};
*/
*/
},
"tooltipModels": {
/*
Tooltip models for an asset model.
[key: string]: {
tooltipLabel: string;
content: [
{
category: "metadata";
label?: {
locales: {
[locale: string]: {
friendlyName: string;
description: string;
};
};
};
metadataPath: string;
suffix?: string;
},
{
category: "measure";
label?: {
locales: {
[locale: string]: {
friendlyName: string;
description: string;
};
};
};
measureSlot: string;
measureValuePath: string;
suffix?: string;
},
{
category: "static";
label?: {
locales: {
[locale: string]: {
friendlyName: string;
description: string;
};
};
};
type: "link" | "image" | "text" | "title" | "separator";
value: string;
}
];
};
*/
},
"measures": [
// Array of measure definition with type and name
Expand All @@ -84,6 +134,7 @@ Method: POST
- `defaultValues`: Default values for the metadata
- `metadataDetails`: Metadata group and translations
- `metadataGroups`: Groups list with translations for group name
- `tooltipModels`: Tooltip model list, containing each labels and tooltip content to display
- `measures`: Array of measure definition. Each item define a `type` and `name` properties for the measure.

---
Expand Down
80 changes: 80 additions & 0 deletions lib/modules/model/ModelService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
MetadataGroups,
MetadataMappings,
ModelContent,
TooltipModels,
} from "./types/ModelContent";
import {
AskModelAssetGet,
Expand Down Expand Up @@ -185,6 +186,7 @@ export class ModelService extends BaseService {
metadataDetails: MetadataDetails,
metadataGroups: MetadataGroups,
measures: AssetModelContent["asset"]["measures"],
tooltipModels: TooltipModels,
): Promise<KDocument<AssetModelContent>> {
if (Inflector.pascalCase(model) !== model) {
throw new BadRequestError(`Asset model "${model}" must be PascalCase.`);
Expand All @@ -198,6 +200,7 @@ export class ModelService extends BaseService {
metadataGroups,
metadataMappings,
model,
tooltipModels,
},
engineGroup,
type: "asset",
Expand Down Expand Up @@ -545,4 +548,81 @@ export class ModelService extends BaseService {

return result.hits[0];
}

/**
* Update an asset model
*/
async updateAsset(
engineGroup: string,
model: string,
metadataMappings: MetadataMappings,
defaultMetadata: JSONObject,
metadataDetails: MetadataDetails,
metadataGroups: MetadataGroups,
measures: AssetModelContent["asset"]["measures"],
tooltipModels: TooltipModels,
request: KuzzleRequest,
): Promise<KDocument<AssetModelContent>> {
if (Inflector.pascalCase(model) !== model) {
throw new BadRequestError(`Asset model "${model}" must be PascalCase.`);
}

this.checkDefaultValues(metadataMappings, defaultMetadata);

const existingAsset = await this.getAsset(engineGroup, model);

// The field must be deleted if an element of the table is to be deleted
await this.sdk.document.deleteFields(
this.config.adminIndex,
InternalCollection.MODELS,
existingAsset._id,
["asset.tooltipModels"],
{ source: true },
);

const measuresUpdated =
measures.length === 0 ? existingAsset._source.asset.measures : measures;

const assetModelContent: AssetModelContent = {
asset: {
defaultMetadata,
measures: measuresUpdated,
metadataDetails,
metadataGroups,
metadataMappings,
model,
tooltipModels,
},
engineGroup,
type: "asset",
};
const assetModel = {
_id: existingAsset._id,
_source: assetModelContent,
};

const conflicts = await ask<AskEngineUpdateConflict>(
"ask:device-manager:engine:doesUpdateConflict",
{ twin: { models: [assetModelContent], type: "asset" } },
);

if (conflicts.length > 0) {
throw new MappingsConflictsError(
`Assets mappings are causing conflicts`,
conflicts,
);
}

const endDocument = await this.updateDocument<AssetModelContent>(
request,
assetModel,
{
collection: InternalCollection.MODELS,
engineId: this.config.adminIndex,
},
{ source: true },
);

return endDocument;
}
}
Loading

0 comments on commit 865ae6d

Please sign in to comment.