Skip to content

Commit

Permalink
feat: update measure model for localization (#385)
Browse files Browse the repository at this point in the history
* feat: update measure model for localization

* feat: update asset model for localization and description

* feat: add modelFriendlyName in asset mapping and in asset create endpoint

* feat: implement updateModelFriendlyName

* feat: add asset history for modelFriendlyName update

* style: change naming

* fix: update Container definition for ci

* docs: add documentation for asset model and updateModelLocales endpoint

* feat: add locales in MeasureModelContent type

* fix: let locales optional in measure model

* feat: add scroll to assets query

* feat: update entiry the method to include assetHistory in scroll loop

* fix: update to use only updateByQuery

* fix: update documentation

* fix: change PUT to PATCH in documentation

* feat: group result by index

* feat: implement error management 206, 400

* refactor: update wordings and errors

* refactor: remove AssetHistoryEventModelLocales type
  • Loading branch information
cyrng authored Feb 14, 2025
1 parent 648b299 commit 429678f
Show file tree
Hide file tree
Showing 19 changed files with 316 additions and 11 deletions.
24 changes: 24 additions & 0 deletions doc/2/concepts/models/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ A measure model contains the following information:
- `measure`: type of the measure
- `valuesMappings`: measurements mappings (See [Collection Mappings](https://docs.kuzzle.io/core/2/guides/main-concepts/data-storage/#collection-mappings))
- `valuesDetails`: (optional) Metadata and translations of measurements. You can use it to keep consistency on translations between your apps
- `locales`: (optional) Translation for the measure model

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

Expand Down Expand Up @@ -98,6 +99,16 @@ await sdk.query({
},
},
},
locales: {
en: {
modelFriendlyName: "Light measurement",
modelDescription: "Light measurement",
},
fr: {
modelFriendlyName: "Mesure de lumière",
modelDescription: "Mesure de lumière",
},
}
},
});
```
Expand All @@ -124,6 +135,7 @@ An asset model contains the following information:
- Editor hint: it unlock functionalities depending on the metadata type you define.
- `metadataGroups`: (optional) Map of group names to their translations. You can use it to group metadata.
- `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
- `locales`: (optional) Translation for asset model

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

Expand All @@ -150,10 +162,22 @@ import {
{ name: "temperatureInternal", type: "temperature" },
{ name: "temperatureExternal", type: "temperature" },
],
locales: {
en: {
friendlyName: "Container translated by model",
description: "Containerized container",
},
fr: {
friendlyName: "Conteneur traduit par modèle",
description: "Conteneur conteneurisé",
},
}
},
};
```

**INFO: If the locales has changed, use [updateModelLocales](../../controllers/assets/updateModelLocales/index.md) to update all assets manually and make the search on assets up to date**

The API also allows to:

- list available models `device-manager/models:listAssets`
Expand Down
55 changes: 55 additions & 0 deletions doc/2/controllers/assets/updateModelLocales/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
code: true
type: page
title: updateModelLocales
description: Update all assets localization
---

# update

Update all existing assets localization.

The `updateModelLocales` operation allows you to update the `locales` of all `assets` related to the specified `asset model`.
The process retrieve the locales values stored in the asset model and update the locales of all the assets with these values. For the moment, this operation has to be done `manually` when the asset model locales changed to make the `search` operation on assets to `be up to date`.

## Query Syntax

### HTTP

```http
URL: http://kuzzle:7512/_/device-manager/assets/modelLocales
Method: PATCH
```

## Other protocols

```js
{
"controller": "device-manager/assets",
"action": "updateModelLocales",
"model": "Container",
"engineGroup": "commons"
}
```

## Arguments

- `engineGroup`: Engine group
- `model`: asset model

## Response

```js
{
"action": "updateModelLocales",
"collection": "assets",
"controller": "device-manager/assets",
"error": null,
"headers": {},
"index": "engine-ayse",
"node": "knode-gigantic-iago-99422",
"requestId": "3b86b6d1-8004-4273-ba03-94526b019b8a",
"status": 200,
"volatile": null
}
```
95 changes: 93 additions & 2 deletions lib/modules/asset/AssetService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BadRequestError, KuzzleRequest, User } from "kuzzle";
import { BadRequestError, KuzzleRequest, PartialError, User } from "kuzzle";
import { ask, onAsk } from "kuzzle-plugin-commons";
import {
BaseRequest,
Expand Down Expand Up @@ -34,7 +34,10 @@ import {

import { AssetHistoryService } from "./AssetHistoryService";
import { AssetSerializer } from "./model/AssetSerializer";
import { ApiAssetMigrateTenantResult } from "./types/AssetApi";
import {
ApiAssetMigrateTenantResult,
ApiAssetUpdateModelLocales,
} from "./types/AssetApi";
import { AssetContent } from "./types/AssetContent";
import {
AskAssetRefreshModel,
Expand Down Expand Up @@ -314,6 +317,7 @@ export class AssetService extends DigitalTwinService {
measures,
metadata: { ...assetMetadata, ...metadata },
model,
modelLocales: assetModel.asset.locales,
reference,
softTenant: [],
},
Expand Down Expand Up @@ -598,6 +602,93 @@ export class AssetService extends DigitalTwinService {
return replacedAssets;
}

/**
* Retrieve locales with the specified model and search all assets related to the model to update the locales.
* The operation is historized.
* @param request
* @param engineGroup
* @param model
*/
public async updateModelLocales(
request: KuzzleRequest,
engineGroup: string,
model: string,
): Promise<ApiAssetUpdateModelLocales[]> {
let resultModel: JSONObject;

try {
const { result: fetchedResult } = await this.sdk.query({
action: "getAsset",
body: {},
controller: "device-manager/models",
engineGroup,
model,
});
resultModel = fetchedResult;
} catch (e) {
throw new BadRequestError(
`${e} Verify in your arguments that the asset model belongs to the engineGroup "${engineGroup}"`,
{},
400,
);
}

const locales = resultModel._source.asset.locales;

const engines = await ask<AskEngineList>("ask:device-manager:engine:list", {
group: engineGroup,
});

const results: ApiAssetUpdateModelLocales[] = [];

for (const engine of engines) {
const resultUpdateByQuery =
await this.sdk.document.updateByQuery<AssetContent>(
engine.index,
"assets",
{
bool: {
must: [
{
term: {
model: model,
},
},
],
},
},
{
modelLocales: locales,
},
);

results.push({
engineIndex: engine.index,
result: {
errors: [...resultUpdateByQuery.errors],
successes: [...resultUpdateByQuery.successes],
},
});
}

const errorsFiltered = results.filter((re) => re.result.errors.length > 0);

if (errorsFiltered.length === results.length) {
throw new BadRequestError("All the assets failed to be updated", {}, 400);
}

if (errorsFiltered.length < results.length && errorsFiltered.length !== 0) {
throw new PartialError(
"Some assets failed to be updated",
errorsFiltered,
{},
206,
);
}

return results;
}

private async getEngine(engineId: string): Promise<JSONObject> {
const engine = await this.sdk.document.get(
this.config.adminIndex,
Expand Down
31 changes: 31 additions & 0 deletions lib/modules/asset/AssetsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
ApiAssetMGetLastMeasuredAtResult,
ApiAssetMGetLastMeasuresResult,
ApiAssetGetLastMeasuredAtResult,
ApiAssetUpdateModelLocales,
} from "./types/AssetApi";
import { isSourceApi } from "../measure/types/MeasureSources";
import { getValidator } from "../shared/utils/AJValidator";
Expand Down Expand Up @@ -204,6 +205,15 @@ export class AssetsController {
},
],
},
updateModelLocales: {
handler: this.updateModelLocales.bind(this),
http: [
{
path: "device-manager/assets/modelLocales",
verb: "patch",
},
],
},
},
};
/* eslint-enable sort-keys */
Expand Down Expand Up @@ -745,4 +755,25 @@ export class AssetsController {

return this.assetService.mGetLastMeasuredAt(engineId, assetIds);
}

/**
* Update modelLocales of all assets related to the specified asset model.
* This operation is done to make the search assets feature up to date
* @param request
*/
async updateModelLocales(
request: KuzzleRequest,
): Promise<ApiAssetUpdateModelLocales[]> {
const model = request.getString("model");
const engineGroup = request.getString("engineGroup", "commons");

try {
return this.assetService.updateModelLocales(request, engineGroup, model);
} catch (e) {
request.response.configure({
status: (e as KuzzleError).status,
});
return e;
}
}
}
27 changes: 27 additions & 0 deletions lib/modules/asset/collections/assetsMappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,33 @@ export const assetsMappings: CollectionMappings = {
type: "keyword",
fields: { text: { type: "text" } },
},
modelLocales: {
dynamic: "false",
properties: {
en: {
properties: {
description: {
type: "text",
},
friendlyName: {
type: "keyword",
fields: { text: { type: "text" } },
},
},
},
fr: {
properties: {
description: {
type: "text",
},
friendlyName: {
type: "keyword",
fields: { text: { type: "text" } },
},
},
},
},
},
reference: {
type: "keyword",
fields: { text: { type: "text" } },
Expand Down
13 changes: 12 additions & 1 deletion lib/modules/asset/types/AssetApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { JSONObject, KDocument, KHit, SearchResult } from "kuzzle-sdk";
import {
JSONObject,
KDocument,
KHit,
SearchResult,
UpdateByQueryResponse,
} from "kuzzle-sdk";

import { MeasureContent, Measurement } from "../../../modules/measure";
import {
Expand Down Expand Up @@ -257,3 +263,8 @@ export type ApiAssetMGetLastMeasuredAtRequest =
ApiDigitalTwinMGetLastMeasuredAtRequest<AssetsControllerName>;
export type ApiAssetMGetLastMeasuredAtResult =
ApiDigitalTwinMGetLastMeasuredAtResult;

export type ApiAssetUpdateModelLocales = {
engineIndex: string;
result: UpdateByQueryResponse<AssetContent>;
};
4 changes: 4 additions & 0 deletions lib/modules/measure/types/MeasureDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SchemaObject } from "ajv";
import { JSONObject } from "kuzzle-sdk";
import { LocaleDetails } from "lib/modules/model";

/* *
* Represents a measure information and localization
Expand Down Expand Up @@ -63,6 +64,9 @@ export interface MeasureValuesDetails {
*/

export interface MeasureDefinition {
locales?: {
[valueName: string]: LocaleDetails;
};
/**
* Mappings for the measurement values in order to index the fields
*/
Expand Down
Loading

0 comments on commit 429678f

Please sign in to comment.