TMS Aurelius CRUD Endpoints
XData can be run completely independent of TMS Aurelius. It is a different product. You can simply declare your service operations and implement any custom logic, you want, including, of course, database operations. However, in conjunction with TMS Aurelius it offers certain advantages. If you use TMS Aurelius, XData offers a nice integration feature named TMS Aurelius CRUD endpoints.
This chapter assumes you have existing, basic knowledge about TMS Aurelius and your application already has some objects mapped to the database. Please refer to the TMS Aurelius documentation, "Quick Start" section for a brief introduction.
The following are the topics related to automatic TMS Aurelius CRUD Endpoints.
Overview of Aurelius CRUD Endpoints
If you have TMS Aurelius entities declared in your application, once your server is running, Aurelius entities are accessible through a REST/JSON architecture. The XData server defines URL conventions for the CRUD enpoints so you know how to build the URL to access the resources you need. The message payload in HTTP requests and responses must be in JSON format that also follows an XData specification. Then, XData clients can retrieve Aurelius entities by requesting data from the server or request data modification on Aurelius entities.
To illustrate this, suppose you have an Aurelius entity TCustomer associated with a TCountry entity, both declared in your application, you will have the following URL addresses published by TMS XData:
http://server:2001/tms/xdata/Customer (lists all TCustomer entities)
http://server:2001/tms/xdata/Country (lists all TCountry entities)
http://server:2001/tms/xdata/Customer(1) (retrieves TCustomer entity with id = 1)
http://server:2001/tms/xdata/Customer(2)/Country (retrieves the TCountry object associated to the customer with id = 2 via Country property)
http://server:2001/tms/xdata/Customer?$filter=Country/Name eq 'USA'&$orderby=Name&$top=10 (retrieves top 10 customers in the USA, ordered by name)
Furthermore, you can perform GET requests to retrieve entities, POST to create new entities, PUT/PATCH to update existing entities, and DELETE to remove entities.
You can also easily execute service operations from Delphi code through
the service interfaces using a TXData
uses {...}, MyServiceInterface, XData.Client;
var
Client: TXDataClient;
MyService: IMyService;
Customer: TCustomer;
begin
Client := TXDataClient.Create;
Client.Uri := 'http://server:2001/tms/xdata';
MyService := Client.Service<IMyService>;
Customer := Client.CustomerByName('Joe');
{...}
end;
Do not forget that you have to use those Aurelius entities anywhere in your server application otherwise they will be removed by the linker, at the very least, you should call RegisterEntity(TCustomer), for example, to make sure TCustomer will not be removed.
Aurelius Equivalence to XData Model
When you create the XData server module, the CRUD endpoints are automatically created from the current Aurelius mapping, using a default entity model builder, and you don't need to define it yourself. This topic describes the rules used by the model builder to create the model based on Aurelius mapping.
Note that the XData server module creates the EM based on a TMappingExplorer object, which contains all the Aurelius mapping information.
Classes become entity types
Every mapped class in Aurelius becomes an entity type. By default, the
name of entity type is the class name. So, if for example your mapping
has classes TCustomer, TOrder and TCountry, each of those classes become
an entity type in the EM.
Entity sets
XData creates an entity set for each entitytype, so that all entities
can be easily accessible from the server. The entity set associated with
the class contains all entities (objects) of that class. By default the
name of the entity set is the name of the entity type associated to it.
Thus, if you have a mapped class named "TCustomer", XData will define
an entity type "Customer" in the EM, and will also define an entity
set "Customer" that represents all entities of that type. You can
prevent the "T" from being removed from class name by using the
entity model builder.
Properties
Every mapped property or field in an Aurelius class becomes either a
property or navigation property in the entity type. If the class
property/field is an association, it becomes a navigation property. If
it's a scalar property, it becomes a single property. It's important
to note that only mapped class members
become an entity type property. The name of the property in the EM will
be the name of the mapped class property or the name of mapped class
field with the "F" prefix removed. As an example, consider the
following Aurelius mapping:
[Entity]
[Automapping]
TCustomer = class
strict private
FId: Integer;
FName: string;
[Transient]
FSex: Nullable<TSex>;
[Column('BIRTHDAY', [])]
FBirthday: Nullable<TDate>;
FCountry: TTC_Country;
[Column('Description', [])]
FDescription: TBlob;
[Transient]
FPhoto: TBlob;
[Transient]
FPhoto2: TBlob;
public
property Id: Integer read FId write FId;
property Name: string read FName write FName;
[Column('SEX', [])]
property Sex: Nullable<TSex> read FSex write FSex;
property Birthday: Nullable<TDate> read FBirthday write FBirthday;
property Country: TCountry read FCountry write FCountry;
property Description: TBlob read FDescription write FDescription;
[Column('Photo', [TColumnProp.Lazy])]
property Photo: TBlob read FPhoto write FPhoto;
property Photo2: TBlob read FPhoto2 write FPhoto2;
end;
The above class will become an entity type named "TCustomer", with the following named properties: Id, Name, Birthday, Country, Description, Sex and Photo. Fields like FId and FName are considered because the class is marked with [Automapping] attribute, so all fields are included (with "F" prefix removed). Also, properties Sex and Photo are also included because they are explicitly mapped. Finally, fields FSex, FPhoto and FPhoto2 are not included because they were excluded from Aurelius mapping because of the [Transient] attribute usage. You can prevent the "F" from being removed of field names by using the entity model builder.
All properties will be created as simple properties, except "Country" which will be a navigation property associated with the "TCountry" that is also an entity type created from TCountry class.
Entity Sets Permissions
By default, all entity sets defined in the XData model will be published by the server with full permissions. This means the clients can list, retrieve, modify, delete, create entities using the entity sets (see Requesting Data and Data Modification for more information).
You can change those permissions individually by each entity set, by choosing which operations can be performed in each entity set.
This is accomplished using the SetEntitySetPermissions method of the
TXData
procedure SetEntitySetPermissions(const EntitySetName: string;
Permissions: TEntitySetPermissions);
The EntitySetName parameter specifies the name of the entity set (note
that it's not necessarily the name of the class), or '*' for all
entity sets and Permissions parameter defines the allowed operations in
that entity set. Valid values are (declared in unit XData.Module.Base
):
type
TEntitySetPermission = (List, Get, Insert, Modify, Delete);
TEntitySetPermissions = set of TEntitySetPermission;
const
EntitySetPermissionsAll = [Low(TEntitySetPermission)..High(TEntitySetPermission)];
EntitySetPermissionsRead = [TEntitySetPermission.List, TEntitySetPermission.Get];
EntitySetPermissionsWrite = [TEntitySetPermission.Insert, TEntitySetPermission.Modify,
TEntitySetPermission.Delete];
So you can use constants EntitySetPermissionsAll (all operations), EntitySetPermissionsRead (read-only operations), EntitySetPermissionsWrite (write-only operations), or any combination of TEntitySetPermission enumerated value, which mean:
TEntitySetPermission values
Name | Description |
---|---|
TEntitySetPermission.List | Allows clients to query entities from the entity set using query criteria. |
TEntitySetPermission.Get | Allows clients to retrieve a single entity and its subproperties and associated entities. |
TEntitySetPermission.Insert | Allows clients to create a new entity in the entity set. |
TEntitySetPermission.Modify | Allows clients to modify an entity, by either method (PUT, PATCH, etc.) or even modifying properties or collection properties. |
TEntitySetPermission.Delete | Allows clients to delete an entity from the entity set. |
Examples
Allows all operations in entity set Country, except deleting an entity:
Module.SetEntitySetPermissions('Country',
EntitySetPermissionsAll - [TEntitySetPermission.Delete]);
Allows read-only operatiosn in City entity set:
Module.SetEntitySetPermissions('City', EntitySetPermissionsRead);
For the History entity set, allows only querying entities, retrieve a single entity, or create a new one:
Module.SetEntitySetPermissions('History',
[TEntitySetPermission.List, TEntitySetPermission.Get, TEntitySetPermission.Insert]);
Allows all entities to be read-only by default (list and get only). You can override this setting by setting permissions for a specific entity set:
Module.SetEntitySetPermissions('*', EntitySetPermissionsRead);
URL Conventions
This chapter describes what URL addresses are made available by the XData Server based on the entity model. It explains the rules for constructing URL address that identify data and metadata exposed by the server.
When you create the XData server, the root URL must be specified. Following the root URL part, additional URL constructions might be added, like path segments, query or fragment parts. In this manual, all examples consider the server root URL to be http://server:2001/tms/xdata/.
The URL used by the XData server for entity resources has three significant parts: root URL, resource path and query options, according to the example below.
http://server:2001/tms/xdata/Customer?$top=2&$orderby=Name
\___________________________/\______/ \__________________/
| | |
Root URL resource path query options
For service operations, the URL uses the following format:
http://server:2001/tms/xdata/MathService/Multiply
\___________________________/\__________/\______/
| | |
Root URL service operation
The following topics describe in details the parts of the URL as understood by the XData Server.
Resource Path
This topic defines the rules for resource path construction according to XData URL conventions. Resources exposed by the XData server are addressable by corresponding resource path URL components to enable interaction of the client with that resource aspect. To illustrate the concept, some examples for resources might be: customers, a single customer, orders related to a single customer, and so forth. Examples of addressable aspects of these resources as exposed by the data model might be: collections of entities, a single entity, properties, and so on.
The following topics explain several different ways to access data through an URL.
Addressing Entity Sets
An entity set can be addressed by using the name of entity set after the server root. As a general rule, the name of the entity set is the name of the base entity type of all entities belonging to that entity set (see Aurelius Equivalence to Entity Model for more information). So, for example, the entity set "Customer" will contain all entities of type "Customer" and its descendants. The collection of all Customer entities, for example, can be accessed with the following URL:
http://server:2001/tms/xdata/Customer
A query part can be appended to the URL address of an entity set, so entities in an entity set can be filtered, ordered, etc., for example:
http://server:2001/tms/xdata/Customer?$order=Name
There is a specific chapter about all the query options you can use in the query part of the URL.
XData uses JSON Format to represent the entity set resources available in such addresses. To know more about the format specification, please refer to the topic that explains JSON format representation of a collection of entities.
You can suffix the address with $count segment to return the number of entities:
http://server:2001/tms/xdata/Customer/$count
By default the name of entity set is the name of the associated Aurelius class name without the leading "T". Thus, if the class name is TCustomer, the entity set path will be "Customer". You can change that name explicitly by adding URIPath attribute to the class:
uses {...}, XData.Service.Common;
type
[URIPath('Customers')]
TCustomer = class
When the attribute is applied, the entity set for TCustomer entities will be accessible in the following address (considering server base url is server:2001/tms/xdata):
http://server:2001/tms/xdata/Customers
Addressing Single Entity
A single entity in an entity set can be addressed by passing the entity key between parenthesis after the entity set name. The following example represents a specified Customer object with id (key) equals to 3.
http://server:2001/tms/xdata/Customer(3)
The literals in key predicates can also represent other types than integer, such as strings or guids. The following example retrieves the employee with id equals to "XYZ".
http://server:2001/tms/xdata/Employee('XYZ')
If you have entity types with compound keys (not recommended, actually), you can access it by naming each value and separate them using commas. The order is not significant. The next example represents the URL address of a Person entity identified by a compounded key made up of two properties, LastName and FirstName, which values are "Doe" and "John", respectively.
http://server:2001/tms/xdata/Person('Doe','John')
http://server:2001/tms/xdata/Person(LastName='Doe',FirstName='John')
XData uses JSON Format to represent the entity resources available in such URL addresses. To know more about the format specification, please refer to the topic that explains JSON format representation of a single entity.
Key as segments
If property TXData
http://server:2001/tms/xdata/Customer/3
http://server:2001/tms/xdata/Employee/XYZ
Compound keys can be passed one after order, each also separated by slashes:
http://server:2001/tms/xdata/Person/Doe/John
Addressing Navigation Properties
From a single entitiy, you can access associated entities through navigation properties. For example, if a customer type has a country associated to it, you can access it using the name of navigation property. The following example retrieves the country object associated with customer 3:
http://server:2001/tms/xdata/Customer(3)/Country
You can only have a maximum of one level of nested navigation property. The next address, for example, trying to request the country associated with a customer that is associated to an order, is not valid. To request such country, you should use the address in previous example (Customer()/Country) or access the country directly using Country(<key>).
http://server:2001/tms/xdata/Order(41)/Customer/Country
(invalid address)
Note that it's also possible to retrieve collection of entities if the navigation property is a collection. For example, the following URL addresses the collection of order items for a specific order:
http://server:2001/tms/xdata/Order(41)/OrderItems
For such navigation properties that represents collections (many-valued associations), it's not possible to use key predicates (to identify a single entity in the collection), or query options (to filter or order the collection).
XData uses JSON Format to represent the entity resources available in such URL addresses. To know more about the format specification, please refer to the topic that explains JSON format representation of a single entity.
Addressing Individual Properties
It's also possible to directly access a simple property. In the example below, the URL represents the Name property of customer 3. Note there is a specific behavior for streams (blob) properties.
http://server:2001/tms/xdata/Customer(3)/Name
When accessing a simple property resource, it contains a JSON representation of the property value. You can also alternatively access the raw value of property using $value suffix:
http://server:2001/tms/xdata/Customer(3)/Name/$value
The difference between the two address is that the latter ($value) contains the property value in raw format (plain text), while the former, contains the property value wrapped in JSON format.
The only exception is a property address that represents a named stream (blob). In this case, the resource will contain the raw value of the blob, without needing to add the "$value" suffix. As an example, the following URL address contains the raw binary data representing the photo of a customer:
http://server:2001/tms/xdata/Customer(3)/Photo
XData uses JSON Format to represent the individual property resources available in such URL addresses. To know more about the format specification, please refer to the topic that explains JSON format representation of an individual property.
Addressing Streams (Blobs)
You can access a stream (blob) property directly using the property name. In the example below, the URL represents the Photo property of customer 3.
http://server:2001/tms/xdata/Customer(3)/Photo
The stream property resource contains the raw (binary) value of the blob content. Content-type is not specified. Unlike addressing individual properties, stream properties do not support the $value suffix, since the stream property resource already provides the raw value of the property.
Counting Entities
You can append "$count" path segment to a url that retrieves an entity set or a navigation property that returns a list of entities. That will retrieve the number of entities in that resource, in text/plain format, instead of the entities themselves.
For example, the following url will return the total number of Customer entities:
http://server:2001/tms/xdata/Customer/$count
While the following url will return the number of Customer entities with name is equal to "John":
http://server:2001/tms/xdata/Customer/$count?$filter=Name eq 'John'
You can also use $count with associated entities:
http://server:2001/tms/xdata/Order(41)/OrderItems/$count
Model Metadata
Any XData module provides the $model URL that returns the metadata for the API model. It lists the entities, service operations, parameters, etc. For now the model metadata is used by XData tools (like clients) and is subject to modification.
http://server:2001/tms/xdata/$model
Query Options
When addressing resources, it's possible to include a query string in the URL to perform extra operations on the resource being addressed, like filtering or ordering entities. Query options start with ? character and can be provided in the format name=value, separated by the & character. Many query options can be applied in a single URL. For example, the URL below retrieves the first 10 customers which country is USA, ordered by name:
http://server:2001/tms/xdata/Customer?$filter=Country/Name eq 'USA'&$orderby=Name&$top=10
The following table lists all the query options that can be used in a query string:
Query option | Description |
---|---|
$filter | Allows filtering the entities in an entity set by a specified condition. |
$orderby | Specifies the order of retrieved entities, by one or more properties or expression. |
$top | Specifies the maximum number of entities to be returned by the server. |
$skip | Specifies the number of entities that the server should skip before returning the requested entities. |
$inlinecount | Includes the total number of entities (without paging) in the JSON response. |
$expand | Expands associated objects in by forcing the server to include them inline in JSON response. |
$select | Allows selecting the fields to be present in the JSON response |
$filter
The $filter query option can be used in a URI query string to specify a predicate by which entities will be filtered from an entity set. The $filter option only applies when addressing entity sets. The filter format is the following:
$filter=<boolean expression>
where <boolean expression> is a boolean expression with the predicate. Example:
http://server:2001/tms/xdata/Customer?$filter=Name eq 'John'
Properties can be accessed through their name, as in the previous example. The properties available are the ones from the entity type of the specified entity set. In the previous example, the entity set being retrieved contains instances of entity type Customer, which means properties of Customer are directly accessible in the boolean expression.
To access a property of an associated entity (navigation property), you can use slashes to build the path to the property name. The following example retrieves all customers which country is USA:
http://server:2001/tms/xdata/Customer?$filter=Country/Name eq 'USA'
You can use logical operators and parenthesis to define multiple expressions and change evaluation order:
http://server:2001/tms/xdata/Customer?$filter=(Name eq 'John' or Name eq 'Jack') and Country/Name eq 'USA'
The previous examples use string literals in the expressions. String literals are always defined using single quotes. You can have literals of other types in the expression. Since the filter query option is part of URL, you must always format the literals according to the rules defined in Literals in URI section.
The following table lists the operators supported in filter expressions, and the order of precedence from highest to lowest. Operators in the same category have equal precedence.
Category | Expression | Description |
---|---|---|
Grouping | (x) | Enclosing parenthesis - expressions are evaluated with higher precedence |
Primary | Name/SubName | Slash, allowing access to a sub property (navigation property) |
Unary | -x | Negate x |
Unary | not x | Logical not applied to x |
Multiplicative | x mul y | Multiplies x by y |
Multiplicative | x div y | Divides x by y |
Additive | x add y | Adds x and y |
Additive | x sub y | Subtracts y from x |
Relational | x lt y | Evaluates true if x is lower than y |
Relational | x gt y | Evaluates true is x is greater than y |
Relational | x le y | Evaluates true if x is lower than or equal to y |
Relational | x ge y | Evaluates true if x is greater than or equal to y |
Equality | x eq y | Evaluates true if x is equal to y |
Equality | x ne y | Evaluates true is x is not equal to y |
AND Condition | x and y | Evaluates true is both x and y are true |
OR Condition | x or y | Evaluates true is either x or y are true |
$orderby
The $orderby query option can be used in a URL query string to determine which values are used to order the entities in the entity set. The $orderby option only applies for URI addressing entity sets. The format is the following:
$orderby=<expression> [asc/desc]
$orderby=<expression> [asc/desc],<expression> [asc/desc],...,<expression> [asc/desc]
Where <expression> must contain the value by which the entities must be order, which is mostly the name of a property of sub property. Example:
http://server:2001/tms/xdata/Customer?$orderby=Name
The asc/desc identifier is optional and indicates if the entities must be sorted by the specified expression values in ascending or descending order. If not specified, ascending order is used. You can use slashes to access associations (navigation properties) and sort by values of such associations. As an example, the following URI will retrieve all invoices sorted by the name of the customer's country associated with that invoice, in descending order:
http://server:2001/tms/xdata/Invoice?$orderby=Customer/Country/Name desc
You can also order by multiple values, separated by comma. The following example lists all customers by last name and for customers with the same last name, by first name:
http://server:2001/tms/xdata/Customer?$orderby=LastName,FirstName
$top
The $top query option can be used in a URI query string to specify the maximum number of entities that will be returned by the server. The $top option only applies for URI addressing entity sets. The format is the following:
$top=<integer>
The returned entities are always the first entities in the result set. Usually $top is used together with $orderby query option so that the order by which the entities are returned are known. It is also often used together with $skip query option to perform paging of results. In the following example, the server will return the first 10 invoice entities ordered by Date, even if there are more than 10 invoices in the server:
http://server:2001/tms/xdata/Order?$orderby=Date&$top=10
$skip
The $skip query option can be used in a URI query string to specify the number of entities that will be skipped by the server before returning the results. The $skip option only applies for URI addressing entity sets. The format is the following:
$skip=<integer>
The skipped entities are always the first entities in the result set. Usually $skip is used together with $orderby query option so that the order by which the entities are returned are known. It is also often used together with $top query option to perform paging of results. In the following URI example, the server will return invoice entities ordered by Date in descending order, buy skipping the 10 first invoices and starting in the 11th one:
http://server:2001/tms/xdata/Order?$orderby=Date desc&$skip=10
$inlinecount
The $inlinecount query option allows including the total number of entities in the JSON response representing a collection of objects. The counting of entities ignores paging query options like $top and $skip. It's useful when paging results, so you can get a single page but also retrieve the total number of entities so the client can know in a single request how many entities are in total even when requesting only a page.
The $inline option only applies for URI addressing entity sets. The format is the following:
$inlinecount=none|allpages
When using "none" (the default), no info is included. When using "allpages", the total number of entities for all pages will be included in the JSON response, in a property named $xdata.count.
In the following example, the server will return the first 10 invoice entities ordered by Date, and ask for the total number of entities available.
http://server:2001/tms/xdata/Order?$orderby=Date&$top=10&$inlinecount=allpages
The JSON result would be something like this (supposing the total number of entities is 142):
{
"@xdata.count": 142,
"value": [
<_list_of_Order_objects_here_>
]
}
$expand
The $expand query option allows fine-grain control about how associations will be represented in JSON response sent by the server or how blobs will be represented.
Expanding associations
By default, the server will represent any associated object in a JSON response using the reference convention. For example, suppose a request for a Customer object:
http://server:2001/tms/xdata/Customer(3)
will return a JSON response like this:
{
"$id": 1,
"@xdata.type": "XData.Default.Customer",
"Id": 3,
"Name": "Bill",
"Country@xdata.ref": "Country(10)"
}
Note the associated Country is represented as a reference (Country with
Id = 10). This is very handy when using Delphi client
(TXData
However, in some cases you might want to have the full Country object inline. With the $expand query option you can explicitly indicate which associated properties you want to be inline:
http://server:2001/tms/xdata/Customer(3)?$expand=Country
Response will be:
{
"$id": 1,
"@xdata.type": "XData.Default.Customer",
"Id": 3,
"Name": "Bill",
"Country": {
"$id": 2,
"@xdata.type": "XData.Default.Country",
"Id": 10,
"Name": "Germany",
}
}
$expand applies to both single association and lists. It also forces any server-side proxy to be loaded, so even if Country is lazy-loaded, $expand will retrieve the entity inline. XData optimizes this retrieval process by forcing the proxy to be loaded eagerly in a single SQL statement (except for lists).
Expanding blob properties
You can also use $expand to ask for the server to bring blob content inline in object representation. By default, blob properties will be represented as a proxy reference:
"Data@xdata.proxy": "Customer(55)/Data"
If you want the blob content to be retrieved together with the object, you can ask to $expand the property:
http://server:2001/tms/xdata/Invoice(3)?$expand=Data
Then the blob content will be inline as a base64 string:
"Data": "T0RhdGE"
Expanding multiple properties
You can expand multiple properties by separating them using comma. The following example expands properties Customer, Seller and Products:
http://server:2001/tms/xdata/Invoice(10)?$expand=Customer,Seller,Products
You can also expand subproperties (deeper levels) by using slash to specify subproperties. The following example expands property Customer and also the Country property of such customer (and Seller) property:
http://server:2001/tms/xdata/Invoice(10)?$expand=Customer/Country,Seller
$select
The $select query option allows fine-grained control on the fields present in the JSON response of the XData server. You can specify the fields to be present using a comma-separated. Considering the following request:
http://server:2001/tms/xdata/Customer(3)
Ends up with the following response:
{
"Id": 3,
"FirstName": "Bill",
"LastName": "Smith",
"Birthday": "1980-01-01",
"Country@xdata.ref": "Country(15)"
}
You can use $select
query option to include just a few fields in response:
http://server:2001/tms/xdata/Customer(3)?$select=Id,FirstName,Birthday
Results in:
{
"Id": 3,
"FirstName": "Bill",
"Birthday": "1980-01-01"
}
This option not only saves bandwidth by reducing the JSON response size, but it also effectively optimizes the server execution, by not including the missing fields in the SQL request. You can specify associations and its subproperties as well:
http://server:2001/tms/xdata/Customer(3)?$select=Id,FirstName,Birthday,Country,Country/Name&$expand=Country
Results in:
{
"Id": 3,
"FirstName": "Bill",
"Birthday": "1980-01-01",
"Country": {
"Name": "USA"
}
}
Built-in Functions
In addition to operators, a set of functions is also defined for use with the $filter or $orderby system query options. The following sections describe the available functions.
Upper
The upper function returns the input parameter string value with all characters converted to uppercase.
Example:
http://server:2001/tms/xdata/Customer?$filter=upper(Name) eq 'PAUL'
Lower
The lower function returns the input parameter string value with all characters converted to lowercase.
Example:
http://server:2001/tms/xdata/Customer?$filter=lower(Name) eq 'paul'
Length
The length function returns the number of characters in the parameter value.
Example:
http://server:2001/tms/xdata/Customer?$filter=length(Name) eq 15
Substring
The substring function returns a substring of the first parameter string value identified by selecting M characters starting at the Nth character (where N is the second parameter integer value and M is the third parameter integer value). N parameter is 1-based, thus N = 1 means the first character.
Example:
http://server:2001/tms/xdata/Customer?$filter=substring(CompanyName, 2, 4) eq 'oogl'
Position
The position function returns the 1-based character position of the first occurrence of the first parameter value in the second parameter value. If the string in first parameter is not found in the string in second parameter, it returns zero.
Example:
http://server:2001/tms/xdata/Customer?$filter=position('jr', Name) gt 0
Concat
The concat function returns the string concatenation of the two string input parameters passed to it.
Example:
http://server:2001/tms/xdata/Customer?$filter=concat(concat(Name, ' '), LastName) eq 'PAUL SMITH'
Contains
The contains function returns True if the first string parameter contains the string specified by the second string parameter. False otherwise.
The following example will return all customers which Name contains the string "Walker":
http://server:2001/tms/xdata/Customer?$filter=contains(Name, 'Walker')
StartsWith
The startswith function returns True if the first string parameter starts with the string specified by the second string parameter. False otherwise.
The following example will return all customers which Name starts with the string "Paul":
http://server:2001/tms/xdata/Customer?$filter=startswith(Name, 'Paul')
EndsWith
The endswith function returns True if the first string parameter ends with the string specified by the second string parameter. False otherwise.
The following example will return all customers which Name ends with the string "Smith":
http://server:2001/tms/xdata/Customer?$filter=endswith(Name, 'Smith')
Year
The year function returns the year component of a single date/time parameter value.
Example:
http://server:2001/tms/xdata/Customer?$filter=year(BirthDate) eq 1971
Month
The month function returns the month component of a single date/time parameter value.
Example:
http://server:2001/tms/xdata/Customer?$filter=month(BirthDate) eq 12
Day
The day function returns the day component of a single date/time parameter value.
Example:
http://server:2001/tms/xdata/Customer?$filter=day(BirthDate) eq 31
Hour
The hour function returns the hour component of a single date/time parameter value.
Example:
http://server:2001/tms/xdata/Customer?$filter=hour(BirthDate) eq 14
Minute
The minute function returns the minute component of a single date/time parameter value.
Example:
http://server:2001/tms/xdata/Customer?$filter=minute(BirthDate) eq 45
Second
The second function returns the second component of a single date/time parameter value.
Example:
http://server:2001/tms/xdata/Customer?$filter=second(BirthDate) eq 30
Literals in URI
When building an URI to perform a request to the server, you sometimes need to include literal values in it. You might need to use literals, for example, when addresing a single entity, where you need to pass the key value in the URI. Or when you use query options like $filter, where you usually include boolean expressions that compare property values to literal values. The following table indicates how to build literal values of several different data types.
Data Type | Examples | Description |
---|---|---|
null | null | Null value |
Boolean | true false |
Boolean values |
DateTime | 2013-12-25 2013-12-25T12:12 2013-12-25T12:12:20.125 |
Date time value must be in the ISO 8601 format (YYYY-MM-DDTdd:mm:ss.zzz). Parts of time can be omitted, for example, you can omit the milliseconds part (zzz) and seconds part (ss), or the full time and provide only the date part. |
Float | 3.14 1.2e-5 |
Float number values. Exponential format is also supported. |
Guid | E314E4B3-ECE5-4BD5-9D41-65B7E74F7CC8 | Guid value must not have enclosing brackets, must have the hyphens separating the five guid blocks (8 char, 4 char, 4 char, 4 char, 12 char). Each guid block is composed by hexadecimal digits. |
Integer | 1234 | Integer values |
String | 'John' | Strings must be enclosed in single quotes. |
Enumerated | csActive TCustomerStatus.csActive |
Values of enumerated types can be directly referred by their name, without any quotes. To avoid ambiguity, you can prefix the enumeration name with the type name and a dot. |
Custom Functions
Besides the built-in functions you can use in $filter and $orderby query options, you can also register your own custom functions. Such functions will then be translated into Aurelius' LINQ "SQL functions" that also need to be previously registered for the query to work.
For example, to register a function named "unaccent":
uses {...}, XData.Query.Parser;
TQueryParser.AddMethod('unaccent', TQueryMethod.Create('unaccent', 1));
The numeric parameter (1) indicates the number of parameters the function receives.
Then to use the function from query API:
http://server:2001/tms/xdata/Customer?$filter=unaccent(Name) eq 'Andre'
Requesting Data
XData server support requests for data via HTTP GET requests. You can follow URL conventions to determine the correct URL to be requested according to data needed.
The resource path of the URL specifies the target of the request (for example, collection of entities, entity, associated entity (navigation property), etc.). Additional query operators, such as filter, sort, page are specified through query options. The format of returned data depends on the data being requested but usually follows the specified XData JSON Format.
The following topics describe the types of data requests defined by XData and related information.
Querying Collections
XData server supports querying collections of entities. The target collection is specified through a URL that addresses an entity set (or an URL that addresses navigation properties representing a collection of items, like items of an order for example), and query operations such as filter, sort, paging are specified as system query options provided as query options. The names of all system query options are prefixed with a dollar ($) character.
For example, to query all the existing Country objects in the server, you perform a GET request to the respective entity set URL:
GET http://server:2001/tms/xdata/Country HTTP/1.1
You can add query options to the URL to filter the collection of entities. The following GET request returns the first 10 customers which country name is equals to "USA", ordered by customer name:
GET http://server:2001/tms/xdata/Customer?$filter=Country/Name eq 'USA'&$orderby=Name&$top=10 HTTP/1.1
The following example retrieves the items of order with id equals to 10.
GET http://server:2001/tms/xdata/Order(10)/Items HTTP/1.1
Note
You can only use query options on entity sets. When addressing navigation properties that represent a collection (like the previous example), query options are not available.
Requesting Associated Entities
To request associated entities according to a particular relationship, the client issues a GET request to the source entity's request URL, followed by a forward slash and the name of the navigation property representing the relationship, according to URL conventions for addressing associated entities.
If the navigation property does not exist on the entity indicated by the request URL, the service returns 404 Not Found.
If the association terminates on a collection (many-valued association), then behavior is similar as described in topic "Querying Collections", without the exception that query options (filtering, ordering, etc.) cannot be applied.
If the association terminates on a single entity (single-valued association), then behavior is the same as described in topic "Requesting Single Entities". If no entity is related, the service returns 204 No Content.
Examples
Retrieving the customer associated with order 10 (single entity):
GET http://server:2001/tms/xdata/Order(10)/Customer HTTP/1.1
Retrieving items associated with order 10 (collection of entities):
GET http://server:2001/tms/xdata/Order(10)/Items HTTP/1.1
Requesting Individual Properties
To retrieve an individual property, the client issues a GET request to the property URL. The property URL is the entity read URL with "/" and the property name appended, according to URL conventions for addressing simple properties. For example:
GET http://server:2001/tms/xdata/Product(1)/Name HTTP/1.1
The result format following the JSON format specification for individual properties, i.e., a JSON object with a name/value pair which name is "value" and the value contains the actual property value. The result for the above request might be, for example:
{
"value": "Silver Hammer XYZ"
}
If the property has the null value, the service responds with 204 No Content.
If the property doesn't exist the service responds with 404 Not Found.
You can alternatively request the raw value of the property. To retrieve the raw value of a primitive type property, the client sends a GET request to the property value URL, which is the property value URL suffixed with "$value":
GET http://server:2001/tms/xdata/Product(1)/Name/$value HTTP/1.1
The Content-Type of the response is text/plain and the response content is the property value in plain text, formatted according to the JSON format specification for property values. The response content of the above request might something like the following:
Silver Hammer XYZ
A $value request for a property that is null results in a 204 No Content response.
If the property doesn't exist the service responds with 404 Not Found.
Requesting Streams (Blobs)
To retrieve the content of a stream (blob) property, the client issues a GET request to a URL that addresses a stream (blob) resource. For example:
GET http://server:2001/tms/xdata/Customer(1)/Photo HTTP/1.1
The server will provide the binary blob content in response body, without specifying the content type. It's up to your client application to determine the type of content and process it accordingly. Note that this behavior is different from when you request individual properties, which in this case provide a JSON representation of the property value.
If the property has the null value, or the stream content is empty, the service responds with 204 No Content, otherwise a sucessful 200 OK response is provided.
If the property doesn't exist the service responds with 404 Not Found.
HTTP Request Headers
XData defines semantics around the following HTTP request and response headers. Additional headers may be specified, but have no unique semantics defined in XData.
The following are the available request headers.
xdata-expand-level
Clients can optionally include this header in the request to define the maximum depth for which the associated entities will be expanded (serialized inline) in an entity JSON representation.
xdata-expand-level: 3
An associated entity (navigation property) can be represented as an association reference, or inline object.
If XData-ExpandLevel is notpresent, the value 0 (zero) is assumed, which means all direct associated entities will be represented as references. When the header is present and value is higher, then all associated entities will be serialized inline, until the depth level specified by the header. The higher the expand level, the bigger the response payload will be, since more objects will be serialized. But if client will need those objects anyway, this will minimize the need of further server requests to retrieve associated objects. Proxies are not affected by this.
xdata-put-mode
Overrides the value of
TXData
xdata-put-mode: update
Valid values are "update" and "merge".
xdata-serialize-instance-ref
Overrides the value of
TXData
xdata-serialize-instance-ref: ifrecursive
Valid values are "always" and "ifrecursive".
xdata-serialize-instance-type
Overrides the value of
TXData
xdata-serialize-instance-type: ifneeded
Valid values are "always" and "ifneeded".
Requesting Single Entities
To retrieve a single entity, the client makes a GET request to the read URL of an entity.
The read URL can be obtained by following the URL conventions, either that addresses a single entity, or associated entities. The read URL can be also obtained from a response payload in JSON format containing information about associated entities (through navigation properties of the entity represented in the payload).
If no entity exists with the key values specified in the request URL, the service responds with 404 Not Found.
Examples
Retrieving a customer with id equals to 3:
GET /tms/xdata/Customer(3) HTTP/1.1
Host: server:2001
Retrieving the customer associated with order 10:
GET /tms/xdata/Order(10)/Customer HTTP/1.1
Host: server:2001
In both examples above, the response content might be:
{
"$id": 1,
"@xdata.type": "XData.Default.Customer",
"Id": 55,
"Name": "Joseph",
"Birthday": "1980-05-20",
"Sex": "tsMale",
"Picture": null
}
Data Modification
XData Server supports Create, Update, and Delete operations for some or all exposed resources (entities, individual properties, etc.). You can follow URL conventions to determine the correct URL address of the resource path in order to perform the data modification operation.
Since XData Server follows REST/JSON architecture, such data modification operations are performed by sending HTTP requests to the server using POST, PUT, PATCH or DELETE methods, and when applicable, providing data in message payload using JSON format.
The following topics explains the several different methods of performing data modification in a XData Server.
Create an Entity
To create an entity in a collection, clients must perform a POST request to that collection's URL, which can be either the entity set URL address or a navigation property URL address that represents a collection. The POST body must contain a single valid JSON representation of the entity. The entity representation can be full (all possible properties) or partial (a subset of all available properties for the entity type).
Properties in entity representation must be valid properties or navigation properties of the entity type as specified in the entity model. Additional property values beyond those specified in the entity type should not be sent in the request body. The request will fail if unable to persist all property values specified in the request. Missing properties will be set to their default value. If a missing property is required and does not have a default value in the database server, the request will fail.
The following example creates a new Country entity in the server.
POST /tms/xdata/Country HTTP/1.1
Host: server:2001
{
"@xdata.type": "XData.Default.Country",
"Name": "Brazil"
}
When creating an entity, the "xdata.
In the following example the request will create a Cat entity in entity set Animal. It's a valid request. If the provided xdata.type annotation indicates a type that is different or not inherited from the type of entity set being POST'ed, the request will fail.
POST /tms/xdata/Animal HTTP/1.1
Host: server:2001
{
"@xdata.type": "XData.Default.Cat",
"Name": "Wilbur",
"CatBreed": "Persian"
}
If "xdata.type" is not present, the created entity will have the same type as the base entity type of the collection addressed by the URL. For example, if client POST to the url "Cat" without "xdata.type" annotation present in payload, an entity of type Cat wil be created.
The entity representation in request payload must not contain associated entities as inline content. If values for navigation properties should be specified, then they must be represented as an association reference. For single-valued (single entity) navigation properties, this will update the relationship (set the value of property to reference the associated object represented in the association reference) after entity is created.
The following example creates a new City entity which is associated with a Country object of id 2.
POST /tms/xdata/City HTTP/1.1
Host: server:2001
{
"$id": 1,
"Name": "Frankfurt",
"Country@xdata.ref": "Country(2)"
}
Proxy values can be present in the entity payload (for example, when you are sending back a modified JSON representation previously retrieved from the server, it's possible that proxy values are present), but they are ignored by the server and navigation properties with proxy values that were not modified.
When perform POST request to a navigation property URL that represents a collection property, for example, Order(1)/Items which represent items of a specific order, the newly created entity will be automatically associated with (added to) that collection.
On successful update, the server returns with a 201 Created success response and the JSON representation of the updated entity in the message payload. The response will also contain a Location header that contains the URL of the created entity. Note that the returned entity might be different from the one sent by the client, since some properties might have been updated automatically by XData Server and/or the database server.
Update an Entity
To update an entity, clients must perform a PATCH or PUT request to a single entity URL address, providing the JSON representation of the entity in message payload of the request. The entity representation can be full (all possible properties) or partial (a subset of all available properties for the entity type).
PATCH method should be preferred for updating an entity as it provides more resiliency between clients and services by directly modifying only those values specified by the client. The semantics of PATCH, as defined in [RFC5789], are to merge the content in the request payload with the entity's current state, applying the update only to those components specified in the request body. Thus, properties provided in the payload will replace the value of the corresponding property in the entity or complex type. Missing properties of the containing entity will not be altered.
In the following example, only properties Name and DogBreed of resource Dog(1) will be updated in the server.
PATCH /tms/xdata/Dog(1) HTTP/1.1
Host: server:2001
{
"@xdata.type": "XData.Default.Dog",
"Id": 1,
"Name": "Willy",
"DogBreed": "Yorkshire"
}
PUT method can also be used, but you should be aware of the potential for data-loss for missing properties in the message payload. When using PUT, all property values will be modified with those specified in the request body. Missing properties will be set to their default values.
In the following example, properties Name and DogBreed will be updated, and any other existing property of Dog will be set to its default value (strings will be blank, associations will be nil, etc.). If one of missing properties is required and not present, the server might raise an error indicating a required property should have a value.
PUT /tms/xdata/Dog(1) HTTP/1.1
Host: server:2001
{
"@xdata.type": "XData.Default.Dog",
"Id": 1,
"Name": "Willy",
"DogBreed": "Yorkshire"
}
Key and other non-updatable properties can be omitted from the request. If the request contains a value for one of these properties the server will ignore that value when applying the update. That's the reason why Id property is not updated in the above examples.
The entity must not contain associated entities as inline content. If values for navigation properties should be specified, then they must be represented as an association reference. For single-valued (single entity) navigation properties, this will update the relationship (set the value of property to reference the associated object represented in the association reference).
The following example updates the Country association of a City object to the country with id equals to 2.
PATCH /tms/xdata/City(1) HTTP/1.1
Host: server:2001
{
"$id": 1,
"@xdata.type": "XData.Default.City",
"Country@xdata.ref": "Country(2)"
}
Proxy values can be present in the entity payload (for example, when you are sending back a JSON representation previously retrieved from the server, it's possible that proxy values are present), but they are ignored by the server and navigation properties with proxy values are not modified.
For update operations (PUT and PATCH), the
"xdata.
On successful update, the server returns with a success response and the JSON representation of the updated entity in the message payload. Note that the returned entity might be different from the one sent by the client, since some properties might have been updated automatically by XData Server and/or the database server.
Delete an Entity
To delete an entity, clients must perform a DELETE request to a single entity URL address. The request body must be empty. The entity represented by that address will be removed from server.
The following example will delete the entity resource at address /tms/xdata/Dog(1) - which is a Dog entity with id equals to 1:
DELETE /tms/xdata/Dog(1) HTTP/1.1
Host: server:2001
Note that depending on cascades configured in the server, associated entities might also be removed. If the entity being removed is associated by another entity that can't be removed in a cascade operation, a constraint enforcement will fail and the server will return an error without actually removing the entity.
On successful delete, the server responds with a 204 No Content response and no content in message payload.
Managing Streams (Blobs)
To update the content of a stream (blob) property, clients must perform a PATCH or PUT request to a stream property URL address, providing stream content in message body. You don't need to specify content-type and if you do, it will be ignored. Using either PATCH or PUT method results in same server behavior.
The request is an example of how to update the content of Photo property of the customer identified by id equals to 3.
PUT /tms/xdata/Customer(1)/Photo HTTP/1.1
Host: server:2001
<binary photo content>
You can also use DELETE method to clear the blob content (remove any data):
DELETE /tms/xdata/Customer(1)/Photo HTTP/1.1
Host: server:2001
On successful UPDATE or DELETE calls, the server will respond with a 204 No Content status code.