open-insight/LSL2/STPROC/HTTP_SCAN_SERVICES_CUTOVER.txt
2024-05-22 14:06:46 -07:00

478 lines
23 KiB
Plaintext

Function HTTP_Scan_Services(RemainingURL)
/***********************************************************************************************************************
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_Scan_Services
Description : Handler program for the HTTP Scan service module.
Notes : All HTTP web services should include the HTTP_SERVICE_SETUP insert. This will provide several useful
variables:
HTTPMethod - The HTTP Method (Verb) submitted by the client (e.g., GET, POST, etc.)
APIURL - The URL for the API entry point (e.g., api.mysite.com/v1).
SelfURL - The URL path representing the current service.
FullEndPointURL - The URL submitted by the client. This can be the same or longer than
the SelfURL.
NextSegment - The URL segment immediately following the SelfURL (if any). This
could contain the name of the next service or it could contain the
Item ID for the current service (aka resource).
CurrentServiceHandler - The name of this stored procedure.
Parameters :
RemainingURL [in] -- The remaining portion of the URL that follows the URL that launched this current
service. This information is used in the HTTP_SERVICE_SETUP insert to populate other
useful variables (see Notes above).
Response [out] -- Response to be sent back to the Controller (HTTP_MCP) or requesting procedure. Web API
services do not rely upon anything being returned in the response. This is what the
various services like SetResponseBody and SetResponseStatus services are for. A response
value is only helpful if the developers want to use it for debug purposes.
History : (Date, Initials, Notes)
07/31/18 dmb Original programmer.
09/04/18 dmb Revamp so Scan is its own resource rather than a parent to a sub-resource. Migrate the Tools
API logic into this routine as a starting point. Retrofit several SCANS column name equates
to match the changes made to the dictionary.
***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler
$insert APP_INSERTS
$insert HTTP_SERVICE_SETUP
$insert HTTP_INSERTS
$insert SCANS_EQUATES
Declare function Scan_Services, Database_Services, RTI_CreateGUID
Declare subroutine Scan_Services, Database_Services
// Assume the current HTTP method is valid until proven otherwise.
ValidMethod = True$
// Assume the current web service is valid until provent otherwise.
ValidService = True$
// Assume no HTTP methods are valid until proven otherwise.
AllowedMethods = ''
// A list of all services able to be called from this URL.
AllowedServices = ''
// Handle the HTTP request as needed.
Begin Case
Case RemainingURL _EQC ''
// This means the URL ends with /scan, which means this is the end point. The HTTP methods roughly function
// as follows:
//
// POST - Creates a new resource. Assumes the server will generate the scan ID, which will be returned in
// the HTTP response.
AllowedMethods = 'POST,OPTIONS'
Locate HTTPMethod in AllowedMethods using ',' setting MethodPos then
On MethodPos GoSub Post, Options
end else
ValidMethod = False$
end
Case Count(RemainingURL, '/') EQ 0
// This means the URL ends with /scan/{scanID}. {scanID} is also known as the resource ID. When a resource is
// closely mapped to a database row (as is the case with this Contacts API), this is where the basic CRUD
// functionality will be added. The HTTP methods roughly function as follows:
//
// PUT - Creates* a resource using the Key ID contained in the URL. This is equivalent to the Write
// statement in BASIC+.
// GET - Reads the resource referenced by the Key ID contained in the URL. This is equivalent to the Read
// statement in BASIC+.
// PUT - Updates* the resource referenced by the Key ID contained in the URL. This is the exact same
// feature defined above. This should make sense since the Write statement in BASIC+ is used to
// create and update database rows. Note, the PUT method assumes the entire resource is within the
// request body, not just the changes. See the PATCH method below.
// DELETE - Deletes the source referenced by the Key ID contained in the URL. This is equivalent to the Delete
// statement in BASIC+.
//
// * Many people use the POST method for creating (and updating) a resource. However, per the HTTP
// specification, POST is to be used when creating a new resource that does not yet have a resource ID
// (i.e., Key ID). The server determines the Key ID and this is returned to the client for future use.
//
// PATCH - Updates specific properties (e.g., data columns) of the resource referenced by the Key ID
// contained in the URL. This is similar in concept to the WriteV statement in BASIC+, although
// multiple changes in the resource can be updated with one PATCH method.
AllowedMethods = 'POST,GET,PUT,PATCH,DELETE,OPTIONS'
Locate HTTPMethod in AllowedMethods using ',' setting MethodPos then
On MethodPos GoSub PostItem, GetItem, PutItem, PatchItem, DeleteItem, OptionsItem
end else
ValidMethod = False$
end
Case Otherwise$
ValidService = False$
End Case
// Resolve any invalid conditions with the HTTP request.
Begin Case
Case Not(ValidService)
HTTP_Services('SetResponseStatus', 404, NextSegment : ' is not a valid service request within the ' : CurrentServiceHandler : ' module.')
Case Not(ValidMethod)
HTTP_Services('SetResponseStatus', 405, HTTPMethod : ' is not valid for this service.')
GoSub SetAllowedMethods
End Case
Return Response OR ''
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options BOOLEAN = True$, False$
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Web Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------------------------------------
// Post
//
// Attempts to create a new scan. Creating a new scan follows these guidelines:
//
// - Any unexpected system errors will return a 500 status code (Internal Server Error).
// - If no errors occur then a 201 (Created) status code is returned. The Content-Location response header will be
// set to the value of the URL that will allow the client to GET the newly created resource.
// - If there is an error locking the resource then a 423 status code (Locked) is returned.
//----------------------------------------------------------------------------------------------------------------------
Post:
ScanID = Scan_Services('CreateScansRow', 'Lot')
If Error_Services('NoError') then
ItemURL = SelfURL : '/' : ScanID
StatusCode = 201
GoSub CreateHALItem
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 500, Message)
end
return
//----------------------------------------------------------------------------------------------------------------------
// Options
//
// Sets the appropriate response header fields for an OPTIONS request.
//----------------------------------------------------------------------------------------------------------------------
Options:
GoSub SetCommonOptionResponseHeaders
return
//----------------------------------------------------------------------------------------------------------------------
// PostItem
//
// Attempts to add new scan data to the scan resource.
//----------------------------------------------------------------------------------------------------------------------
PostItem:
ScanID = NextSegment
// The resource will have been put into the POST string.
Body = HTTP_Services('GetHTTPPostString')
If Body NE '' then
// The POST string will have been encoded so use percent (URL) decoding.
Body = HTTP_Services('DecodePercentString', Body)
ParseResponse = SRP_JSON(hBody, 'PARSE', Body)
If (ParseResponse EQ '') then
ValidScan = False$ ; // Assume the scan is not valid for now.
ScanData = SRP_JSON(hBody, 'GetValue', 'scanData')
SRP_JSON(hBody, 'Release')
Scan_Services('ProcessScanData', ScanID, ScanData)
If Error_Services('NoError') then
ItemURL = SelfURL : '/' : ScanID
StatusCode = 200 ; // This service assume the resource already exists.
GoSub CreateHALItem
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 400, Message)
end
end else
HTTP_Services('SetResponseStatus', 400, 'Unable to parse the scanData JSON.')
end
end else
HTTP_Services('SetResponseStatus', 400, 'JSON object is missing from the request.')
end
return
//----------------------------------------------------------------------------------------------------------------------
// GetItem
//
// Returns the specific resource.
//
// The easiest way to return a resource that is mapped to a database row is to use the GetDatabaseItem service. This
// is being done in the code below. However, to demonstrate how then basic functionality can be extended, there is
// additional code below that will show how to add the Contact resource's image URL to the JSON response.
//----------------------------------------------------------------------------------------------------------------------
GetItem:
ScanID = NextSegment
ItemURL = SelfURL : '/' : ScanID
ScanRow = Scan_Services('GetScansRow', ScanID)
If Error_Services('NoError') then
StatusCode = 200
GoSub CreateHALItem
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 404, Message)
end
return
//----------------------------------------------------------------------------------------------------------------------
// PutItem
//
// Attempts to replace the indicated scan resource with the incoming JSON object.
//----------------------------------------------------------------------------------------------------------------------
PutItem:
ScanID = NextSegment
// The resource will have been put into the POST string.
Body = HTTP_Services('GetHTTPPostString')
If Body NE '' then
// The POST string will have been encoded so use percent (URL) decoding.
Body = HTTP_Services('DecodePercentString', Body)
ParseResponse = SRP_JSON(hBody, 'PARSE', Body)
If (ParseResponse EQ '') then
ScanRow = Database_Services('ReadDataRow', 'SCANS', ScanID)
If Error_Services('NoError') then
// Existing scan resource is being replaced.
StatusCode = 200
end else
// Scan resource is being created for the first time.
StatusCode = 201
end
If ScanRow<SCANS.CREATED_DATE$> EQ '' then ScanRow<SCANS.CREATED_DATE$> = Date()
If ScanRow<SCANS.CREATED_TIME$> EQ '' then ScanRow<SCANS.CREATED_TIME$> = Time()
ScanRow<SCANS.EMPLOYEE_ID$> = SRP_JSON(hBody, 'GetValue', 'employeeID', '')
ScanRow<SCANS.TOOL_ID$> = SRP_JSON(hBody, 'GetValue', 'toolID', '')
ScanRow<SCANS.CASSETTE_IDS$> = SRP_JSON(hBody, 'GetValue', 'cassetteID', '')
Accept = SRP_JSON(hBody, 'GetValue', 'accept', False$)
If Accept EQ True$ then
// Before allowing the accept field to be set to True, verify that all required data has been populated.
Begin Case
Case ScanRow<SCANS.EMPLOYEE_ID$> EQ '' ; Accept = False$
Case ScanRow<SCANS.TOOL_ID$> EQ '' ; Accept = False$
Case ScanRow<SCANS.CASSETTE_IDS$> EQ '' ; Accept = False$
End Case
end
ScanRow<SCANS.ACCEPTED$> = Accept
If ScanRow<SCANS.ACCEPTED$> EQ True$ then
ScanRow<SCANS.ACCEPTED_DATE$> = Date()
ScanRow<SCANS.ACCEPTED_TIME$> = Time()
end
Database_Services('WriteDataRow', 'SCANS', ScanID, ScanRow, True$, False$, True$)
If Error_Services('NoError') then
ItemURL = SelfURL : '/' : ScanID
GoSub CreateHALItem
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 500, Message)
end
SRP_JSON(hBody, 'Release')
end else
HTTP_Services('SetResponseStatus', 400, 'Unable to parse the scanData JSON.')
end
end else
HTTP_Services('SetResponseStatus', 400, 'JSON object is missing from the request.')
end
return
//----------------------------------------------------------------------------------------------------------------------
// PatchItem
//
// Attempts to update the indicated scan resource with the incoming JSON object. The only permissible field to be
// updated is "accept".
//----------------------------------------------------------------------------------------------------------------------
PatchItem:
ScanID = NextSegment
// First confirm that this is a valid Scan ID.
jsonScan = Scan_Services('GetScansRow', ScanID, True$)
If Error_Services('NoError') then
// Confirm that all required data has been scanned before allowing the "accept" field to be updated.
ParseResponse = SRP_JSON(objResource, 'Parse', jsonScan)
If ParseResponse EQ '' then
ScanAcceptable = SRP_JSON(objResource, 'GetValue', 'scan.acceptable', False$)
If ScanAcceptable NE True$ then
ScanNotAcceptableReason = SRP_JSON(objResource, 'GetValue', 'scan.notAcceptableReason')
Error_Services('Add', ScanNotAcceptableReason)
end
SRP_JSON(objResource, 'Release')
end else
Error_Services('Add', 'Unable to parse the JSON scan resource.')
end
If Error_Services('NoError') then
// The resource will have been put into the POST string.
Body = HTTP_Services('GetHTTPPostString')
If Body NE '' then
// The POST string will have been encoded so use percent (URL) decoding.
Body = HTTP_Services('DecodePercentString', Body)
ParseResponse = SRP_JSON(hBody, 'PARSE', Body)
If ParseResponse EQ '' then
AcceptedStatus = SRP_JSON(hBody, 'GetValue', 'accepted.status')
If AcceptedStatus NE '' then
ScansRow = Scan_Services('GetScansRow', ScanID, False$)
ScansRow<SCANS.ACCEPTED$> = AcceptedStatus
If AcceptedStatus EQ True$ then
ScansRow<SCANS.ACCEPTED_DATE$> = Date()
ScansRow<SCANS.ACCEPTED_TIME$> = Time()
end
Scan_Services('SetScansRow', ScanID, ScansRow)
If Error_Services('NoError') then
ItemURL = SelfURL : '/' : ScanID
StatusCode = 200
GoSub CreateHALItem
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 500, Message)
end
end else
HTTP_Services('SetResponseStatus', 400, 'accepted.status field is missing from the JSON object.')
end
SRP_JSON(hBody, 'Release')
end else
HTTP_Services('SetResponseStatus', 400, 'Unable to parse the scanData JSON.')
end
end else
HTTP_Services('SetResponseStatus', 400, 'JSON object is missing from the request.')
end
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 400, Message)
end
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 404, Message)
end
return
//----------------------------------------------------------------------------------------------------------------------
// DeleteItem
//
// Attempts to delete the resource. Deleting a resource which is a database row follows these guidelines:
//
// - Any unexpected system errors will return a 500 status code (Internal Server Error).
// - If no errors occur then a 204 (No Content) status code is returned.
// - If the resource was already deleted then a 204 (No Content) status code is returned.
// - If there is an error locking the resource then a 423 status code (Locked) is returned.
//----------------------------------------------------------------------------------------------------------------------
DeleteItem:
ScanID = NextSegment
ScanRow = Database_Services('ReadDataRow', 'SCANS', ScanID)
If Error_Services('NoError') then
If ScanRow<SCANS.ACCEPTED$> NE True$ then
Database_Services('DeleteDataRow', 'SCANS', ScanID, True$, False$)
If Error_Services('NoError') then
HTTP_Services('SetResponseStatus', 200)
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 500, Message)
end
end else
HTTP_Services('SetResponseStatus', 403, 'This scan is already accepted and cannot be deleted.')
end
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 404, Message)
end
return
//----------------------------------------------------------------------------------------------------------------------
// OptionsItem
//
// Sets the appropriate response header fields for an OPTIONS request.
//----------------------------------------------------------------------------------------------------------------------
OptionsItem:
GoSub SetCommonOptionResponseHeaders
return
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------------------------------------
// SetCommonOptionResponseHeaders
//
// Sets the response headers that will be common for all OPTIONS methods.
//----------------------------------------------------------------------------------------------------------------------
SetCommonOptionResponseHeaders:
HTTP_Services('SetResponseHeaderField', 'Access-Control-Allow-Headers', 'authorization', True$)
HTTP_Services('SetResponseHeaderField', 'Access-Control-Allow-Headers', 'x-authorization', True$)
HTTP_Services('SetResponseHeaderField', 'Access-Control-Max-Age', 1728000)
GoSub SetAllowedMethods
return
//----------------------------------------------------------------------------------------------------------------------
// SetAllowedMethods
//
// Sets the Allow response header field as appropriate for the requested URL.
//----------------------------------------------------------------------------------------------------------------------
SetAllowedMethods:
If AllowedMethods NE '' then
For Each Method in AllowedMethods using ','
HTTP_Services('SetResponseHeaderField', 'Allow', Method, True$)
Next Method
end
return
//----------------------------------------------------------------------------------------------------------------------
// CreateHALItem
//
// Creates a HAL+JSON object based on the OpenInsight data row representation of the scan.
//----------------------------------------------------------------------------------------------------------------------
CreateHALItem:
jsonScan = Scan_Services('ConvertMVScanToJSON', ScanID, '', itemURL)
If Error_Services('NoError') then
If SRP_JSON(objResource, 'Parse', jsonScan) EQ '' then
lastModified = SRP_JSON(objResource, 'GetValue', 'lastModified')
end else
lastModified = ''
end
SRP_JSON(objResource, 'Release')
HTTP_Services('SetResponseHeaderField', 'Content-Location', ItemURL)
HTTP_Services('SetResponseHeaderField', 'Last-Modified', lastModified)
HTTP_Services('SetResponseBody', jsonScan, False$, 'application/hal+json')
HTTP_Services('SetResponseStatus', StatusCode)
end else
Message = Error_Services('GetMessage')
HTTP_Services('SetResponseStatus', 500, 'Error in the ' : CurrentServiceHandler : ' service. Message: ': Message)
end
return