1783 lines
106 KiB
Plaintext
1783 lines
106 KiB
Plaintext
Function SRP_EDITOR_TEMP_HTTP_RESOURCE_SERVICES_FRAMEWORKS(@Service, @Params)
|
|
/***********************************************************************************************************************
|
|
|
|
This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
|
|
permission from SRP Computer Solutions, Inc.
|
|
|
|
Name : HTTP_Resource_Services
|
|
|
|
Description : Handler program for all module related services.
|
|
|
|
Notes : The generic parameters should contain all the necessary information to process the services. Often
|
|
this will be information like the data Record and Key ID.
|
|
|
|
Parameters :
|
|
Service [in] -- Name of the service being requested
|
|
Param1-10 [in/out] -- Additional request parameter holders
|
|
Response [out] -- Response to be sent back to the Controller (MCP) or requesting procedure
|
|
|
|
Metadata :
|
|
|
|
History : (Date, Initials, Notes)
|
|
04/19/15 dmb [SRPFW-95] Original programmer.
|
|
04/21/15 dmb [SRPFW-95] Fix bug in Prepare_Column_Values gosub that ignored calculated columns. Thanks to
|
|
Barry Stevens for finding this.
|
|
04/21/15 dmb [SRPFW-95] Add PostDatabaseItem service.
|
|
04/21/15 dmb [SRPFW-95] Fix bug in Prepare_Column_Names gosub that ignored calculated columns.
|
|
04/21/15 dmb [SRPFW-95]Modify the GetDatabaseItems service to support multiple types of options in the
|
|
Filter (formerly Query) argument.
|
|
05/04/15 dmb [SRPFW-95] Convert Prepare_Column_Names gosub into the GetColumnNames service. Retrofit all
|
|
calls to the gosub to use the new service.
|
|
05/04/15 dmb [SRPFW-95] Convert Prepare_Column_Values gosub into the GetColumnValues service. Retrofit
|
|
all calls to the gosub to use the new service.
|
|
05/04/15 dmb [SRPFW-95] Move the logic to exclude non-master columns and full Key ID columns into the
|
|
GetColumnNames service. The GetColumnValues service will no longer be doing any column name
|
|
filter. It will just work with the list provided. It will, however, still make sure the
|
|
column name exists before attempting to get a value for it.
|
|
05/04/15 dmb [SRPFW-95] Modify the GetColumnValues service to return full Key IDs if the column name
|
|
requires it. Add logic to decrement delimiters if @RM or @FM characters are found in the
|
|
value.
|
|
05/04/15 dmb [SRPFW-95] Modify the PostDatabaseItem service to handle SRP_JSON PARSE errors better and
|
|
return a 400 response status code.
|
|
05/15/15 dmb [SRPFW-95] Modify the GetDatabaseItems service to return an empty HAL+JSON embedded response
|
|
if there are no matches. Remove the 404 status since the URL itself is valid although the
|
|
database returns no matches.
|
|
06/09/15 dmb [SRPFW-95] Add ItemArrayLabel option for GetDatabaseItem and GetDatabaseItems services. This
|
|
allows the calling service to customize the name of the primary HAL item array.
|
|
02/27/16 dmb [SRPFW-112] Update various services to use Memory_Services for caching.
|
|
03/23/16 dmb [SRPFW-114] Comment out the logic that attempted to remove full Key ID values from the
|
|
response in the GetColumnValues services. Mark Boorman pointed out a bug in this logic, but
|
|
the reason for doing this was unnecessary.
|
|
03/24/16 dmb [SRPFW-115] Add GetMVGroupNames service.
|
|
03/25/16 dmb [SRPFW-119] Update all SRP_JSON GETVALUE services used in the PostDatabaseItem service to
|
|
use the Default argument to return an empty string if the objects value is null.
|
|
04/11/16 dmb [SRPFW-121] Update GetDatabaseItems service to use %RECORDS% if the Filter argument is
|
|
empty. This will provide a sorted list of Key IDs automatically.
|
|
04/11/16 dmb [SRPFW-121] Update PostDatabaseItems service to use %RECORDS% if %SK% does not exist. This
|
|
will make it easier to calculate the next available Key ID.
|
|
07/26/16 dmb [SRPFW-126] Refactor the GetColumnNames service and resolve a bug that Barry Steven's
|
|
discovered where non-master data type columns would be returned.
|
|
10/13/16 dmb [SRPFW-129] Update the PostDatabaseItem service to set the response status to 201 when the
|
|
ItemID is being passed in but it does not yet exist in the database.
|
|
07/01/17 dmb [SRPFW-184] Refactor using Enhanced BASIC+ syntax.
|
|
07/05/17 dmb Add PutDatabaseItem and PatchDatabaseItem services. Refactor so all DatabaseItem create and
|
|
update services share relevant common code.
|
|
07/05/17 dmb Update several variables and comments to better match database terminology rather than
|
|
HTTP terminology.
|
|
07/19/18 dmb Update the PostDatabaseItem, PutDatabaseItem, and PatchDatabaseItem services to support a
|
|
new argument, AllowedColumnNames, which will be used to filter out column names which are
|
|
submitted in the payload but should not be updated on the server.
|
|
11/29/18 dmb [SRPFW-259] Fix GetColumnNames to properly remove XREF columns.
|
|
11/29/18 dmb [SRPFW-259] Add GetObjects service.
|
|
11/29/18 dmb [SRPFW-259] Add GetSerializedResource service.
|
|
11/29/18 dmb [SRPFW-259] Add ParseResource service.
|
|
11/29/18 dmb [SRPFW-259] Add AddLinkRelationships service.
|
|
11/29/18 dmb [SRPFW-259] Add AddProperties service.
|
|
11/30/18 dmb [SRPFW-259] Add AddEmbeddedResources service.
|
|
11/30/18 dmb [SRPFW-259] Add AddNestedProperties service.
|
|
12/01/18 dmb [SRPFW-259] Update GetDatabaseItem to use the new resource services.
|
|
12/01/18 dmb [SRPFW-259] Update GetDatabaseItems to use the new resource services.
|
|
11/30/18 dmb [SRPFW-259] Add AddFormAction service.
|
|
12/02/18 dmb [SRPFW-259] Update GetColumnValues service so when a Key ID is missing it will set
|
|
Error_Services and set the response status to 404.
|
|
12/04/18 dmb [SRPFW-259] Add support for multiple nested objects in the AddNestedProperties service.
|
|
12/06/18 dmb [SRPFW-257] Add LoremIpsum service.
|
|
12/12/18 dmb [SRPFW-259] Update GetObjects so that specific Key ID parts can be identified to be used in
|
|
the self relationship URL and also add feature so the default '*' delimiter can be swapped
|
|
out for another character.
|
|
01/03/19 dmb [SRPFW-259] Fix bug AddNestedProperties when using Array formatting.
|
|
02/14/19 dmb [SRPFW-259] Add IsArray argument for the AddProperties service so data can be automatically
|
|
formatted as an array regardless if @VMs are found. This is meant to maintain formatting
|
|
integrity of the property regardless of 0, 1, or multiple values.
|
|
03/04/19 dmb [SRPFW-259] Add GetObject service. Refactored GetObjects service to use GetObject service.
|
|
03/04/19 dmb [SRPFW-259] Add AddProperty service. Update AddProperties service to call AddProperty for
|
|
each property.
|
|
03/04/19 dmb [SRPFW-259] Add AddSubProperty and AddSubProperties service.
|
|
03/04/19 dmb [SRPFW-259] Add AddSubResource and AddSubResources service.
|
|
03/05/19 dmb [SRPFW-259] Add AddSubResourceObject and AddSubResourceObjects service.
|
|
03/05/19 dmb [SRPFW-259] Remove AddNestedProperties service. Refit other services to use AddSubResources.
|
|
03/05/19 dmb [SRPFW-259] Add AddLinkRelationship service. Update AddLinkRelationships service to call
|
|
AddLinkRelationship for each relationship.
|
|
03/05/19 dmb [SRPFW-259] Update all resource related services to use new services and remove direct calls
|
|
to SRP_JSON except for the lowest level services.
|
|
03/05/19 dmb [SRPFW-259] Add AllowKeyProperty argument to the GetObject, GetObjects, and GetColumnNames
|
|
services to override the default behavior of removing full Key ID columns from being
|
|
represented in the object. This is useful for sub-resources.
|
|
03/05/19 dmb [SRPFW-259] Fix bug in GetObject service where associated property names were not being
|
|
removed if the column names were removed by the GetColumnNames service.
|
|
03/06/19 dmb [SRPFW-259] Update the GetDatabaseItem and GetDatabaseItems service so the SelfURL argument
|
|
is now optional.
|
|
04/10/19 dmb [SRPFW-271] Replace the Content-Location response header with Location in UpdateDatabaseRow.
|
|
This is the correct header for 201 responses.
|
|
(cf. https://tools.ietf.org/html/rfc2616#section-10.2.2)
|
|
05/28/19 dmb [SRPFW-274] Update the AddEmbeddedResources service to support a new Singular argument. This
|
|
provides support for an embedded resource that is unique and should be represented as
|
|
sub-properties rather than sub-resources.
|
|
05/28/19 dmb [SRPFW-274] Update the AddSubProperty service to correctly handle the SubPropertyValue
|
|
argument if it contains an object handle.
|
|
05/28/19 dmb [SRPFW-274] Rename the GetLinkRelationship service to GetLinkRelation. Rename the
|
|
GetLinkRelationships service to GetLinkRelations.
|
|
05/31/19 dmb [SRPFW-276] Update the GetColumnValues service so Error_Services is called after the
|
|
SetResponseError call. This prevents Error_Services from being cleared prematurely.
|
|
05/31/19 dmb [SRPFW-276] Update the GetColumnNames service so Error_Services is called after the
|
|
SetResponseError call. This prevents Error_Services from being cleared prematurely.
|
|
05/31/19 dmb [SRPFW-276] Update the GetMVGroupNames service so Error_Services is called after the
|
|
SetResponseError call. This prevents Error_Services from being cleared prematurely.
|
|
12/09/19 dmb [SRPFW-296] Update all calls to Memory_Services to use a specific cache name.
|
|
12/16/19 dmb [SRPFW-296] Update the AddEmbeddedResources so it can be called more than once for the same
|
|
embedded resource name. New resources will be added rather than replace what is already
|
|
present.
|
|
01/22/20 dmb [SRPFW-296] Update the AddLinkRelation and AddLinkRelations services to support IsTemplate
|
|
(IsTemplates) Boolean argument. This is to add support for URI Templates:
|
|
https://tools.ietf.org/html/rfc6570
|
|
https://tools.ietf.org/html/draft-kelly-json-hal-08#section-5.2
|
|
01/26/20 dmb [SRPFW-296] Add GetURLTemplate service.
|
|
01/26/20 dmb [SRPFW-296] Update the AddLinkRelation service to call the GetURLTemplate service to create
|
|
a URL Template out of an indicated expanded URL.
|
|
|
|
***********************************************************************************************************************/
|
|
|
|
#pragma precomp SRP_PreCompiler
|
|
|
|
$insert LOGICAL
|
|
$insert HTTP_INSERTS
|
|
$insert SERVICE_SETUP
|
|
$insert INET_EQUATES
|
|
$insert INET_HEADERS
|
|
$insert DICT_EQUATES
|
|
|
|
Equ CRLF$ to \0D0A\
|
|
Equ CacheName$ to 'SRPHTTPFramework'
|
|
|
|
Declare function HTTP_Resource_Services, HTTP_Resource_Manager_Services, Database_Services, Memory_Services
|
|
Declare function SRP_Array
|
|
Declare subroutine HTTP_Resource_Services, RList, Make.List, Activate_Save_Select, Memory_Services
|
|
|
|
GoToService else
|
|
HTTP_Services('SetResponseError', '', '', 404, Service : ' is not a valid service request within the HTTP Resource services module.')
|
|
end
|
|
|
|
Return Response OR ''
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Service Parameter Options
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
Options BOOLEAN = True$, False$
|
|
Options LAYOUT = 'Array', 'List'
|
|
Options METHODS = 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'
|
|
Options STYLES = 'Fast', 'Styled', 'DropNulls'
|
|
Options ALLDATATYPES = 'String', 'Number', 'Boolean', 'StringList', 'NumberList', 'BooleanList', 'ObjectHandle'
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Services
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetObject
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Optional]
|
|
// KeyID - Key ID of a database row that should be used to build the resource. - [Optional]
|
|
// ColumnNames - A delimited list of column names to build into the resource. Default will be all legitimate
|
|
// columns which are returned by the GetColumnNames service. - [Optional]
|
|
// PropertyNames - A delimited list of property names to use for each column. Default will be the column name.
|
|
// - [Optional]
|
|
// DataTypes - A delimited list of JSON data types (e.g., string, number, Boolean, etc.). Default will be
|
|
// string. - [Optional]
|
|
// MVGroupNames - A delimited list of MV group names that are to be matched with the delimited list of nested
|
|
// group names. - [Optional]
|
|
// NestedGroupNames - A delimited list of nested group names to use for each MV group name. This is only used if
|
|
// a list of MV group names is also provided. - [Optional]
|
|
// BaseSelfRelURL - The base URL to use for creating a self link relationship. If empty then no self link
|
|
// relationship will be created. Otherwise, the Key ID will be appended as a segment to this
|
|
// URL and added using the AddLinkRelations service. - [Optional]
|
|
// KeyParts - A delimited list of Key ID parts that should be used to created the self link relationship.
|
|
// Default is all Key ID parts will be used. - [Optional]
|
|
// KeyDelimiter - The character to use as the delimiter for multi-part Key IDs when used in the self link
|
|
// relationship. Default is '*', although this is not a URL friendly character. - [Optional]
|
|
// AllowKeyProperty - A Boolean flag indicating that the resource object be allowed to represent the Key ID of the
|
|
// database row to exists. This ultimately informs the GetColumnNames service to avoid stripping
|
|
// out the Key ID column. - [Optional]
|
|
//
|
|
// Creates one or more new resource objects. The database table, Key ID, and list of column mames are provided, these
|
|
// will be used to create properties within the resource object.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetObject(TableName, KeyID, ColumnNames, PropertyNames, DataTypes, MVGroupNames, NestedGroupNames, BaseSelfRelURL, KeyParts, KeyDelimiter, AllowKeyProperty)
|
|
|
|
ObjectHandle = ''
|
|
|
|
If (TableName NE '') AND (KeyID NE '') then
|
|
FieldArray = Database_Services('ReadDataRow', 'DICT.' : TableName, '%FIELDS%', False$, 60)
|
|
Convert @Lower_Case to @Upper_Case in ColumnNames
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in ColumnNames
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in PropertyNames
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in DataTypes
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in MVGroupNames
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in NestedGroupNames
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in KeyParts
|
|
If KeyDelimiter EQ '' then KeyDelimiter = '*'
|
|
MVColumnGroupNames = ''
|
|
CompareColumnNames = ColumnNames ; // Save the original column names for comparison after they've been vetted.
|
|
ColumnNames = HTTP_Resource_Services('GetColumnNames', TableName, ColumnNames, AllowKeyProperty)
|
|
// Check each original column name against the vetted list. If it is missing, remove the associated property name
|
|
// as well.
|
|
NumColumnNames = DCount(CompareColumnNames, @FM)
|
|
For ColumnPos = NumColumnNames to 1 Step -1
|
|
ColumnName = CompareColumnNames<ColumnPos>
|
|
Locate ColumnName in ColumnNames using @FM setting fPos else
|
|
PropertyNames = Delete(PropertyNames, ColumnPos, 0, 0)
|
|
end
|
|
Next ColumnPos
|
|
hTable = Database_Services('GetTableHandle', TableName)
|
|
If Error_Services('NoError') then
|
|
If SRP_JSON(ObjectHandle, 'New', 'Object') then
|
|
ColumnValues = HTTP_Resource_Services('GetColumnValues', TableName, ColumnNames, KeyID)
|
|
If Error_Services('NoError') then
|
|
Convert @VM : @SVM : @TM : @STM to @SVM : @TM : @STM : Char(249) in ColumnValues
|
|
Convert @Lower_Case to @Upper_Case in ColumnNames
|
|
For Each ColumnName in ColumnNames using @FM setting fPos
|
|
PropertyName = PropertyNames<fPos>
|
|
If PropertyName EQ '' then PropertyNames<fPos> = Oconv(ColumnName, 'MCL')
|
|
DataType = DataTypes<fPos>
|
|
If DataType EQ '' then DataTypes<fPos> = 'String'
|
|
Locate ColumnName in FieldArray<FIELDS_NAME$> using @VM setting vPos then
|
|
MVGroupName = FieldArray<FIELDS_MV_GROUPNAME$, vPos>
|
|
If MVGroupName NE '' then
|
|
Locate MVGroupName in MVGroupNames using @FM setting Pos then
|
|
NestedGroupName = NestedGroupNames<Pos>
|
|
If NestedGroupName EQ '' then NestedGroupName = Oconv(MVGroupName, 'MCL')
|
|
MVColumnGroupNames<fPos> = NestedGroupName
|
|
end else
|
|
MVColumnGroupNames<fPos> = Oconv(MVGroupName, 'MCL')
|
|
end
|
|
end
|
|
end
|
|
Next ColumnName
|
|
ColumnArray = ColumnNames : @RM : PropertyNames : @RM : DataTypes : @RM : MVColumnGroupNames : @RM : ColumnValues
|
|
ColumnArray = SRP_Array('Rotate', ColumnArray, @RM, @FM)
|
|
Convert @FM to @VM in ColumnArray
|
|
Convert @RM to @FM in ColumnArray
|
|
ColumnArray = SRP_Array('SortRows', ColumnArray, 'AL4', 'LIST')
|
|
NestedValueArray = ''
|
|
NestedPropertyNames = ''
|
|
PrevMVColumnGroupName = ''
|
|
For Each ColumnInfo in ColumnArray using @FM setting fPos
|
|
ColumnName = ColumnInfo[1, @VM]
|
|
PropertyName = ColumnInfo[Col2() + 1, @VM]
|
|
DataType = ColumnInfo[Col2() + 1, @VM]
|
|
MVColumnGroupName = ColumnInfo[Col2() + 1, @VM]
|
|
Value = ColumnInfo[Col2() + 1, @VM]
|
|
Convert @SVM : @TM : @STM : Char(249) to @VM : @SVM : @TM : @STM in Value
|
|
If MVColumnGroupName NE '' then
|
|
If (MVColumnGroupName NE PrevMVColumnGroupName) AND (PrevMVColumnGroupName NE '') then
|
|
NestedPropertyNames[-1, 1] = ''
|
|
NestedValueArray[-1, 1] = ''
|
|
NestedValueArray = SRP_Array('Rotate', NestedValueArray, @FM, @VM)
|
|
Convert @FM to @RM in NestedValueArray
|
|
Convert @VM to @FM in NestedValueArray
|
|
HTTP_Resource_Services('AddSubResources', ObjectHandle, PrevMVColumnGroupName, NestedPropertyNames, NestedValueArray)
|
|
NestedPropertyNames = ''
|
|
NestedValueArray = ''
|
|
end
|
|
NestedPropertyNames := PropertyName : @FM
|
|
NestedValueArray := Value : @FM
|
|
PrevMVColumnGroupName = MVColumnGroupName
|
|
end else
|
|
HTTP_Resource_Services('AddProperty', ObjectHandle, PropertyName, Value, DataType)
|
|
end
|
|
Next ColumnInfo
|
|
If PrevMVColumnGroupName NE '' then
|
|
NestedPropertyNames[-1, 1] = ''
|
|
NestedValueArray[-1, 1] = ''
|
|
NestedValueArray = SRP_Array('Rotate', NestedValueArray, @FM, @VM)
|
|
Convert @FM to @RM in NestedValueArray
|
|
Convert @VM to @FM in NestedValueArray
|
|
HTTP_Resource_Services('AddSubResources', ObjectHandle, PrevMVColumnGroupName, NestedPropertyNames, NestedValueArray)
|
|
end
|
|
If BaseSelfRelURL NE '' then
|
|
If KeyParts NE '' then
|
|
NewKeyID = ''
|
|
For Each KeyPart in KeyParts using @FM
|
|
NewKeyID := Field(KeyID, '*', KeyPart, 1) : KeyDelimiter
|
|
Next KeyPart
|
|
NewKeyID[-1, 1] = ''
|
|
Transfer NewKeyID to KeyID
|
|
end else
|
|
Convert '*' to KeyDelimiter in KeyID
|
|
end
|
|
SelfRelURL = BaseSelfRelURL : '/' : KeyID
|
|
HTTP_Resource_Services('AddLinkRelations', ObjectHandle, 'self', SelfRelURL)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error creating resource object in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
If SRP_JSON(ObjectHandle, 'New', 'Object') else
|
|
Error_Services('Add', 'Error creating resource object in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetObjects
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Optional]
|
|
// Filter - Information that informs the service of how the database should be filtered. There are four
|
|
// options: 1. Empty string, which is the default, meaning all database rows should be selected,
|
|
// 2. A "SELECT" query that will be used in an RList() statement, 3. The name of a saved list of
|
|
// Key IDs, or 4. An @FM delimited list of Key IDs. - [Optional]
|
|
// ColumnNames - A delimited list of column names to build into the resource. Default will be all legitimate
|
|
// columns which are returned by the GetColumnNames service. - [Optional]
|
|
// PropertyNames - A delimited list of property names to use for each column. Default will be the column name.
|
|
// - [Optional]
|
|
// DataTypes - A delimited list of JSON data types (e.g., string, number, Boolean, etc.). Default will be
|
|
// string. - [Optional]
|
|
// MVGroupNames - A delimited list of MV group names that are to be matched with the delimited list of nested
|
|
// group names. - [Optional]
|
|
// NestedGroupNames - A delimited list of nested group names to use for each MV group name. This is only used if
|
|
// a list of MV group names is also provided. - [Optional]
|
|
// BaseSelfRelURL - The base URL to use for creating a self link relationship. If empty then no self link
|
|
// relationship will be created. Otherwise, the Key ID will be appended as a segment to this
|
|
// URL and added using the AddLinkRelations service. - [Optional]
|
|
// KeyParts - A delimited list of Key ID parts that should be used to created the self link relationship.
|
|
// Default is all Key ID parts will be used. - [Optional]
|
|
// KeyDelimiter - The character to use as the delimiter for multi-park Key IDs when used in the self link
|
|
// relationship. Default is '*', although this is not a URL friendly character. - [Optional]
|
|
// AllowKeyProperty - A Boolean flag indicating that the resource object be allowed to represent the Key ID of the
|
|
// database row to exists. This ultimately informs the GetColumnNames service to avoid stripping
|
|
// out the Key ID column. - [Optional]
|
|
//
|
|
// Creates one or more new resource objects. The database table, Key ID, and list of column mames are provided, these
|
|
// will be used to create properties within the resource object.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetObjects(TableName, Filter, ColumnNames, PropertyNames, DataTypes, MVGroupNames, NestedGroupNames, BaseSelfRelURL, KeyParts, KeyDelimiter, AllowKeyProperty)
|
|
|
|
ObjectHandles = ''
|
|
|
|
If TableName NE '' then
|
|
hTable = Database_Services('GetTableHandle', TableName)
|
|
If Error_Services('NoError') then
|
|
Begin Case
|
|
Case Filter EQ ''
|
|
Filter = Xlate(TableName, '%RECORDS%', '', 'X')
|
|
If Len(Filter) then
|
|
rv = Set_Status(0)
|
|
Make.List(0, Filter, '', '')
|
|
end else
|
|
Filter = 'SELECT ' : TableName : ' BY @ID'
|
|
rv = Set_Status(0)
|
|
RList(Filter, 5, '', '', '')
|
|
end
|
|
|
|
Case Filter[1, 7] EQ 'SELECT '
|
|
rv = Set_Status(0)
|
|
RList(Filter, 5, '', '', '')
|
|
|
|
Case Count(Filter, @FM)
|
|
rv = Set_Status(0)
|
|
Make.List(0, Filter, '', '')
|
|
|
|
Case Otherwise$
|
|
// First, test to see if this is a valid saved list. If not, assume it is a single Key ID.
|
|
If Num(Filter) then
|
|
SaveList = Quote(Filter)
|
|
end else
|
|
SaveList = Filter
|
|
end
|
|
Activate_Save_Select(SaveList)
|
|
If Get_Status() then
|
|
rv = Set_Status(0)
|
|
Make.List(0, Filter, '', '')
|
|
end
|
|
End Case
|
|
EOF = False$
|
|
Loop
|
|
Readnext KeyID else EOF = True$
|
|
Until EOF EQ True$
|
|
ObjectHandle = HTTP_Resource_Services('GetObject', TableName, KeyID, ColumnNames, PropertyNames, DataTypes, MVGroupNames, NestedGroupNames, BaseSelfRelURL, KeyParts, KeyDelimiter, AllowKeyProperty)
|
|
If Error_Services('NoError') then
|
|
ObjectHandles := ObjectHandle : @FM
|
|
end else
|
|
Error_Services('Add', 'Error creating resource object in the ' : Service : ' service.')
|
|
end
|
|
While Error_Services('NoError')
|
|
Repeat
|
|
ObjectHandles[-1, 1] = ''
|
|
end
|
|
end else
|
|
ObjectHandles = HTTP_Resource_Services('GetObject')
|
|
end
|
|
|
|
Response = ObjectHandles
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ParseResource
|
|
//
|
|
// SerializedResource - Name of the database table where the resource (database row) is stored. - [Optional]
|
|
//
|
|
// Creates a new resource object. If a database table, Key ID, and list of column mames are provided, these will be used
|
|
// to create properties within the resource object.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ParseResource(SerializedResource)
|
|
|
|
ObjectHandle = ''
|
|
|
|
If SerializedResource NE '' then
|
|
If SRP_JSON(ObjectHandle, 'Parse', SerializedResource) EQ '' else
|
|
Error_Services('Add', 'Error parsing the serialized resource in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'SerializedResource argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddProperty
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the GetObject
|
|
// service. - [Optional]
|
|
// PropertyName - The property name being added. - [Required]
|
|
// PropertyValue - The property value being added. Default will be an empty string. - [Optional]
|
|
// DataType - The data type for the property value. These can be the standard JSON data types (e.g., 'String',
|
|
// 'Number', or 'Boolean'). If the PropertyValue contains @VM delimiters, these will be formatted as
|
|
// a JSON Array. There are a few special data types as well:
|
|
// - 'StringList' - Same as 'String', but will force the values to be stored in a JSON Array even
|
|
// if there are no @VM delimiters in the value.
|
|
// - 'NumberList' - Same as 'Number', but will force the values to be stored in a JSON Array even
|
|
// if there are no @VM delimiters in the value.
|
|
// - 'BooleanList' - Same as 'Boolean', but will force the values to be stored in a JSON Array even
|
|
// if there are no @VM delimiters in the value.
|
|
// - 'ObjectHandle' - A JSON object handle. This is expected to be a sub-property or a sub-resource
|
|
// object. This object handle will be released automatically.
|
|
// If DataType is empty, the default will be 'String'. - [Optional]
|
|
//
|
|
// Adds a property to the indicated resource object along with any value provided.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddProperty(ObjectHandle, PropertyName, PropertyValue, DataType=ALLDATATYPES)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (PropertyName NE '') then
|
|
If DataType EQ '' then DataType = 'String'
|
|
If DataType _EQC 'ObjectHandle' then
|
|
If Num(PropertyValue) then
|
|
Transfer PropertyValue to objProperty
|
|
SRP_JSON(ObjectHandle, 'Set', PropertyName, objProperty)
|
|
SRP_JSON(objProperty, 'Release')
|
|
end else
|
|
Error_Services('Add', 'The PropertyValue argument from the ' : Service : ' service does not contain a valid object handle.')
|
|
end
|
|
end else
|
|
IsList = (Count(PropertyValue, @VM) GE 1) OR (DataType[-4, 4] _EQC 'List')
|
|
If IsList EQ True$ then
|
|
If SRP_JSON(arrayItem, 'New', 'Array') then
|
|
For Each ItemValue in PropertyValue using @VM
|
|
SRP_JSON(arrayItem, 'AddValue', ItemValue, DataType)
|
|
Next ItemValue
|
|
SRP_JSON(ObjectHandle, 'Set', PropertyName, arrayItem)
|
|
SRP_JSON(arrayItem, 'Release')
|
|
end
|
|
end else
|
|
SRP_JSON(ObjectHandle, 'SetValue', PropertyName, PropertyValue, DataType)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle or PropertyName argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddProperties
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the GetObject
|
|
// service. - [Optional]
|
|
// PropertyNames - An @FM delimited list of one or more property names. - [Required]
|
|
// PropertyValues - An @FM delimited list of property values being added. Default will be an empty string.
|
|
// - [Optional]
|
|
// DataTypes - An @FM delimited list of data types for the property values. See the DataType description for the
|
|
// AddProperty service. - [Optional]
|
|
//
|
|
// Adds one or more properties in the indicated resource object. This is a wrapper for the AddProperty service to make
|
|
// it easier to add multple properties to a resource object in one call.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddProperties(ObjectHandle, PropertyNames, PropertyValues, DataTypes)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (PropertyNames NE '') then
|
|
For Each PropertyName in PropertyNames using @FM setting fPos
|
|
PropertyValue = PropertyValues<fPos>
|
|
DataType = DataTypes<fPos>
|
|
HTTP_Resource_Services('AddProperty', ObjectHandle, PropertyName, PropertyValue, DataType)
|
|
Next PropertyName
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle or PropertyNames argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddSubProperty
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the GetObject
|
|
// service. - [Optional]
|
|
// PropertyName - The property name being added or updated. - [Required]
|
|
// SubPropertyName - The sub-property name being added. - [Required]
|
|
// SubPropertyValue - The sub-property value being added. Default will be an empty string. - [Optional]
|
|
// DataType - The data type for the sub-property value. See the DataType description for the AddProperty
|
|
// service. - [Optional]
|
|
//
|
|
// Adds a sub-property to the indicated property of the indicated resource object along with any value provided. This is
|
|
// a one-service alternative to creating a temporary resource object and property (via GetObject with AddProperty) and
|
|
// then adding this object to the primary resource as a property (via AddProperty using 'ObjectHandle' data type).
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddSubProperty(ObjectHandle, PropertyName, SubPropertyName, SubPropertyValue, DataType=ALLDATATYPES)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (PropertyName NE '') then
|
|
If DataType EQ '' then DataType = 'String'
|
|
PropertyObjectHandle = SRP_JSON(ObjectHandle, 'Get', PropertyName)
|
|
If PropertyObjectHandle EQ 0 then PropertyObjectHandle = '' ; // A 0 means the property doesn't exist in the primary resource. Make this empty so it will get created.
|
|
PropertyObjectHandle = HTTP_Resource_Services('AddProperty', PropertyObjectHandle, SubPropertyName, SubPropertyValue, DataType)
|
|
If Error_Services('NoError') then
|
|
HTTP_Resource_Services('AddProperty', ObjectHandle, PropertyName, PropertyObjectHandle, 'ObjectHandle')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, PropertyName, or SubPropertyName argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddSubProperties
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the GetObject
|
|
// service. - [Optional]
|
|
// PropertyName - The property name being added or updated. - [Required]
|
|
// SubPropertyNames - An @FM delimited list of one or more sub-property names. - [Required]
|
|
// SubPropertyValues - An @FM delimited list of sub-property values being added. Default will be an empty string.
|
|
// - [Optional]
|
|
// DataTypes - An @FM delimited list of data types for the sub-property values. See the DataType description for
|
|
// the AddProperty service. - [Optional]
|
|
//
|
|
// Adds one or more sub-properties to the indicated property of the indicated resource object. This is a wrapper for the
|
|
// AddSubProperty service to make it easier to add multple sub-properties to a property in one call.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddSubProperties(ObjectHandle, PropertyName, SubPropertyNames, SubPropertyValues, DataTypes)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (SubPropertyNames NE '') then
|
|
For Each SubPropertyName in SubPropertyNames using @FM setting fPos
|
|
SubPropertyValue = SubPropertyValues<fPos>
|
|
DataType = DataTypes<fPos>
|
|
HTTP_Resource_Services('AddSubProperty', ObjectHandle, PropertyName, SubPropertyName, SubPropertyValue, DataType)
|
|
Next PropertyName
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, PropertyName, or SubPropertyNames argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddSubResourceObject
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the GetObject
|
|
// service. - [Optional]
|
|
// PropertyName - The property name being added or updated. - [Required]
|
|
// SubResourceObjectHandle - The object handle to a sub-resource being added. - [Required]
|
|
//
|
|
// Adds a sub-resource object handle to the indicated property of the indicated resource object. Any pre-existing
|
|
// sub-resources will be preserved and new ones will be appended.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddSubResourceObject(ObjectHandle, PropertyName, SubResourceObjectHandle)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (PropertyName NE '') AND (SubResourceObjectHandle NE '') then
|
|
PropertyArrayHandle = SRP_JSON(ObjectHandle, 'Get', PropertyName)
|
|
If PropertyArrayHandle EQ 0 then SRP_JSON(PropertyArrayHandle, 'New', 'Array')
|
|
If PropertyArrayHandle GT 0 then
|
|
SRP_JSON(PropertyArrayHandle, 'Add', SubResourceObjectHandle)
|
|
SRP_JSON(SubResourceObjectHandle, 'Release')
|
|
SRP_JSON(ObjectHandle, 'Set', PropertyName, PropertyArrayHandle)
|
|
SRP_JSON(PropertyArrayHandle, 'Release')
|
|
end else
|
|
Error_Services('Add', 'Error creating property array handle in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, PropertyName, or SubResourceObjectHandle argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddSubResourceObjects
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the GetObject
|
|
// service. - [Optional]
|
|
// PropertyName - The property name being added or updated. - [Required]
|
|
// SubResourceObjectHandles - An @FM delimited list of sub-resource object handles being added. - [Required]
|
|
//
|
|
// Adds one or more sub-resource object handles to the indicated property of the indicated resource object. This is a
|
|
// wrapper for the AddSubResourceObject service to make it easier to add multple sub-resource object handles to a
|
|
// property in one call.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddSubResourceObjects(ObjectHandle, PropertyName, SubResourceObjectHandles)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (PropertyName NE '') AND (SubResourceObjectHandles NE '') then
|
|
For Each SubResourceObjectHandle in SubResourceObjectHandles using @FM
|
|
HTTP_Resource_Services('AddSubResourceObject', ObjectHandle, PropertyName, SubResourceObjectHandle)
|
|
Next SubResourceObjectHandle
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, PropertyName, or SubResourceObjectHandles argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddSubResource
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the GetObject
|
|
// service. - [Optional]
|
|
// PropertyName - The property name being added or updated. - [Required]
|
|
// SubResourcePropertyNames - An @FM delimited list of one or more sub-resource property names being added.
|
|
// - [Required]
|
|
// SubResourcePropertyValues - An @FM delimited list of one or more sub-resource property values being added. Default
|
|
// will be an empty string. - [Optional]
|
|
// DataTypes - An @FM delimited list of data types for the sub-resource property values. See the
|
|
// DataType description for the AddProperty service. - [Optional]
|
|
//
|
|
// Adds a sub-resource to the indicated property of the indicated resource object. A sub-resource object handle is
|
|
// created using the sub-resource property names and values and then added. Any pre-existing sub-resources will be
|
|
// preserved and new ones will be appended.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddSubResource(ObjectHandle, PropertyName, SubResourcePropertyNames, SubResourcePropertyValues, DataTypes)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (PropertyName NE '') AND (SubResourcePropertyNames NE '') then
|
|
PropertyArrayHandle = SRP_JSON(ObjectHandle, 'Get', PropertyName)
|
|
If PropertyArrayHandle EQ 0 then SRP_JSON(PropertyArrayHandle, 'New', 'Array')
|
|
If PropertyArrayHandle GT 0 then
|
|
SubResourceObjectHandle = HTTP_Resource_Services('GetObject')
|
|
If SubResourceObjectHandle GT 0 then
|
|
For Each SubResourcePropertyName in SubResourcePropertyNames using @FM setting fPos
|
|
SubResourcePropertyValue = SubResourcePropertyValues<fPos>
|
|
DataType = DataTypes<fPos>
|
|
HTTP_Resource_Services('AddProperty', SubResourceObjectHandle, SubResourcePropertyName, SubResourcePropertyValue, DataType)
|
|
Next PropertyName
|
|
HTTP_Resource_Services('AddSubResourceObject', ObjectHandle, PropertyName, SubResourceObjectHandle)
|
|
end else
|
|
Error_Services('Add', 'Error creating sub-resource object handle in the ' : Service : ' service.')
|
|
end
|
|
SRP_JSON(PropertyArrayHandle, 'Release')
|
|
end else
|
|
Error_Services('Add', 'Error creating property array handle in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, PropertyName, or SubResourcePropertyNames argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddSubResources
|
|
//
|
|
// ObjectHandle - Object handle to the resource. If empty, a new one will be created using the
|
|
// GetObject service. - [Optional]
|
|
// PropertyName - The property name being added or updated. - [Required]
|
|
// SubResourcePropertyNames - An @FM delimited list of one or more sub-resource property names being added.
|
|
// - [Required]
|
|
// SubResourcePropertyValuesArray - An @RM/@FM delimited array of one or more sub-resource property values being
|
|
// added. Each @FM list will be treated as a sub-resource. Default will be an empty
|
|
// string. - [Optional]
|
|
// DataTypes - An @FM delimited list of data types for the sub-resource property values. See the
|
|
// DataType description for the AddProperty service. - [Optional]
|
|
//
|
|
// Adds one or more sub-resources to the indicated property of the indicated resource object. This is a wrapper for the
|
|
// AddSubResource service to make it easier to add multple sub-resources to a property in one call.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddSubResources(ObjectHandle, PropertyName, SubResourcePropertyNames, SubResourcePropertyValuesArray, DataTypes)
|
|
|
|
If ObjectHandle EQ '' then ObjectHandle = HTTP_Resource_Services('GetObject')
|
|
|
|
If (ObjectHandle NE '') AND (PropertyName NE '') AND (SubResourcePropertyNames NE '') then
|
|
For Each SubResourcePropertyValues in SubResourcePropertyValuesArray using @RM
|
|
HTTP_Resource_Services('AddSubResource', ObjectHandle, PropertyName, SubResourcePropertyNames, SubResourcePropertyValues, DataTypes)
|
|
Next SubResourcePropertyValues
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, PropertyName, or SubResourcePropertyNames argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ObjectHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddLinkRelation
|
|
//
|
|
// ObjectHandle - Object handle to the resource. - [Required]
|
|
// Relation - The relation being added. - [Required]
|
|
// URL - The URL associated to the indicated relation. - [Required]
|
|
// Title - The friendly title for the indicated relation. - [Optional]
|
|
// IsTemplate - Boolean flag to indicated if the link is URI Templated. - [Optional]
|
|
// IgnoreResourceID - Boolean flag to determine if a resource ID segment should be left untemplated. - [Optional]
|
|
// IgnoreProperty - Boolean flag to determine if a property segment should be left untemplated. - [Optional]
|
|
// IgnoreQueryParam - Boolean flag to determine if existing query params should be left untemplated. - [Optional]
|
|
//
|
|
// Adds a _links relation to the indicated resource object. This is part of the HAL specification.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddLinkRelation(ObjectHandle, Relation, URL, Title, IsTemplate=BOOLEAN, IgnoreResourceID=BOOLEAN, IgnoreProperty=BOOLEAN, IgnoreQueryParam=BOOLEAN)
|
|
|
|
If (ObjectHandle NE '') AND (Relation NE '') AND (URL NE '') then
|
|
objRel = HTTP_Resource_Services('GetObject')
|
|
If Error_Services('NoError') then
|
|
If IsTemplate EQ True$ then
|
|
URL = HTTP_Resource_Services('GetURLTemplate', URL, IgnoreResourceID, IgnoreProperty, IgnoreQueryParam)
|
|
If Index(URL, '{', 1) then
|
|
// The GetURLTemplate service was able to convert the URL to a URL Template.
|
|
HTTP_Resource_Services('AddProperty', objRel, 'templated', True$, 'Boolean')
|
|
end
|
|
end
|
|
HTTP_Resource_Services('AddProperty', objRel, 'href', URL)
|
|
If Title NE '' then
|
|
HTTP_Resource_Services('AddProperty', objRel, 'title', Title)
|
|
end
|
|
HTTP_Resource_Services('AddSubProperty', ObjectHandle, '_links', Relation, objRel, 'ObjectHandle')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, Relation, or URL argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddLinkRelations
|
|
//
|
|
// ObjectHandle - Object handle to the resource. - [Required]
|
|
// Relations - A delimited list of relations being added. - [Required]
|
|
// URLs - A delimited list of URLs for each indicated relation. - [Required]
|
|
// Titles - A delimited list of display friendly titles for each indicated relation. - [Optional]
|
|
// IsTemplates - A delimited list of Boolean flags to indicated if the link is URI Templated. - [Optional]
|
|
// IgnoreResourceIDs - Boolean flag to determine if a resource ID segment should be left untemplated. - [Optional]
|
|
// IgnoreProperties - Boolean flag to determine if a property segment should be left untemplated. - [Optional]
|
|
// IgnoreQueryParams - Boolean flag to determine if existing query params should be left untemplated. - [Optional]
|
|
//
|
|
// Adds one or more _links relations in the indicated resource object. This is part of the HAL specification.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddLinkRelations(ObjectHandle, Relations, URLs, Titles, IsTemplates, IgnoreResourceIDs=BOOLEAN, IgnoreProperties=BOOLEAN, IgnoreQueryParams=BOOLEAN)
|
|
|
|
If (ObjectHandle NE '') AND (Relations NE '') AND (URLs NE '') then
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in Relations
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in URLs
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in Titles
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in IsTemplates
|
|
For Each Relation in Relations using @FM setting fPos
|
|
URL = URLs<fPos>
|
|
Title = Titles<fPos>
|
|
IsTemplate = IsTemplates<fPos>
|
|
IgnoreResourceID = IgnoreResourceIDs<fPos>
|
|
IgnoreProperty = IgnoreProperties<fPos>
|
|
IgnoreQueryParam = IgnoreQueryParams<fPos>
|
|
HTTP_Resource_Services('AddLinkRelation', ObjectHandle, Relation, URL, Title, IsTemplate, IgnoreResourceID, IgnoreProperty, IgnoreQueryParam)
|
|
Next Name
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, Relations, or URLs argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddEmbeddedResources
|
|
//
|
|
// ObjectHandle - Object handle to the resource. - [Required]
|
|
// EmbeddedResourceName - Name of the embedded resource. - [Required]
|
|
// EmbeddedResourceObjectHandles - An @FM delimited list of resource object handles being added. Each embedded
|
|
// resource object handle will be released automatically. - [Required]
|
|
// Singular - A Boolean flag indicating that the embedded resource object should be represented
|
|
// as singular (i.e., unique) rather than as multiple. A singular embedded resource
|
|
// object will be displayed using sub-properties but multiple embedded resources
|
|
// (even if there is just one) will be displayed using sub-resources. The default
|
|
// value is False. - [Optional]
|
|
//
|
|
// Adds one or more embedded resources in the indicated resource object. Embedded resources are stand-alone resources
|
|
// on their own (and usually have their own endpoint), but are included, at least partially, in another resource due to
|
|
// the close relationship between them. This is often done to eliminate multiple requests to pull in all significant
|
|
// resource data. This is part of the HAL specification.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddEmbeddedResources(ObjectHandle, EmbeddedResourceName, EmbeddedResourceObjectHandles, Singular)
|
|
|
|
If (ObjectHandle NE '') AND (EmbeddedResourceName NE '') AND (EmbeddedResourceObjectHandles NE '') then
|
|
If Singular NE True$ then Singular = False$
|
|
If Singular EQ True$ then
|
|
objEmbedded = HTTP_Resource_Services('AddSubProperty', '', EmbeddedResourceName, '', EmbeddedResourceObjectHandles, 'ObjectHandle')
|
|
end else
|
|
objEmbedded = SRP_JSON(ObjectHandle, 'Get', '_embedded')
|
|
If objEmbedded EQ 0 then objEmbedded = ''
|
|
objEmbedded = HTTP_Resource_Services('AddSubResourceObjects', objEmbedded, EmbeddedResourceName, EmbeddedResourceObjectHandles)
|
|
end
|
|
If Error_Services('NoError') then
|
|
HTTP_Resource_Services('AddProperty', ObjectHandle, '_embedded', objEmbedded, 'ObjectHandle')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, EmbeddedResourceName, or EmbeddedResourceObjectHandles argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddFormAction
|
|
//
|
|
// ObjectHandle - Object handle to the resource. - [Required]
|
|
// Name - Name associated with this form action. - [Required]
|
|
// Method - HTTP method used by this form action. - [Required]
|
|
// URL - URL used as the target by this form action. - [Required]
|
|
// Title - Display friendly title for this form action. Default is the Name argument. - [Optional]
|
|
// Fields - A delimited list of fields that need to be submitted with this action. Fields can be interpreted
|
|
// in any way the API chooses, but typically they map to a property of the current resource.
|
|
// - [Optional]
|
|
// FieldProperties - A structured @FM/@VM delimited array of property values associated with the fields. - [Optional]
|
|
// For each field, the value structure follows this pattern:
|
|
// <x, 1> = Default value. The value that should be passed in unless otherwise overridden by the
|
|
// client.
|
|
// <x, 2> = Required flag. A Boolean flag indicating if this property requires a value to be
|
|
// passed in.
|
|
// <x, 3> = Visible flag. A Boolean flag indicating if this property should be visible to the
|
|
// client. A non-visible field simply means that the default value will automatically
|
|
// be passed in.
|
|
//
|
|
// Adds one _forms action in the indicated resource object. Due to the complexity and depth of a single action, this
|
|
// service will only add one action at a time. Additional actions can be added by calling this service multiple times.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddFormAction(ObjectHandle, Name, Method=METHODS, URL, Title, Fields, FieldProperties)
|
|
|
|
If (ObjectHandle NE '') AND (Name NE '') AND (Method NE '') AND (URL NE '') then
|
|
If Title EQ '' then Title = Name
|
|
Convert @VM : @SVM : @TM : @STM : ',' to @FM : @FM : @FM : @FM : @FM in Fields
|
|
objAction = HTTP_Resource_Services('GetObject')
|
|
If Error_Services('NoError') then
|
|
HTTP_Resource_Services('AddProperty', objAction, 'method', Method)
|
|
HTTP_Resource_Services('AddProperty', objAction, 'action', URL)
|
|
HTTP_Resource_Services('AddProperty', objAction, 'title', Title)
|
|
objFields = HTTP_Resource_Services('GetObject')
|
|
If Error_Services('NoError') then
|
|
If Fields NE '' then
|
|
For Each Field in Fields using @FM setting fPos
|
|
HTTP_Resource_Services('AddSubProperty', objFields, Field, 'default', FieldProperties<fPos, 1>, 'String')
|
|
HTTP_Resource_Services('AddSubProperty', objFields, Field, 'required', FieldProperties<fPos, 2>, 'Boolean')
|
|
HTTP_Resource_Services('AddSubProperty', objFields, Field, 'visible', FieldProperties<fPos, 3>, 'Boolean')
|
|
Next Field
|
|
HTTP_Resource_Services('AddProperty', objAction, 'fields', objFields, 'ObjectHandle')
|
|
end
|
|
end
|
|
HTTP_Resource_Services('AddSubProperty', ObjectHandle, '_forms', Name, objAction, 'ObjectHandle')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle, Name, Method, or URL argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSerializedResource
|
|
//
|
|
// ObjectHandle - Object handle to the resource needing to be serialized. - [Required]
|
|
// Style - Formatting style for the final output (Fast, DropNulls, or Styled). Default = Fast. - [Optional]
|
|
//
|
|
// Serializes the indicated resource object and then releases the object.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSerializedResource(ObjectHandle, Style=STYLES)
|
|
|
|
SerializedResource = ''
|
|
|
|
If ObjectHandle NE '' then
|
|
If Style EQ '' then Style = 'Fast'
|
|
SerializedResource = SRP_JSON(ObjectHandle, 'Stringify', Style)
|
|
SRP_JSON(ObjectHandle, 'Release')
|
|
end else
|
|
Error_Services('Add', 'ObjectHandle argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = SerializedResource
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// LoremIpsum
|
|
//
|
|
// Creates a dummy HTTP response using lorem ipsum content. This is a placeholder service used when new endpoint
|
|
// handlers are automatically created. It is expected to be replaced by the actual API logic.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service LoremIpsum()
|
|
|
|
ObjectHandle = HTTP_Resource_Services('GetObjects')
|
|
LoremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. '
|
|
LoremIpsum := 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure '
|
|
LoremIpsum := 'dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non '
|
|
LoremIpsum := 'proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
|
HTTP_Resource_Services('AddProperty', ObjectHandle, 'content', LoremIpsum, 'String')
|
|
FullEndPointURL = HTTP_Services('GetFullEndPointURL')
|
|
APIURL = HTTP_Services('GetAPIRootURL', True$)
|
|
HTTP_Resource_Services('AddLinkRelations', ObjectHandle, 'self' : @FM : 'apiEntryPoint', FullEndpointURL : @FM : APIURL)
|
|
jsonResource = HTTP_Resource_Services('GetSerializedResource', ObjectHandle, 'Fast')
|
|
HTTP_Services('SetResponseBody', jsonResource, False$, 'application/hal+json')
|
|
HTTP_Services('SetResponseStatus', 200)
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetURLTemplate
|
|
//
|
|
// URL - The URL that needs to be transformed into a URL template. - [Required]
|
|
// IgnoreResourceID - Boolean flag to determine if a resource ID segment should be left untemplated. - [Optional]
|
|
// IgnoreProperty - Boolean flag to determine if a property segment should be left untemplated. - [Optional]
|
|
// IgnoreQueryParam - Boolean flag to determine if existing query params should be left untemplated. - [Optional]
|
|
//
|
|
// Returns the indicated URL as a URL Template.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetURLTemplate(URL, IgnoreResourceID=BOOLEAN, IgnoreProperty=BOOLEAN, IgnoreQueryParam=BOOLEAN)
|
|
|
|
URLTemplate = ''
|
|
|
|
If IgnoreResourceID NE True$ then IgnoreResourceID = False$
|
|
If IgnoreProperty NE True$ then IgnoreProperty = False$
|
|
If IgnoreQueryParam NE True$ then IgnoreQueryParam = False$
|
|
|
|
If URL NE '' then
|
|
If Index(URL, '{', 1) then
|
|
// The URL already appears to be formatted as a template. Return the URL that was passed in.
|
|
URLTemplate = URL
|
|
end else
|
|
// The URL is not formatted as a template. Match it to a resource and create a templated URL.
|
|
If URL[1, 1] EQ '/' then
|
|
// URL is formatted as a relative URL. Start the URL Template as empty.
|
|
URLTemplate = ''
|
|
URL[1, 1] = ''
|
|
end else
|
|
// Assume the URL is absolute. Start the URL Template with the base URL.
|
|
URLTemplate = Field(URL, '/', 1, 3)
|
|
URL = Field(URL, '/', 4, 999)
|
|
end
|
|
If Index(URL, '?', 1) then
|
|
// URL contains query params. Remove these for now and add them back only if IgnoreQueryParams is set
|
|
// to True$. This way the original query params will be included in the URL Template.
|
|
OrigQueryParams = Field(URL, '?', 2, 1)
|
|
URL[-1, Neg(Len(OrigQueryParams) + 1)] = ''
|
|
end else
|
|
OrigQueryParams = ''
|
|
end
|
|
For Each Segment in URL using '/'
|
|
URLTemplate := '/' : Segment
|
|
ResourceClass = HTTP_Resource_Manager_Services('GetResourceProperty', URLTemplate, 'CLASS')
|
|
Begin Case
|
|
Case ResourceClass _EQC 'RESOURCE_ID'
|
|
If IgnoreResourceID NE True$ then
|
|
// This segment is a Resource ID. Get the NAME property for this URL and use this for the
|
|
// segment instead.
|
|
ResourceID = HTTP_Resource_Manager_Services('GetResourceProperty', URLTemplate, 'NAME')
|
|
URLTemplate[-1, Neg(Len(Segment))] = ResourceID
|
|
end
|
|
Case ResourceClass _EQC 'PROPERTY'
|
|
If IgnoreProperty NE True$ then
|
|
// This segment is a Property. Use {propertyName} for the segment instead.
|
|
URLTemplate[-1, Neg(Len(Segment))] = '{propertyName}'
|
|
end
|
|
End Case
|
|
Next Segment
|
|
If IgnoreQueryParam NE True$ then
|
|
// Now the the URLTemplate is complete. Check to see if there are query params supported by this endpoint.
|
|
QueryParams = HTTP_Resource_Manager_Services('GetResourceProperty', URLTemplate, 'QUERY_PARAMS')
|
|
If QueryParams NE '' then
|
|
QueryParams = '{?' : QueryParams : '}'
|
|
URLTemplate := QueryParams
|
|
end
|
|
end else
|
|
If OrigQueryParams NE '' then
|
|
URLTemplate := '?' : OrigQueryParams
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'URL argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = URLTemplate
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetDatabaseItem
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// SelfURL - The URL that identifies the item resource. - [Optional]
|
|
// KeyID - Key ID for the database row. - [Required]
|
|
// ColumnNames - An @FM delimited list of column names to build into the resource. If empty then all documented
|
|
// column names from the %FIELDS% dictionary record will be used. - [Optional]
|
|
// ItemArrayLabel - Label text to use when naming the primary HAL item array. If empty then "item" will be
|
|
// used. - [Optional]
|
|
//
|
|
// Gets the resource item from a database table. The format will be HAL+JSON.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetDatabaseItem(TableName, SelfURL, KeyID, ColumnNames, ItemArrayLabel)
|
|
|
|
BaseSelfURL = Field(SelfURL, '/', 1, Count(SelfURL, '/'))
|
|
ObjectHandle = HTTP_Resource_Services('GetObjects', TableName, KeyID, ColumnNames, ItemArrayLabel)
|
|
If SelfURL NE '' then
|
|
HTTP_Resource_Services('AddLinkRelations', ObjectHandle, 'self' : @FM : 'collection', SelfURL : @FM : BaseSelfURL)
|
|
end
|
|
HAL = HTTP_Resource_Services('GetSerializedResource', ObjectHandle)
|
|
HTTP_Services('SetResponseBody', HAL, False$, 'application/hal+json')
|
|
|
|
// Use the Response variable for internal monitoring, tracking, and debugging. This does not affect the HTTP
|
|
// response.
|
|
Response = HAL
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetDatabaseItems
|
|
//
|
|
// Filter - Information that informs the service of how the database should be filtered. There are four
|
|
// options: 1. Empty, if all database rows should be selected, 2. A "SELECT" query that will be used
|
|
// in an RList() statement, 3. The name of a saved list of Key IDs, or 4. An @FM delimited list of
|
|
// Key IDs. - [Optional]
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// SelfURL - The URL that identifies the collection resource. - [Optional]
|
|
// ColumnNames - An @FM delimited list of column names to build into the resource. If empty then all documented
|
|
// column names from the %FIELDS% dictionary record will be used. - [Optional]
|
|
// ItemArrayLabel - Label text to use when naming the primary HAL item array. If empty then "item" will be
|
|
// used. - (Optional)
|
|
//
|
|
// Gets the resource items from a database table. The format will be HAL+JSON.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetDatabaseItems(Filter, TableName, SelfURL, ColumnNames, ItemArrayLabel)
|
|
|
|
HAL = ''
|
|
|
|
ObjectHandle = HTTP_Resource_Services('GetObjects')
|
|
If Error_Services('NoError') then
|
|
objItems = HTTP_Resource_Services('GetObjects', TableName, Filter, ColumnNames, ItemArrayLabel, '', '', '', SelfURL)
|
|
HTTP_Resource_Services('AddEmbeddedResources', ObjectHandle, 'item', objItems)
|
|
If SelfURL NE '' then
|
|
// Add _links sub-properties for HAL implementation.
|
|
HTTP_Resource_Services('AddLinkRelations', ObjectHandle, 'self', SelfURL)
|
|
end
|
|
If Error_Services('NoError') then
|
|
// Serialize the object into a JSON string.
|
|
HAL = HTTP_Resource_Services('GetSerializedResource', ObjectHandle)
|
|
// Set the response body with the JSON string and set the Content-Type response header.
|
|
HTTP_Services('SetResponseBody', HAL, False$, 'application/hal+json')
|
|
end
|
|
end
|
|
|
|
Response = HAL
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// DeleteDatabaseItem
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// KeyID - Key ID for the database row. - [Required]
|
|
//
|
|
// Deletes the resource item from a database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service DeleteDatabaseItem(TableName, KeyID)
|
|
|
|
If (TableName NE '') AND (KeyID NE '') then
|
|
Open TableName to hTable then
|
|
Lock hTable, KeyID then
|
|
Read DataRow from hTable, KeyID then
|
|
Delete hTable, KeyID then
|
|
HTTP_Services('SetResponseStatus', 204)
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 500, 'Error deleting ' : KeyID : ' from the ' : TableName : ' table.')
|
|
end
|
|
end else
|
|
HTTP_Services('SetResponseStatus', 204)
|
|
end
|
|
Unlock hTable, KeyID else Null
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 423, KeyID : ' is currently locked.')
|
|
end
|
|
end else
|
|
// Unable to open the database table. Set a 500 status code.
|
|
HTTP_Services('SetResponseError', '', '', 500, 'Unable to open the ' : TableName : ' table.')
|
|
end
|
|
end else
|
|
// Required arguments are missing so just set a 404 status code.
|
|
HTTP_Services('SetResponseError', '', '', 404, 'Required arguments are missing.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PutDatabaseItem
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// SelfURL - The URL that identifies the resource. - [Required]
|
|
// KeyID - Key ID for the database row. - [Required]
|
|
// AllowedColumnNames - An @FM delimited list of column names that are permitted in this service. - [Optional]
|
|
//
|
|
// Creates a new or updates a specific resource item in the database table. To conform to the requirements of the PUT
|
|
// method, the entire resource will be replaced with the content being passed in, even if only a few properties (e.g.,
|
|
// database columns) are being updated. Thus, it is assumed that both changed and unchanged properties will be passed
|
|
// into the request body.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PutDatabaseItem(TableName, SelfURL, KeyID, AllowedColumnNames)
|
|
|
|
If (TableName NE '') AND (SelfURL NE '') AND (KeyID NE '') then
|
|
Open TableName to hTable then
|
|
HaveKeyID = False$ ; // Assume false for now.
|
|
|
|
Lock hTable, KeyID then
|
|
ItemURL = SelfURL
|
|
Read OrigDataRow from hTable, KeyID then
|
|
StatusCode = 200 ; // Updating an existing resource.
|
|
end else
|
|
StatusCode = 201 ; // Creating a new resource.
|
|
end
|
|
DataRow = '' ; // Clearing data row of all content. PUT assumes the entire resource is being updated.
|
|
HaveKeyID = True$
|
|
end
|
|
|
|
If HaveKeyID then
|
|
GoSub UpdateDatabaseRow
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 423, KeyID : ' is currently locked.')
|
|
end
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 500, 'Error opening the ' : TableName : ' table.')
|
|
end
|
|
end else
|
|
// Required arguments are missing so just set a 404 status code.
|
|
HTTP_Services('SetResponseError', '', '', 404, 'Required arguments are missing.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PostDatabaseItem
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// SelfURL - The URL that identifies the resource. - [Required]
|
|
// AllowedColumnNames - An @FM delimited list of column names that are permitted in this service. - [Optional]
|
|
//
|
|
// Creates a new resource item in the database table. To conform to the requirements of the POST method, the Key ID
|
|
// will be created automatically by the server and the entire resource will be created with the content being passed in.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PostDatabaseItem(TableName, SelfURL, AllowedColumnNames)
|
|
|
|
If (TableName NE '') AND (SelfURL NE '') then
|
|
Open TableName to hTable then
|
|
HaveKeyID = False$ ; // Assume false for now.
|
|
|
|
// Logic to create a new KeyID is placed here. The following code attempts to retrieve the KeyID from a
|
|
// %SK% counter. If this fails, it will attempt to retrieve it from %RECORD%. Finally, if this fails then
|
|
// KeyID will be set to a value of 1 and the code will increment this value until a new Key ID can be found.
|
|
KeyID = Xlate('DICT.' : TableName, '%SK%', 1, 'X')
|
|
If KeyID EQ '' then
|
|
KeyIDs = Xlate(TableName, '%RECORDS%', '', 'X')
|
|
KeyID = KeyIDs[-1, 'B' : @FM]
|
|
KeyID += 1
|
|
end
|
|
If KeyID EQ '' then KeyID = 1
|
|
Loop
|
|
Lock hTable, KeyID then
|
|
Read DataRow from hTable, KeyID then
|
|
Unlock hTable, KeyID else Null
|
|
end else
|
|
OrigDataRow = ''
|
|
DataRow = ''
|
|
ItemURL = SelfURL : '/' : KeyID
|
|
StatusCode = 201 ; // Creating a new resource.
|
|
HaveKeyID = True$
|
|
end
|
|
end
|
|
Until HaveKeyID
|
|
KeyID += 1
|
|
Repeat
|
|
|
|
If HaveKeyID then
|
|
GoSub UpdateDatabaseRow
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 400, 'Error attempting to POST the resource to the ' : TableName : ' table.')
|
|
end
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 500, 'Error opening the ' : TableName : ' table.')
|
|
end
|
|
end else
|
|
// Required arguments are missing so just set a 404 status code.
|
|
HTTP_Services('SetResponseError', '', '', 404, 'Required arguments are missing.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// PatchDatabaseItem
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// SelfURL - The URL that identifies the resource. - [Required]
|
|
// KeyID - Key ID for the database row. - [Required]
|
|
// AllowedColumnNames - An @FM delimited list of column names that are permitted in this service. - [Optional]
|
|
//
|
|
// Updates a specific resource item in the database table. To conform to the requirements of the PATCH method, only
|
|
// specific properties (e.g., database columns) of the resource will be updated. All properties not identified will
|
|
// be left alone.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service PatchDatabaseItem(TableName, SelfURL, KeyID, AllowedColumnNames)
|
|
|
|
If (TableName NE '') AND (SelfURL NE '') AND (KeyID NE '') then
|
|
Open TableName to hTable then
|
|
HaveKeyID = False$ ; // Assume false for now.
|
|
|
|
Lock hTable, KeyID then
|
|
ItemURL = SelfURL
|
|
Read OrigDataRow from hTable, KeyID then
|
|
DataRow = OrigDataRow
|
|
StatusCode = 200 ; // Updating an existing resource.
|
|
HaveKeyID = True$
|
|
end else
|
|
StatusCode = 404 ; // Creating a new resource, which is unsupported for the PATCH method.
|
|
end
|
|
end
|
|
|
|
If HaveKeyID then
|
|
GoSub UpdateDatabaseRow
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 423, KeyID : ' is currently locked.')
|
|
end
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 500, 'Error opening the ' : TableName : ' table.')
|
|
end
|
|
end else
|
|
// Required arguments are missing so just set a 404 status code.
|
|
HTTP_Services('SetResponseError', '', '', 404, 'Required arguments are missing.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetColumnNames
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// ColumnNames - An @FM delimited list of column names to build into the resource. If empty then all documented
|
|
// column names from the %FIELDS% dictionary record will be used. - [Optional]
|
|
// AllowKeyProperty - A Boolean flag indicating that the resource object be allowed to represent the Key ID of the
|
|
// database row to exists. This ultimately informs the GetColumnNames service to avoid stripping
|
|
// out the Key ID column. - [Optional]
|
|
//
|
|
// Gets the column names from the dictionary of the table provided that will be used to create the resource. If the
|
|
// ColumnNames argument is used then this service will validate the list as valid columns. In either case, the following
|
|
// rules will be applied:
|
|
//
|
|
// - Regardless of the case used in the ColumnNames argument it will be upper cased to match against the dictionary
|
|
// names correctly.
|
|
// - All column names bound to a Key ID of part 0 (i.e., the entire key) will be removed (unless AllowKeyProperty is
|
|
// set to True).
|
|
// - All non-master column names will be removed to avoid duplicates.
|
|
// - SRP FrameWorks audit column names (MODIFIED_BY, MODIFIED_DATE, and MODIFIED_TIME) will be removed.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetColumnNames(TableName, ColumnNames, AllowKeyProperty)
|
|
|
|
If AllowKeyProperty NE True$ then AllowKeyProperty = False$
|
|
|
|
// Make any column names passed in safe to work with.
|
|
Convert @Lower_Case to @Upper_Case in ColumnNames
|
|
Convert ',' to @FM in ColumnNames
|
|
CompareColumnNames = ColumnNames
|
|
|
|
// Create a service Key ID suitable for caching the results.
|
|
ServiceKeyID := '*' : TableName
|
|
If ColumnNames NE '' then
|
|
ServiceKeyID := '*' : SRP_Hash(ColumnNames, 'SHA-1', 'BASE32')
|
|
end
|
|
// Check to see if column names have been cached this session.
|
|
ColumnNames = Memory_Services('GetValue', ServiceKeyID, '', '', CacheName$)
|
|
|
|
If ColumnNames EQ '' then
|
|
If TableName NE '' then
|
|
Open TableName to hTable then
|
|
Open 'DICT.' : TableName to @DICT then
|
|
Read Fields from @DICT, '%FIELDS%' else Fields = ''
|
|
// Sort the fields array by descending Field Type. This puts Calculated (Type = 'S') columns before
|
|
// Data (Type = 'F') columns. All Calculated columns qualify, so the list can stop when the
|
|
// calculated columns are reached.
|
|
Fields = SRP_Array('SortRows', Fields, 'DL' : FIELDS_TYPE$)
|
|
FieldsList = SRP_Array('Rotate', Fields)
|
|
|
|
// Restore the list of required column names, if any.
|
|
Transfer CompareColumnNames to ColumnNames
|
|
|
|
// Loop through the fields starting at the bottom and remove all fields that do not qualify. These
|
|
// conditions disqualify a field:
|
|
// - Full Key IDs - This allows Key Part columns to properly represent the data and abstract the
|
|
// resource better.
|
|
// - Non-master data type columns - This prevents duplication of data.
|
|
// - Empty Column Names - This prevents special dictionary columns, like 'G' type from being
|
|
// included.
|
|
// - Cross-reference columns - These are calculated columns meant for indexing only.
|
|
FieldPtr = DCount(FieldsList, @FM)
|
|
Loop
|
|
Until FieldPtr EQ 0
|
|
Master = Fields<FIELDS_MASTER_FLAG$, FieldPtr> ; // Calculated columns have no Master flag.
|
|
Type = Fields<FIELDS_TYPE$, FieldPtr>
|
|
Conv = Fields<FIELDS_CONV$, FieldPtr>
|
|
MV = Fields<FIELDS_MVFLAG$, FieldPtr>
|
|
Pos = Fields<FIELDS_FIELD_NO$, FieldPtr>
|
|
ColumnName = Fields<FIELDS_NAME$, FieldPtr>
|
|
KeyPart = Fields<FIELDS_PART$, FieldPtr>
|
|
GroupName = Fields<FIELDS_MV_GROUPNAME$, FieldPtr>
|
|
Indexed = Fields<FIELDS_INDEX$, FieldPtr>
|
|
If (Master AND (Not(Pos EQ 0 AND KeyPart EQ 0) OR (AllowKeyProperty EQ True$))) OR ((Type _EQC 'S') AND (ColumnName[-5, 5] _NEC '_XREF')) then
|
|
// The column must be a Master column (but not a full Key ID).
|
|
If ColumnName EQ '' then
|
|
// If the column does not have a name in the FIELDS_NAME$ value (for whatever reason) then
|
|
// remove it from the array so it will be be included in the final results.
|
|
Fields = Delete(Fields, FIELDS_FIELD_NO$, FieldPtr, 0)
|
|
Fields = Delete(Fields, FIELDS_PART$, FieldPtr, 0)
|
|
FieldsList = Delete(FieldsList, FieldPtr, 0, 0)
|
|
end
|
|
end else
|
|
// The column does not meet the criteria. Remove the reference from the Fields array. Check
|
|
// to see if this field was included in the ColumnNames argument and remove from that as
|
|
// well.
|
|
Fields = Delete(Fields, FIELDS_FIELD_NO$, FieldPtr, 0)
|
|
Fields = Delete(Fields, FIELDS_PART$, FieldPtr, 0)
|
|
FieldsList = Delete(FieldsList, FieldPtr, 0, 0)
|
|
Locate ColumnName in ColumnNames using @FM setting fPos then ColumnNames = Delete(ColumnNames, fPos, 0, 0)
|
|
end
|
|
// Decrement the Field Position counter so the next item above in the array can be reviewed.
|
|
FieldPtr -= 1
|
|
Repeat
|
|
Fields = SRP_Array('Rotate', FieldsList)
|
|
FieldNames = Fields<FIELDS_NAME$>
|
|
Convert @VM to @FM in FieldNames
|
|
If Len(ColumnNames) then
|
|
// Check the supplied list of column names to make sure they are valid.
|
|
CompareColumnNames = ColumnNames
|
|
For Each ColumnName in CompareColumnNames using @FM
|
|
Locate ColumnName in FieldNames using @FM setting fPos else
|
|
Locate ColumnName in ColumnNames using @FM setting fPos then
|
|
ColumnNames = Delete(ColumnNames, fPos, 0, 0)
|
|
end
|
|
end
|
|
Next ColumnName
|
|
end else
|
|
ColumnNames = Fields<FIELDS_NAME$>
|
|
Convert @VM to @FM in ColumnNames
|
|
end
|
|
// Exclude the audit column names in case they exist.
|
|
Locate 'MODIFIED_BY' in ColumnNames using @FM setting fPos then ColumnNames = Delete(ColumnNames, fPos, 0, 0)
|
|
Locate 'MODIFIED_DATE' in ColumnNames using @FM setting fPos then ColumnNames = Delete(ColumnNames, fPos, 0, 0)
|
|
Locate 'MODIFIED_TIME' in ColumnNames using @FM setting fPos then ColumnNames = Delete(ColumnNames, fPos, 0, 0)
|
|
If Len(ServiceKeyID) then
|
|
Memory_Services('SetValue', ServiceKeyID, ColumnNames, CacheName$)
|
|
end
|
|
end else
|
|
// Unable to open the dictionary table. Set a 500 status code.
|
|
ErrorMessage = 'Unable to open the ' : TableName : ' dictionary table.'
|
|
HTTP_Services('SetResponseError', '', '', 500, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end else
|
|
// Unable to open the database table. Set a 500 status code.
|
|
ErrorMessage = 'Unable to open the ' : TableName : ' table.'
|
|
HTTP_Services('SetResponseError', '', '', 500, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end else
|
|
// Required arguments are missing so just set a 404 status code.
|
|
ErrorMessage = 'Required arguments are missing.'
|
|
HTTP_Services('SetResponseError', '', '', 404, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end
|
|
|
|
Response = ColumnNames
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetColumnValues
|
|
//
|
|
// TableName - Name of the database table where the resource (database row) is stored. - [Required]
|
|
// ColumnNames - An @FM delimited list of column names to build into the resource. - [Required]
|
|
// KeyID - Key ID for the database row. - [Required]
|
|
//
|
|
// Gets the column values from the database row from the provided table and ID that will be used to create the resource.
|
|
// This is returned as an @FM delimited list of values.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetColumnValues(TableName, ColumnNames, KeyID)
|
|
|
|
ServiceKeyID := '*' : SRP_Hash(TableName : ColumnNames : KeyID, 'SHA-1', 'BASE32')
|
|
ColumnValues = Memory_Services('GetValue', ServiceKeyID, True$, 10, CacheName$)
|
|
|
|
If ColumnValues EQ '' then
|
|
If (TableName NE '') AND (ColumnNames NE '') AND (KeyID NE '') then
|
|
Open TableName to hTable then
|
|
Open 'DICT.' : TableName to @DICT then
|
|
Read Fields from @DICT, '%FIELDS%' else Fields = ''
|
|
@ID = KeyID
|
|
Read @RECORD from hTable, @ID then
|
|
Convert @Lower_Case to @Upper_Case in ColumnNames
|
|
NumColumns = Count(ColumnNames, @FM) + (ColumnNames NE '')
|
|
|
|
If ColumnNames NE '' then
|
|
For Each ColumnName in ColumnNames using @FM setting ColumnCnt
|
|
ColumnName = ColumnNames<ColumnCnt>
|
|
ColumnValue = ''
|
|
Locate ColumnName in Fields<FIELDS_NAME$> using @VM setting vPos then
|
|
Master = Fields<FIELDS_MASTER_FLAG$, vPos>
|
|
Type = Fields<FIELDS_TYPE$, vPos>
|
|
Conv = Fields<FIELDS_CONV$, vPos>
|
|
MV = Fields<FIELDS_MVFLAG$, vPos>
|
|
Pos = Fields<FIELDS_FIELD_NO$, vPos>
|
|
KeyPart = Fields<FIELDS_PART$, vPos>
|
|
GroupName = Fields<FIELDS_MV_GROUPNAME$, vPos>
|
|
|
|
// Now get the value based on factors like data vs. calculated and whole key vs. key part
|
|
Begin Case
|
|
Case Type EQ 'F'
|
|
// Data column.
|
|
If Len(KeyPart) then
|
|
// This is a Key ID column. If KeyPart is 0 then this represents the entire
|
|
// Key ID, which is not normally returned since the Item ID is already known
|
|
// and will appear in the URL. If this is a multipart Key ID then KeyPart
|
|
// will be an integer. This value should be returned since it likely is a
|
|
// significant part of the resource.
|
|
If KeyPart EQ 0 then
|
|
ColumnValue = @ID
|
|
end else
|
|
ColumnValue = Field(@ID, '*', KeyPart, 1)
|
|
end
|
|
end else
|
|
ColumnValue = @RECORD<Pos>
|
|
end
|
|
|
|
Case Type EQ 'S'
|
|
// Calculated (symbolic) column. If @RM or @FM delimiters are discovered then all
|
|
// delimiters will be decremented as needed so the response payload will not become
|
|
// out of sync.
|
|
ColumnValue = Calculate(ColumnName)
|
|
If Index(ColumnValue, @RM, 1) then
|
|
Convert @STM to Char(248) in ColumnValue
|
|
Convert @TM to Char(249) in ColumnValue
|
|
Convert @SVM to @STM in ColumnValue
|
|
Convert @VM to @TM in ColumnValue
|
|
Convert @FM to @SVM in ColumnValue
|
|
Convert @RM to @VM in ColumnValue
|
|
end else
|
|
If Index(ColumnValue, @FM, 1) then
|
|
Convert @STM to Char(249) in ColumnValue
|
|
Convert @TM to @STM in ColumnValue
|
|
Convert @SVM to @TM in ColumnValue
|
|
Convert @VM to @SVM in ColumnValue
|
|
Convert @FM to @VM in ColumnValue
|
|
end
|
|
end
|
|
|
|
End Case
|
|
|
|
// If there is a conversion pattern then apply it.
|
|
If Len(Conv) then ColumnValue = Oconv(ColumnValue, Conv)
|
|
|
|
* If KeyPart EQ 0 then
|
|
* ColumnNames = Delete(ColumnNames, ColumnCnt, 0, 0)
|
|
* end else
|
|
ColumnValues := ColumnValue : @FM
|
|
* end
|
|
end
|
|
Next ColumnName
|
|
end
|
|
|
|
ColumnValues[-1, 1] = ''
|
|
Memory_Services('SetValue', ServiceKeyID, ColumnValues, CacheName$)
|
|
end else
|
|
ErrorMessage = KeyID : ' is not a valid row in the ' : TableName : ' table.'
|
|
HTTP_Services('SetResponseError', '', '', 404, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end else
|
|
// Unable to open the dictionary table. Set a 500 status code.
|
|
ErrorMessage = 'Unable to open the ' : TableName : ' dictionary table.'
|
|
HTTP_Services('SetResponseError', '', '', 500, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end else
|
|
// Unable to open the database table. Set a 500 status code.
|
|
ErrorMessage = 'Unable to open the ' : TableName : ' table.'
|
|
HTTP_Services('SetResponseError', '', '', 500, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end else
|
|
// Required arguments are missing so just set a 404 status code.
|
|
ErrorMessage = 'Required arguments are missing.'
|
|
HTTP_Services('SetResponseError', '', '', 404, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end
|
|
|
|
Response = ColumnValues
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetMVGroupNames
|
|
//
|
|
// TableName - Name of the database table. - [Required]
|
|
// ColumnNames - An @FM delimited list of column names. - [Required]
|
|
//
|
|
// Gets the MV group names for the dictionary columns passed in. Note: the ColumnNames argument should be the same list
|
|
// that was returned from the GetColumnNames service. This will return an @FM delimited list of MV group
|
|
// names. Thus, for non-multivalue fields, the values will be empty. The MV group names will be returned in lower case.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetMVGroupNames(TableName, ColumnNames)
|
|
|
|
ServiceKeyID := '*' : SRP_Hash(TableName : ColumnNames, 'SHA-1', 'BASE32')
|
|
MVGroupNames = Memory_Services('GetValue', ServiceKeyID, '', '', CacheName$)
|
|
|
|
If MVGroupNames EQ '' then
|
|
If (TableName NE '') AND (ColumnNames NE '') then
|
|
Open TableName to hTable then
|
|
Open 'DICT.' : TableName to @DICT then
|
|
Read Fields from @DICT, '%FIELDS%' else Fields = ''
|
|
MVGroupNames = ''
|
|
AllColumnNames = Fields<FIELDS_NAME$>
|
|
AllGroupNames = Fields<FIELDS_MV_GROUPNAME$>
|
|
// Convert to upper case for now so that matches against the dictionary column names can be made.
|
|
Convert @Lower_Case to @Upper_Case in ColumnNames
|
|
// Loop through the fields and append the matching MV group names to the list.
|
|
NumColumns = DCount(ColumnNames, @FM)
|
|
For ColumnCnt = 1 to NumColumns
|
|
ColumnName = ColumnNames<ColumnCnt>
|
|
Locate ColumnName in AllColumnNames using @VM setting vPos then MVGroupNames := AllGroupNames<0, vPos> : @FM
|
|
Next ColumnCnt
|
|
MVGroupNames[-1, 1] = ''
|
|
Convert @Upper_Case to @Lower_Case in MVGroupNames
|
|
Memory_Services('SetValue', ServiceKeyID, MVGroupNames, CacheName$)
|
|
end else
|
|
// Unable to open the dictionary table. Set a 500 status code.
|
|
ErrorMessage = 'Unable to open the ' : TableName : ' dictionary table.'
|
|
HTTP_Services('SetResponseError', '', '', 500, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end else
|
|
// Unable to open the database table. Set a 500 status code.
|
|
ErrorMessage = 'Unable to open the ' : TableName : ' table.'
|
|
HTTP_Services('SetResponseError', '', '', 500, ErrorMessage)
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end else
|
|
// Required arguments are missing so just set a 404 status code.
|
|
ErrorMessage = 'Required arguments are missing.'
|
|
HTTP_Services('SetResponseError', '', '', 404, 'Required arguments are missing.')
|
|
Error_Services('Add', ErrorMessage)
|
|
end
|
|
end
|
|
|
|
Response = MVGroupNames
|
|
|
|
end service
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Internal GoSubs
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UpdateDatabaseRow
|
|
//
|
|
// Updates the database row with the data contained in the Request Body. This will write the updated row data to the
|
|
// table and it will update the HTTP response status and headers as needed. This gosub is used by the PostDatabaseItem,
|
|
// PutDatabaseItem, and PatchDatabaseItem services.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
UpdateDatabaseRow:
|
|
|
|
// The new resource will have been put into the POST string.
|
|
Body = HTTP_Services('GetHTTPPostString')
|
|
// The POST string will have been encoded so use percent (URL) decoding.
|
|
Body = HTTP_Services('DecodePercentString', Body)
|
|
Convert @Lower_Case to @Upper_Case in AllowedColumnNames
|
|
|
|
If Body EQ '' then
|
|
ParseResponse = SRP_JSON(hBody, 'NEW', 'OBJECT')
|
|
end else
|
|
ParseResponse = SRP_JSON(hBody, 'PARSE', Body)
|
|
end
|
|
If (ParseResponse EQ '') OR (ParseResponse EQ 1) then
|
|
// Go through the column names in the JSON object and map the column values to the respective
|
|
// field positions in the database row.
|
|
Fields = Xlate('DICT.' : TableName, '%FIELDS%', '', 'X')
|
|
NumColumns = SRP_JSON(hBody, 'GETCOUNT')
|
|
ColumnNames = SRP_JSON(hBody, 'GETMEMBERS')
|
|
If ColumnNames NE '' then
|
|
For Each ColumnName in ColumnNames using @FM setting ColumnCnt
|
|
ColumnName = ColumnNames<ColumnCnt>
|
|
hColumn = SRP_JSON(hBody, 'GET', ColumnName)
|
|
JSONType = SRP_JSON(hColumn, 'TYPE')
|
|
If ColumnName[1, 1] NE '_' then
|
|
// Ignore special properties which are for HATEOAS purpose.
|
|
Begin Case
|
|
Case JSONType _EQC 'Array'
|
|
// Count the number of members in the array then loop through to build the @VM value list.
|
|
ColumnValues = ''
|
|
NumItems = SRP_JSON(hColumn, 'GETCOUNT')
|
|
For ItemCnt = 1 to NumItems
|
|
hItem = SRP_JSON(hColumn, 'GET', ItemCnt)
|
|
ItemNames = SRP_JSON(hItem, 'GETMEMBERS')
|
|
If ItemNames NE '' then
|
|
For Each ColumnName in ItemNames using @FM setting ItemPtr
|
|
ColumnValues<ItemPtr> = ColumnValues<ItemPtr> : SRP_JSON(hItem, 'GETVALUE', ColumnName, '') : @VM
|
|
Next ItemName
|
|
end
|
|
SRP_JSON(hItem, 'Release')
|
|
Next NumItems
|
|
If ItemNames NE '' then
|
|
For Each ColumnName in ItemNames using @FM setting ItemPtr
|
|
ColumnValue = ColumnValues<ItemPtr>
|
|
ColumnValue[-1, 1] = ''
|
|
GoSub UpdateColumnData
|
|
Next ColumnName
|
|
end
|
|
|
|
Case JSONType _EQC 'Object'
|
|
// This is likely an MV group with one or more embedded arrays which need to be processed
|
|
// individually.
|
|
NumMVColumns = SRP_JSON(hColumn, 'GETCOUNT')
|
|
MVColumnNames = SRP_JSON(hColumn, 'GETMEMBERS')
|
|
If MVColumnNames NE '' then
|
|
For Each ColumnName in MVColumnNames using @FM setting MVColumnCnt
|
|
ColumnName = MVColumnNames<MVColumnCnt>
|
|
hMVColumn = SRP_JSON(hColumn, 'GET', ColumnName)
|
|
ColumnValue = ''
|
|
NumValues = SRP_JSON(hMVColumn, 'GETCOUNT')
|
|
For ValueCnt = 1 to NumValues
|
|
ColumnValue := SRP_JSON(hMVColumn, 'GETVALUE', ColumnName : '[' : ValueCnt : ']', '') : @VM
|
|
Next ValueCnt
|
|
ColumnValue[-1, 1] = ''
|
|
SRP_JSON(hMVColumn, 'RELEASE')
|
|
GoSub UpdateColumnData
|
|
Next ColumnName
|
|
end
|
|
|
|
Case Otherwise$
|
|
// This is just a regular column. Get the value.
|
|
ColumnValue = SRP_JSON(hBody, 'GETVALUE', ColumnName, '')
|
|
GoSub UpdateColumnData
|
|
|
|
End Case
|
|
end
|
|
|
|
SRP_JSON(hColumn, 'RELEASE')
|
|
Next ColumnName
|
|
end
|
|
|
|
Write DataRow to hTable, KeyID then
|
|
// The database row has been successfully updated. Set the response status as originally identified by the
|
|
// calling service. Set the Content-Location response header with the new or updated items URL so the
|
|
// client can retrieve the resource if needed.
|
|
HTTP_Services('SetResponseStatus', StatusCode)
|
|
HTTP_Services('SetResponseHeaderField', 'Location', ItemURL)
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 500, 'Error writing ' : KeyID : ' to the ' : TableName : ' table.')
|
|
end
|
|
end else
|
|
HTTP_Services('SetResponseError', '', '', 400, ParseResponse)
|
|
end
|
|
Unlock hTable, KeyID else Null
|
|
|
|
return
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UpdateColumnData
|
|
//
|
|
// Updates the specified database column with the indicated value. This only updates the dynamic array and does not
|
|
// write the data to the table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
UpdateColumnData:
|
|
|
|
Convert @Lower_Case to @Upper_Case in ColumnName
|
|
|
|
AllowContinue = True$ ; // Assume this can continue for now.
|
|
If AllowedColumnNames NE '' then
|
|
Locate ColumnName in AllowedColumnNames using @FM setting AllowedFound else
|
|
// This column is not approved for updating.
|
|
AllowContinue = False$
|
|
end
|
|
end
|
|
|
|
Locate ColumnName in Fields<FIELDS_NAME$> using @VM setting vPos then
|
|
Pos = Fields<FIELDS_FIELD_NO$, vPos>
|
|
Type = Fields<FIELDS_TYPE$, vPos>
|
|
If Type EQ 'F' then
|
|
If AllowContinue EQ True$ then
|
|
Conv = Fields<FIELDS_CONV$, vPos>
|
|
If Len(Conv) then ColumnValue = IConv(ColumnValue, Conv)
|
|
DataRow<Pos> = ColumnValue
|
|
end else
|
|
// This column is not allowed to be updated so make sure it retains the value stored in the database.
|
|
DataRow<Pos> = OrigDataRow<Pos>
|
|
end
|
|
end
|
|
end
|
|
|
|
return
|