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:
- discovery —
list_repositories(gateway-level; also an auth/health probe) andlist_nav_children(Navigation tree walk), used by the Hoppa API's file-selection UI; - enrichment —
get_documents/get_document(by stable GUID), used by the connector on re-bind; - content —
file_url(the$fileendpoint) plusauth_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.
|
required |
repository_id
|
str
|
The WSG repository id, e.g.
|
required |
token_provider
|
WsgTokenProvider
|
Supplies the |
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 |
None
|
environment_id
|
Any
|
The document's |
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.