NG ConnectionPack Guide


Package Architecture


Components

 

NG ConnectionPack installs components into Delphi Component Palette, where each component represents a single REST service. All components are descendants of TNGRestService base class. Base class provides common public interface for application setup, authentication and token handling.

 

TNGRestService base class

 

The following properties are used to make initial application setup:

 

ClientID

This property should be set to the corresponding client id of the registered application.

Secret

This property should be set to the corresponding client secret code of the registered application.

RedirectUrl

This property has a default value for some services, which should not be changed in most cases. But, unfortunately, some service vendors does not provide a default redirect page, so its user's responsibility to provide one. For example, for TNGBoxNet tests and demos we've used https://blank.org publicly available site.

Scopes

This property is can be used to setup access scopes. Scopes property has initial default value, so specifying scopes is optional. Also SetDefaultScopes method can be used at run-time to reset scopes list to its default value. Changing Scopes property at run-time can reset current access token.

 

The following properties and methods can be used to handle authentication token:

 

RefreshAccess

Checks, that the current access token is exist and is not expired. Access tokens have limited live time, so they should be refreshed from time to time. NG ConnectionPack do this automatically, so, usually, there no need to call this method explicitly. Also, note, that this method does not guarantee that the subsequent REST API call will succeed, but, usually it will. Read more here.

ResetToken

Clears existing access token. The method can be used to "log out" current user, and subsequently, the user will be forced to re-authenticate.

SaveToken

Save current access token and related information to stream or file.

LoadToken

Load access token information from stream or file.

Token

Provides read-only access to current access token value.

TokenScopes

Provides read-only access to scopes with which current access token is compatible.

Refresh

Provides read-only access to current refresh token value.

Expires

Provides read-only access to the date-time point, when current access token will expire.

OnTokenChanged

The event is fired when the user is re-authenticated or access token refreshed using RefreshAccess or ResetToken methods or, implicitly, as a result of calling REST API function.

 

REST operations

 

Rest operations are implemented as component public methods, which return corresponding request objects. For example:

 

var
  rq:  TNGGDrive.TListFiles;
  lst: TNGFileList;
begin
  rq := NGGDrive1.Files_List;
  rq.Q('trashed=false');
  lst := rq.Execute;
end;

 

Here ListFiles is a REST operation, which correspond to Google Drive API Files.List. The method is a public method of the corresponding TNGGDrive component class, which returns TListFiles request object. TListFiles request type is declared as a nested type inside the parent REST service component class, and so should be referenced in custom code as TNGGDrive.TListFiles.

 

Request object contains builder methods, which correspond to REST API parameters, and allow to setup REST request. Please note, that some parameters can be optional, while other are mandatory. Request object parameters are then converted to parts of the HTTP request URL, URL query parameters, HTTP request headers, or HTTP body. For example, Q method has been used above to specify file search query. Builder methods has been specially designed to shorten request configuration code, and always returns Self (request). So, the example can be re-written in a more compact way:

 

var

  lst: TNGFileList;

begin

  lst := NGGDrive1.Files_List

                  .Q('trashed=false')

                  .Execute;

end;

 

Builder methods only allow to set request parameters, there no ways to retrieve values of specified request parameter back.

 

To send request to REST server and execute it, each request object declare Execute method. The method returns operation results data, which is of type TNGFileList in above example.

 

Data types

 

As described above REST operation methods return request objects, and, as well, request Execute methods return resulting data. NG ConnectionPack has its own formal way of describing structure of request object builder parameters and returning resulting data. Usually, a lot of data types are declared in addition to a service component class. Above example uses TNGFileList data type, which is a collection of file objects returned from ListFiles operation. The following type kinds are supported by NG ConnectionPack data type system:

 

NGString

NGInteger

NGInt64

NGBoolean

NGFloat

NGDateTime

NGDate

NGUpload

NGDownload

NGList<T>

NGMap<T>

NGObject

 

Primitive types, such as NGString and NGInteger represents individual primitive values, such as object property or array element values. The main difference between corresponding Delphi primitive types is that NG ConnectionPack values are nullable, which is described below in more details. NGList<T> template type allow to declare collection like type of any primitive, map or object elements. NGMap<T> template type allow to declare a dictionary of values of type T with string keys, which is used in some services to decode JSON object values; for example, a collection of colors in Google Calendars API. NGObject type is a base class for object like values, which have predefined typed set of properties. NGUpload and NGDownload types are special types for performing stream based data uploading and downloading.

 

Nullable types

 

Primitive types are nullable; they can store special "null" value, which should be treated as undefined or unspecified. The rationale of having nullable types is:

 

Starting from 2018 release NG ConnectionPack introduce REST services "as is". Component set of operation methods are closely related to the corresponding REST service API operations, as well, as operations parameters and resulting data structure. This way we provide most complete implementation of REST services. However, we cannot test, even theoretically, all possible combinations of requests parameters and returned data. So, its impossible to decide, which parameter can be omitted in request or which property can be unspecified in operation result data.

 

So, nullable types allow to omit request parameter or a property in an object request parameter, which is very important in REST services, due to its very dynamic nature. As well nullable types allow to know for the user, which properties was really returned in a resulting data object.

 

Most types, except NGObject, are really implemented as smart records in Delphi, which are records types with properties, methods and overloaded operators. NG ConnectionPack types support the following interface, which relates to nullablity concept:

 

HasValue

The function allows to determine whether the variable or property has assigned a value of its null (undefined). Trying to read value from null (undefined) variable, will raise an Exception.

SetNull

The function allow to clear value from variable or property; after that HasValue function will return False.

Implicit cast

Implicit cast operator from special TNGNullType type. Works just like SetNull function. TNGNullType type is simple an enumeration with a single declared value NGNull.

 

Examples:

 

var

  v: NGInteger;

 

Check, whether the variable v has value inside:

 

if not v.HasValue then

  ShowMessage('v is null');

 

Clear variable v value using implicit operator and special value NGNull (which is of TNGNullType):

 

:= NGNull;

Primitive types

 

Primitive types, like NGString or NGInteger can store simple values of corresponding Delphi's primitive types. Primitive types provide declare the following public interface:

 

HasValue

HasValue and Implicit cast operator from TNGNullType - nullablity handling function, as described above.

Value

Read-only property which allows to read the corresponding value stored in variable or property. If the variable has no value, exception will be raised.

ValueOrDefault

The function returns either stored in a variable value, or, if variable stores no value, default value.

Implicit cast

Two implicit operators for converting from/to corresponding Delphi primitive type. Read operator will raise exception, if a variable has no value, just like Value function. Write operator always succeeds.

 

Examples:

 

var

  v: NGInteger;

 

Convert some integer number to NGInteger type using implicit operator and assign the resulting value to v:

 

:= 7;

 

Read variable v value using Value property or implicit cast operator:

 

:= v.Value;

:= v; // Implicit conversion.

 

Read variable v value using ValueOrDefault method:

 

:= v.ValueOrDefault(7);

 

In some cases Delphi compiler will not deduct that implicit conversion to base primitive type is required. This can happens while overloaded methods resolution or in some build-in binary operators. To workaround such cases, in addition to Value property shown above, explicit type conversion can be used:

 

SomeMyProc(Integer(v));

 

Type "any"

 

Type NGAny can represent a values of any supported in JSON type, like string, number, boolean, array or object. The support of any type in NG ConnectionPack is limited, and its value can be read/written as pure JSON string only. This JSON string is injected into requests "as is" without any validation.

 

HasValue

HasValue and Implicit cast operator from TNGNullType - nullablity handling function, as described above.

Json

Read-only string property which allows to read the corresponding JSON value stored in variable or property. If the variable has no value, empty string is returned.

FromJson

Static function, which allow to create NGAny values from pure JSON string or from TNGJson.TBuilder values. 'null' JSON string value is convcerted to undefined NGAny value.

Implicit cast

Several implicit cast operators are provided for converting from Delphi primitive type, like strings, numbers, booleans, dates or date-time values.

 

Examples:

 

var

  v: NGAny;

 

Convert some values to NGAny type using implicit operator and assign the resulting value to v:

 

:= 7;

:= 'my string value';

:= True;

 

Read variable v value using Json property:

 

json := v.Json;

 

Lists

 

Lists, which are collections of objects or primitive values are represented by NGList<T> template type. Some nullable underlying type, such as NGString or NGFloat should be used as a T parameter value. For simplicity lists are not nullable. They can only be empty - contains no items. List instances are auto-initialized, including object properties and list/map items of NGList<T> type; so lists usage is very simple:

 

var

  obj: TNGSomeDataObject;

begin

  obj.ListProp.Add(7);

  obj.ListProp.Add(9);

  ...

end;

 

In case of list of objects there no need to destroy object item values, because the values are owned by the parent list instance and will be automatically destroyed with it.

 

List declare the following public method and properties:

 

Add(Value: T)

Method adds new value to the list.

Delete(AIndex: Integer)

Method deletes list item at AIndex index. Since memory management is mostly automatic in NG ConnectionPack, there no need to dispose deleted item value

Clear

Method removes all existing items from the list.

Count

Read-only property, which provide access to list items count.

High

Read-only property, which is equal to Count - 1, and works similar to Delphi's standard High function.

Items[AIndex: Integer]: T

Property, which provides access to list items.

 

Examples:

 

var

  a: NGListy<NGInteger>;

 

Add new item to the list:

 

a.Add(7);

 

Iterate item elements:

 

sum := 0;

for i := 0 to x.High do

  Inc(sum, a[i]);

 

Clear items variable:

 

a.Clear;

 

Maps

 

Maps implements a dictionary concept with string keys. Maps are similar to objects, however, unlike objects, which provides predefined set of properties of different types, maps allows to have dynamic set of properties of the same type. Maps are useful in some cases to represent JSON data that need to be sent or received from REST service. One example of maps usage is a collection of colors in Google Calendars service, where element keys represent color names, and element values represent HEX color values.

 

Map are represented by NGMap<T> template type. Some nullable underlying type, such as NGString or NGFloat should be used as a T parameter value. For simplicity maps are not nullable like lists. They can only be empty - contains no items. Map instances are auto-initialized, including object properties and list/map items of NGMap<T> type; so maps usage is very simple:

 

var

  obj: TNGSomeDataObject;

begin

  obj.MapProp['MyKeyA'] := 7;

  obj.MapProp['MyKeyB'] := 9;

  ...

end;

 

In case of map of objects there no need to destroy object item values, because the values are owned by the parent map instance and will be automatically destroyed with it.

 

Maps declare the following public method and properties:

 

Remove(AKey: string)

Procedure deletes map element with AKey string key. Since memory management is automatic in NG ConnectionPack, there no need to dispose deleted element.

ContainsKey(AKey: string)

Function determined, whether specified element with specified AKey key is resided inside map.

Keys: TArray<string>

Function returns Delphi's array of keys, contained in map.

Clear

Method removes all existing elements from the map.

Count

Read-only property, which provide access to maps's element count.

Items[AKey: string]: T

Property, which provides access to map elements.

 

Examples:

 

var

  m: NGMap<NGInteger>;

 

Add new or replace existing element in map:

 

m['MyKey'] := 7;

 

Iterate map elements:

 

keys := m.Keys;

for i := 0 to High(keys) do

begin

  elem := m[keys[i]];

  ...

end;

 

Clear map variable:

 

m.Clear;

 

Objects

 

Objects provides predefined set of properties of different types. Unlike maps and arrays, which are template types, objects are pre-declared types in NG ConnectionPack. NG ConnectionPack contains a lot of pre-defined data objects types, since each REST service require quite various data for different operations. Unlike other types, objects are implemented in Delphi using Delphi object types, so, explicit destruction is required for object instances in some cases. The nullability of objects is supported via standard Delphi nil concept. Data object types declares parameter-less constructor, which should be used to create object instances:

 

var

  o: TNGSomeDataObject;

begin

  o := TNGSomeDataObject.Create;

  try

    ...

  finally

    o.Free;

  end;

end;

 

Object properties of object types (child objects) are owned by the parent object instance and will be automatically destroyed with it:

 

var

  o: TNGSomeDataObject;

begin

  o := TNGSomeDataObject.Create;

  try

    o.ObjProp := TNGOtherDataObject.Create;

    o.ObjProp.:= 7;

    ...

  finally

    o.Free; // o.ObjProp instance is automatically destroyed here.

  end;

end;

 

Objects declare various read-write data specific properties; for example, TNGFile object type declares a lot of supported by Google Drive API properties, such as Id, Name, Size, ect.

 

Object properties are undefined (null) by default. Internal implementation does not store underfined properties, and this is another implementation aspect, which allow NG ConnectionPack to provide most complete REST services implementation, because, usually, rest data is quite big and represents a lot of properties. For example, TNGFile data type declares 51 property.

 

Examples:

 

var

  o: TNGFile;

 

Create new object instance using default constructor:

 

:= TNGFile.Create;

 

Assign values to object properties:

 

o.id   := '234234234';

o.Name := 'MyFile.txt';

o.Size := 1024;

 

Destroy object variable:

 

o.Free;

 

Upload and download

 

When REST service operation is intended for data uploading, its request object have a builder method with a parameter of NGUpload type. For example, request object of TNGGDrive.CreateFileUpload operation have Data(Value: NGUpload) method, which allows to setup uploading data:

 

var

  upl: NGUpload;

  fl:  TNGFile;

begin

  upl.Stream := MyReadDataStream;

  fl := NGGDrive1.Files_CreateUpload

                 .Data(upl)

                 .Execute;

end;

 

NGUpload type declares the following properties, which allows to setup data stream:

 

Stream

Property allows to specify data to be uploaded as a standard Delphi's TStream object. The content of this associated stream will be read during REST operation execution. To read whole stream NG ConnectionPack will first reset stream's position to point to the beginning of the stream. Non nil value for Stream property is required to be specified. Otherwise an exception will be raised during operation execution.

MimeType

Property allows to specify data mime type. This property is optional for some operations, but required for others. As well, operation can use some predefined default MimeType, if its not specified in NGUpload object.

 

For downloading data NGDownload type can be used. Its generally similar to NGUpload type. And its also provide Stream property, which in this case should be set to reference of TStream object, which will receive data during operation execution. As well, for download operations, request Execute method takes NGDownload object as a parameter:

 

var

  dnl: NGDownload;

begin

  dnl.Stream := MyWriteDataStream;

  NGGDrive1.Files_GetDownload

           .Execute(dnl);

end;

 

Please note, that big data download is performed incrementally, using several underlying network messages. So, a network failure error can occurre, somewhere in the middle of downloading process, when some downloaded data has been already written into the stream.

 

Associated TStream object is not owned by NGUpload or NGDownload objects, and so, need to be destroyed eventually after operation execution.

 

The rationale of using distinct NGUpload and NGDownload types instead of simple Delphi's TStream type is:

 

First, NGUpload and NGDownload types are smart records, just like any other data types in NG ConnectionPack.

Second, NG ConnectionPack types provide some additional interface, such as MimeType property in NGUpload type; and more properties can be added in future.

 

Exceptions

 

Among other possible exceptions, NG ConnectionPack declare two additional exception classes:

 

EHttpError

Exception is raised when HTTP requests return error status codes, such as 4XX codes. Exceptions of this type provide extended information about the error, such as StatusCode, StatusText and ErrorInfo text.

EHttpClient

Exception is raised when some system error occures while executing HTTP request, such as WinAPI error, ect. In such situations HTTP request may even not be executed yet, so, no extended information from the server is available.

 

EHttpError public interface:

 

Message

Standard Delphi property, inherited from TException class. Message property value contains StatusText; as well, if EHttpError.ShortFormat class propertry is set to False, extended ErrorInfo will be added to Message property value.

ShortFormat

Class level (static) property, which determines, whether extended ErrorInfo should be added to Message property value.

StatusCode

Property provides access to HTTP status code.

StatusText

Property provides access to short HTTP status text.

ErrorInfo

Property contains extended information, returned as a HTTP body. Usually, this information is a JSON object with error description. The format of JSON information vary in different services, so we provide the text as is, without any attempt to parse it.