API Reference

API reference introduction

Web Services Gateway Web API follows REST guidelines and exposes all information as resources. Each resource object can only be an instance of a certain class. Class describes properties of instances, and are types of resources that are logically grouped into services. Each service is described by a schema that is simply a list of classes in the service. The following list summarizes the main terms used in this reference:

  • Schema – describes the “resource service” from the client's perspective. Schema contains a list of classes.
  • Class – describes the “resource type”. Each class contains a list of property definitions. Classes can inherit properties from each other and can be arranged into a hierarchy.
  • Instance – represents concrete resources. Instance is identified by an instance ID that can be specific to each repository type. Instance contains values of properties (which are defined in class definition).
  • Relationship class – defines which classes’ instances may be linked together.
  • Relationship – links instances together.

Schema (service) examples:

  • Navigation service exposes NavNode class intances for exposing object hierarchy in the repository.
  • Views service exposes View and ContentGroup class instances for describing custom views in the repository, if any.
  • MetaSchema schema service exposes ECSchemaDef, ECClassDef and ECPropertyDef class instances to be used in describing the services themselves.
  • Persistence service exposes all domain class instances available in the repository. This is the most used service when querying or manipulating domain objects. Persistence service can be accessed using names of the schemas exposed by repository; it is common for repositories to expose more than one schema for grouping and separating domain objects (for example, a standard Bentley ProjectWise repository exposes schema named PW_WSG).

Bentley Web Services Gateway Web API is dynamic by design. Names of schemas, classes and properties are used in constructing the exposed URLs.

Please visit the API Explorer tool delivered with each Bentley Web Services deployment, found at https://localhost/ws.

Plugin architecture

Web Services Gateway Web API can expose any type of repository. Repository support is based on a dynamic plugin infrastructure, and can be extended and customized. Standard installation can deliver plugins like Bentley ProjectWise or Bentley eB Insight. It is possible to add support to other repository types by implementing the custom plugin. As a result, the URLs exposed by Web Service Gateway Web API can differ depending on:

  • Installed and deployed plugins – each plugin can add an additional repository type.
  • Repositories available in the installed environment – each repository can have a different set of classes defined by the consumer.
  • Customizations – customization allows users to add their own URLs by defining additional schemas and implementing support for them.

Generic JSON format

As all information in Web API is modeled as instances of some class, the same JSON format is returned for all requests. Depending on the request type, the response in JSON may slightly differ in the number of properties or presence of linked instances. However, the general parts remain the same.

The 2.0 and later JSON format is not compatible with 1.x series responses, the main changes being:

  • ETag in the response header and JSON
  • Schema and class names for each instance
  • Relationship and related instance support
  • $id changed to instanceId due to limitations of JSON.NET library

Instances with one relationship and one related instance (relationship and related instances are present only if specifically requested using the $select statement):

{
  "instances": [
    {
      "instanceId": "{instanceId}",
      "className": "{class}",
      "schemaName": "{schema}",
      "eTag": "Value_Of_ETag",
      "properties": {
        "property1": "Value_Of_Property1",
        "property2": "Value_Of_Property2",
        "propertyN": "Value_Of_PropertyN"
      },
      "relationshipInstances": [
        {
          "instanceId": "{instanceId}",
          "className": "{class}",
          "schemaName": "{schema}",
          "eTag": "Value_Of_ETag",
          "direction": "forward",
          "properties": {},
          "relatedInstance": {
            "instanceId": "{relatedInstanceId}",
            "className": "{class}",
            "schemaName": "{schema}",
            "eTag": "Value_Of_ETag",
            "properties": {
              "relProperty1": "Value_Of_RelProperty1",
              "relProperty2": "Value_Of_RelProperty2",
              "relPropertyN": "Value_Of_RelPropertyN"
            }
          }
        }
      ]
    }
  ]
}

For example:

{
  "instances": [
    {
      "instanceId": "8b1f8955-a5fc-463e-97a6-23479b5512b7",
      "className": "Document",
      "schemaName": "PW_WSG",
      "eTag": "hzTGd1g/0yjMX2iAtIL0OeAN9fA=",
      "properties": {
        "FileName": "img1.jpg",
        "MimeType": "raster",
        "CreateTime": "2014-12-09T11:54:02.63Z",
        "UpdateTime": "2015-01-23T13:29:12.2Z",
        "FileUpdateTime": "2014-12-09T11:54:02.85Z",
        "CreatedById": 14,
        "UpdatedById": 14,
        "FileSize": "849860",
        "IsLatest": true,
        "Description": "img.changeset.013",
        "Version": "",
        "Name": "img1",
        "IsLocked": false,
        "ParentGuid": "b2f5e500-c6d4-4dac-8036-15e2f0b19f7f",
        "FileUpdatedById": 14,
        "EnvironmentId": 101
      },
      "relationshipInstances": [
        {
          "instanceId": "",
          "className": "DocumentEnvironment",
          "schemaName": "PW_WSG",
          "eTag": "vD4ORP/4zxAdzlzmhDfYGMb7DdQ=",
          "direction": "forward",
          "properties": {},
          "relatedInstance": {
            "instanceId": "661d2cef-d777-4396-8818-bab51edef4d8",
            "className": "Env_101",
            "schemaName": "PW_WSG_Dynamic",
            "eTag": "UEXGpvsPWMgZUpUB5U9G93pq/bQ=",
            "properties": {
              "Color": "Red",
              "Count": 1
            }
          }
        }
      ]
    }
  ]
}

Generic URL format

Despite the fact that exact URLs exposed by Web Services Gateway Web API can vary between deployments, they all follow the same format. The generic URL format is:

v2.5/{GlobalSchema}/{GlobalInstanceId}/...

Where GlobalInstanceId is the ID of the default class in GlobalShema. The following global schemas are delivered with the default installation:

  • Repositories – used to expose the contents of each repository. This API reference will mostly focus on this service.
  • Plugins – this service exposes information about available plugins.
  • Policies – this service can be used to retrieve information about the features supported by plugins, repositories, etc., for example, information if the repository can be modified.
  • MetaSchema – service for retrieving information about schemas and properties. This can be useful when client-side applications cannot anticipate what kind of resources will be exposed in the repository.

Example:

GET v2.5/Repositories/Bentley.PW--PW/...

All services work in the same style; they expose classes and API allows operations with instances of those classes. All the features described in this document are valid for the Repository and all exposed services.

Repositories service

In-repository URL format

In addition to global services (Repositories, Plugins, Policies, MetaSchema), each repository exposes “internal” services described by schemas. The URL format is:

v2.5/Repositories/{repositoryId}/{schema}/...

Example:

GET v2.5/Repositories/Bentley.PW--PW/Navigation

By default, Web Services Gateway Web API exposes a set of standard in-repository service schemas (Policies, Views, Contents, Navigation, MetaSchema) and helper schemas (Bentley_Standard_Classes, DataSourceSpecification, EditorCustomAttributes, Bentley_Standard_CustomAttributes). The schemas describing a persistence service are unique for each repository. It is common for repositories to expose more than one schema for grouping and separating domain objects. For example, the standard Bentley ProjectWise repository exposes schema named PW_WSG.

Repositories requests

GET v2.5/Repositories

Retrieves a list of repositories from all registered plugins.

Since: 2.0

Response example:

{
    "instances": [
        {
        "instanceId": "Bentley.eB--MyWarehouse",
        "className": "RepositoryIdentifier",
        "schemaName": "Repositories",
        "eTag": "uBXbj6nWff66M53yGm59yn72WIQ=",
        "properties": 
            {
            "ECPluginID": "Bentley.eB",
            "Location": "MyWarehouse",
            "DisplayLabel": "MyWarehouse",
            "Description": "MyWarehouse"
            }
        },
        {
        "instanceId": "Bentley.PW--PW",
        "className": "RepositoryIdentifier",
        "schemaName": "Repositories",
        "eTag": "DlB30QqHqT7vokR9tL1/n+IST+g=",
        "properties": 
            {
            "ECPluginID": "Bentley.PW",
            "Location": "PW",
            "DisplayLabel": "PW",
            "Description": "PW"
            }
        }
    ]
}

Usage example

$.get(
    "https://localhost/ws/v2.5/Repositories",
    function(data) {
        for (var i = 0; i < data.length; i++) {
            alert(data[i].id);
        }
    },
    "json"
);
GET v2.5/Repositories?$filter={propertyName}+eq+’{propertyValue}’

Retrieves a list of repositories from all registered plugins with an applied filter. Please refer to the Instance querying options for a full OData query description and examples.

Since: 2.0

Parameters

propertyName

Optional. Property name to filter by. Must be specified together with propertyValue

propertyValue

Optional. Property value to filter by. Must be specified together with propertyName

Response example

{
    "instances": [
        {
        "instanceId": "Bentley.eB--MyWarehouse",
        "className": "RepositoryIdentifier",
        "schemaName": "Repositories",
        "eTag": "uBXbj6nWff66M53yGm59yn72WIQ=",
        "properties": 
            {
            "ECPluginID": "Bentley.eB",
            "Location": "MyWarehouse",
            "DisplayLabel": "MyWarehouse",
            "Description": "MyWarehouse"
            }
        }
    ]
}

Usage example

$.get( "https://localhost/ws/v2.5/Repositories/?filter=Location+eq+%27MyWarehouse%27",
    function(data) {
        for (var i = 0; i < data.length; i++) {
            alert(data[i].id);
        }
    },
"json");
);

Navigation service

Navigation is a presentation service.

Exposed schemas: Navigation

Exposed classes: NavNode

The Navigation service uses NavNode class instances to represent a hierarchal tree-like view of the repository. NavNode instance represents a node in the navigation tree and can be related to an instance of any other domain class, for example, a folder or document. The relationship is not mandatory; it is legal to have virtual NavNode instances that have no corresponding domain class instance (for example, a tree node for grouping modified documents from last month). Each NavNode instance contains properties with display and identification information of related domain class instances, if any.

GET v2.5/Repositories/{repositoryId}/Navigation/NavNode

Retrieves root Navigation tree nodes.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

Response example

{
 "instances": [
  {
    "instanceId": "ECObjects--PW_WSG-Project-8b674542-442b-430c-ba1b-127ba4ce8350",
    "className": "NavNode",
    "schemaName": "Navigation",
    "eTag": "lDJ/l6UbHcFDeRFJzm9VWqMnY5w=",
    "properties": {
        "Key_TypeSystem": "ECObjects",
        "Key_SchemaName": "PW_WSG",
        "Key_ClassName": "Project",
        "Key_InstanceId": "8b674542-442b-430c-ba1b-127ba4ce8350",
        "Label": "dmsSystem",
        "Description": "",
        "CollapsedImageID": "",
        "ExpandedImageID": "",
        "HasChildren": false,
        "Selectable": true,
        "IsEditable": false,
        "IsFileBacked": false,
        "IsCheckBoxEnabled": true,
        "IsCheckBoxVisible": false,
        "IsChecked": false,
        "IsDragEnabled": true
    }
  },
  {
    "instanceId": "ECObjects--PW_WSG-Project-947ce29e-fc26-493b-9606-53b8f61b1b1b",
    "className": "NavNode",
    "schemaName": "Navigation",
    "eTag": "SKOTh827nTJTkeKKRjab5r3z3Dg=",
    "properties": {
        "Key_TypeSystem": "ECObjects",
        "Key_SchemaName": "PW_WSG",
        "Key_ClassName": "Project",
        "Key_InstanceId": "947ce29e-fc26-493b-9606-53b8f61b1b1b",
        "Label": "LL84 Coordinate system_whole world",
        "Description": "",
        "CollapsedImageID": "",
        "ExpandedImageID": "",
        "HasChildren": false,
        "Selectable": true,
        "IsEditable": false,
        "IsFileBacked": false,
        "IsCheckBoxEnabled": true,
        "IsCheckBoxVisible": false,
        "IsChecked": false,
        "IsDragEnabled": true
    }
  }
 ]
}
GET v2.5/Repositories/{repositoryId}/Navigation/NavNode/{parentNodeId}/NavNode

Retrieves the child nodes of a specified parent node.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

parentNodeId

Required. The ID of parent NavNode instance.

Response example

{
 "instances": [
  {
    "instanceId": "ECObjects--PW_WSG-Project-bb281b1c-09a2-46ad-bc4e-e6c2889e267a",
    "className": "NavNode",
    "schemaName": "Navigation",
    "eTag": "YZIiza1Zn95GOiRI7l7rc41dIaw=",
    "properties": {
        "Key_TypeSystem": "ECObjects",
        "Key_SchemaName": "PW_WSG",
        "Key_ClassName": "Project",
        "Key_InstanceId": "bb281b1c-09a2-46ad-bc4e-e6c2889e267a",
        "Label": "components",
        "Description": "",
        "CollapsedImageID": "",
        "ExpandedImageID": "",
        "HasChildren": false,
        "Selectable": true,
        "IsEditable": false,
        "IsFileBacked": false,
        "IsCheckBoxEnabled": true,
        "IsCheckBoxVisible": false,
        "IsChecked": false,
        "IsDragEnabled": true
    }
  },
  {
    "instanceId": "ECObjects--PW_WSG-Project-2cd6700e-37bc-41ed-bd5b-3c01ae29562c",
    "className": "NavNode",
    "schemaName": "Navigation",
    "eTag": "58EOOBBQPij+UQ7BfwNbErU7QtY=",
    "properties": {
        "Key_TypeSystem": "ECObjects",
        "Key_SchemaName": "PW_WSG",
        "Key_ClassName": "Project",
        "Key_InstanceId": "2cd6700e-37bc-41ed-bd5b-3c01ae29562c",
        "Label": "Spatial",
        "Description": "",
        "CollapsedImageID": "",
        "ExpandedImageID": "",
        "HasChildren": false,
        "Selectable": true,
        "IsEditable": false,
        "IsFileBacked": false,
        "IsCheckBoxEnabled": true,
        "IsCheckBoxVisible": false,
        "IsChecked": false,
        "IsDragEnabled": true
    }
  }
 ]
}
GET v2.5/Repositories/{repositoryId}/Navigation/NavNode?filter={propertyName}+eq+'{propertyValue}'

Retrieves root Navigation tree nodes with a filter (a filter can also be applied when retrieving child nodes of specific NavNode). Please refer to Instance querying options for the full OData query description and examples.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

propertyName

Optional. Property name to filter by. Must be specified together with propertyValue

propertyValue

Optional. Property value to filter by. Must be specified together with propertyName

Response example

{
 "instances": [
  {
    "instanceId": "ECObjects--PW_WSG-Project-8b674542-442b-430c-ba1b-127ba4ce8350",
    "className": "NavNode",
    "schemaName": "Navigation",
    "eTag": "lDJ/l6UbHcFDeRFJzm9VWqMnY5w=",
    "properties": {
        "Key_TypeSystem": "ECObjects",
        "Key_SchemaName": "PW_WSG",
        "Key_ClassName": "Project",
        "Key_InstanceId": "8b674542-442b-430c-ba1b-127ba4ce8350",
        "Label": "dmsSystem",
        "Description": "",
        "CollapsedImageID": "",
        "ExpandedImageID": "",
        "HasChildren": false,
        "Selectable": true,
        "IsEditable": false,
        "IsFileBacked": false,
        "IsCheckBoxEnabled": true,
        "IsCheckBoxVisible": false,
        "IsChecked": false,
        "IsDragEnabled": true
    }
  },
  {
    "instanceId": "ECObjects--PW_WSG-Project-947ce29e-fc26-493b-9606-53b8f61b1b1b",
    "className": "NavNode",
    "schemaName": "Navigation",
    "eTag": "DSKOTh827nTJTkeKKRjab5r3z3Dg=A",
    "properties": {
        "Key_TypeSystem": "ECObjects",
        "Key_SchemaName": "PW_WSG",
        "Key_ClassName": "Project",
        "Key_InstanceId": "947ce29e-fc26-493b-9606-53b8f61b1b1b",
        "Label": "LL84 Coordinate system_whole world",
        "Description": "",
        "CollapsedImageID": "",
        "ExpandedImageID": "",
        "HasChildren": false,
        "Selectable": true,
        "IsEditable": false,
        "IsFileBacked": false,
        "IsCheckBoxEnabled": true,
        "IsCheckBoxVisible": false,
        "IsChecked": false,
        "IsDragEnabled": true
    }
  }
 ]
}

Meta schema service

Meta schema service allows access to information about available schemas, classes, properties both in a global and repository scope.

Exposed schemas: MetaSchema

Exposed classes: ECSchemaDef, ECClassDef, ECPropertyDef

All GET requests support additional parameters for filtering found instances. Please refer to Instance querying options for the full OData query description and examples.

GET v2.5/Repositories/{repositoryId}/MetaSchema/ECSchemaDef

Retrieves all the schemas available in the repository. Results are represented as instances of ECSchemaDef class.

Since: 2.0

Response example

{
 "instances": [
  {
    "instanceId": "N~3AMetaSchema.02.00",
    "className": "ECSchemaDef",
    "schemaName": "MetaSchema",
    "eTag": "Lmde3qNdVcK/kuSvCMxnq6Y1qBw=",
    "properties": {
        "Name": "MetaSchema",
        "DisplayLabel": "MetaSchema",
        "NameSpacePrefix": "ms",
        "Description": null,
        "VersionMajor": 2,
        "VersionMinor": 0
    }
  },
  {
    "instanceId": "N~3ABentley_Standard_Classes.01.00",
    "className": "ECSchemaDef",
    "schemaName": "MetaSchema",
    "eTag": "s0iqEEQ+2h2hXsk0Gj2pahh9xDg=",
    "properties": {
        "Name": "Bentley_Standard_Classes",
        "DisplayLabel": "Bentley Standard Classes",
        "NameSpacePrefix": "bsm",
        "Description": "Bentley Standard Classes",
        "VersionMajor": 1,
        "VersionMinor": 0
    }
  },
  ...
 ]
}

POST v2.5/Repositories/{repositoryId}/MetaSchema/ECSchemaDef

Imports a new schema into the repository. Requests should contain an ECSchemaDef instance with the schema file attached. All connections are updated instantly; IIS restart is not required.

Please refer to POST v2.5/Repositories/{repositoryId}/{schema}/{class} request description for more information and examples.

Since: 2.3

GET v2.5/Repositories/{repositoryId}/MetaSchema/ECClassDef

Retrieves all the classes from every schema available in the repository. Results are represented as instances of ECClassDef class.

Since: 2.0

Response example

{
 "instances": [
  {
    "instanceId": "N~3ANavigation.01.00~3ANavNode",
    "className": "ECClassDef",
    "schemaName": "MetaSchema",
    "eTag": "vkArHlzFcXldn2wkfAZPtdJSzCw=",
    "properties": {
        "Name": "NavNode",
        "DisplayLabel": "Navigation Node",
        "Schema": "Navigation.01.00",
        "Description": null,
        "IsStruct": false,
        "IsCustomAttributeClass": false,
        "IsDomainClass": true,
        "HasBaseClasses": false,
        "IsRelationshipClass": false,
        "BaseClasses": [ ]
    }
  },
  {
    "instanceId": "N~3ABentley_Standard_CustomAttributes.01.11~3AAllowDuplicateLocalizedValues",
    "className": "ECClassDef",
    "schemaName": "MetaSchema",
    "eTag": "OXIh7uxwiS05VfXTbgiZA1gbscU=",
    "properties": {
        "Name": "AllowDuplicateLocalizedValues",
        "DisplayLabel": "Allow duplicate localized values",
        "Schema": "Bentley_Standard_CustomAttributes.01.11",
        "Description": "Keeps the localizable values from this element from being combined with those from another element if duplication occurs.",
        "IsStruct": false,
        "IsCustomAttributeClass": true,
        "IsDomainClass": false,
        "HasBaseClasses": false,
        "IsRelationshipClass": false,
        "BaseClasses": [ ]
    }
  },
  ...
 ]
}

GET v2.5/Repositories/{repositoryId}/MetaSchema/ECClassDef?$filter=SchemaHasClass-backward-ECSchemaDef.$id+in+['{instanceId}']

Retrieves all the classes in a specific schema. Please refer to Instance querying options for the full OData query description and examples.

Since: 2.0

Response example

{
 "instances": [
  {
    "instanceId": "N~3ABentley_Standard_CustomAttributes.01.11~3AAllowDuplicateLocalizedValues",
    "className": "ECClassDef",
    "schemaName": "MetaSchema",
    "eTag": "OXIh7uxwiS05VfXTbgiZA1gbscU=",
    "properties": {
        "Name": "AllowDuplicateLocalizedValues",
        "DisplayLabel": "Allow duplicate localized values",
        "Schema": "Bentley_Standard_CustomAttributes.01.11",
        "Description": "Keeps the localizable values from this element from being combined with those from another element if duplication occurs.",
        "IsStruct": false,
        "IsCustomAttributeClass": true,
        "IsDomainClass": false,
        "HasBaseClasses": false,
        "IsRelationshipClass": false,
        "BaseClasses": [ ]
    }
  },
  {
    "instanceId": "N~3ABentley_Standard_CustomAttributes.01.11~3APropertyReferenceProperties",
    "className": "ECClassDef",
    "schemaName": "MetaSchema",
    "eTag": "+9NiOogkTLs1D2Shzwfz70PvwoQ=",
    "properties": {
        "Name": "PropertyReferenceProperties",
        "DisplayLabel": "Property reference properties",
        "Schema": "Bentley_Standard_CustomAttributes.01.11",
        "Description": "Applied to a custom attribute class defiition to indicate which string properties refer to a property in the class.",
        "IsStruct": false,
        "IsCustomAttributeClass": true,
        "IsDomainClass": false,
        "HasBaseClasses": false,
        "IsRelationshipClass": false,
        "BaseClasses": [ ]
    }
  },
  ...
 ]
}

Persistence service

Persistence service implements access to all domain objects exposed from the repository.

Exposed schemas: multiple repository specific schemas. Please use the MetaSchema service to query for schemas available in the repository.

Exposed classes: multiple repository specific (custom) classes in each of the schemas. Please use the MetaSchema service to query for classes available in the repository.

GET v2.5/Repositories/{repositoryId}/{schema}/{class}

Retrieves a list of all instances in the class.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing class to be queried.

class

Required. The class name of the instances to be retrieved.

Response example

{
 "instances": [
  {
    "instanceId": "6b7d6991-82ae-4060-b1f3-d810d270e127",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "NLUKmsIl2eRij9C+fsGttWI51cA=",
    "properties": {
        "FileName": "concatenate.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:09:58.29Z",
        "UpdateTime": "2014-08-22T21:01:09.257Z",
        "FileUpdateTime": "2014-08-22T21:01:09.257Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "8775",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "concatenate.oxygenicity",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  },
  {
    "instanceId": "100e8c87-9585-4d5f-8c25-e327f8e1d74e",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "DjBREV7zWcqXWaBYFLI+xKdbu/c=",
    "properties": {
        "FileName": "furrily.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:10:42.697Z",
        "UpdateTime": "2014-08-22T21:02:30.357Z",
        "FileUpdateTime": "2014-08-22T21:02:30.357Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "526",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "furrily.supersensual",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  }
 ]
}

Usage example

$.get("https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Document/",
    function(data) {
        for (var i = 0; i < data.length; i++) { 
            alert(data[i].id); 
        } 
    },
"json");

GET v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}

Retrieves a specific instance of the class.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing class to be queried.

class

Required. The class name of the instance to be retrieved.

instanceId

Optional. The ID of the instance to be retrieved.

Response example

{
 "instances": [
  {
    "instanceId": "6b7d6991-82ae-4060-b1f3-d810d270e127",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "NLUKmsIl2eRij9C+fsGttWI51cA=",
    "properties": {
        "FileName": "concatenate.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:09:58.29Z",
        "UpdateTime": "2014-08-22T21:01:09.257Z",
        "FileUpdateTime": "2014-08-22T21:01:09.257Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "8775",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "concatenate.oxygenicity",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
   }
  }
 ]
}

Usage example

$.get("https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Document/6b7d6991-82ae-4060-b1f3-d810d270e127/",
    function(data) {
        for (var i = 0; i < data.length; i++) { 
            alert(data[i].id); 
        } 
    },
    "json" );

GET v2.5/Repositories/{repositoryId}/{schema}/{class}?filter={propertyName}+eq+'{propertyValue}'

Retrieves a list of instances with a filter applied. Please refer to Instance querying options for the full OData query description and examples.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing class to be queried.

class

Required. The class name of the instances to be retrieved.

propertyName

Optional. Property name to filter by. Must be specified together with propertyValue

propertyValue

Optional. Property value to filter by. Must be specified together with propertyName

Response example

{
 "instances": [
  {
    "instanceId": "6b7d6991-82ae-4060-b1f3-d810d270e127",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "NLUKmsIl2eRij9C+fsGttWI51cA=",
    "properties": {
        "FileName": "concatenate.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:09:58.29Z",
        "UpdateTime": "2014-08-22T21:01:09.257Z",
        "FileUpdateTime": "2014-08-22T21:01:09.257Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "8775",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "concatenate.oxygenicity",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  },
  {
    "instanceId": "100e8c87-9585-4d5f-8c25-e327f8e1d74e",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "DjBREV7zWcqXWaBYFLI+xKdbu/c=",
    "properties": {
        "FileName": "furrily.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:10:42.697Z",
        "UpdateTime": "2014-08-22T21:02:30.357Z",
        "FileUpdateTime": "2014-08-22T21:02:30.357Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "526",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "furrily.supersensual",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  }
 ]
}

Usage example

$.get(
    "https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Document/?filter=FileName+like+%27*.json%27",
    function(data) {
        for (var i = 0; i < data.length; i++) {
            alert(data[i].id);
        }
    },
    "json"
);

GET v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}?select={propertyName1},{propertyNameN},

Retrieves a specific instance with selected properties only. Note: If a URL specifies the {instanceId}, only $select can be used from the list of available query options of OData queries. Please refer to Instance querying options for the full OData query description and examples.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing class to be queried.

class

Required. The class name of the instance to be retrieved.

propertyName1

Optional. Property name to select in the retrieved result.

propertyNameN

Optional. Property name to select in the retrieved result.

Response example

{
  "instances": [
  {
    "instanceId": "6b7d6991-82ae-4060-b1f3-d810d270e127",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "NLUKmsIl2eRij9C+fsGttWI51cA=",
    "properties": {
        "propertyName1": "concatenate.json",
        "propertyNameN": "unknown",
    }
  }
 ]
}

Usage example

$.get("https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Document/6b7d6991-82ae-4060-b1f3-d810d270e127/",
    function(data) {
        for (var i = 0; i < data.length; i++) {
        alert(data[i].id);
        }
    },
    "json"
);

GET v2.5/Repositories/{repositoryId}/{schema1}/{class1}?$filter={schema1}.{relationshipClass}-{direction}-{schema2}.{class2}.$id+eq+'{instanceId}'

Retrieves a list of all instances in class1 related to the instance with an instanceId of class2 (for example, all documents in specific folder). Please refer to Relationships and related classes for a full description about relationships.

Since: 2.0

Parameters:

repositoryId

Required. The ID of the repository against which to authenticate.

schema1

Required. The schema name of the class1.

class1

Required. The class name of the instances to be retrieved.

instanceId

Required. The ID of the linked class2 instance.

Notes on the parameters:

'schema1' parameter is optional if 'relationshipClass' and 'class1' belongs to the same schema.
'schema2' parameter is optional if the 'class2' belongs to the same schema as 'class1'.
'{schema1}.{relationshipClass}-{direction}' parameter is optional if there is only a single relationship class between 'class1' and 'class2'.

Response example

{
 "instances": [
  {
    "instanceId": "6b7d6991-82ae-4060-b1f3-d810d270e127",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "NLUKmsIl2eRij9C+fsGttWI51cA=",
    "properties": {
        "FileName": "concatenate.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:09:58.29Z",
        "UpdateTime": "2014-08-22T21:01:09.257Z",
        "FileUpdateTime": "2014-08-22T21:01:09.257Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "8775",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "concatenate.oxygenicity",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  },
  {
    "instanceId": "100e8c87-9585-4d5f-8c25-e327f8e1d74e",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "DjBREV7zWcqXWaBYFLI+xKdbuc=",
    "properties": {
        "FileName": "furrily.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:10:42.697Z",
        "UpdateTime": "2014-08-22T21:02:30.357Z",
        "FileUpdateTime": "2014-08-22T21:02:30.357Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "526",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "furrily.supersensual",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  }
 ]
}

Usage example

$.get("https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Document/?$filter=PW_WSG.DocumentParent-forward-PW_WSG.Project.$id+eq+'c78aa831-109d-4ad7-9307-fb651f682a4a'",
    function(data) {
        for (var i = 0; i < data.length; i++) {
        alert(data[i].id);
        }
    },
    "json"
);

GET v2.5/Repositories/{repositoryId}/{schema2}/{class2}/{instanceId}/{schema1}.{class1}

Retrieves a list of all instances in class1 related to the instance with an instanceId of class2. This is another form that retrieves the same results as using:

GET v2.5/repositories/{repositoryId}/{schema1}/{class1}?$filter={schema1}.{relationshipClass}-{direction}-{schema2}.{class2}.$id+eq+'{instanceId}'

This shorter form is possible only when class “class1” and class “class2” have a single possible relationship defined in the schema.

Response example

{
 "instances": [
  {
    "instanceId": "6b7d6991-82ae-4060-b1f3-d810d270e127",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "NLUKmsIl2eRij9C+fsGttWI51cA=",
    "properties": {
        "FileName": "concatenate.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:09:58.29Z",
        "UpdateTime": "2014-08-22T21:01:09.257Z",
        "FileUpdateTime": "2014-08-22T21:01:09.257Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "8775",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "concatenate.oxygenicity",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  },
  {
    "instanceId": "100e8c87-9585-4d5f-8c25-e327f8e1d74e",
    "className": "Document",
    "schemaName": "PW_WSG",
    "eTag": "DjBREV7zWcqXWaBYFLI+xKdbu/c=",
    "properties": {
        "FileName": "furrily.json",
        "MimeType": "unknown",
        "CreateTime": "2014-08-22T20:10:42.697Z",
        "UpdateTime": "2014-08-22T21:02:30.357Z",
        "FileUpdateTime": "2014-08-22T21:02:30.357Z",
        "CreatedById": 1,
        "UpdatedById": 1,
        "FileSize": "526",
        "IsLatest": true,
        "Description": "VisualStudioTests",
        "Version": "",
        "Name": "furrily.supersensual",
        "IsLocked": false,
        "ParentGuid": "3da143ea-23d5-4c6e-886d-4b1652ed4a18",
        "FileUpdatedById": 1,
        "EnvironmentId": 0
    }
  }
 ]
}

Usage example

$.get("https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Project/c78aa831-109d-4ad7-9307-fb651f682a4a/PW_WSG.Document",
    function(data) {
        for (var i = 0; i < data.length; i++) {
        alert(data[i].id);
        }
    },
    "json"
);

GET v2.5/Repositories/{repositoryId}/{schema2}/{class2}/{instanceId}/{schemaOfRelationshipClass}.{RelationshipClass}-{direction}-{schema1}.{class1}

Since: 2.3

Retrieves a list of all instances in class1 related to the instance with an instanceId of class2 using a specified relationship. This is another form that retrieves the same results as using:

GET v2.5/repositories/{repositoryId}/{schema1}/{class1}?$filter={schema1}.{relationshipClass}-{direction}-{schema2}.{class2}.$id+eq+'{instanceId}'

Please refer to GET v2.5/Repositories/{repositoryId}/{schema2}/{class2}/{instanceId}/{schema1}.{class1} for usage and response examples.

POST v2.5/Repositories/{repositoryId}/{schema}/{class}

Creates a new instance with the posted property values under the optionally specified related instance. For file creation and content upload please refer to Persistence file service support

Since: 1.1

Parameters

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing the class.

class

Required. The class name of the object to create.

Response example

{
    "changedInstance":
     {
        "change":"Created",
        "instanceAfterChange": {
            "instanceId": "6de337b1-86b2-4b7e-b6f0-e7668dc0a8cb",
            "className": "document",
            "schemaName": "{schema}",
            "properties": {
                "name":"MyText.txt",
                "version":"Testing",
                "description":null
                },
            "relationshipInstances":[
              {
                "instanceId":"2acbffec-88f5-4216-bf8c-1a042cbb5f36",
                "className":"DocumentParent",
                "schemaName":"{schema}",
                "direction":"forward",
                "properties":{},
                "relatedInstance": {
                    "instanceId":"8db0c801-cceb-4b18-8d50-75d542eed35a",
                    "className":"project",
                    "schemaName":"{schema}",
                    "properties":{}
                }
              }
            ]
         }
     }
}

Usage example

// The following C# example illustrates posting a document with file in one request.
private static string PostDocumentWithFileInOneRequest ()
    {
    var repository = "{RepositoryId}";
    var schema = "{Schema}";
    var schemaClass = "{Class}";
    // set id empty to create new instance
    var instanceId = "{InstanceId}";
    var existingProjectId = "{ExistingProjectId}";
    Uri requestUri = new Uri(String.Format("https://localhost/v2.5/Repositories/{0}/{1}/{2}/{3}", repository, schema, schemaClass, instanceId));
    var filepath = "{FilePath}";
    var fileName = new System.IO.FileInfo(filepath).Name;
    // Creating json that describes document with property 'name'
    JObject documentToPost = CreateClassInstance("Document", schema, null, new KeyValuePair<string, object>("name", fileName));
    // Specifying relationship that binds document to its parent (project)
    JObject relationship = CreateRelationshipClassInstance("DocumentParent", schema, null, "forward");
    // specifying document parent that is existing project with its id
    JObject documentParent = CreateClassInstance("Project", schema, existingProjectId);
    JToken json = CreateJson(documentToPost, relationship, documentParent);
    var jsonInstance = json.ToString();
    // Posting document
    using (var client = new HttpClient())
        {
        var multipartContent = new MultipartContent("form-data", "-----------------------------" + Guid.NewGuid().ToString("N"));

        var stringContent = new StringContent(jsonInstance, null, "application/json");
        multipartContent.Add(stringContent);
                    
        stringContent.Headers.Add("Content-Disposition", "form-data; name=\"instance\"");
        var fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(filepath));
        fileContent.Headers.Add("Content-Disposition", String.Format("form-data; name=\"file\"; FileName=\"{0}\"", fileName));
        multipartContent.Add(fileContent);
                
        return client.PostAsync(requestUri, multipartContent).Result.Content.ReadAsStringAsync().Result;
        }
    }
    
private static JObject CreateClassInstance (string className, string schemaName, string id, params KeyValuePair<string, object>[] properties)
    {
    var newClass = new JObject ();
    newClass.Add ("className", className);
    newClass.Add ("schemaName", schemaName);
    newClass.Add ("instanceId", id);
    if (properties.Length > 0)
        {
        var propertiesObject = new JObject ();
        foreach (KeyValuePair<string, object> property in properties)
            {
            propertiesObject.Add (property.Key, Newtonsoft.Json.JsonConvert.SerializeObject (property.Value));
            }
        newClass.Add ("properties", propertiesObject);
        }
    return newClass;
    }

private static JObject CreateRelationshipClassInstance (string className, string schemaName, string id, string direction, params KeyValuePair<string, object>[] properties)
    {
    JObject relationshipClass = CreateClassInstance (className, schemaName, id, properties);
    relationshipClass.Add ("direction", direction);
    return relationshipClass;
    }

private static JToken CreateJson (JObject instance, JObject relationshipInstance, JObject relatedInstance)
    {
    relationshipInstance.Add ("relatedInstance", relatedInstance);
    var relationshipsArray = new JArray ();
    relationshipsArray.Add (relationshipInstance);
    instance.Add ("relationshipInstances", relationshipsArray);
    var json = new JObject ();
    json.Add ("instance", instance);
    return json;
    }
        
<!DOCTYPE html>
<html>
<head>
    <title>Sample multipart post</title>
    <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
    <script type="text/javascript">
    function getCreateTabFile() {
        return $('#fileInput')[0].files[0];
    }

    function getUploadUrl() {
        return $('#url').val();
    }

    function getAuthHeader() {
        return $('#authHeader').val();
    }

    function getInstanceJson() {
        return $('#bodyInput').val();
    }

    function uploadFile(url, instanceBody, file, authHeader) {
        var params = {
            address: url,
            body: instanceBody,
            file: file,
        };
        var formData = new FormData();
        formData.append('file', params.file, params.file.name);
        formData.append('instance', new Blob([params.body], {
            type: "application/json"
        }));

        $.support.cors = true; //for IE
        $.ajaxSetup({
            async: true,
            type: 'POST',
            url: params.address
        });
        var ajax = {};
        ajax.beforeSend = function(xhr) {
            xhr.setRequestHeader('Authorization', authHeader);
        };
        ajax.data = formData;
        ajax.processData = false;
        ajax.contentType = false;

        return $.ajax(ajax);
    }

    $(document).ready(function () {
        $('#uploadButton').on('click', function() {
            var response = {};
            uploadFile(getUploadUrl(), getInstanceJson(), getCreateTabFile(), getAuthHeader()).done(function(data, textStatus, jqXHR) {
                response.success = true;
                response.status = jqXHR.status;
                response.statusText = jqXHR.statusText;
                try {
                    response.data = JSON.parse(jqXHR.responseText);
                } catch (exception) {
                    response.data = jqXHR.responseText;
                }
                alert(JSON.stringify(response));
            }).fail(function(jqXHR, textStatus, err) {
                response.success = (jqXHR.status < 400 && jqXHR.status > 0);
                response.status = err.code || jqXHR.status;
                response.statusText = jqXHR.statusText;
                try {
                    response.responseText = JSON.parse(jqXHR.responseText).errorMessage;
                } catch (exception) {}
                try {
                    response.data = JSON.parse(jqXHR.responseText);
                } catch (exception) {
                    response.data = jqXHR.responseText;
                }
                response.headers = jqXHR.getAllResponseHeaders();
                alert(JSON.stringify(response));
            });

        });
    });
    </script>
</head>
<body>
    <input id="fileInput" type="file" name="file" />
    <br>
    <input id="url" type="text" name="url" placeholder="{Host}/{WsgVersion}/Repositories/{RepositoryId}/{Schema}/{Class}/{InstanceId}" style="width:500px" />
    <br>
    <input id="authHeader" type="text" name="authHeader" placeholder="Auth header value." style="width:500px" />
    <br>
    <textarea id="bodyInput" type="textarea" name="bodyInput" rows="20" cols="80" placeholder="Instance json."></textarea>
    <button id="uploadButton">Upload</button>
</body>
</html>
        
    

POST v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}

Updates existing instance properties in the repository and optionally uploads file content associated with the instance that replaces the existing file.

Since: 2.0

Parameters

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing the class.

class

Required. The class name of the instance to be updates.

instanceId

Required. The ID of the instance to be updated.

The content can be posted in the following formats:

  • JSON content with the "Content-Type" header set to "application/json" when doing a handshake.
  • "Multipart/form-data" content with one "application/json" part and optionally one file part when updating a file and instance within a single request.

Updating an instance and file with resumable requests works the same way as described in PUT v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}/$file. The only difference is JSON body in the handshake request. If you use JavaScript, URL escape should be used for file name in headers as well as adding parameter "IsFileNameUrlEncoded" equal to true for Content-Disposition header.

Example:

POST https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Document/585ea57b-667c-459e-8243-9930b7335bf5

Request headers:

Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
Authorization: Basic YWRtaW46YWRtaW4=

Request body:

---------------------------acebdf13572468
Content-Disposition: form-data; name="fieldNameHere"
Content-Type: application/json

{
  "instance" : {
    "instanceId": "585da57b-667c-459e-8223-9930b7335bf0",
    "className": "document",
    "schemaName": "PW_WSG",
    "properties": { "Name": "Document1.txt" },
    "relationshipInstances": [{
        "className": "DocumentParent",
        "schemaName": "PW_WSG",
        "direction": "forward", 
        "relatedInstance": {
            "instanceId": "99faa16e-06cc-4c7f-9c0f-73aef604bc17",
            "className": "Project",
            "schemaName": "PW_WSG"
        }
    }]
  }
}
---------------------------acebdf13572468
Content-Disposition: form-data; name="fieldNameHere"; filename="Test.txt"
Content-Type: text/plain

{File Content}
---------------------------acebdf13572468—

Response example

A response with status 200 (OK).

DELETE v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}

Deletes an existing object instance in the repository with the optionally posted property values.

Since: 2.0

Parameters

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing the class.

class

Required. The class name of the instance to be deleted.

instanceId

Required. The ID of the instance to be deleted.

Response example

A response with status 200 (OK).

Usage example – empty delete request

$.ajax({
    url: "https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Project/6b7d6991-82ae-4060-b1f3-d810d270e127",
    type: "DELETE",
});

Usage example – delete request with JSON body

$.ajax({
    url: "https://localhost/ws/v2.5/Repositories/Bentley.PW--PW/PW_WSG/Project/6b7d6991-82ae-4060-b1f3-d810d270e127",
    type: "DELETE",
    data: "{ instance: {
        instanceId: '6b7d6991-82ae-4060-b1f3-d810d270e127',
        className: 'Project',
        schemaName: 'PW_WSG'
        properties: 
            {
              Name: 'My new name'
            },
    }
}",
    beforeSend : function(req) {
        req.setRequestHeader('content-type', 'application/json'); 
    } 
});

POST v2.5/{globalSchema}/{globalInstanceId}/{schema}/{class}/{instanceId} (for multiple instances)

Creates, updates, and deletes multiple instances in the same request. JSON must contain an instance graph (root instance and related instances) with change state information. Each instance should contain a changeState value explicitly specifying the action to be performed. Possible changeState values:

  • new
  • existing
  • modified
  • deleted

The request URL should match the root instance operation. For example, if the root instance is being modified, the URL must contain {instanceId}. The same {instanceId} must be skipped in the instance creation request.

Similarly for single instance requests, if the request URL implies an update or a new instance operation, then the changeState on the root instance is optional. If JSON contains an instance with an empty instance Id, the “new” operation is assumed automatically.

JSON example:

{
  "instance": {
    "instanceId": "123",
    "className": "Company",
    "schemaName": "Staff",
    "changeState": "modified",
    "properties": {
      "Name": "Bentley"
    },
    "relationshipInstances": [
      {
        "instanceId": "relationship1",
        "className": "Company_x_Employee",
        "schemaName": "Staff",
        "direction": "forward",
        "changeState": "new",
        "relatedInstance": {
          "instanceId": "ned",
          "className": "Employee",
          "schemaName": "Staff",
          "changeState": "new",
          "properties": {
            "Name": "Ned Flanders",
            "Role": "Software Engineer"
          }
        }
      }
    ]
  }
}

Response example

{
  "changedInstance": {
    "change": "Modified",
    "instanceAfterChange": {
      "instanceId": "b97fe947-4bc5-4ef0-a06b-729759935782",
      "className": "Document",
      "schemaName": "PW_WSG",
      "properties": {
        "Name": "Name1",
        "FileName": null,
        "Description": null,
        "MimeType": null,
        "Version": null
      },
      "relationshipInstances": [
        {
          "instanceId": "b97fe947-4bc5-4ef0-a06b-729759935783",
          "className": "DocumentParent",
          "schemaName": "PW_WSG",
          "direction": "forward",
          "properties": {},
          "relatedInstance": {
            "instanceId": "ae12f364-3aac-4dee-81fc-90d646b6b81d",
            "className": "Project",
            "schemaName": "PW_WSG",
            "properties": {
              "Name": "Test1"
            }
          }
        }
      ]
    }
  }
}

POST v2.5/$batch

Web API batch supports the grouping of requests and sends them to the server as a single HTTP request. This functionality is based on standard Microsoft ASP.NET Web API 2 OData batch feature, allowing to pack several API requests and send them as a single request in the following procedures:

  • Accepting mime/multipart request containing multiple requests
  • Processing each request individually
  • Returning multipart/mixed content with responses for each individual request

More information about batch requests can be found at https://blogs.msdn.com/b/webdev/archive/2013/11/01/introducing-batch-support-in-web-api-and-web-api-odata.aspx

Since: 2.0

Example of the request headers:

Content-Type: multipart/mixed; boundary=batch_dc3720cc-435b-4e7a-bcfd-44a569fe7240 
Host: {wsg}

Example of the request body:

--batch_dc3720cc-435b-4e7a-bcfd-44a569fe7240
Content-Type: application/http; msgtype=request

POST /ws/v2.5/Repositories/{repositoryId}/{schema}/project/ HTTP/1.1
Host: {wsg}
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/json; charset=utf-8

{
 "instance": {
    "className": "project",
    "schemaName": "{schema}",
    "properties":{ "Name": "Name1" },
    "relationshipInstances":[
      {
       "className": "projectParent",
       "schemaName": "{schema}", 
       "direction": "forward",
       "relatedInstance":{
          "className": "project",
          "schemaName": "{schema}",
          "instanceId": "ae12f364-3aac-4dee-81fc-90d699b6b999"
       }
      }
    ]
 }
}
--batch_dc3720cc-435b-4e7a-bcfd-44a569fe7240
Content-Type: application/http; msgtype=request

GET /ws/v2.5/Repositories/{repositoryId}/{schema}/project?$filter=name+eq+'Name2' HTTP/1.1
Host: {wsg}
Authorization: Basic YWRtaW46YWRtaW4=


--batch_dc3720cc-435b-4e7a-bcfd-44a569fe7240
Content-Type: application/http; msgtype=request

GET /ws/v2.5/Repositories/{repositoryId}/{schema}/project?$filter=name+like+'Name3' HTTP/1.1
Host: {wsg}
Authorization: Basic YWRtaW46YWRtaW4=


--batch_dc3720cc-435b-4e7a-bcfd-44a569fe7240--

Response example:

--969eb9a6-7129-481c-9f78-f20a699422a0
Content-Type: application/http; msgtype=response

HTTP/1.1 201 Created
Location: https://localhost/ws/ws/v2.5/Repositories/{repositoryId}/{schema}/project/ac9e7d75-9999-499a-b136-2ff79f211fbc
Content-Type: application/json; charset=utf-8

{
  "changedInstance": {
    "change": "Created",
    "instanceAfterChange": {
      "instanceId": "ac9e7d75-9999-499a-b136-2ff79f211fbc",
      "className": "project",
      "schemaName": "{schema}",
      "properties": {
        "Name1": "12345677",
        "description": null
      },
      "relationshipInstances": [{
          "instanceId": "8054b041-2f35-434d-aef0-0babbd288f2f",
          "className": "ProjectParent",
          "schemaName": "{schema}",
          "direction": "forward",
          "properties": {},
          "relatedInstance": {
            "instanceId": "ae12f364-3aac-4dee-81fc-90d646b6b81d",
            "className": "project",
            "schemaName": "{schema}",
            "properties": {}
          }
      }]
    }
  }
}
--969eb9a6-7129-481c-9f78-f20a699422a0
Content-Type: application/http; msgtype=response

HTTP/1.1 200 OK
ETag: "2jmj7l5rSw0yVb/vlWAYkK/YBwk="
Content-Type: application/json; charset=utf-8

{"instances":[]}
--969eb9a6-7129-481c-9f78-f20a699422a0
Content-Type: application/http; msgtype=response

HTTP/1.1 200 OK
ETag: "2jmj7l5rSw0yVb/vlWAYkK/YBwk="
Content-Type: application/json; charset=utf-8

{"instances":[]}
--969eb9a6-7129-481c-9f78-f20a699422a0—

Usage example

Please note: System.Net.Http.Formatting reference is needed (add it by installing Nugent via package manager console "Install-Package System.Net.Http.Formatting.Extension").
public void Batch()
    {
    string url = "https://localhost/ws/v2.5/Repositories/";
    string batchUrl = "https://localhost/ws/v2.5/$batch";
    MultipartContent contents = new MultipartContent ("mixed", "batch_dc3720cc-435b-4e7a-bcfd-44a569fe7240")
        {
        new HttpMessageContent (new HttpRequestMessage ( HttpMethod.Post, url + "{repositoryId}/{schema}/project/")
            {
            Content = new StringContent (@"{""instance"":{""properties"":{""name"":""Name1""},""relationshipInstances"":[{""relatedInstance"":{""className"":""project"",""schemaName"":""{schema}"",""instanceId"":""ae12f364-3aac-4dee-81fc-90d646b6b81d""},""className"":""projectParent"",""schemaName"":""{schema}"", ""direction"":""forward""}],""className"":""project"",""schemaName"":""{schema}""}}", Encoding.UTF8, "application/json")
            }),
        new HttpMessageContent (new HttpRequestMessage( HttpMethod.Get, url + "{repositoryId}/{schema}/project/?filter=name+eq+'Name2'")),
        new HttpMessageContent (new HttpRequestMessage( HttpMethod.Get, url + "{repositoryId}/{schema}/project/?filter=name+like+'Name3'"))
        };
    foreach (var content in contents)
        {
        ((HttpMessageContent)content).HttpRequestMessage.Headers. Authorization = new AuthenticationHeaderValue ("basic", "YWRtaW46YWRtaW4=");
        }
    HttpClient client = new HttpClient ();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("basic", "YWRtaW46YWRtaW4=");
    HttpResponseMessage response = client.PostAsync (batchUrl, contents).Result;
    MultipartStreamProvider streamProvider = response.Content.ReadAsMultipartAsync ().Result;
    foreach (HttpContent content in streamProvider.Contents)
        {
        //Do something
        }
    }

POST v2.5/{globalSchema}/{globalInstanceId}/$changeset

Web API changeSet supports sending multiple instance changes as a single request. This supports:

  • Different class instances
  • Different schema instances

Note: Changeset does not currently accept mime/multipart requests.

Each instance should contain a changeState value that explicitly specifies the action to be performed. Refer to POST v2.5/{globalSchema}/{globalInstanceId}/{schema}/{class}/{instanceId} (for multiple instances) for more information on how to make a valid single instance JSON.

Example of the request body:

{ 
"instances": [{
     "className": "Project", 
     "schemaName": "PW_WSG", 
     "changeState": "new",
     "properties": { 
         "Name": "MSA_fold_new" 
     },
     "relationshipInstances": [{ 
         "className": "ProjectParent", 
         "schemaName": "PW_WSG", 
         "direction": "forward", 
         "relatedInstance": { 
             "instanceId": "ba352e35-4a85-4092-b7ee-436d0aa3cfca", 
             "className": "Project", 
             "schemaName": "PW_WSG" 
         } 
      }] 
 },{
     "className": "Document", 
     "schemaName": "PW_WSG", 
     "changeState": "modified",
     "instanceId": "0e02f061-4b3e-4584-b8fa-b4ea34865b5d",
     "properties": { 
         "Name": "file_modified" 
     },
     "relationshipInstances": [{ 
         "className": "DocumentParent", 
         "schemaName": "PW_WSG", 
         "direction": "forward", 
         "relatedInstance": { 
             "instanceId": "ba352e35-4a85-4092-b7ee-436d0aa3cfca", 
             "className": "Project", 
             "schemaName": "PW_WSG" 
         } 
     }] 
 },{
     "className": "Project", 
     "schemaName": "PW_WSG", 
     "changeState": "deleted",
     "instanceId": "ea20c8c2-d923-4311-ba36-0af77debeea8"
   }]
}

Response example:

{
 "changedInstances":[{
    "change":"Created",
    "instanceAfterChange": {
        "instanceId":"18da79f0-19cd-41c6-b822-3908e7681591",
        "schemaName":"PW_WSG",
        "className":"Project",
        "properties": {
            "Name":"MSA_fold_new",
            "Description":null,
            "EnvironmentId":null,
            "IsRichProject":null,
            "WorkflowId":null
            },
        "relationshipInstances": [{
            "instanceId":"",
            "schemaName":"PW_WSG",
            "className":"ProjectParent",
            "direction":"forward",
            "properties":{},
            "relatedInstance": {
                "instanceId":"ba352e35-4a85-4092-b7ee-436d0aa3cfca",
                "schemaName":"PW_WSG",
                "className":"Project",
                "properties": {
                    "Name":null,
                    "Description":null,
                    "EnvironmentId":null,
                    "WorkflowId":null
                }
            }
        }]
    }
 },{
    "change":"Modified",
    "instanceAfterChange": {
        "instanceId":"0e02f061-4b3e-4584-b8fa-b4ea34865b5d",
        "schemaName":"PW_WSG",
        "className":"Document",
        "properties": {
            "Name":"file_modified",
            "FileName":null,
            "Description":null,
            "MimeType":null,
            "Version":null,
            "StateId":null,
            "IsFinal":null
        },
        "relationshipInstances": [{
            "instanceId":"",
            "schemaName":"PW_WSG",
            "className":"DocumentParent",
            "direction":"forward",
            "properties":{},
            "relatedInstance": {
                "instanceId":"ba352e35-4a85-4092-b7ee-436d0aa3cfca",
                "schemaName":"PW_WSG",
                "className":"Project",
                "properties": {
                    "Name":null,
                    "Description":null,
                    "EnvironmentId":null,
                    "WorkflowId":null
                }
            }
        }]
    }
 },{
    "change":"Deleted",
    "instanceAfterChange": {
        "instanceId":"ea20c8c2-d923-4311-ba36-0af77debeea8",
        "schemaName":"PW_WSG",
        "className":"Project",
        "properties": {
            "Name":null
        }
    }
   }]
}

Persistence service file support

GET v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}/$file

Downloads a file associated with the specified instance.

Recommended file download workflow:

  1. During the initial download of a file, send a request to read the bytes from the response stream. Save the value in the response's ETag header.
  2. When downloading the same file a second time, make the same request and set the If-None-Match header value with the saved ETag header value received in the previous response.
  3. If the file has not changed on the server, the response will have an HTTP status code of 304 (Not Modified) and will be empty of content.
  4. If the file has changed on the server, the response will have an HTTP status code of 200 (OK) and the entire file content. In addition to the content, the ETag header will have a new value, which should be saved to replace the previous one.

File download workflow with an option to resume a broken download in the event that the connection is lost:

  1. Make a request (the If-None-Match header can also be used as explained above).
  2. Read the bytes from the response stream.
  3. Save the response's ETag header value.
  4. In the event that a connection is lost or manually killed before all bytes are read:
    • To resume the download, make a request with the If-Range header value set from the previously saved ETag and Range header value bytes={byteToReadFrom}- , where {byteToReadFrom} is the index of the next byte that was not yet downloaded. The first index value is 0. For example, if 500 bytes were already read, set Range: bytes=500-.
    • If the file has not changed on the server, the response's status code will be 206 (Partial Content) and will contain a Content-Range header with a bytes value of 500-999/1000 (where 999 is the last byte index, 1000 is the total length of the file).
    • If the file has changed on the server, the response's status code will be 200 (OK) and will not contain a Content-Range header, meaning that the entire content was successfully sent from the beginning of the file. The ETag header will have a new value, which should be saved to replace the previous one.

Since: 1.1

Parameters

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing the class.

class

Required. The class name of the instance with associated file.

instanceId

Required. The ID of the instance for which to get the file.

Response example:

Response is the file being downloaded.

Usage example:

window.open("https://localhost/ws/v2.5/Repositories/{RepositoryId}/{Schema}/document/{DocumentId}/$file");
PUT v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}/$file

Uploads a file content associated with the instance, replacing the existing file, if any.

File can be uploaded as byte/stream content in one request without any additional HTTP headers. Otherwise, it is possible to upload files in parts with a possibility to resume upload in the event of a lost connection. The multipart file upload workflow:

  • Send an initial "handshake" request to inform the server of the total size of the file that will be uploaded and the name of the document file. This is done by sending no content with two headers:
    • ”Content-Range: bytes */{fileSize}” where {fileSize} is the size of file in bytes;
    • ”Content-Disposition: attachment; filename="{fileName}"” where {fileName} is the file name and extension (note that {fileName} can be url encoded, to escape special symbols, then add parameter '"IsFileNameUrlEncoded"=true', needed only for JavaScript clients because special symbols are not allowed by browser for JavaScript header requests).
    • If successful, the response will have an HTTP status code of 308 (Resume Incomplete), which indicates that the server is waiting for the next "resume" request with file content. This response will contain an ”ETag” header with a value to future identify the upload.
    • If {fileSize} is greater than the maximum file size allowed on the server, the request will fail with error status 413 (Request Entity Too Large).
  • Send an "upload" request with the file content and the following headers:
    • ”If-Match” with the value of the last response's ”ETag”;
    • ”Content-Range: bytes {startByteIndex}-{endByteIndex}/{fileSize}”, e.g.:
      ”Content-Range: bytes 0-999/1000” to send the whole file;
      ”Content-Range: bytes 0-499/1000” to send the file in parts in case it is too large for a single request message.
    • The sent content length and the ”Content-Length” header should be equal to {endByteIndex} - {startByteIndex} + 1. For example, the length for “bytes 0-499/1000” must be 499 - 0 + 1 = 500.
    • If the server is still missing bytes, the response will contain a status of 308 (Resume Incomplete) and a header ”Range: bytes=0-{endByteIndex}” that indicates how many bytes the server has received and saved.
    • No ”Range” header indicates that the server received 0 bytes from the file. Before each proceeding upload request, this range should be checked and the request's ”Content-Range” set accordingly. For example, if a response reads ”Range: bytes=0-99”, the proceeding request should have ”Content-Range: bytes 100-{endByteIndex}/{fileSize}”.
    • ”Content-Range: bytes 99-{endByteIndex}/{fileSize}” will also work by overwriting the final byte on the server. However, ”Content-Range: bytes 101-{endByteIndex}/{fileSize}” will not work and the response will have the same 308 status and ”Range: bytes=0-99”. This means that the ranges must touch or overlap since it is not possible to skip any number of bytes.
  • When the entire file content is successfully uploaded, the response will have a status of 200 (OK). The file will be permanently saved only after it is fully uploaded. Until then, it is saved as a temporary file. Temporary files are deleted after a fixed period of time. Therefore, if not resumed in time, the upload will need to be restarted.

  • When the upload expires, the ”ETag” for this upload becomes invalid. Using it in a request's ”If-Match” will result in a 412 (Precondition Failed) error status response.
  • If the connection is lost or manually killed before all the bytes were written, then a "query" request should be executed to find out how many bytes the server has received so far. The request should contain empty content and the following headers:
    • ”Content-Range: bytes */{fileSize}”;
    • ”If-Match: {savedETag}”.
    • The upload can be continued from the byte count found in the ”Range” header of the server’s response.

Since: 1.1

Parameters

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing the class.

class

Required. The class name of the instance for which to update the file.

instanceId

Required. The ID of the instance for which to update the file.

Example of uploading an entire file with one request:

PUT https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/document/6b55fa9c-23c6-45fb-a242-d7550cec4c07/$file

Headers:

Content-Type: text/plain
Content-Length: {length}
Content-Disposition: attachment; filename="FileName.txt"
Authorization: Basic YWRtaW46YWRtaW4=

Body:

<Text file contents…> 

Response:

Headers:

HTTP/1.1 200 OK
Content-Length: 224
Content-Type: application/json; charset=utf-8
ETag: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Server: Bentley-WSG/02.00.05.13
Server: Bentley-WebAPI/2.4
Date: Thu, 26 Jun 2014 08:26:18 GMT

Body:

{
  "changedInstance": {
    "change": "Modified",
    "instanceAfterChange": {
      "instanceId": "172c4b1d-0551-495b-b2ed-6ff79d1ab120",
      "className": "document",
      "schemaName": "{schema}",
      "properties": {
        "name": null,
        "filename": null,
        "description": null
      }
    }
  }
}

Example of uploading a file divided into parts:

Request 1 - 'handshake':

PUT https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/document/172c4b1d-0551-495b-b2ed-6ff79d1ab120/$file

Headers:

Authorization: Basic YWRtaW46YWRtaW4=
Content-Disposition: attachment; filename="FileName.txt"
Content-Range: bytes */30
Content-Length: 0

Body:

<EMPTY>

Response to the request 1:

HTTP/1.1 308
Content-Length: 0
ETag: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Server: Bentley-WSG/02.00.05.13
Server: Bentley-WebAPI/2.4
Date: Thu, 26 Jun 2014 06:58:17 GMT

Request 2 - sending first 15 bytes:

PUT https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/document/172c4b1d-0551-495b-b2ed-6ff79d1ab120/$file

Headers:

Content-Type: text/plain
Authorization: Basic YWRtaW46YWRtaW4=
If-Match: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Content-Range: bytes 0-14/30
Content-Length: 15

Body:

<First 15 bytes of file>

Response to the request 2:

HTTP/1.1 308
Content-Length: 0
ETag: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Server: Bentley-WSG/02.00.05.13
Server: Bentley-WebAPI/2.4
Range: bytes=0-14
Date: Thu, 26 Jun 2014 07:03:51 GMT

Request 3 - send last part of file

PUT https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/document/172c4b1d-0551-495b-b2ed-6ff79d1ab120/$file

Headers:

Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: text/plain
If-Match: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Content-Range: bytes 15-29/30
Content-Length: 15

Body:

<Last 15 bytes of file>

Response to the request 3:

Headers:

HTTP/1.1 200 OK
Content-Length: 224
Content-Type: application/json; charset=utf-8
ETag: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Server: Bentley-WSG/02.00.05.13
Server: Bentley-WebAPI/2.4
Date: Thu, 26 Jun 2014 08:26:18 GMT

Body:

{
   "instanceId": "172c4b1d-0551-495b-b2ed-6ff79d1ab120",
   "className": "document",
   "schemaName": "{schema}",
   "properties": {
      "name": null,
      "filename": null,
      "description": null
      }
}

Usage example

// The following C# example illustrates uploading a file in multiple parts.
var guid = "b65018d6-3630-4566-958c-3934e4055c42";
Uri request = new Uri(String.Format("https://localhost/ws/v2.5/Repositories/pw--PW/document/{0}/$file", guid));
Stream uploadFile = new MemoryStream(Encoding.UTF8.GetBytes("My file2 contents...."));
string fileName = "MyFileName.txt";
using (var file = uploadFile)
    {
    HttpResponseMessage response;
    do
        {
        using (var client = new HttpClient())
            {
            var credentials = "admin:admin";
            credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials));
            client.DefaultRequestHeaders.Add("Authorization", String.Format("Basic {0}", credentials));
            // 1. Send an initial "handshake" request to inform the server of the total 
            //    size of the file that will be uploaded;
            HttpContent content = new StringContent("");
            content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            content.Headers.ContentDisposition.FileName = fileName;
            content.Headers.Add("Content-Range", String.Format("bytes */{0}", file.Length));
            response = client.PutAsync(request, content).Result;
            if (response.StatusCode != (HttpStatusCode)308)
                {
                break; // Either Created or an error, handle outside loop.
                }
            int bufferSize = 1024 * 1024; // 1 MB
            var buffer = new byte[bufferSize];
            // 2. Send the file:
            // When the server is still missing some bytes the response will have a status of 308 (Resume Incomplete):
            while (response.StatusCode == (HttpStatusCode)308)
                {
                // Response header Range: bytes=0-{endByteIndex} indicates how many bytes the server has received and saved.
                long start = 0;
                if (response.Headers.Contains("Range")) // No Range header indicates that the server has 0 bytes of the file.
                    {
                    string range = response.Headers.GetValues("Range").First();
                    RangeHeaderValue parsedValue = RangeHeaderValue.Parse(range);
                    start = (long)parsedValue.Ranges.First().To + 1;
                    }
                file.Position = start;
                int readCount = file.Read(buffer, 0, bufferSize);
                // Send a request with the file content and Content-Range: bytes {startByteIndex}-{endByteIndex}/{fileSize} header:
                content = new ByteArrayContent(buffer, 0, readCount);
                content.Headers.ContentRange = new ContentRangeHeaderValue(start, start + readCount - 1, file.Length);
                // A successful response will have an ETag header by which the upload is identified:
                client.DefaultRequestHeaders.IfMatch.Clear();
                client.DefaultRequestHeaders.IfMatch.Add(response.Headers.ETag);
                response = client.PutAsync(request, content).Result;
                }
            }
        } while (response.StatusCode == HttpStatusCode.PreconditionFailed); // If If-Match did not match restart the upload process.
                                                                        // 3. Handle the final response:
    if (response.StatusCode != HttpStatusCode.OK)
        {
        // Error handling should be implemented here.
        return "error";
        }
    else
        {
        string createdObjectIdJson = response.Content.ReadAsStringAsync().Result;
        }
    }
POST v2.5/Repositories/{repositoryId}/{schema}/{class}

Creates a new file instance with the posted property values under the optionally specified related instance. File create and content can be performed in the following formats:

  • 'Multipart/form-data' content with one 'application/json' part and optionally one file part.
  • Since: 1.2 'Application/json' request and subsequent "plain/text" file upload requests (resumable file upload).

File creation and content upload using "Multipart/form-data". Example:

POST https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/{fileClass}

Headers:

Authorization:Basic YWRtaW46YWRtaW4=
Content-Type:multipart/form-data; boundary=-------------------------acebdf13572468

Body:

---------------------------acebdf13572468
Content-Disposition: form-data; name="fieldNameHere"
Content-Type: application/json

{
 "instance": {
    "schemaName": "{schema}",
    "className": "File",
    "changeState": "new",
    "properties": { "Name": "Test.txt" },
    "relationshipInstances" : [{
            "className" : "DocumentFiles",
            "schemaName" : "{schema}",
            "direction" : "backward",
            "properties" : {},
            "relatedInstance" : {
               "instanceId" : "{instanceId}",
               "className" : "Global_Document_DOC1",
               "schemaName" : "{schema}",
            }
    }]
 }
}
---------------------------acebdf13572468
Content-Disposition: form-data; name="fieldNameHere"; filename="Test.txt"
Content-Type: text/plain

"file content text"
---------------------------acebdf13572468--

Response:

{
 "changedInstance": {
   "change":"Created",
   "instanceAfterChange": {
      "instanceId":"{instanceId}",
      "schemaName":"{schema}",
      "className":"File",
      "properties": { "Name":"Test_12.txt" },
      "relationshipInstances": [{
          "instanceId":"{instanceId}",
          "schemaName":"{schema}",
          "className":"DocumentFiles",
          "direction":"backward",
          "properties":{},
          "relatedInstance": {
             "instanceId":"{instanceId}",
             "schemaName":"{schema}",
             "className":"Global_Document_DOC1",
             "properties":{}
          }
      }]
   }
 }
}

File creation and content upload using "resumable file upload". Example:

Request 1 - 'handshake' with JSON body:

POST https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/{fileClass}

Headers:

Content-Range: bytes */36
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/json
Content-Disposition: attachment; filename="test3.txt"
Content-Length: 0

Body:

{
 "instance" : {
    "className": "File",
    "schemaName": "{schema}",
    "properties": { "Name": "test3.txt" },
    "relationshipInstances": [{
             "className": "DocumentFiles",
             "schemaName": "{schema}",
             "direction": "backward", 
             "relatedInstance": {
                 "instanceId": "{instanceId}",
                 "className": "{documentClass}",
                 "schemaName": "{schema}"
             }
         }]
  }
}

Response to the request 1:

HTTP/1.1 308
Content-Length: 0
ETag: "1+36+rs0KaklyqidA7raqntvBzusuBnc="
Server: Bentley-WSG/02.00.05.13
Server: Bentley-WebAPI/2.4
Date: Wed, 12 Oct 2016 11:05:04 GMT

Request 2 - sending first 21 bytes:

POST https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/{fileClass}

Headers:

Content-Type: text/plain
Authorization: Basic YWRtaW46YWRtaW4=
If-Match: "1+36+rs0KaklyqidA7raqntvBzusuBnc="
Content-Range: bytes 0-20/36
Content-Length: 21

Body:

<First 21 bytes of file>

Response to the request 2:

HTTP/1.1 308
Content-Length: 0
ETag: "1+36+rs0KaklyqidA7raqntvBzusuBnc="
Range: bytes=0-20
Server: Bentley-WSG/02.00.05.13
Server: Bentley-WebAPI/2.4
Date: Wed, 12 Oct 2016 11:06:07 GMT

Request 3 - send last part of file

POST https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/{fileClass}

Headers:

Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: text/plain
If-Match: "1+36+rs0KaklyqidA7raqntvBzusuBnc="
Content-Range: bytes 21-35/36
Content-Length: 15

Body:

<Last 15 bytes of file>

Response to the request 3:

Headers:

HTTP/1.1 201 OK
Content-Length: 224
Content-Type: application/json
ETag: "1+36+rs0KaklyqidA7raqntvBzusuBnc="
Server: Bentley-WSG/02.00.05.13
Server: Bentley-WebAPI/2.4
Date: Wed, 12 Oct 2016 11:06:31 GMT

Body:

{
   "instanceId": "{instanceId}",
   "className": "file",
   "schemaName": "{schema}",
   "properties": {
      "name": "{value}",
   },
   "relationshipInstances": [{
      "className": "DocumentFiles",
      "schemaName": "{schema}",
      "direction": "backward",
      "properties": {},
      "relatedInstance": {
         "instanceId": "{instanceId}",
         "className": "DocumentFiles",
         "schemaName": "{schema}",
         "properties": {}
      }
   }]
}
POST v2.5/Repositories/{repositoryId}/{schema}/{class}/{instanceId}

Allows to update instance properties and uploads a file content associated with the instance.

The workflow is almost identical to "PUT" case with minor differences:

  • All requests are of POST type
  • "Handshake" request has JSON body where it is possible to update instance properties

Example of updating instance and uploading a file divided into parts:

Request 1 - 'handshake' + properties update:

POST https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/{fileClass}/{instanceId}

Headers:

Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/json
Content-Disposition: attachment; filename="FileName.txt"
Content-Range: bytes */30
Content-Length: 151

Body:

{ 
  "instance": { 
    "instanceid": "{fileInstanceId}",
    "className": "file", 
    "schemaName": "{schema}", 
    "properties": { "isCheckedOut": false }
   }
}

Response to the request 1:

HTTP/1.1 308
Content-Length: 0
ETag: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Server: Bentley-WSG/02.00.05.10
Server: Bentley-WebAPI/2.4
Date: Wed, 05 Oct 2016 15:11:20 GMT

Request 2 - sending first 15 bytes:

POST https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/{fileClass}/{instanceId}

Headers:

Content-Type: text/plain
Authorization: Basic YWRtaW46YWRtaW4=
If-Match: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Content-Range: bytes 0-14/30
Content-Length: 15

Body:

<First 15 bytes of file>

Response to the request 2:

HTTP/1.1 308
Content-Length: 0
ETag: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Server: Bentley-WSG/02.00.05.10
Server: Bentley-WebAPI/2.4
Range: bytes=0-14
Date: Wed, 05 Oct 2016 15:11:20 GMT

Request 3 - send last part of file

POST https://localhost/ws/v2.5/Repositories/{repositoryId}/{schema}/{fileClass}/{instanceId}

Headers:

Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: text/plain
If-Match: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Content-Range: bytes 15-29/30
Content-Length: 15

Body:

<Last 15 bytes of file>

Response to the request 3:

Headers:

HTTP/1.1 200 OK
Content-Length: 224
Content-Type: application/json; charset=utf-8
ETag: "0+30+72ia7FC3WFHxKWeDx2Vzxg=="
Server: Bentley-WSG/02.00.05.10
Server: Bentley-WebAPI/2.4
Date: Wed, 05 Oct 2016 15:11:20 GMT

Body:

{
  "changedInstance":{
     "change":"Modified",
     "instanceAfterChange":{
        "instanceId":"1825",
        "schemaName":"{schema}",
        "className":"File",
        "properties":{
           "IsCheckedOut":false,
           "Name":"FileName.txt",
           "Path":"{path}",
           "FileDate":"2016-09-21T18:28:24",
           "FileSize":"24",
           "PageSize":"",
           "Id": 1824,
           "IsHistoric":false,
           "IsLocked":false,
           "Remarks":"",
           "IsPending":false,
           "DateAdded":"2016-10-05T13:46:01.583",
           "DateChanged":"2016-10-05T18:12:43.777",
           "InDefaultScope":true,
           "InDefaultVisibleScope":true,
           "ScopeName":"Global",
           "ScopeId":1,
           "MimeType":"TEXT/PLAIN"
        }
     }
  }
}

Exposing plugin persistence provider version

Since: 2.8

Additional 4-number product version value of a particular plugin can be appended to standard Server and custom Mas-Server headers if needed.

There are few conditions to meet:

  • Deployed Web Services Gateway instance must have 'Wsg.Plugins.ExposePluginVersions' setting enabled in web.config.
  • Plugin should either have persistence provider exposed via 'ECExtension' attribute or Settings schema with PluginId attribute property.
  • Plugin must expose at least one custom EC schema.
  • Web API request must be authorized.
  • Web API request should target a specific repository.
  • For example, the following URL will access ECSchemaDef instances in 'Bentley.YourPlugin--YourLocation' repository:

    GET https://localhost/ws/v2.8/Repositories/Bentley.YourPlugin--YourLocation/MetaSchema/ECSchemaDef/

Example 1:

Assuming there is a repository with a backing persistence provider:

  • Repository ID: Bentley.YourPlugin--YourLocation
  • Persistence provider ID: Bentley.YourPlugin

GET https://localhost/ws/v2.8/Repositories/Bentley.YourPlugin--YourLocation/MetaSchema/ECSchemaDef/

Request headers:

Authorization: Basic YWRtaW46YWRtaW4=

Response example:


HTTP/1.1 200 OK
Cache-Control: must-revalidate, no-cache, private
Pragma: no-cache
Content-Length: {contentLength}
Content-Type: application/json
ETag: "{eTagValue}"
Server: mas-server:Bentley-WSG/02.06.06.06, Bentley-WebAPI/2.7, Bentley.YourPlugin/01.00.00.00
Mas-Server: mas-server:Bentley-WSG/02.06.06.06, Bentley-WebAPI/2.7, Bentley.YourPlugin/01.00.00.00
Mas-Request-Id: {RequestId}
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Date: {dateTime}
...

Instance querying options

This section contains detailed information about instance querying options. As Bentley Web Service Gateway Web API exposes all information as instances, the same query options are available for all GET requests. Each repository might choose to support only a part of the options.

Querying instances of the multiple classes

Web API supports queries for instances of multiple classes in the same request.

When querying for the multiple classes, query options $filter and $select can contain only the properties that can be specified without mentioning the class name (for example, when querying for Folder and Document, property Name can be used, but not Folder.Name nor Document.Name). Please refer to the OData style query string specification for a full OData query description and examples.

GET v2.5/Repositories/{repositoryId}/{schema}/{classes}

Query for instances of specified classes from the same schema.

Since: 2.0

Parameters

repositoryId

Required. The ID of the repository against which to authenticate.

schema

Required. The name of the schema containing the class.

classes

Required. One or more comma separated class names.

Example:

GET v2.5/Repositories/{repositoryId}/{schema}/Folder,Document?$select=Name

GET v2.5/Repositories/{repositoryId}/{schema}/{relatedClassSpec}/{relatedInstanceId}/{classes}

Query for multiple instances of specified classes that are related to the instance specified by {relatedClassSpec} and {relatedInstanceId}. Please refer to Relationships and related classes for more information about specifying related classes.

Polymorphic search classes

A search class name can be suffixed with the '!poly' keyword to indicate that a query should be executed as polymorphic. By default, all queries are non-polymorphic. The example query below will be executed for instances of classes Document, Asset and all classes derived from Asset, like Chair, Furniture, PC, etc.

GET v2.5/Repositories/{repositoryId}/{schema}/Asset!poly,Document

Using multiple schemas in the same query string

A class name can be prefixed with a schema name to indicate that the class is from a different schema than the default. If a class name is not prefixed with a schema name, the default schema specified in the URL (preceding the search classes’ specification) is used to find the class. The example query below will be executed for the instances of ClassA from main schema (SchemaA) and ClassB from a different schema (SchemaB).

GET v2.5/Repositories/{repositoryId}/SchemaA/ClassA,SchemaB.ClassB

The same prefix can also be applied before the class name in query options. The example query below will filter and select names of related Projects that are defined in a different schema.

GET v2.5/Repositories/{repositoryId}/SchemaA/Document?
$select=Name,SchemaA.DocumentParent-forward-SchemaB.Project.Name&
$filter=SchemaA.DocumentParent-forward-SchemaB.Project.Name+like+'*abc*'

OData style query string specification

For all instance requests, the query can be specified in an OData URL query format as URL query string parameters/options. Supported options:

  • $filter – a criterion by which to filter all instances of specified classes. The criterion is made of property expressions separated by logical operators (and/or), and optionally grouped by parentheses to specify expression precedence. Criterion components, like logical operators, relational operators, property expressions can be separated by whitespace encoded to '%20' or character '+'.
    g.: $filter={expr}, or (({expr}, or {expr}) and {expr}).
    • Filter expressions with relational operator.
      {expr} is made of a property name, a relational operator and a value or another property name.
      g.: $filter=property1 {rel_op} property2 or property3 {rel_op} {value}.
      {rel_op} must be one of these:
      • eq – equal to;
      • ne – not equal to;
      • lt – less than;
      • le – less than or equal to;
      • gt – greater than;
      • ge – greater than or equal to;
      • in – in set;
      • notin – not in set;
      • like – like;
      • notlike – not like;
    • Filter expressions with canonical functions.
      {expr} is made of a canonical function and two arguments.
      g.: $filter={canon_fn}(property1, {value}).
      {canon_op} must be one of these:
      • startswith – arg1 startswith arg2;
      • endswith – arg1 endswith arg2;
      • contains – arg1 contains arg2;
      • matchesregex – arg1 matches regex arg2;
      • intersects – arg1 geometry spatially intersects with arg2 geometry;

{value} must be in one of these formats:

  • null – applicable to any property type; can be used only with eq and ne rel_op;
  • true, 1, false, 0boolean type;
  • 1, -132-bit integer type;
  • 1, 1l64-bit integer type;
  • 1, 2, -1, 1d, 1.2e10, NaN, INF, -INFdouble type;
  • 'some text', 'Mom''s garden'string type;
  • guid'0050df36-f938-4938-8abc-a804556d67e9'string type but validated as a guid;
  • datetime'2075-06-28', datetime'2075-06-28T17:43:38', datetime'2075-06-28T17:43:38Z', datetime'2075-06-28T17:43:38-07:00'datetime type;
  • x'AECA/w==', binary'AECA/w=='binary type as base-64 encoded string;
  • geometry'WKT_GEOM_VALUE' geometry type as Well-Known Text geometry representation; e.g. geometry'POINT(1.2 2.3)', geometry'POINT Z(1.2 2.3 3.4)',geometry'POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))', geometry'LINESTRING(1 1, 2 3, 4 8)'; can be used only with * canonical functions;

{value} must be of the same type as the property on the left side of expression.

To filter by instance IDs, {expr} can be $id in [‘ab’,’bc’] or $id+eq+'ab'.

  • $orderby – a comma-separated list of property names with asc or desc keywords by which to sort the results. If asc is used, the property will be sorted in ascending order; if desc, the property will be sorted in descending order. If neither asc nor desc are specified, asc is used.
    g.: $orderby=property1,property4 asc,property2 desc.
  • $select – a comma-separated list of property names. Only values of the specified properties are returned for the found instances. If not specified, all property values are returned.
    g.: $select=property1,property4.

To select all properties, specify $select=* or do not specify $select at all.

To select none of the properties, specify $select=$id

  • $skip – count of result instances to skip. If not specified, nothing is skipped and properties are returned from the first found instance.
    Example: $skip=40.
  • $top – count of result instances to return. If not specified, all results are returned. $skip is applied before $top.
    Example: $top=20.
  • $count – returns the number of instances for each specified class.

Example:

GET {schema}/{class1},{class2}/$count?$filter={filter}

Response:

{
  "instances": [
    {
      "instanceId": "{instanceId}",
      "schemaName": "{schema}",
      "className": "InstanceCount",
      "properties": {
        "ECSchemaName": "{schema}",
        "ECClassName": "{class1}",
        "Count": 599 
      },
      "eTag": "U99S1g99huTQSE8u2999999999s="
    },
    {
       "instanceId": "{instanceId}",
       "schemaName": "{schema}",
       "className": "InstanceCount",
       "properties": {
         "ECSchemaName": "{schema}",
         "ECClassName": "{class2}",
         "Count": 120 
      },
      "eTag": "UssS1g99aaaaSE8u2999999999s="
    }
  ]
}

Example:

GET v2.5/Repositories/{repositoryId}/{schema}/Document?
$filter=Name+like+'*.pdf'+and+Size+lt+1000&
$select=Name,Size,Description&
$orderby=Size+desc&
$skip=20&$top=10

Note: for single instance GET requests, only the $select option is applicable.

Example:

GET v2.5/Repositories/{repositoryId}/{schema}/Document/123?$select=Name,Size,Description

Custom query parameters

Any query parameters/options unrecognized by Web Services Gateway Web API parser are treated as custom parameters and transferred to the plugin in the extended data of an ECQuery object. Custom parameters are plugin-specific and are not explained within the scope of this document.

api.ancestor

Since: 2.0

Instructs plugin to query only instances directly or indirectly related to specified ancestor (for example – query all subdocuments under a specific folder, including all subfolders). Applied only if query contains at least one standard parameter.

Example: api.ancestor=List-ALU_Site~2F~7B4C836156-F690-4D6B-B9A0-0BF012270696~7D

Supported options: dash separated class name and instance ID

api.filtersettings

Since: 2.3

Specifies additional options for the plugin to use while filtering instances requested. CaseInsensitive option forces plugin to ignore case of the search text. If option is not specified, plugin can choose the default mode of operation.

Example: api.filtersettings=CaseInsensitive

Supported options: CaseInsensitive

api.singleurlperinstance

Since: 2.4

Instructs plugin to return only one available FileAccess URL for each instance. AzulreBlobSasUrl URL type has higher priority than Default URL type.

Example: ../FileAccess/FileAccessKey?$filter=Permissions+eq+'Read'+and+{SchemaName}.{ClassName}.$id+eq+'{instanceId}'&api.singleurlperinstance=true

Supported options: true and false

api.selectsettings

Since: 2.2

Specifies additional options for the plugin to use while selecting instances requested.

Single option usage example: .../{schema}/{class}?$select={property1},{property2}&api.selectsettings=DistinctValues

Multiple options usage example: .../{schema}/{class}?$select={property1},{property2}&api.selectsettings=IncludeRelationshipsNotInSelect|DistinctValues|IncludeUnsetProperties

Supported options:

IncludeRelationshipsNotInSelect

Since: 2.2

This setting instructs not to filter and return all relationships that are found by plugin (for example not only top level, but all tree of relationships).

Example: .../{schema}/{class}?$select={property1},{property2}&api.selectsettings=IncludeUnrequestedRelationships

DistinctValues

Since: 2.5

DistinctValues option instructs plugin to group instances by common property, provided in select option, values. DistinctValues is applied only if query contains select option with at least one property specified.

Example: .../{schema}/{class}?$select={property1},{property2}&api.selectsettings=DistinctValues

IncludeUnsetProperties

Since: 2.6

IncludeUnsetProperties option allows to display properties without values within instances in queries.

Example: .../{schema}/{class}?$select={property1},{property2}&api.selectsettings=IncludeUnsetProperties

For a better explanation, let us have 1 instance:

  • Fruit, InstanceId=1, Name=Apple, Amount=1, Color unset

The example query without 'IncludeUnsetProperties'.

Request:

GET v2.6/Repositories/{repositoryId}/{schema}/Fruit

Response:

Returns fruit instance with name and amount properties. Since, this apple instance has its color unset, color property is not being displayed.

                            
                        {
                            "instances": [
                            {
                                "instanceId": "1",
                                "schemaName": "{schema}",
                                "className": "Fruit",
                                "properties": {
                                "Name": "Apple",
                                "Amount": "1"
                                }
                                "eTag": ""UOxE31igPz6rHfYnut/z4xMj8bg=""
                            }
                            ]
                        }
                            
                        

The example query with 'IncludeUnsetProperties'.

Request:

GET v2.6/Repositories/{repositoryId}/{schema}/Fruit?api.selectsettings=IncludeUnsetProperties

Response:

Returns fruit instance with all properties. Although, color property is unset, it is still being displayed.

                            
                    {
                        "instances": [
                        {
                            "instanceId": "1",
                            "schemaName": "{schema}",
                            "className": "Fruit",
                            "properties": {
                                "Name": "Apple",
                                "Amount": "1",
                                "Color": null
                            }
                            "eTag": ""UOxE31igPz6rHfYnut/z4xMj8bg=""
                        }
                        ]
                    }
                            
                        

Another example of query with 'IncludeUnsetProperties', selecting only name and color properties.

Request:

GET v2.6/Repositories/{repositoryId}/{schema}/Fruit?$select=Name,Color&api.selectsettings=IncludeUnsetProperties

Response:

Returns fruit instance with two properties. Color property is unset, though it is still being displayed.

                            
                    {
                        "instances": [
                        {
                            "instanceId": "1",
                            "schemaName": "{schema}",
                            "className": "Fruit",
                            "properties": {
                                "Name": "Apple",
                                "Color": null
                            }
                            "eTag": ""UOxE31igPz6rHfYnut/z4xMj8bg=""
                        }
                        ]
                    }
                            
                        

It is worth mentioning that there is a difference between instance having its property value unset and having value set to null. For example, we now have an instance with color property set as null:

  • Fruit, InstanceId=1, Name=Apple, Amount=1, Color=null

Request:

GET v2.6/Repositories/{repositoryId}/{schema}/Fruit

Response:

Returns fruit instance with all properties, since this 'Apple' instance has its color set (null).

                            
                    {
                        "instances": [
                        {
                            "instanceId": "1",
                            "schemaName": "{schema}",
                            "className": "Fruit",
                            "properties": {
                                "Name": "Apple",
                                "Amount": "1",
                                "Color": null
                            }
                            "eTag": ""UOxE31igPz6rHfYnut/z4xMj8bg=""
                        }
                        ]
                    }
                            
                        

Parameter alias

Parameter aliases are parts of URL parameters that can be reused within a URL multiple times. When using complex query string, aliases usage helps to decrease length off the request URL and improve readability. By default, OData only specifies alias use in $filter and $orderby, while Bentley Web Services Gateway Web API supports aliases in multiple options. Aliases can be used to replace:

  • $skip and $top: the entire value
  • $select: a part of or an entire entry (@alias.class.property or @alias.property or @alias)
  • $filter: a property name or an entire value, or a single value within a list (IN operator)
  • $orderby: name of the property
  • ancestor (custom parameter): part of or the entire value

Alias syntax:

  • An alias name starts with the @ character followed by characters, numbers and/or underscores. Aliases are case sensitive, however, it is not permitted to define two aliases in the same URL that only differ in casing (e.g. @alias and @Alias)
  • An alias must be defined in the URL string as a separate parameter, separated by an & symbol

Examples:

GET v2.5/Repositories/{repositoryId}/{schema}/Employee?$select=@p&$filter=@p+in+[@rob]&@rob=’robert’&@p=$id

GET v2.5/Repositories/{repositoryId}/{schema}/Employee?$orderby=@x&@x=Name

GET v2.5/Repositories/{repositoryId}/{schema}/Employee?$select=*,@x.Name,@x.Industry&@x=Company_x_Employee-backward-Company

This section defines the syntax for related class specification the URL query string.

Short format for a single relationship class

A property name in $filter and $select clauses can be prefixed with “relatedClassName.” to indicate that the property is from a class related to search classes. The example query retrieves documents with names of related projects.

GET v2.5/Repositories/{repositoryId}/{schema}/Document?$select=Project.Name

The query will return all instances without properties of the Document class and related instances with property Name of the class Project.

Full format with relationship class and direction

If there are multiple relationships between search classes and the related class, the relationship class name and direction must be specified by prefixing the property name with “relationshipClassName-direction-relatedClassName”. Direction can be the keywords “forward” or “backward.” The example query retrieves documents owned by user 'John'.

GET v2.5/Repositories/{repositoryId}/{schema}/Document?$filter=DocumentOwners-forward-User.Name+eq+'John'

Nested relationships

Related class specifications can be nested by using “/” as a delimiting character. The example query retrieves documents owned by users working at a company with the name Bentley.

GET v2.5/Repositories/{repositoryId}/{schema}/Document/123?$filter=DocumentOwners-forward-User/Company.Name+eq+'Bentley'

In order to select all the properties of a related class, specify “relatedClassName.*” in the $select query option.
To select none of the properties of a related class, specify “relatedClassName.$id” in the $select query option.

Examples:

GET v2.5/Repositories/{repositoryId}/{schema}/Document?$select=Project.*,DocumentOwners-forward-User.$id

If $filter is not used, all Document instances will be returned without any properties (only instance IDs for all Document instances will be returned). Additionally, IDs of related instances of User class and all properties of related instances of Project class will be returned.

To select all properties of the root class and all properties of related class, it is possible to specify “*” in the $select value for both root and related classes:

GET v2.5/Repositories/{repositoryId}/{schema}/Document?$select=*,DocumentOwners-forward-User.*

To select all properties of root class and some of the properties of related class, separate properties by commas:

GET v2.5/Repositories/{repositoryId}/{schema}/Document?$select=*,DocumentOwners-forward-User.Name, DocumentOwners-forward-User.Age

Since: 2.6

Filtering by multiple related properties for a single instance is supported.

$filter clause can contain a relationship specifier followed by parentheses containing multiple related property specifiers.

Example filter:

$filter=relationshipClassName-direction-relatedClassName(relatedProperty1 eq value and relatedProperty2 eq value2)

Relationship specifier

Relationship specifier format can be:

  • Full: {schema}.{relationshipClass}-{direction}-{schema2}.{class2}
    e.g. schema.FruitsInBasket-forward-schema.Fruit
  • Short: {schema2}.{class2}
    e.g. schema.Fruit
  • Nested: {schema2}.{class2}/{schema2}.{relationshipClass2}-{direction}-{schema3}.{class3}
    e.g. Fruit/SeedsInFruit-forward-Seed

Relationship contents

Parentheses put after relationship specifier in filter clause may contain any filter that would be used for a related class defined in the specifier.

Example 1

We have 2 FruitBasket instances with some related fruits:

  • FruitBasket, InstanceId: 1
    • Related: Fruit, Name=Apple, Amount=1, InstanceId: 3
  • FruitBasket, InstanceId: 2
    • Related: Fruit, Name=Banana, Amount=1, InstanceId: 4
    • Related: Fruit, Name=Apple, Amount=5, InstanceId: 5
      • Related: Seed, Size=small, Amount=5, InstanceId: 6

The example query retrieves FruitBaskets that have a related Fruit instance named 'Apple' with amount of 1.

Request:

GET v2.6-beta/Repositories/{repositoryId}/{schema}/FruitBasket?$select=Fruit.*&$filter=FruitsInBasket-forward-Fruit(Name+eq+'Apple'+and+Amount+eq+1)

Response:

Returns only instance 1 because it has matching Fruit instance.

        
     {
        "instances": [
           {
              "instanceId": "1",
              "schemaName": "{schema}",
              "className": "FruitBasket",
              "properties": {},
              "relationshipInstances":[
                 {
                    "instanceId":"2b2cc606-4c7c-4dd5-bbd9-9a082064c5fe",
                    "schemaName":"{schema}",
                    "className":"FruitsInBasket",
                    "direction":"forward",
                    "properties":{},
                    "relatedInstance": {
                       "instanceId":"3",
                       "schemaName":"{schema}",
                       "className":"Fruit",
                       "properties":{
                          "Name": "Apple",
                          "Amount": "1"
                       },
                       "eTag": ""lToc29LK7O0wLcqeJtICxjcoby7=""
                    },
                    "eTag": ""NQKxfbM8Hz1FDuHB/xILXDFp6U4=""
                 }
              ],
              "eTag": ""UOxE31igPz6rHfYnut/z4xMj8bg=""
           }
        ]
     }
            
          

More supproted request examples:

GET v2.6-beta/Repositories/{repositoryId}/{schema}/FruitBasket?$filter=Fruit/Seed(Size+eq+'small')

GET v2.6-beta/Repositories/{repositoryId}/{schema}/FruitBasket?$filter=Fruit(Seed.Size+eq+'small')

GET v2.6-beta/Repositories/{repositoryId}/{schema}/FruitBasket?$filter=Fruit(Seed(Size+eq+'small'))

GET v2.6-beta/Repositories/{repositoryId}/{schema}/FruitBasket?$filter=Fruit(Name+eq+'Apple'+and+Seed(Size+eq+'small'))

Result:

All of these requests will return instance 2, because it has a matching related instances.

Skip/continuation token

Bentley Web Services Gateway Web API provides support for a Skip/Continuation token (commonly used in Cloud services) when the server operation can only run for a limited amount of time. In such workflows, the client receives a token to be used in subsequent calls to continue server processing from the last known position. Web API allows the exchange of tokens between plugin and client. If supported by the repository, the client will receive a token for SkipToken header in the response. For further request execution, send the token value in the SkipToken request header in subsequent requests.

Policies service

Policies service exposes information about permissions and supported functionality in currently-running plugins and repositories.

Exposed schemas: Policies.

Exposed classes: PolicyAssertion.

Policies can be retrieved at different levels of granularity:

  • Per plugin
  • Per repository
  • Per repository connection
  • Per class
  • Per instance

There are few Policy service types indicating an area of the policy:

  • RepositoryConnection – for checking if a repository is:
    • Creatable;
    • Hidden;
    • Openable;
    • SupportsWindowsIntegratedAuthentication;

These policies make sense only per plugin and repository levels.

  • Persistence – for checking if an Class or an Instance:
    • Deleteable;
    • FileBackable;
    • Insertable;
    • Selectable;
    • SelectablePolymorphically;
    • SortableQueries;
    • StreamBackable;
    • SupportsMultipleSearchClasses;
    • Updateable;

Getting policy assertions for Persistence ServiceType by the Plugin/Repository/RepositoryConnection level will return Supported=true if any of the classes/instances supports the functionality in that Plugin/Repository/RepositoryConnection.

  • Navigation:
    • SupportsUpdate
  • View:
    • SupportsPreferredDisplayType
  • Content:
    • SupportsUpdate

In addition to policies mentioned above, Persistence service type contains additional policies exposed from the plugin, but are not currently used by Web API:

  • AssignsIDsOnInsert;
  • ChangeTrackable;
  • DistinctValueQueries;
  • DoNotAllowInsertOrUpdateOperation;
  • ECQueryBackable;
  • FolderBackable;
  • Lockable;
  • Replaceable;
  • RequiresOwnSchemas;
  • SchemaImport;
  • SchemaUpdate;
  • SpatialQueries;
  • SubstringPreFilterQueries;
  • SupportsFilterExpressions;
  • SupportsFilterQueries;
  • SupportsGlobalId;
  • SupportsInstanceChangeEvents;
  • SupportsMaxResults;
  • SupportsMonikers;
  • SupportsOrderedRelationships;
  • SupportsSupplementedSchemas;
  • SupportsUnitExtensionSchemas;
  • SupportsUnits;
  • SupportsWorkingDirectory;

It is worth mentioning that if policies like SortableQueries, SupportsMaxResults or SupportsResultRangeOffset are not supported, the functionality can still work. For example, if it is not supported by the ProjectWise plugin, then ECServices tries to apply its functionality to the results. (Example 1)

Example 1

By default, the SortableQueries policy for ProjectWise is not supported, but queries are still executable with the '$orderby' parameter to get sorted results:

Request:

GET v2.5/Repositories/{repositoryId}/policies/PolicyAssertion/Persistence.SortableQueries

Response:

{
 "instances": [{
     "instanceId": "Persistence.SortableQueries",
     "schemaName": "Policies",
     "className": "PolicyAssertion",
     "properties": {
         "ServiceType": "Persistence",
         "Name": "SortableQueries",
         "Supported": false,
         "AdhocProperties": []
     },
     "eTag": ""pFj3hQuh+WgQ9czeYYoS08hd0Ks=""
   }]
}

We can see that the “SortableQueries” policy is not supported. Now try to use “$orderby” parameter by querying documents.

Request:

GET v2.5/Repositories/{repositoryId}/{schema}/Document?$select=FileUpdateTime&$orderby=FileUpdateTime&$skip=13&$top=3

Response:

{
 "instances": [{
     "instanceId": "4b270cd7-1e2a-4bb2-a0fa-468c02d5b517",
     "schemaName": "PW_WSG",
     "className": "Document",
     "properties": {
         "FileUpdateTime": "2013-01-07T12:25:23.61Z"
      },
      "eTag": ""12ShH/C/I2IPkeNRv9p+JbhmMes=""
 },{
     "instanceId": "51605460-2c65-44ac-b3f2-bd0dda3983ec",
     "schemaName": "PW_WSG",
     "className": "Document",
     "properties": {
         "FileUpdateTime": "2013-01-07T12:25:26.703Z"
     },
     "eTag": ""F8vdUB6hR9w3DL9JL6x5oDZNYd4=""
 },{
     "instanceId": "6cd7ed77-8f44-4db6-9313-6144edd2a9bf",
     "schemaName": "PW_WSG",
     "className": "Document",
     "properties": {
         "FileUpdateTime": "2013-02-13T12:55:32.887Z"
     },
     "eTag": ""YLtc+/cuLPAzExRi6vuzugfdKpg=""
 }]
}

Example 2

Query for a repository connection service policy assertion named "Hidden" for the specified plugin.
This request maps to the RepositoryConnectionService.GetProviderPolicy method call.

Request:

GET v2.5/Policies?$filter=ServiceType+eq+'RepositoryConnection'+and+Name+eq+'Hidden'+and+PolicyAppliesTo-forward-Plugins.PluginIdentifier.$id+in+['myPlugin']

Response:

{
 "instances": [{
     "instanceId": "RepositoryConnection.Hidden--Plugins.PluginIdentifier. Bentley.PW ",
     "className": "PolicyAssertion",
     "schemaName": "Policies",
     "eTag": "lGoMJecbNcY6MX375uLGCoKgrJY=",
     "properties": {
         "Supported": false,
         "ServiceType": "RepositoryConnection",
         "Name": "Hidden"
     }
 }]
}

Example 3

Query for all policy assertions for a specified repository.
This request maps to the ECService.GetRepositoryPolicy method call.

Request:

GET v2.5/Policies?$filter=PolicyAppliesTo-forward-Repositories.RepositoryIdentifier.$id+in+['myPlugin--myRepoLocation']

Response:

{
 "instances": [{
     "instanceId": "RepositoryConnection.Openable--Repositories.RepositoryIdentifier. Bentley.PW--RepoServerName~3ARepoName",
     "className": "PolicyAssertion",
     "schemaName": "Policies",
     "eTag": "IpBqrMF7lclul+/3CW5T+YcyTKw=",
     "properties": {
         "Supported": true,
         "ServiceType": "RepositoryConnection",
         "Name": "Openable"
     }
 },{
     "instanceId": "RepositoryConnection.SupportsWindowsIntegratedAuthentication--Repositories.RepositoryIdentifier. Bentley.PW--RepoServerName~3ARepoName",
     "className": "PolicyAssertion",
     "schemaName": "Policies",
     "eTag": "tQY+z6jwtAma99ylJLE7XkcEtY0=",
     "properties": {
         "Supported": false,
         "ServiceType": "RepositoryConnection",
         "Name": "SupportsWindowsIntegratedAuthentication"
     }
 },
  ...
 ]
}

Example 4

Query for all policy assertions for a specified repository’s connection.
This request maps to the ECService.GetRepositoryConnectionPolicy method call.

GET v2.5/Repositories/{repositoryId}/Policies/PolicyAssertion

Example 5

Query for all persistence service policy assertions for a specified repository’s connection for specified class.
This request maps to the PersistenceService.GetClassPolicy method call.

GET v2.5/Repositories/{repositoryId}/Policies/PolicyAssertion?$filter=ServiceType+eq+'Persistence'+and+PolicyAppliesTo-forward-MetaSchema.ECClassDef.$id+in+['N~3APW_WSG.02.00~3AProject']

Example 6

Query for all policy assertions for a specified repository’s connection for specified instances.
This request maps to the ECService.GetInstancePolicy method call.

GET v2.5/Repositories/{repositoryId}/Policies/PolicyAssertion?$filter=PolicyAppliesTo-forward-PW_WSG.Project.$id+in+['guid']

Presentation rule sets

PresentationRuleSet is a list of rules that define tree hierarchy and content provided by RuleDrivenPresentationECPlugin. To setup a presentation rule set in Bentley Web Services Gateway, add the following configuration to the ecom.config file:

<PresentationRuleSets>
   <RuleSetLocatersForPlugin pluginId="<Plugin ID>">
      <RuleSets>
         <Path><Relative path to PresentationRuleSet.xml ></Path>
      </RuleSets>
   </RuleSetLocatersForPlugin>
</PresentationRuleSets>

Where "<Plugin ID>" is your plugin identifier and "<Relative path to PresentationRuleSet.xml >" is the relative path from ecom.config to the presentation rule set file.

More information about PresentationRuleSet format can be found in the EC Framework documentation.

Asynchronous request execution

Since: 2.7

Asynchronous request allows starting execution of the requests at some time in future.

To enable asynchronous request execution, it must be configured on the server side.

Initializing new asynchronous request

To initialize one, we must add 'Mas-Async-Job' header with value, that indicates whether asynchronousness is required or optional. Possible values:

  • None – executes request non-asynchronously.
  • Allow – initializes new asynchronous request when the API supports it. It will execute the request the same way as using 'Mas-Async-Job: None' if the API does not support asynchronous request.
  • Force – always initializes new asynchronous request and will result in 400 (Bad Request) error status response if the API does not support asynchronous request.

Supported HTTP methods for asynchronous request are:

  • PUT
  • POST (except when it is request that will execute queries i.e. ends with '/$query').
  • DELETE

Request header example:

Mas-Async-Job:Force

Header and it's values are case insensitive, but if value is misspelled server will result in 400 (Bad Request) error status response.

Request result:

When request is executed asynchronously, we will get empty response body, but response headers will contain 'Operation-Location' header.

'Operation-Location' header value will contain request message, that would query for asynchronous request result:

Operation-Location: https://localhost/v2.7/Repositories/{repositoryId}/Jobs/Job/{asynconousRequestResultId}

Querying for asynchronous request result

It is allowed just to 'GET' or 'DELETE' result class instances.

Request structure:

https://localhost/v2.7/Repositories/{repositoryId}/Jobs/Job/{asynconousRequestResultId}

Response structure:

{
  "instances": [
    {
        "instanceId": "{asynchonousRequestResultId}",
        "schemaName": "Jobs",
        "className": "Job",
        "properties": {
            "Status": "Succeeded",
            "ResponseStatusCode": 201,
            "ResponseContent": "{"changedInstance":{"change":"Created","instanceAfterChange":{"instanceId":"4e207b21-2916-4f85-a1b7-c8fc....properties":{"DocumentName":"jkljk"}}}}",
            "ResponseHeaders": "Pragma: no-cache\nMas-License-Error-Id: NoClientLicense\n....Connection: close\n",
            "StartTime": "2000-01-01T13:44:01.2349987Z",
            "ScheduleTime": "2000-01-01T13:43:57.3126099Z",
            "CompleteTime": "2000-01-01T13:44:01.6563628Z"
        },
        "eTag": "{eTagValue}"
    }
  ]
}

Result instance properties:

Status

Possible values:

  • NotStarted
  • Running
  • Failed
  • Succeeded

If value is 'NotStarted' neither the property 'ResponseStatusCode' or 'ResponseContent' or 'ResponseHeaders' or 'StartTime' or 'CompleteTime' are being set.

If value is 'Running' property 'StartTime' will be set.

If value is 'Failed' property 'CompleteTime' will be set.

If value is 'Succeeded' all properties will be set.

ResponseStatusCode

If set, it will be one of (HTTP) response status codes.

ResponseContent

If asynchronous request sent successfully will contain the response body from the sent synchronous request.

ResponseHeaders

If asynchronous request sent successfully will contain the response headers from the sent synchronous request.

ScheduleTime

Always set. Datetime when asynchronous request was sent.

StartTime

If set, indicates when asynchronous request is started to execute, otherwise not started.

CompleteTime

If set, indicates when asynchronous request is finished to execute, otherwise not finished.

Example 1:

Result of not started syncronous request.

Request:

GET https://localhost/ws/v2.7/Repositories/Bentley.PW--PW/Jobs/Job/552ae4c6-f2d3-490d-9b88-ff9f49b3ac53

Response:

{
  "instances": [
    {
        "instanceId": "552ae4c6-f2d3-490d-9b88-ff9f49b3ac53",
        "schemaName": "Jobs",
        "className": "Job",
        "properties": {
            "Status": "NotStarted",
            "ScheduleTime": "2000-01-01T13:43:57.3126099Z"
        },
        "eTag": "{eTagValue}"
    }
  ]
}

Example 2:

Result of started asyncronous request.

Request:

GET https://localhost/ws/v2.7/Repositories/Bentley.PW--PW/Jobs/Job/6b39f111-e38e-41b2-bf33-1c9b2a0965e1

Response:

{
  "instances": [
    {
        "instanceId": "6b39f111-e38e-41b2-bf33-1c9b2a0965e1",
        "schemaName": "Jobs",
        "className": "Job",
        "properties": {
            "Status": "Running",
            "StartTime": "2000-01-01T13:44:01.2349987Z",
            "ScheduleTime": "2000-01-01T13:43:57.3126099Z"
        },
        "eTag": "{eTagValue}"
    }
  ]
}

Example 3:

Result of failed asyncronous request.

Request:

GET https://localhost/ws/v2.7/Repositories/Bentley.PW--PW/Jobs/Job/96fec14e-61ed-41a4-a8e8-7831b173e5f6

Response:

{
  "instances": [
    {
        "instanceId": "96fec14e-61ed-41a4-a8e8-7831b173e5f6",
        "schemaName": "Jobs",
        "className": "Job",
        "properties": {
            "Status": "Failed",
            "StartTime": "2000-01-01T13:44:01.2349987Z",
            "ScheduleTime": "2000-01-01T13:43:57.3126099Z",
            "CompleteTime": "2000-01-01T13:44:01.6563628Z"
        },
        "eTag": "{eTagValue}"
    }
  ]
}

Copyright © 2018 Bentley Systems, Incorporated. All rights reserved.