Skip to content

Commit

Permalink
Merge pull request #323 from kuzzleio/KZLPRD-166-upsert-assets
Browse files Browse the repository at this point in the history
Kzlprd 166 upsert assets
  • Loading branch information
Leagian committed Jan 9, 2024
2 parents db7432f + 104df51 commit 2bfa312
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 1 deletion.
67 changes: 67 additions & 0 deletions doc/2/controllers/assets/upsert/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
code: true
type: page
title: upsert
description: Update or Create an asset
---

# upsert

Update or Create an asset.
The Upsert operation allows you to create a new asset or update an existing one if it already exists. This operation is useful when you want to ensure that an asset is either created or updated in a single request.

## Query Syntax

### HTTP

```http
URL: http://kuzzle:7512/_/device-manager/:engineId/assets/:_id
Method: POST
```

## Other protocols

```js
{
"controller": "device-manager/assets",
"action": "upsert",
"engineId": "<engineId>",
"_id": "<assetId>",
"body": {
"metadata": {
"<metadata name>": "<metadata value>"
}
}
}
```

---

## Arguments

- `engineId`: Engine ID
- `_id`: Asset ID

## Body properties

- `metadata`: Object containing metadata

---

## Response

```js
{
"status": 200,
"error": null,
"controller": "device-manager/assets",
"action": "update",
"requestId": "<unique request identifier>",
"result": {
"_id": "<assetId>",
"_source": {
// Asset content
},
}
}
```
72 changes: 71 additions & 1 deletion lib/modules/asset/AssetService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class AssetService extends BaseService {
}

/**
* Updates an asset metadata
* Update an asset metadata
*/
public async update(
engineId: string,
Expand Down Expand Up @@ -135,6 +135,73 @@ export class AssetService extends BaseService {
});
}

/**
* Update or Create an asset metadata
*/
public async upsert(
engineId: string,
assetId: string,
model: string,
reference: string,
metadata: Metadata,
request: KuzzleRequest
): Promise<KDocument<AssetContent>> {
return lock(`asset:${engineId}:${assetId}`, async () => {
const asset = await this.get(engineId, assetId, request).catch(
() => null
);

if (!asset) {
return this.create(engineId, model, reference, metadata, request);
}

const updatedPayload = await this.app.trigger<EventAssetUpdateBefore>(
"device-manager:asset:update:before",
{ asset, metadata }
);

const updatedAsset = await this.updateDocument<AssetContent>(
request,
{
_id: assetId,
_source: { metadata: updatedPayload.metadata },
},
{
collection: InternalCollection.ASSETS,
engineId,
},
{ source: true }
);

await this.assetHistoryService.add<AssetHistoryEventMetadata>(engineId, [
{
asset: updatedAsset._source,
event: {
metadata: {
names: Object.keys(flattenObject(updatedPayload.metadata)),
},
name: "metadata",
},
id: updatedAsset._id,
timestamp: Date.now(),
},
]);

await this.app.trigger<EventAssetUpdateAfter>(
"device-manager:asset:update:after",
{
asset: updatedAsset,
metadata: updatedPayload.metadata,
}
);

return updatedAsset;
});
}

/**
* Create an asset metadata
*/
public async create(
engineId: string,
model: string,
Expand Down Expand Up @@ -207,6 +274,9 @@ export class AssetService extends BaseService {
});
}

/**
* Delete an asset metadata
*/
public async delete(
engineId: string,
assetId: string,
Expand Down
26 changes: 26 additions & 0 deletions lib/modules/asset/AssetsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AssetService } from "./AssetService";
import { AssetSerializer } from "./model/AssetSerializer";
import {
ApiAssetCreateResult,
ApiAssetUpsertResult,
ApiAssetDeleteResult,
ApiAssetGetMeasuresResult,
ApiAssetGetResult,
Expand All @@ -37,6 +38,12 @@ export class AssetsController {
handler: this.create.bind(this),
http: [{ path: "device-manager/:engineId/assets", verb: "post" }],
},
upsert: {
handler: this.upsert.bind(this),
http: [
{ path: "device-manager/:engineId/assets/:_id", verb: "post" },
],
},
delete: {
handler: this.delete.bind(this),
http: [
Expand Down Expand Up @@ -129,6 +136,25 @@ export class AssetsController {
return AssetSerializer.serialize(asset);
}

async upsert(request: KuzzleRequest): Promise<ApiAssetUpsertResult> {
const engineId = request.getString("engineId");
const assetId = request.getId();
const model = request.getBodyString("model");
const reference = request.getBodyString("reference");
const metadata = request.getBodyObject("metadata");

const upsertAsset = await this.assetService.upsert(
engineId,
assetId,
model,
reference,
metadata,
request
);

return AssetSerializer.serialize(upsertAsset);
}

async update(request: KuzzleRequest): Promise<ApiAssetUpdateResult> {
const assetId = request.getId();
const engineId = request.getString("engineId");
Expand Down
17 changes: 17 additions & 0 deletions lib/modules/asset/types/AssetApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ export interface ApiAssetUpdateRequest extends AssetsControllerRequest {
}
export type ApiAssetUpdateResult = KDocument<AssetContent>;

export interface ApiAssetUpsertRequest extends AssetsControllerRequest {
action: "upsert";

_id: string;

refresh?: string;

body: {
model: string;

reference: string;

metadata: Metadata;
};
}
export type ApiAssetUpsertResult = KDocument<AssetContent>;

export interface ApiAssetCreateRequest extends AssetsControllerRequest {
action: "create";

Expand Down
1 change: 1 addition & 0 deletions lib/modules/shared/services/BaseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ArgsDocumentControllerCreate,
ArgsDocumentControllerDelete,
ArgsDocumentControllerUpdate,
ArgsDocumentControllerUpsert,
Backend,
BaseRequest,
DocumentSearchResult,
Expand Down
64 changes: 64 additions & 0 deletions tests/scenario/migrated/asset-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,68 @@ describe("features/Asset/Controller", () => {
sdk.document.exists("engine-kuzzle", "devices", "DummyTemp-foobar")
).resolves.toBe(true);
});

it("Upsert asset", async () => {
const response = await sdk.query({
controller: "device-manager/assets",
action: "upsert",
engineId: "engine-kuzzle",
_id: "Container-linked2",
body: {
model: "Container",
reference: "linked2",
metadata: { height: 21, weight: 42 },
},
});

expect(response).toBeDefined();
expect(response.result).toBeDefined();
expect(response.result._id).toEqual("Container-linked2");
expect(response.result._source.model).toEqual("Container");
expect(response.result._source.reference).toEqual("linked2");
expect(response.result._source.metadata).toEqual({
height: 21,
trailer: null,
weight: 42,
});
});

it("Upsert asset - update existing asset", async () => {
// create asset
await sdk.query({
controller: "device-manager/assets",
action: "upsert",
engineId: "engine-kuzzle",
_id: "Container-linked2",
body: {
model: "Container",
reference: "linked2",
metadata: { height: 21, trailer: null, weight: 42 },
},
});

// update asset
const response = await sdk.query({
controller: "device-manager/assets",
action: "upsert",
engineId: "engine-kuzzle",
_id: "Container-linked2",
body: {
model: "Container",
reference: "linked2",
metadata: { height: 22, trailer: null, weight: 43 },
},
});

expect(response).toBeDefined();
expect(response.result).toBeDefined();
expect(response.result._id).toEqual("Container-linked2");
expect(response.result._source.model).toEqual("Container");
expect(response.result._source.reference).toEqual("linked2");
expect(response.result._source.metadata).toEqual({
height: 22,
trailer: null,
weight: 43,
});
});
});

0 comments on commit 2bfa312

Please sign in to comment.