Changes in Web API 2.4

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

FileAccess

To find out if plugin supports direct file access URLs a new policy, named SupportsFileAccessUrl, is introduced. This Policy has 4 properties:

  • RequiresConfirmation - Boolean value. Upload confirmation is required after file upload is completed.
  • SupportedTypes - string value. Comma separated types that plugin supports. Currently there are two types: AzureBlobSasUrl and Default.
  • SupportsReadAccess - Boolean value. Identifies if plugin can generate URL with read permissions.
  • SupportsWriteAccess - Boolean value. Identifies if plugin can generate URL with write permissions.

To get file access key you need to query ../FileAccess/FileAccessKey and provide 2 filters:

  • Permissions (valid values: Read, Write),
  • Related instance search criteria by instance ID.

Query example:
https://localhost/ws/v2.4/Repository/{repositoryId}/FileAccess/FileAccessKey?$filter=Permissions+eq+'Read'+and+{schemaName}.{className}.$id+eq+'{instanceId}'

What is more, to achieve the same result, shorten URL form can be used:
GET https://localhost/ws/v2.4/Repository/{repositoryId}/{schemaName}/{className}/{instanceId}/FileAccess.FileAccessKey?$filter=Permissions+eq+'Read'

Response:

{
  "instances": [{
    "instanceId": ".AzureBlobSasUrl.Read.{schemaName}.{className}.{instanceId}",
    "schemaName": "FileAccess",
    "className": "FileAccessKey",
    "properties": {
        "Url": "https://...",
        "Type": "AzureBlobSasUrl",
        "Permissions": "Read",
        "RequiresConfirmation": false
    }
  },
  {
    "instanceId": ".Default.Read.{schemaName}.{className}.{instanceId}",
    "schemaName": "FileAccess",
    "className": "FileAccessKey",
    "properties": {
        "Url": "../{schemaName}/{className}/{instanceId}/$file?token=3AT5H174NDD13",
        "Type": "Default",
        "Permissions": "Read",
        "RequiresConfirmation": false
    }
  }
 ]
}

If you only want one URL per instance, you can add api.singleurlperinstance=true as query parameter. This will return first available URL for each instance. It means that AzulreBlobSasUrl will be returned if it is supported and only if it is not supported - Default URL will be returned.

  • Valid for X minutes, where X is defined by the configuration (Web.config). Manage the value using key <add key="FileAccessTokenLifeSpanMinutes" value="3" /> in 'Web.config' file
  • Valid only from the same machine.
  • Valid only for the same document.
  • Takes Mas-Connection-Info header into consideration.

If URL required upload confirmation, a POST request needs to be done to https://localhost/ws/v2.4/Repositories/{repositoryId}/FileAccess/UploadConfirmation

{
  "instance": {
    "schemaName": "FileAccess",
    "className": "UploadConfirmation",
    "relationshipInstances": [{
         "schemaName": "FileAccess",
         "className": "UploadConfirmationForKey",
         "direction": "forward",
         "relatedInstance":            {
             "schemaName": "FileAccess",
             "className": "FileAccessKey",
             "instanceId": "{previouslyRetrievedFileAccessKeyInstanceId}"
            }
        }]
   }
}

As a shorthand POST URL introduced in v2.4, you can also POST to this URL without any payload:

POST https://localhost/ws/v2.4/Repositories/{repositoryId}/FileAccess/FileAccessKey/{previouslyRetrievedFileAccessKeyInstanceId}/UploadConfirmation"

If you want to update instance at the same time when confirming file upload, you can do it by specifying it as additional related item to UploadConfirmation instance. Instance should be the same as {previouslyRetrievedFileAccessKeyInstanceId}.
Example:

{
  "instance": {
    "schemaName": "FileAccess",
    "className": "UploadConfirmation",
    "relationshipInstances": [{
         "schemaName": "FileAccess",
         "className": "UploadConfirmationForKey",
         "direction": "forward",
         "relatedInstance": {
             "schemaName": "FileAccess",
             "className": "FileAccessKey",
             "instanceId": "{previouslyRetrievedFileAccessKeyInstanceId}"
         },
        },
        {
        "schemaName": "FileAccess",
        "className": "UploadConfirmationInstance",
        "direction": "forward",
        "relatedInstance":           {
            "schemaName": "{schemaName}",
            "className": "{className}",
            "instanceId": "{instanceId}",
            "properties": {
                 {Properties to update while confirming}
             }
           }
       }]
   }
}

FileAccess redirect support

File access API was added to current file GET/PUT requests as well.

When you normally get a file using GET https://localhost/ws/v2.4/repositories/{repositoryId}/{schema}/{class}/{instanceId}/$file, you can add additional header Mas-Allow-Redirect: true.
Adding this header will inform WSG that if it can generate direct file access URL for the file - it should redirect to it.

  • If WSG manages to generate direct file access URL, it will return 307 (Temporary Redirect) result with 2 headers Mas-File-Access-Url-Type: {UrlType}; Location: {Url}.
  • If direct file access is not supported, file will be downloaded as previously.

When you normally put a file using PUT https://localhost/ws/v2.4/repositories/{repositoryId}/{schema}/{class}/{instanceId}/$file, you can add additional header Mas-Allow-Redirect: true.
Adding this header will inform WSG that if it can generate direct file access URL for the file - it should redirect to it.

  • If WSG manages to generate direct file access URL, it will return 307 (Temporary Redirect) result with 2 or 3 headers Mas-File-Access-Url-Type: {UrlType}; Location: {Url}; Mas-Upload-Confirmation-Id: {confirmationIdentificator}.
  • Mas-Upload-Confirmation-Id is returned only if plugin requires you to confirm file upload process. Upload confirmation is then performed using same PUT request as before to https://localhost/ws/v2.4/repositories/{repositoryId}/{schema}/{class}/{instanceId}/$file with identifier header set to the one you received.
  • If direct file access is not supported, file will be put as previously.

Note! After you get redirected, it is up to you to work with its API. Currently, only AzureSasBlobUrl type is supported. So you will get redirected to either GET or PUT to Azure blob.

Starting with version 2.4, the $orderby keyword can be used to order by related class properties. For example:

ConnectionFormatInfo Exposed

Starting with version 2.4, it is possible to query connection parameters formats for all or specific plugins. For example:

Support for Count system query option

Count query returns the number of instances for each specified class. Query example:

GET https://localhost/ws/v2.4/Repositories/{repository}/{schema}/{classes}/$count?$filter={filter}

  • Supports count for multiple classes.
  • Results can be filtered.
  • Returns 'InstanceCount' instance as a result.

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

Note for developers:
'InstanceCountQuery' is passed to plugin. It contains the inner query in extended data. The inner query is the same executed query but without count part. Some plugins may require it because this query contains more information. Inner query is retrieved from EC Framework 'CountQueryAccessor' class.

Support for Repository hiding

It is possible to hide specific repositories from users by modifying <hiddenRepositories> attribute in "ecom.config" file. Moreover, there is an option to deny access if needed by specifying denyAccess="true" parameter (false by default). Location property value can be found by querying for repositories.

If you need only to hide repository:

<HiddenRepositories>
    <Repository pluginId="Bentley.PW" location="host.domain.com:datasource" />
</HiddenRepositories>

If you need to hide and deny access to repository:

<HiddenRepositories>
    <Repository pluginId="Bentley.PW" location="host.domain.com:datasource" denyAccess="true"/>
</HiddenRepositories>

New syntax to create instances and relate them to existing ones

It is now possible to POST and link a new instance to the existing instance in the same request. A relationship will automatically be created between them. The request otherwise is identical to the conventional POST instance request.

POST https://localhost/ws/v2.4/Repositories/{repositoryId}/{schema}/{exitingClass}/{existingInstanceId}/{classForNewRelatedInstance}

Sorting messages in Bentley Web Services Gateway log file by request ID

Each request is assigned a unique request ID that can be found in a request's Mas-Request-Id header. In a log file, the request ID is saved as ActivityID and can be used to sort log messages and isolate those connected to a specific request.

Support for object references in JSON

Instance data JSON now supports references. It is possible to use $id property when serializing an object for the first time, and $ref property in all other places where the same object must be serialized in the same JSON. If a request contains both $id and $ref properties, the response will preserve the same object references. For example:

{
 "instances": [{
    "$id" : "1",
    "className": "{class}",
    "schemaName": "{schema}",
    "properties": { 
        "Name": "CreateDoc1236df6905k119.txt", 
        "Code": "Code0060" 
    },
    "relationshipInstances": [{
      "className": "FolderDocument",
      "schemaName": "{schema}",
      "direction": "backward", 
      "relatedInstance": {
        "$id" : "2",
        "instanceId": "186",
        "className": "Folder",
        "schemaName": "{schema}"
      }
     }]
 },{
    "$id" : "3",
    "className": "{class}",
    "schemaName": "{schema}",
    "properties": { 
       "Name": "CreateDoc12362gf1691k58.txt",
       "Code": "Code0061"
    },
    "relationshipInstances": [{
      "className": "FolderDocument",
      "schemaName": "{schema}",
      "direction": "backward", 
      "relatedInstance": {
         "$ref" : "2"
      }
    }]
  }]
}

POST request to execute queries with extra long query string

To overcome URL length limitation, Bentley Web Services Gateway Web API supports query execution using a POST request. The supported format is: POST https://localhost/ws/v2.4/Repositories/{repositoryId}/{schema}/{classes}/$query with string as a request body. For example:

GET https://localhost/ws/v2.4/Repositories/{repositoryId}/eb/Document!poly?$filter=Name+eq+'test'

can be replaced with the following POST request:

POST https://localhost/ws/v2.4/Repositories/{RepositoryId}/eb/Document!poly/$query

Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4 =
Host: localhost
Content-Length: 22
$filter=Name+eq+'test'

Request Options

Request options can be used to control how a request is executed and how a response is constructed in the server. Support for a specific option can be the subject of availability for each plugin. Request options can be specified in the JSON body. For example:

{
 "instance": { 
  "instanceId": "142",
  "schemaName": "{schema}",
  "className": "{class}",
  "properties": {
   "Code": "DOC1-000058"
  },
  "relationshipInstances": [{
   "instanceId": "120",
   "schemaName": "{schema}",
   "className": "Global_Relationship_DOCDOC2",
   "direction": "forward",
   "properties": {},
   "relatedInstance": {
     "changeState": "modified",
     "instanceId": "143",
     "schemaName": "{schema}",
     "className": "{class}",
     "properties": {
        "Code": "DOC1-000058"
     },
     "relationshipInstances": [{
       "instanceId": "106",
       "schemaName": "{schema}",
       "className": "Global_Relationship_DOCDOC2",
       "direction": "forward",
       "properties": {},
       "relatedInstance": {
          "changeState": "modified",
          "instanceId": "144",
          "schemaName": "{schema}",
          "className": "{class}",
          "properties": {
             "Code": "DOC1-000058"
          }
       }
     }]
   }
  }]
 },
 "requestOptions": {
  "FailureStrategy": "Continue"
 }
}

Currently available request options:

  1. FailureStrategy request option controls tolerance to the errors when processing multiple instances in the changeset. Possible values: Stop, Continue.

    FailureStrategy: Stop causes the request to execute until the first error. If an error occurs, a standard error JSON is returned. This is default behavior.

    FailureStrategy: Continue causes the server to execute all changeset requests, even if some requests fail. "Change" property is returned for all instances indicating if it was changed. Error property is returned indicating which instances failed to execute. Response will return success, even if some operations fail. Sample response:

    {
        "changedInstance": {
            "change": "Unchanged",
            "instanceAfterChange": {
                "instanceId": "142",
                "schemaName": "{schema}",
                "className": "{class}",
                "change": "Unchanged",
                "error": {
                    "errorMessage": "There is already another Document with this Prefix, Middle and Revision.",
                    "httpStatusCode": 500
                },
                "properties": {
                    "Code": "DOC1-000058"
                },
                "relationshipInstances": [{
                    "instanceId": "120",
                    "schemaName": "{schema}",
                    "className": "Global_Relationship_DOCDOC2",
                    "change": "Unchanged",
                    "direction": "forward",
                    "properties": {},
                    "relatedInstance": {
                        "instanceId": "143",
                        "schemaName": "{schema}",
                        "className": "{class}",
                        "change": "Unchanged",
                        "error": {
                            "errorMessage": "There is already another Document with this Prefix, Middle and Revision.",
                            "httpStatusCode": 500
                        },
                        "properties": {
                            "Code": "DOC1-000058"
                        },
                        "relationshipInstances": [{
                            "instanceId": "106",
                            "schemaName": "{schema}",
                            "className": "Global_Relationship_DOCDOC2",
                            "change": "Unchanged",
                            "direction": "forward",
                            "properties": {},
                            "relatedInstance": {
                                "instanceId": "144",
                                "schemaName": "{schema}",
                                "className": "{class}",
                                "change": "Unchanged",
                                "error": {
                                    "errorMessage": "There is already another Document with this Prefix, Middle and Revision.",
                                    "httpStatusCode": 500
                                },
                                "properties": {
                                    "Code": "DOC1-000058"
                                }
                            }
                        }]
                    }
                }]
            }
        }
    }
  2. ResponseContent option controls the amount of data returned in the response. Possible values: FullInstance, Empty, InstanceId.

    ResponseContent:FullInstance results full instance information to be returned in the response (this is default behavior).
    ResponseContent:Empty causes the response body to be empty - only the HTTP status is returned.
    ResponseContent:InstanceId results only class information and instance IDs to be returned. All properties are removed from the response.

  3. RefreshInstances controls whether instance property values need to be reloaded after executing a request. This can be helpful if actions executed by a request result in changes not directly specified in the request itself. Possible values: true, false.

    RefreshInstances:false is a default option. Additional steps are not executed after the request.

    RefreshInstances:true results that newly created or modified instances need to be refreshed before serializing to JSON. If a plugin does not support a refresh operation, Bentley Web Services Gateway will automatically execute additional requests to refresh instances. An additional property ""Refreshed"" is returned for all instances indicating if the refresh operation has succeeded.

  4. CustomOptions can be used to pass any user data for the plugin via extended parameters. Possible values: any <string,string> dictionary. Usage example:
    "requestOptions": {
      "CustomOptions": { 
        "CustomOption1": "CustomValue1" 
      }
    }

Token authorization header

Starting with API 2.4, the Bearer authorization header was replaced with a Token header. Header format has not changed and must be a base64-encoded SAML token. Bearer header will still be accepted for backward compatibility, but will not be returned as available.

Support for canonical operation 'contains' in query filter

Usage example:

https://localhost/ws/v2.4/Repositories/{repositoryId}/{schema}/Document?$filter=contains(Name, 'example')


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