Skip to content

Bentley ProjectWise

Client for a single Bentley ProjectWise datasource exposed through WSG.

One :class:ProjectWiseClient instance maps to one WSG instance — a single repository (datasource) on a single WSG host. Construct it with the WSG base URL, the repository id, and a :class:~workbench.clients.projectwise_token.WsgTokenProvider. The client owns transport (bearer header, error-body logging, instances envelope parsing) and exposes:

  • discoverylist_repositories (gateway-level; also an auth/health probe) and list_nav_children (Navigation tree walk), used by the Hoppa API's file-selection UI;
  • enrichmentget_documents / get_document (by stable GUID), used by the connector on re-bind;
  • contentfile_url (the $file endpoint) plus auth_headers (the bearer header the streaming fetch must send, since WSG offers no SAS/self- authenticating URL on this datasource — see spec-projectwise-client.md §8.4).

See spec-projectwise-client.md for the full API field notes and gotchas. Key ones honoured here: the repository id keeps its ~3A encoding literal (never re-encoded); document reads are scoped (never an unscoped class sweep, §6 #1); and 4xx/5xx bodies (which carry errorId/errorMessage) are logged before the exception is raised (§6 #5).

ProjectWiseClient

Read access to one ProjectWise datasource via the WSG REST API.

Parameters:

Name Type Description Default
base_url str

WSG API root up to and including the version, e.g. https://example-pw-ws.bentley.com/ws/v2.9. A trailing slash is tolerated.

required
repository_id str

The WSG repository id, e.g. Bentley.PW--example-pw.bentley.com~3AEXAMPLE. Passed through verbatim — the ~3A (WSG's encoding of :) must NOT be URL-encoded again.

required
token_provider WsgTokenProvider

Supplies the Authorization header WSG accepts on demand (Bearer or Basic). The PoC uses :class:EnvBearerTokenProvider or :class:BasicAuthProvider; production swaps in an IMS exchange.

required
timeout int

Per-request timeout in seconds.

60

auth_headers

auth_headers()

Authorization header for an authenticated WSG request.

The provider owns the full header value (Bearer … or Basic …), so the client is auth-scheme agnostic. Also passed (as a callable) to AzureBlobDocumentVersion so the content-streaming fetch can authenticate $file — WSG requires the Authorization header and offers no self-authenticating URL on this datasource. Recomputed on every call so a refreshed token / rotated credential is picked up transparently.

build_web_url staticmethod

build_web_url(repository_id, instance_id)

Build the ProjectWise Web permalink from just a repository id + GUID.

Static (no client/credentials needed) so a metadata-only bind — where no authenticated client was stood up — can still produce the permalink.

ds is the repository id minus the Bentley.PW-- plugin prefix (~3A kept literal); doc is the document GUID with hyphens removed.

file_url

file_url(instance_id)

Return the $file content URL for a document.

The bytes stream through the gateway and the request must carry the bearer header (see :meth:auth_headers); there is no SAS/self-authenticating URL on this datasource (§8.4).

get_document

get_document(instance_id)

Fetch a single Document instance by GUID, or None if not found.

get_document_attributes

get_document_attributes(instance_id, doc=None, environment_id=None)

Return a document's custom/environment attributes as a flat dict.

Generic — nothing about the datasource is hardcoded. Resolves the document's EnvironmentId to its Env_<id>_* dynamic class at runtime, then reaches the per-document attribute instance by traversing the DocumentEnvironment relationship from the document. This is keyed by the document GUID (fast, and version-correct — the env instance's own id carries an unguessable _<n> suffix), and names the relationship explicitly to disambiguate it from DocumentDefaultEnvironment (WSG errors on the ambiguous form). Returns the related instance's properties, or an empty dict when the document has no environment.

Parameters:

Name Type Description Default
instance_id str

The document GUID.

required
doc dict | None

An already-fetched Document instance, to read EnvironmentId from without a re-fetch.

None
environment_id Any

The document's EnvironmentId if already known (e.g. from a config-blob snapshot) — avoids fetching the document.

None

get_documents

get_documents(instance_ids)

Fetch Document instances by GUID, batched into one query.

Uses the collection + $id in [...] filter form the ProjectWise Web UI uses (confirmed working on Example), rather than the per-instance path or the relationship-filter form (which 500s on this plugin — §6 #2). !poly includes derived classes.

get_dynamic_classes

get_dynamic_classes()

Cached list of PW_WSG_Dynamic class names (Env_*, PrType_*).

The Schema property on ECClassDef is the versioned name (e.g. PW_WSG_Dynamic.01.02), so it is matched by prefix, not eq.

get_instance

get_instance(schema, class_name, instance_id)

Fetch any single class instance by exact instance id, or None.

list_nav_children

list_nav_children(node_id=None)

List child nodes of a Navigation node (or the roots when None).

Each node's properties carry Key_ClassName (Project = folder, Document = doc) and Key_InstanceId (the real GUID for metadata / $file). HasChildren is unreliable (§6 #4) — don't gate recursion on it; attempt to descend any Project node within a depth bound.

list_repositories

list_repositories()

List the datasources (repositories) this token can see on the host.

Gateway-level call (no datasource session required), so it doubles as an auth/health probe and as the source for instance discovery. Each instance carries instanceId (the repository id) and a DisplayLabel.

query

query(schema, class_name, *, filter_expr=None, select=None, top=None, poly=False)

Run a scoped query against any schema/class; return instances.

Schema-agnostic primitive. filter_expr / select are raw OData $filter / $select values (e.g. "$id+in+['abc']" and "DocumentEnvironment-forward-PW_WSG_Dynamic.Env_104_EXAMPLE.*") — built into the URL so requests doesn't reshape the operators. Always scope queries (a folder, an id, …); never sweep a class unscoped (§6 #1).

web_url

web_url(instance_id)

Stable ProjectWise Web permalink that opens the document in a browser.

The pwlink app=webview form opens the document viewer directly and needs only the datasource + document GUID — no work-area/project ids — so it is built purely from data already held, with no network call. It is GUID/URN-based, so it survives the document being moved or renamed.