Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\bin\\run",
"program": "${workspaceFolder}/bin/run.js",
"args": [
"changed-elements",
"enable",
Expand All @@ -25,7 +25,7 @@
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\bin\\run",
"program": "${workspaceFolder}/bin/run.js",
"args": [
"docs-generator"
]
Expand Down
29 changes: 29 additions & 0 deletions docs/itwin/share/create.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# itp itwin share create

Create a new iTwin Share.

## Options

- **`-i, --itwin-id`**
The ID of the iTwin to be shared.
**Type:** `string` **Required:** Yes

- **`--contract`**
The name of share contract. Defaults to 'Default' name if omitted.
**Type:** `string` **Required:** No

- **`--expiration`**
The expiration date for the share. Defaults to the maximum allowed period for the given share contract if omitted
**Type:** `string` **Required:** No

## Examples

```bash
itp itwin share create --itwin-id ad0ba809-9241-48ad-9eb0-c8038c1a1d51

itp itwin share create --itwin-id ad0ba809-9241-48ad-9eb0-c8038c1a1d51 --contract Default --expiration 2025-12-31T23:59:59Z
```

## API Reference

[Create iTwin Share](https://developer.bentley.com/apis/access-control-v2/operations/create-itwin-share/)
23 changes: 23 additions & 0 deletions docs/itwin/share/info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# itp itwin share info

Retrieves the specified iTwin Share for the specified iTwin.

## Options

- **`-i, --itwin-id`**
The ID of the iTwin.
**Type:** `string` **Required:** Yes

- **`--share-id`**
iTwin Share ID.
**Type:** `string` **Required:** Yes

## Examples

```bash
itp itwin share info --itwin-id ad0ba809-9241-48ad-9eb0-c8038c1a1d51 --share-id f012944d-417f-436c-8e9c-ddc70c7a338b
```

## API Reference

[Get iTwin Share](https://developer.bentley.com/apis/access-control-v2/operations/get-itwin-share/)
19 changes: 19 additions & 0 deletions docs/itwin/share/list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# itp itwin share list

Retrieves a list of available iTwin shares that are currently active for a specified iTwin.

## Options

- **`-i, --itwin-id`**
The ID of the iTwin.
**Type:** `string` **Required:** Yes

## Examples

```bash
itp itwin share list --itwin-id ad0ba809-9241-48ad-9eb0-c8038c1a1d51
```

## API Reference

[Get a list of created iTwin Shares](https://developer.bentley.com/apis/access-control-v2/operations/get-itwin-shares/)
23 changes: 23 additions & 0 deletions docs/itwin/share/revoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# itp itwin share revoke

Revokes a specified share for a specified iTwin. Any future requests made with the associated shareKey will no longer work.

## Options

- **`-i, --itwin-id`**
The ID of the iTwin to be shared.
**Type:** `string` **Required:** Yes

- **`--share-id`**
iTwin Share ID.
**Type:** `string` **Required:** Yes

## Examples

```bash
itp itwin share revoke --itwin-id ad0ba809-9241-48ad-9eb0-c8038c1a1d51 --share-id bf4d8b36-25d7-4b72-b38b-12c1f0325f42
```

## API Reference

[Revokes a specified share for a specified iTwin.](https://developer.bentley.com/apis/access-control-v2/operations/revoke-itwin-share/)
2 changes: 2 additions & 0 deletions integration-tests/itwin/itwin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import deleteTests from "./delete.test";
import infoTests from "./info.test";
import listTests from "./list.test";
import repositoryTests from "./repository.test";
import shareTests from "./share.test";
import updateTests from "./update.test";

const tests = () =>
Expand All @@ -19,6 +20,7 @@ const tests = () =>
updateTests();
deleteTests();
repositoryTests();
shareTests();
});

export default tests;
Expand Down
105 changes: 105 additions & 0 deletions integration-tests/itwin/share.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

import { expect } from "chai";

import { runCommand } from "@oclif/test";

import { ItwinShare } from "../../src/services/access-control/models/itwin-share.js";
import { ResultResponse } from "../../src/services/general-models/result-response.js";
import { createITwin } from "../utils/helpers";
import runSuiteIfMainModule from "../utils/run-suite-if-main-module";

function assertItwinShare(share?: ItwinShare): ItwinShare {
expect(share).to.be.instanceOf(Object);
expect(share).to.have.property("id");
expect(share).to.have.property("iTwinId");
expect(share).to.have.property("shareKey");
expect(share).to.have.property("shareContract");
expect(share).to.have.property("expiration");
expect(share!.shareContract).equals("Default");
expect(share!.expiration).match(/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/);
return share!;
}

const tests = () =>
describe("share", () => {
let testITwinId: string;
let shareId: string;

before(async () => {
const testITwin = await createITwin(`cli-itwin-integration-test-${new Date().toISOString()}`, "Thing", "Asset");
testITwinId = testITwin.id as string;

// Create iTwin Share
const { result: iTwinShare, error } = await runCommand<ItwinShare>(`itwin share create --itwin-id ${testITwinId}`);
expect(error).to.be.undefined;
const share = assertItwinShare(iTwinShare);
shareId = share.id;
});

after(async () => {
const { result: deleteResult } = await runCommand<ResultResponse>(`itwin delete --itwin-id ${testITwinId}`);
expect(deleteResult).to.have.property("result", "deleted");
});

it("should get specific iTwin share info", async () => {
const { result: iTwinShare, error } = await runCommand<ItwinShare>(`itwin share info --itwin-id ${testITwinId} --share-id ${shareId}`);
expect(error).to.be.undefined;
assertItwinShare(iTwinShare);
});

it("should get a list of iTwin shares", async () => {
const { result: iTwinShares, error } = await runCommand<ItwinShare[]>(`itwin share list --itwin-id ${testITwinId}`);
expect(error).to.be.undefined;
expect(iTwinShares).to.be.instanceOf(Array);
expect(iTwinShares).to.have.lengthOf(1);
assertItwinShare(iTwinShares![0]);
});

it("should return error if share does not exist", async () => {
const invalidShareId = "bf4d8b36-25d7-4b72-b38b-12c1f0325f42";
const { result: iTwinShare, error } = await runCommand<ResultResponse>(`itwin share revoke --itwin-id ${testITwinId} --share-id ${invalidShareId}`);
expect(iTwinShare).to.be.undefined;
expect(error?.message).to.contain("ShareNotFound");
});

it("should create and revoke iTwin share", async () => {
// Create iTwin Share
const { result: iTwinShare, error: createError } = await runCommand<ItwinShare>(`itwin share create --itwin-id ${testITwinId}`);
expect(createError).to.be.undefined;
const share = assertItwinShare(iTwinShare);

// Revoke iTwin share
const { result: revokeResult, error: revokeError } = await runCommand<ResultResponse>(
`itwin share revoke --itwin-id ${testITwinId} --share-id ${share.id}`,
);
expect(revokeError).to.be.undefined;
expect(revokeResult).to.have.property("result", "revoked");

// Verify the share has been revoked
const { result: getResult, error: getError } = await runCommand<ResultResponse>(`itwin share info --itwin-id ${testITwinId} --share-id ${share.id}`);
expect(getResult).to.be.undefined;
expect(getError?.message).to.contain("ShareNotFound");
});

it("should not create iTwin share with too long expiration period", async () => {
const { result: iTwinShare, error } = await runCommand<ItwinShare>(`itwin share create --itwin-id ${testITwinId} --expiration 2100-01-01`);
expect(iTwinShare).to.be.undefined;
expect(error?.message).to.contain("InvalidShareRequest");
expect(error?.message).to.contain("Value outside of valid range.");
});

it("should not create iTwin share with non-existing contract name", async () => {
const { result: iTwinShare, error } = await runCommand<ItwinShare>(`itwin share create --itwin-id ${testITwinId} --contract InvalidContractName`);
expect(iTwinShare).to.be.undefined;
expect(error?.message).to.contain("ShareContractNotFound");
expect(error?.message).to.contain("Requested share contract is not available.");
});
});

export default tests;

runSuiteIfMainModule(import.meta, tests);
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const tests = () =>
`access-control group create --itwin-id ${iTwinId} --name "${groupName}" --description "${groupDescription}"`,
);
expect(createError).to.not.be.undefined;
expect(createError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(createError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ const tests = () =>

const { error: deleteError } = await runCommand<ResultResponse>(`access-control group delete --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(deleteError).to.not.be.undefined;
expect(deleteError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(deleteError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});

it("should return an error when group is not found", async () => {
const response = AccessControlApiMock.groups.deleteiTwinGroup.groupNotFound(iTwinId, groupId);

const { error: deleteError } = await runCommand<ResultResponse>(`access-control group delete --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(deleteError).to.not.be.undefined;
expect(deleteError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(deleteError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
4 changes: 2 additions & 2 deletions mocked-integration-tests/access-control/group/info.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ const tests = () =>

const { error: deleteError } = await runCommand<ResultResponse>(`access-control group info --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(deleteError).to.not.be.undefined;
expect(deleteError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(deleteError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});

it("should return an error when group is not found", async () => {
const response = AccessControlApiMock.groups.getiTwinGroup.groupNotFound(iTwinId, groupId);

const { error: deleteError } = await runCommand<ResultResponse>(`access-control group info --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(deleteError).to.not.be.undefined;
expect(deleteError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(deleteError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const tests = () =>

const { error: createError } = await runCommand<Group>(`access-control group list --itwin-id ${iTwinId}`);
expect(createError).to.not.be.undefined;
expect(createError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(createError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const tests = () =>
`access-control group update --itwin-id ${iTwinId} --group-id ${groupId} --name "${groupName}" --description "${groupDescription}" --member ${members[0].email} --ims-group "${imsGroups[0]}"`,
);
expect(createError).to.not.be.undefined;
expect(createError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(createError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const tests = () =>
);

expect(createError).to.not.be.undefined;
expect(createError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(createError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});

it("should return an error when provided iTwin is not found", async () => {
Expand All @@ -72,7 +72,7 @@ const tests = () =>
);

expect(createError).to.not.be.undefined;
expect(createError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(createError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});

it("should return an error when provided group is not found", async () => {
Expand All @@ -89,7 +89,7 @@ const tests = () =>
);

expect(createError).to.not.be.undefined;
expect(createError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(createError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ const tests = () =>

const { error: deleteError } = await runCommand<ResultResponse>(`access-control member group delete --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(deleteError).to.not.be.undefined;
expect(deleteError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(deleteError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});

it("should return an error when iTwin is not found", async () => {
const response = AccessControlApiMock.members.removeiTwinGroupMember.iTwinNotFound(iTwinId, groupId);

const { error: deleteError } = await runCommand<ResultResponse>(`access-control member group delete --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(deleteError).to.not.be.undefined;
expect(deleteError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(deleteError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ const tests = () =>

const { error: infoError } = await runCommand<GroupMemberInfo>(`access-control member group info --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(infoError).to.not.be.undefined;
expect(infoError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(infoError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});

it("should return an error when provided user member is not found", async () => {
const response = AccessControlApiMock.members.getiTwinGroupMember.teamMemberNotFound(iTwinId, groupId);

const { error: infoError } = await runCommand<GroupMemberInfo>(`access-control member group info --itwin-id ${iTwinId} --group-id ${groupId}`);
expect(infoError).to.not.be.undefined;
expect(infoError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(infoError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const tests = () =>

const { error: listError } = await runCommand<GroupMemberInfo[]>(`access-control member group list --itwin-id ${iTwinId}`);
expect(listError).to.not.be.undefined;
expect(listError?.message).to.be.equal(`HTTP error! ${JSON.stringify(response)}`);
expect(listError?.message).to.be.equal(JSON.stringify(response.error, null, 2));
});
});

Expand Down
Loading