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
15 changes: 15 additions & 0 deletions .changeset/moody-hounds-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@itwin/changed-elements-react": major
---

Add support for Changed Elements API v3 alongside v1/v2.

**Breaking changes:**
- `IComparisonJobClient` now requires `readonly apiVersion: "v2" | "v3"` field. Custom client implementations must add this property.

**New features:**
- `DiffJobClient` class for v3 API (`/diff`) with changeset Id resolution and progress tracking.
- `apiVersion` prop on `ChangedElementsWidget` to select API workflow (v1/v2/v3). Replaces reliance on `useV2Widget`.

**Deprecations:**
- `useV2Widget` prop on `ChangedElementsWidget` is now deprecated in favor of `apiVersion`.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@
"form-data@<4.0.6": ">=4.0.6",
"dompurify@<=3.4.10": ">=3.4.11",
"@google-cloud/storage@<7.21.0": ">=7.21.0",
"uuid@<11.1.1": ">=11.1.1"
"uuid@<11.1.1": ">=11.1.1",
"linkify-it@<=5.0.0": ">=5.0.1",
"js-yaml@<3.15.0": ">=3.15.0",
"read-yaml-file>js-yaml": "^3.14.1"
}
},
"devDependencies": {
Expand Down
43 changes: 39 additions & 4 deletions packages/changed-elements-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

## About

This package provides React components that help implement iTwin version comparison workflows. These components are designed to communicate with [iTwin Platform Changed Elements API](https://developer.bentley.com/apis/changed-elements-v2), which is used to retrieve data about iModel change history.
This package provides React components that help implement iTwin version comparison workflows. These components are designed to communicate with iTwin Platform Changed Elements APIs (v1/v2/v3) to retrieve data about iModel change history.
Comment thread
mattbjordan marked this conversation as resolved.

API Documentation:
- [V3 API Documentation - Tech Preview](https://developer.bentley.com/apis/changed-elements-v3/)
- [V2 API Documentation](https://developer.bentley.com/apis/changed-elements-v2/)
- [V1 API Documentation - DEPRECATED](https://developer.bentley.com/apis/changed-elements/)

## Installation

Expand All @@ -18,9 +23,14 @@ To begin using this package in your application, you will need to:
2. Provide `<VersionCompareContext />` somewhere in your app.

```tsx
import { VersionCompareContext } from "@itwin/changed-elements-react";
import { ComparisonJobClient, VersionCompare, VersionCompareContext } from "@itwin/changed-elements-react";

const comparisonJobClient = new ComparisonJobClient({
baseUrl: "https://api.bentley.com/changedelements",
getAccessToken: VersionCompare.getAccessToken,
});

<VersionCompareContext iModelsClient={iTwinIModelsClient}>
<VersionCompareContext iModelsClient={iTwinIModelsClient} comparisonJobClient={comparisonJobClient}>
<App />
</VersionCompareContext>
```
Expand All @@ -41,7 +51,7 @@ To begin using this package in your application, you will need to:
import { ChangedElementsWidget } from "@itwin/changed-elements-react";

<ChangedElementsWidget
useV2Widget
apiVersion="v2"
feedbackUrl="https://example.com"
iModelConnection={UiFramework.getIModelConnection()}
enableComparisonJobUpdateToasts
Expand All @@ -50,6 +60,31 @@ To begin using this package in your application, you will need to:
/>,
```

### V3 setup

Use `DiffJobClient` in `VersionCompareContext` and set `apiVersion="v3"` on `ChangedElementsWidget`.

```tsx
import { DiffJobClient, VersionCompare, VersionCompareContext } from "@itwin/changed-elements-react";

const comparisonJobClient = new DiffJobClient({
baseUrl: "https://api.bentley.com/changedelements",
getAccessToken: VersionCompare.getAccessToken,
iModelsClient: iTwinIModelsClient,
diffingStrategy: "VersionCompare",
});

<VersionCompareContext iModelsClient={iTwinIModelsClient} comparisonJobClient={comparisonJobClient}>
<ChangedElementsWidget
apiVersion="v3"
iModelConnection={UiFramework.getIModelConnection()}
enableComparisonJobUpdateToasts
/>
</VersionCompareContext>
```

`useV2Widget` is still supported for backward compatibility, but `apiVersion` is the preferred prop moving forward.

5. The `<NamedVersionSelectorWidget />` is an **experimental** React component that lets users inspect differences in properties between versions, generate reports, search for changed elements, and control element visibility. The following code shows an example widget initialization (does not show off all props).

```ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"error_versionCompare": "Version Compare Error",
"error_cantStart": "Could not start comparison against version",
"error_invalidToken": "Could not authenticate user for version compare usage",
"error_cantLoadNamedVersions": "Could not load named versions and changesets",
"msg_openingTarget": "Opening target iModel",
"msg_getChangedElements": "Requesting changed elements",
"msg_initializingComparison": "Initializing comparison",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export function NamedVersionSelectorWidget(props: Readonly<NamedVersionSelectorW
return;
}
setDisableStartComparison(true);
await runManagerStartComparisonV2({
const started = await runManagerStartComparisonV2({
comparisonJob: {
comparisonJob: {
status: "Completed",
Expand All @@ -145,6 +145,9 @@ export function NamedVersionSelectorWidget(props: Readonly<NamedVersionSelectorW
getToastsEnabled: () => true,
iModelsClient,
});
if (!started) {
setDisableStartComparison(false);
}
};

const stopComparisonCallback = useCallback(async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/changed-elements-react/src/ResizeObserver.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export interface ResizeObserverWrapperProps extends Omit<HTMLAttributes<HTMLDivE
*/
export const ResizeObserverWrapper = forwardRef < HTMLDivElement, ResizeObserverWrapperProps>(
function ResizeObserverWrapper(props, ref) {
const divRef = useRef(null as unknown as HTMLDivElement);
const divRef = useRef<HTMLDivElement>(null);
const size = useResizeObserver(divRef);
const mergedRefs = useMemo(() => mergeRefs(divRef, ref), [divRef, ref]);
return <div ref={mergedRefs} className={props.className}>{size && props.children(size)}</div>;
Expand Down
2 changes: 2 additions & 0 deletions packages/changed-elements-react/src/api/VersionCompare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface VersionCompareFeatureTracking {
trackVersionSelectorV2Usage: () => void;
/** Track when the user opens the version compare selector dialog to start a comparison */
trackVersionSelectorUsage: () => void;
/** Track when the user opens the version compare selector dialog to start a comparison using V3 API */
trackVersionSelectorV3Usage?: () => void;
/** Tracks when the user does a property comparison and opens the side-by-side frontstage */
trackPropertyComparisonUsage: () => void;
/** Tracks when the user opens the change report dialog */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
*--------------------------------------------------------------------------------------------*/
import type {
ChangedElementsPayload, IComparisonJobClient, ComparisonJob, GetComparisonJobParams, GetComparisonJobResultParams,
PostComparisonJobParams,
PostComparisonJobParams, PostComparisonJobParamsWithIds,
DeleteComparisonJobParams
} from "./IComparisonJobClient.js";
import { callITwinApi, throwBadResponseCodeError } from "./iTwinApi.js";
import { isChangedElementsPayload, isComparisonJob } from "./typeGuards.js";

export interface ComparisonJobClientParams {
baseUrl: string;
getAccessToken: () => Promise<string>;
}

export class ComparisonJobClient implements IComparisonJobClient {
public readonly apiVersion = "v2" as const;
private static readonly _acceptHeader = "application/vnd.bentley.itwin-platform.v2+json";
private _baseUrl: string;
private _getAccessToken: () => Promise<string>;
Expand All @@ -30,7 +32,7 @@ export class ComparisonJobClient implements IComparisonJobClient {
* @throws on a non 2XX response
*/
public async deleteComparisonJob(args: DeleteComparisonJobParams): Promise<void> {
return callITwinApi({
await callITwinApi({
url: `${this._baseUrl}/comparisonJob/${args.jobId}/iTwin/${args.iTwinId}/iModel/${args.iModelId}`,
method: "DELETE",
getAccessToken: this._getAccessToken,
Expand All @@ -39,7 +41,7 @@ export class ComparisonJobClient implements IComparisonJobClient {
Accept: ComparisonJobClient._acceptHeader,
...args.headers,
},
}) as unknown as Promise<void>;
});
}

/**
Expand All @@ -57,7 +59,8 @@ export class ComparisonJobClient implements IComparisonJobClient {
Accept: ComparisonJobClient._acceptHeader,
...args.headers,
},
}) as unknown as Promise<ComparisonJob>;
validate: isComparisonJob,
});
}

/**
Expand All @@ -79,15 +82,26 @@ export class ComparisonJobClient implements IComparisonJobClient {
if (!response.ok) {
await throwBadResponseCodeError(response, "Changed Elements request failed.");
}
return response.json() as unknown as Promise<ChangedElementsPayload>;

const body: unknown = await response.json();
if (!isChangedElementsPayload(body)) {
throw new Error(`Changed Elements request to ${args.comparisonJob.comparison.href} returned an unexpected response shape.`);
}

return body;
}

/**
* Gets comparison job.
* @returns ComparisonJob
* @throws on a non 2XX response.
*/
public async postComparisonJob(args: PostComparisonJobParamsWithIds): Promise<ComparisonJob>;
public async postComparisonJob(args: PostComparisonJobParams): Promise<ComparisonJob> {
if (!args.startChangesetId || !args.endChangesetId) {
throw new Error("ComparisonJobClient requires startChangesetId and endChangesetId.");
}

return callITwinApi({
url: `${this._baseUrl}/comparisonJob`,
method: "POST",
Expand All @@ -103,6 +117,7 @@ export class ComparisonJobClient implements IComparisonJobClient {
startChangesetId: args.startChangesetId,
endChangesetId: args.endChangesetId,
},
}) as unknown as Promise<ComparisonJob>;
validate: isComparisonJob,
});
}
}
Loading
Loading