Changes in Web API 2.5

Note: In order to use any of the listed new features, the URL version part must be v2.5 or higher.

New header: Mas-File-ETag

Mas-File-ETag is now returned when uploading a file (with the last request when using resumable upload). Mas-File-ETag is the same as ETag the user would get when downloading the uploaded file. Mas-File-ETag received after upload can be used in header "if-none-match" : "received_etag" when downloading a file which will result in HTTP code 304 "Not modified" if file content has not changed.

To make use of this feature - ECClass of the file must have its properties responding to file size and date modified - mapped to known file properties using EC Class custom attribute - "FileDependentProperties" with values of "FileSize" and "DateModified" set to property names. If this custom attribute is not set - Mas-File-ETag header will not be returned after upload.

ECClass with required custom attribute example:

<ECClass typeName="File" isDomainClass="True">
    <ECCustomAttributes>
        <FileDependentProperties xmlns="Bentley_Standard_CustomAttributes.01.00">
            <FileSize>FileSize</FileSize>
            <DateModified>FileDate</DateModified>
        </FileDependentProperties>
    </ECCustomAttributes>
    <ECProperty propertyName="FileDate" typeName="dateTime" readOnly="True"></ECProperty>
    <ECProperty propertyName="FileSize" typeName="long" readOnly="True"></ECProperty>
</ECClass>

Service versions

Service versioning allows plugin developers, service owners and deployers to control the whole Web API versioning (WSG Web API version that defines the API format + ECPlugin versions that defines the data model, i.e. ECSchemas).

Issue that this feature is solving: there is no way to version an ECPlugin or an ECSchema so that the REST API client could choose which version to use.

Background:

  • In WSG deployments there are many separate components that change independently, thereby they are versioned independently.
  • The only version appearing in the REST API is the WSG API version, which only defines the features that WSG supports, not the features that a plugin supports.

Workarounds that existed for versioning:

  • Separate site WSG deployments with URL redirects based on client UserAgent version.
  • Adding new schemas or classes, for example: Project, Project_V2, Project_V3.
  • Adding new repositories, for example: Repositories/IssuesPlugin--default, Repositories/IssuesPlugin--default_v1.1

Each service version is mapped to a set of WSG WebAPI version and ECPlugin versions (each plugin version can define its ECSchema versions).

From WSG Web API v2.5 the plugin deployer, service owner can version all of its plugins and schemas under a single version number, e.g.:

GET https://{host}/ws/sv1.0/Repositories/{repositoryId}/{schema}/{class}

GET https://{host}/ws/sv1.1/Repositories/{repositoryId}/{schema}/{class}

The service versions are configured in your ecom.config file. For example:

<ServiceVersions>
 <ServiceVersion version="1.1" defaultWebApiVersion="2.5">
   <Plugin id="Bentley.PluginA" version="1.1"/>
   <Plugin id="Bentley.PluginB" version="1.2"/>
 </ServiceVersion>

 <ServiceVersion version="1.2" defaultWebApiVersion="2.5">
   <Plugin id="Bentley.PluginA" version="1.1"/>
   <Plugin id="Bentley.PluginB" version="1.3"/>
 </ServiceVersion>
</ServiceVersions>

A service version is composed by the following elements:

ServiceVersions element has child ServiceVersion elements:

ServiceVersion element

  • Attributes it has:
    • version: unique service version that will be used in the url. Format: "<Number>.<Number>", for example in URL, GET https://{host}/ws/sv1.1/Repositories
    • defaultWebApiVersion: Web API of WSG to use with defined service version. Format: "<Number>.<Number>".
      This is the number that you would have used as, for example, GET https://{host}/ws/v2.5/Repositories, which will now not be in URL, but just mapped from the service version (sv) in URL.
  • Child elements it can have:
    • plugin
      • Attributes it has:
        • id: plugin's name to identify a plugin. Format of string.
        • version: plugin's version to use for plugin in defined serviceVersion xml element. Format: "<Number>.<Number>"

Restrictions:

ServiceVersion with version="1.0" cannot be defined, as it is reserved for default settings, i.e. all plugins of version 1.0 are used.

If a plugin is not specified in configuration, its version of 1.0 is used.

Keep in mind:

    ServiceVersion:
  • Defining serviceVersion with incorrect property values will skip the serviceVersion.
  • Default Web Api version cannot be less than 2.5. If such (for example, 2.4) is specified, the service version will be skipped, i.e. not loaded.
    • Plugin:
    • If plugin version is blank, it will skip the plugin, but not the whole serviceVersion.
    • If plugin is missing the id, the plugin will be skipped as well.

If multiple same version elements are defined, first one will be picked.
All requests with the old format (for example: GET https://{host}/ws/2.5/Repositories ) will be thought of as sv1.0, which is the default configuration.

All loaded configuration and possible warnings/errors are saved into WSG logs.

URL format for calling with ServiceVersion:

Example:

GET ../sv{serviceVersion}/{remaining URL portion}

Request like:

GET ../sv1.0/Repositories

is equivalent to:

GET ../v2.5/Repositories

Query available service versions:

GET ../v2.5/ServiceVersions

This request will return all configured service versions.

Common queries:

Get latest service version:

GET ../sv1.0/ServiceVersions?$top=1&$orderby=WebApiVersionMajor desc, WebApiVersionMinor desc

Get WSG Web API version for a known service version e.g. 1.4:

GET ../sv1.4/ServiceVersions?$filter=IsCurrent+eq+true&$select=WebApiVersionMajor,WebApiVersionMinor

Anti-CSRF Protection

When using Passive IMS authentication client is prone for CSRF attacks from malicious websites. Cross-site request forgery, also known as one-click attack or session riding and abbreviated as CSRF (sometimes pronounced sea-surf) or XSRF, is a type of malicious exploit of a website where unauthorized commands are transmitted from a user that the website trusts. To protect from such attacks, so called double cookie, ANTI-CSRF token has been established.

How to enable it:

  1. Activate Passive IMS Authentication
  2. Add activation setting in web.config
  3. <add key="EnableAntiCSRFProtection" value="true" />
When Anti-CSRF is enabled, every response is being updated to contain a new header
Mas-Anti-CSRF-Token: Base64TokenValue
This token, contained in response headers, MUST be inserted into data altering requests. For example, if ANTI-CSRF is enabled, every POST request must contain the same token that has been received from any GET response. If no such header has been found, the post request will be canceled and an exception thrown.

Class Feature GUIDs tracking

This feature allows tracking usage of specific classes in all WSG requests. In order to track class you need to add some configuration to ecom.config file:

<LicensingFeatureGuids>
  <ClassFeatureGuid pluginId="PluginName" schema="SchemaName" class="ClassName" guid="000ENTER-000A-0NEW-GUID-00000000HERE" />
</LicensingFeatureGuids>

After adding this configuration, every usage of the class "ClassName" in any CRUD operation will be tracked in licensing server. Additional user data is added to feature tracking entries:

  • For select queries : "READ:1";
  • For create operations : "CREATE:(count of created instances)";
  • For update operations : "UPDATE:(count of updated instances)";
  • For delete operations : "DELETE:(count of deleted instances)".

Class feature tracking is polymorphic: if you configure tracking of base class, all derived classes will be also tracked as the base class.

Plugins can configure ClassFeatureGuid settings automatically by using EcomConfigConfigurationSections in deployment Settings.xml configuration.

<EcomConfigConfigurationSections xmlns="DeploymentCustomAttributes.01.04">
 <ConfigurationSections>
  <ConfigurationSection>
   <SectionName>LicensingFeatureGuids</SectionName>
   <SectionContent>
    <ClassFeatureGuid pluginId="PluginName" schema="SchemaName" class="ClassName" guid="000ENTER-000A-0NEW-GUID-00000000HERE" />
   </SectionContent>
  </ConfigurationSection>
 </ConfigurationSections>
</EcomConfigConfigurationSections>

Beta Versions

This feature allows to access upcoming WSG version features, by adding "-beta" keyword at the end of upcoming API version number.

GET https://{host}/ws/v2.5-beta/repositories

This doesn't work for already released versions. For example, with version v2.4 it is not going to work:

GET https://{host}/ws/v2.4-beta/repositories

Same goes both ways, if v2.5 is not released request will fail:

GET https://{host}/ws/v2.5/repositories

Distinct Select

This feature allows to select only distinct properties (property groups) by adding DistinctValues setting to api select settings:

&api.selectsettings=DistinctValues

This query returns only distinct properties, no instance or class id are required since they become redundant.

Example #1:

GET https://{host}/ws/v2.5/repositories/{repository}/{schema}/{class}!poly?$select=Mimetype&api.selectsettings=DistinctValues

Response:

{
   instances: [
      {
         properties: {
            MimeType: "UNKNOWN MIME TYPE"
         }
      },
      {
         properties: {
            MimeType: "TEXT/PLAIN"
         }
      },
      {
         properties: {
            MimeType: "IMAGE/X-PNG"
         }
      },
      {
         properties: {
            MimeType: "IMAGE/JPEG"
         }
      },
      {
         properties: {
            MimeType: "TEXT/XML"
         }
      },
      {
         properties: {
            MimeType: "APPLICATION/ZIP"
         }
      },
      {
         properties: {
            MimeType: "APPLICATION/EXECUTABLE FILE"
         }
      }
   ]
}

Example #2 - selecting multiple properties:

GET https://{host}/ws/v2.5/repositories/{repositoryId}/{schema}/{class}!poly?$select=Mimetype,IsCheckedOut&api.selectsettings=DistinctValues

Response:

{
   instances: [
      {
         properties: {
            MimeType: "UNKNOWN MIME TYPE",
            IsCheckedOut: false
         }
      },
      {
         properties: {
            MimeType: "TEXT/PLAIN",
            IsCheckedOut: false
         }
      },
      {
         properties: {
            MimeType: "IMAGE/X-PNG",
            IsCheckedOut: false
         }
      },
      {
         properties: {
            MimeType: "TEXT/PLAIN",
            IsCheckedOut: true
         }
      },
      {
         properties: {
            MimeType: "UNKNOWN MIME TYPE",
            IsCheckedOut: true
         }
      },
      {
         properties: {
            MimeType: "IMAGE/JPEG",
            IsCheckedOut: false
         }
      },
      {
         properties: {
            MimeType: "TEXT/XML",
            IsCheckedOut: false
         }
      },
      {
         properties: {
            MimeType: "TEXT/XML",
            IsCheckedOut: true
         }
      },
      {
         properties: {
            MimeType: "APPLICATION/ZIP",
            IsCheckedOut: false
         }
      },
      {
         properties: {
            MimeType: "APPLICATION/EXECUTABLE FILE",
            IsCheckedOut: true
         }
      }
   ]
}

New changeState: newOrModified

This feature lets you modify and insert new instances with single POST request. New changeState was introduced "newOrModified" (case insensetive) Only newOrModified and Existing (mostly used for parent and relation instances) changeStates are allowed with this request.

Example:

POST https://{host}/ws/v2.5/Repositories/{repositoryId}/$changeset

Authorization: Basic YWRtaW46YWRtaW4=
Host: {host}
Content-Type: application/json

{ 
 "instances": [{
     "className": "{class}", 
     "schemaName": "{schema}", 
     "instanceId": "{instanceId}",
     "changeState": "newOrmodified",
     "properties": { 
         "Name": "newName" 
     }
 }]
}

Response:

HTTP/1.1 200 OK
Content-Length: 528
Content-Type: application/json
Server: Bentley-WSG/9.99.00.00,Bentley-WebAPI/2.5 Microsoft-HTTPAPI/2.0
Mas-License-Status-Pending: true
Mas-Request-Id: 032e537e-1503-4761-874c-be60ff78da5c
Date: Mon, 27 Feb 2017 08:44:05 GMT

{
   "changedInstances": [{
         "change": "Modified",
         "instanceAfterChange": {
            "instanceId": "{instanceId}",
            "schemaName": "{schema}",
            "className": "{class}",
            "properties": {
               "Name": "newName",
               "Path": ".",
               "FileDate": "2016-01-18T09:32:31",
               "FileSize": "14",
               "PageSize": "",
               "Id": 1234,
               "IsHistoric": false,
               "IsLocked": false,
               "Remarks": "",
               "IsCheckedOut": false,
               "IsPending": false,
               "DateAdded": "2016-04-21T02:56:11.407",
               "DateChanged": "2017-02-27T09:58:08.01",
               "InDefaultScope": true,
               "InDefaultVisibleScope": true,
               "ScopeName": "Global",
               "ScopeId": 1,
               "MimeType": "UNKNOWN MIME TYPE"
            }
         }
      }
   ]
}

Schema update

Now it is possible to update schema in repository. User should post ECSchemaDef instance into https://{host}/ws/v2.5/repositories/{repositoryId}/Metaschema/ECSchemaDef/{instanceId} with schema file attached.


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