Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 66 Next »


Conventions


The Jiwa REST API is based on the Data Transfer Object (DTO) pattern.

Every request accepts a DTO and almost every response is a DTO.  You can, in some cases encode the request DTO as URL parameters instead, such as authentication and query requests.

The DTO classes can be obtained from the following routes:

LanguageRoute
C#/types/csharp
VB.NET/types/vbnet
F#/types/fsharp
Typescript/types/typescript
Java/types/java
Kotlin/types/kotlin
Swift/types/swift

Additionally, you can use your tool of choice to generate the DTO's from the Open API specification document located at /openapi

Content Negotiation

By default the Jiwa REST API is configured to accept json DTO's and return json DTO's. This default content type can be changed in the configuration, or the client can override the content type. If using the ServiceStack client, then there is no concern of the content type as it is automatically negotiated.

In addition to using the standard Accept HTTP Header to retrieve the response a different format, you can also request an alternative Content-Type by appending ?format=ext to the query string, e.g:

    • /Debtors/{DebtorID}?format=json
    • /Debtors/{DebtorID}?format=xml
    • /Debtors/{DebtorID}?format=csv

Or by appending the format .ext to the end of the route, e.g:

    • /Debtors/{DebtorID}.json
    • /Debtors/{DebtorID}.xml
    • /Debtors/{DebtorID}.csv

Verbs

  • GET operations are used to retrieve data
  • POST operations are used to create data
  • PATCH operations are used to update existing data
  • DELETE operations are used to delete existing data

HTTP Response Codes

The response code returned will vary based on the type of operation and the result of the request.

200 OK

Will be returned for most GET operations - unless there was nothing to return, then a 204 No Content will be returned

201 Created

Will be returned for successful POST operations

204 No Content

Will be returned for successful DELETE operations

401 Not Authenticated

Will be returned if the operation requires the user to be authenticated, and they are not - or if they are authenticating and it failed due to bad credentials.

403 Forbidden

Will be returned if the user is authenticated, but do not have permission to perform the operation. See Setting Route Permissions for information on setting route permissions.

404 Not Found

Will be returned if the route is invalid, or the resource requested does not exist.

409 Conflict

Will be returned if the Jiwa business logic determines you can't perform the operation. This may occur with DELETE operations if the record cannot be deleted because it is referenced elsewhere (e.g: You can't delete a product because it is used on a sales order).

You may also get a 409 response due to a concurrency conflict.  Jiwa uses optimistic concurrency control, and if the record changes because of another users or processes actions between a read and save, then this error will be returned.

Routing

ServiceStack clients don't care about routes

Note that if using a ServiceStack client you do not need to be concerned with routes. The route is worked out by the client based on the type of the DTO being sent.

The convention used for route structure is to use a plural noun of the resource followed by the primary key (usually an ID) of the resource identifier:

/SalesOrders/{InvoiceID}

The above URL route when used as a GET operation will retrieve the sales order with the InvoiceID provided:

/SalesOrders/5244dd5e199749f4b6fe

will retrieve sales order with InvoiceID of 5244dd5e199749f4b6fe

The same applies to PATCH and DELETE operations

For POST operations, you don't know and cannot supply the InvoiceID, as it is generated for you - so it is simply a POST to:

/SalesOrders

with the content body being the DTO.

Where there is relational data, such as sales orders have many possible related notes the URL appends to the sales order route URL the plural of the relational data (Notes) and when appropriate the primary key of the note (NoteID). A GET of the following route:

/SalesOrders/5244dd5e199749f4b6fe/Notes

will retrieve all notes for InvoiceID 5244dd5e199749f4b6fe

A POST to the same route will add a new note:

/SalesOrders/5244dd5e199749f4b6fe/Notes

A PATCH to the following route updates the note with NoteID 66581f3f-3efb-4cb5-878c-37f6a55405cf which is attached to sales order with InvoiceID 5244dd5e199749f4b6fe

/SalesOrders/5244dd5e199749f4b6fe/Notes/66581f3f-3efb-4cb5-878c-37f6a55405cf

A DELETE to the same route will remove the note from the sales order

/SalesOrders/5244dd5e199749f4b6fe/Notes/66581f3f-3efb-4cb5-878c-37f6a55405cf


An alternative method of POST or PATCH operations for relational data is not to use the explicit route, but provide the relational data in a POST or PATCH of the parent route.

An example below shows the addition of a new note, the addition of a new line, and the edit of an existing line to an existing sales order using a single PATCH operation to the sales order:

Updates an existing order - adds a new line, adjusts an existing line and adds a note
 ServiceStack Client C#
var client = new ServiceStack.JsonServiceClient("https://api.jiwa.com.au");
var authResponse = client.Get(new ServiceStack.Authenticate() { UserName = "admin", Password = "password" });

var salesOrderPATCHRequest = new JiwaFinancials.Jiwa.JiwaServiceModel.SalesOrderPATCHRequest()
{
	InvoiceID = "babce67cdbf64f778536",
	ExpectedDeliveryDate = DateTime.Now.AddDays(5)                             
};
salesOrderPATCHRequest.Lines.Add(new JiwaFinancials.Jiwa.JiwaServiceModel.SalesOrders.SalesOrderLine { PartNo = "1172", QuantityOrdered = 5 });
salesOrderPATCHRequest.Lines.Add(new JiwaFinancials.Jiwa.JiwaServiceModel.SalesOrders.SalesOrderLine { InvoiceLineID = "88a358a2822e4319b9b9", QuantityOrdered = 10, CustomFieldValues = new List<JiwaFinancials.Jiwa.JiwaServiceModel.CustomFields.CustomFieldValue> { new JiwaFinancials.Jiwa.JiwaServiceModel.CustomFields.CustomFieldValue { SettingID = "1ae102b94dc54dfc8a45                ", Contents = "Adjustment requested by phone" } } });

salesOrderATCHRequest.Notes.Add(new JiwaFinancials.Jiwa.JiwaServiceModel.Notes.Note { NoteText = "Customer telephoned and asked for another few hours of labour" });

var salesOrderATCHResponse = client.Patch(salesOrderATCHRequest);
 C#
using (var webClient = new System.Net.WebClient())
{
    // Authenticate               
    webClient.QueryString.Add("username", "Admin");
    webClient.QueryString.Add("password", "password");
     
    string responsebody = webClient.DownloadString("https://api.jiwa.com.au/auth");               
    // Above returns something like this: {"SessionId":"0hKBFAnutUk8Mw6YY6DN","UserName":"api","DisplayName":"","ResponseStatus":{}}
 
    // Deserialise response into a dynamic - below requires the Newtonsoft.Json nuget package
    var authResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responsebody);
    var sessionId = authResponse.SessionId;
 
    webClient.Headers.Add(System.Net.HttpRequestHeader.Cookie, string.Format("ss-id={0}", sessionId));
    webClient.Headers[System.Net.HttpRequestHeader.ContentType] = "application/json";
	
	string json = Newtonsoft.Json.JsonConvert.SerializeObject(new
	{                    
		ExpectedDeliveryDate = DateTime.Now.AddDays(5),
		Lines = new List<object>
		{
			new { PartNo = "1172", QuantityOrdered = 5 },
			new { InvoiceLineID = "88a358a2822e4319b9b9", QuantityOrdered = 10, CustomFieldValues = new List<object> { new { SettingID = "1ae102b94dc54dfc8a45                ", Contents = "Adjustment requested by phone" } } }
		},
		Notes = new List<object>
		{
			new { NoteText = "Customer telephoned and asked for another few hours of labour" }
		}
	});

	responsebody = webClient.UploadString("https://api.jiwa.com.au/SalesOrders/babce67cdbf64f778536", "PATCH", json); 
}
 Curl
curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET https://api.jiwa.com.au/auth -d '{"username":"Admin","password":"password"}'

Returns the following authentication response, containing the SessionId which subsequent requests will need to include in the cookie "ss-id"

{"SessionId":"6w1nLX8r0sIrJHClX9Vj","UserName":"Admin","DisplayName":"","ResponseStatus":{}}

Then, with the SessionId now known, the route can be called:

curl -H 'Accept: application/json' -H 'Content-Type: application/json' --cookie 'ss-id=6w1nLX8r0sIrJHClX9Vj' -X PATCH https://api.jiwa.com.au/SalesOrders/babce67cdbf64f778536 -d '{"ExpectedDeliveryDate":"2017-09-20","Lines":[{"PartNo":"1172","QuantityOrdered":5},{"InvoiceLineID":"88a358a2822e4319b9b9","QuantityOrdered":10,"CustomFieldValues":[{"SettingID":"1ae102b94dc54dfc8a45                ", "Contents":"Adjustment requested by phone"}]}],"Notes":[{"NoteText":"Customer telephoned and asked for another few hours of labour"}]}'

The response returned from the above request will be a json document representing the full sales order DTO model from the business logic - see the meta data page for the SalesOrderPATCHRequest for more detail.

Note(1): The DebtorID or DebtorAccountNo can be provided. If both are provided, then the DebtorID will be used to resolve the debtor.

Note(2): A sales order line can update an existing line by providing the InvoiceLineID property. New lines are appended by omitting InvoiceLineID.

Note(3): A new sales order line added can provide either the InventoryID, or the PartNo. If both are provided, then the InventoryID will be used to resolve the inventory item.

Note(4): When sales order lines are added, omitting the price will cause Jiwa to determine the price per normal pricing scheme logic.

Note(5): The last line added in the above example sets a custom line field value for a setting with ID "1ae102b94dc54dfc8a45"

Note(6): When adding notes if the NoteType is omitted, then the default note type configured in Jiwa is used.


Authenticating


API Key Authentication

Jiwa 07.02.00 has added API Key authentication:

Error rendering macro 'jira' : Unable to locate Jira server for this macro. It may be due to Application Link configuration.

Unable to locate Jira server for this macro. It may be due to Application Link configuration.

Most routes will require authentication. There are 2 methods of authentication - user credential and API Key. API Key authentication was introduced in version 7.2 of Jiwa.

User Credentials Authentication


User credentials authentication is performed by invoking the /auth route and passing the username and password of a Jiwa user.  A successful authentication will return an authentication response containing the SessionId.  All subsequent requests will need to provide the SessionId as the value for the cookie "ss-id".

An alternative to using a cookie, is to send subsequent requests with the header "X-ss-id" set to the SessionId.

Authenticate by calling the /auth route
 ServiceStack Client C#

See Installing the ServiceStackVS Extension for Visual Studio on how to configure your Visual Studio project to use the ServiceStack client needed for this example.

var client = new ServiceStack.JsonServiceClient("https://api.jiwa.com.au");
var authResponse = client.Get(new ServiceStack.Authenticate() { UserName = "admin", Password = "password" });

 C#

using (var webClient = new System.Net.WebClient())
{
    // Authenticate               
    webClient.QueryString.Add("username", "Admin");
    webClient.QueryString.Add("password", "password");
     
    string responsebody = webClient.DownloadString("https://api.jiwa.com.au/auth");               
}

 Curl

curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET https://api.jiwa.com.au/auth -d '{"username":"Admin","password":"password"}'

 Web Browser

Navigate to the auth URL and provide the username and password as parameters:

https://api.jiwa.com.au/auth?username=admin&password=password&format=json

This authenticates the user and creates a cookie, so a subsequent request will automatically include the SessionId. Note the &format=json in the above URL this overrides the content type returned. For browsers the default content type is HTML - if a content type override is omitted, then a HTML razor view of the data will be returned instead of json. xml and csv are also valid overrides for the content type to be returned. We only used ?format=json in this example to demonstrate the return value.

The response returned from the above request:

{"SessionId":"6w1nLX8r0sIrJHClX9Vj","UserName":"Admin","DisplayName":"","ResponseStatus":{}}

API Key Authentication


API Keys can be provided either as a HTTP Bearer token in the request header or as a URL or form parameter. There is no authentication step with API Keys, you simply provide the API Key with the request being made.

HTTP Bearer token
curl -H 'Authorization: Bearer znis9fHm0_D5EGRrGdWG7WA2EP7Hke_gloC6R76A2t0' -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET http://localhost/Debtors
URL Parameter
curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET http://localhost/Debtors?apikey=znis9fHm0_D5EGRrGdWG7WA2EP7Hke_gloC6R76A2t0


As API Keys are associated with a Jiwa staff member, all permissions are that of the staff member - for all intents the permissions are the same as if the authentication was performed with username and password.  API Keys can have an expiration date, and also can be revoked by marking the key as not Enabled. Requests using an expired or revoked key will fail with a 401 Not Authenticated error.

There are 2 types of API Keys in Jiwa 7.2 - Staff API Keys and Debtor API Keys.

Staff API Keys

Staff API Keys are simply an alternate way of a staff member or user authenticating to the username and password. A staff member can have an API key associated with them via the Staff Maintenance form in Jiwa:

Debtor API Keys

Debtor API keys are intended to be given to customers to interact with your Jiwa API to perform such functions as:

    • Retrieve a filterable, paginated list of products
    • Retrieve a product
    • Retrieve a filterable, paginated list of sales orders
    • Place sales orders
    • Retrieve a sales order
    • Retrieve a filterable, paginated list of sales quotes
    • Place a sales quote (request for quote)
    • Retrieve a sales quote
    • Retrieve a list of their transactions
    • Retrieve their account record

Debtor API Keys are associated with a debtor via the API Keys tab of the Debtor Maintenance form:

Note that the API Key is linked to a staff member.  When a Debtor API Key is used in a request the API will login into Jiwa as the associated staff member - so it is recommended to create a new user (staff member) with restricted permissions which you should link all your Debtor API Keys to.

Permissions

It is recommended to create a specific user which will be used for Debtor API Keys, and also a specific User Group to which the user belongs to. The REST API permissions for the User Group should be set as shown below:

Recommended Route Exposure

As the Debtor API key is expected to be given to a customer, care must be taken to only expose routes/services necessary and not to leak sensitive information. You must not trust any request using a Debtor API Key, and so validation and filtering must be performed within the REST API.

By default the REST API plugin will filter some requests and responses to prevent unwanted information disclosure. The following lists which routes currently filter the requests and responses:

RouteSummaryVerbRequest Filtering PerformedResponse Filtering Performed
 /Inventory/{InventoryID}Retrieves an inventory itemGET  None
        • Removes costs
        • Removes debtor specific prices for other debtors
        • Removed debtor classifaction prices for other debtors
 /Queries/SalesOrderListRetrieves a list of sales orders GETFiltered on DebtorID None
 /SalesOrders/{InvoiceID}Retrieves a sales order GETFiltered on DebtorIDRemoves the following from sales order lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • TaxRate.LedgerAccount
        • UnitCost
        • Line Details Cost
        • Line Details SOHID
        • Line Details SpecialPrice
 /SalesOrders Creates a new sales order POSTDebtorID replaced Removes the following from sales order lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • TaxRate.LedgerAccount
        • UnitCost
        • Line Details Cost
        • Line Details SOHID
        • Line Details SpecialPrice
/Queries/SalesQuoteListRetrieves a list of sales quotesGETFiltered on DebtorID None
/SalesQuotes/{QuoteID}Retrieves a sales QuoteGET Filtered on DebtorIDRemoves the following from sales quote lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • UnitCost
/SalesQuotesCreates a new sales QuotePOST DebtorID replacedRemoves the following from sales quote lines:
        • DiscountedPercentage
        • DiscountGiven
        • PriceExGst
        • UnitCost
/Queries/DebtorTransactionListRetrieves a list of debtor transactionsGET Filtered on DebtorIDNone
/DebtorsRetrieves a debtor for a API Key authenticated customerGET None - DebtorID impliedRemoves the following:
        • Budgets
        • DebtorLedgers
        • Classification.DebtorLedgers

Example of calling a route


This example shows how to retrieve a debtor record for a known DebtorID.

Retreiving a debtor record from the /Debtors/{DebtorID} route
 ServiceStack Client C#

See Installing the ServiceStackVS Extension for Visual Studio on how to configure your Visual Studio project to use the ServiceStack client needed for this example.

var client = new ServiceStack.JsonServiceClient("https://api.jiwa.com.au");
var authResponse = client.Get(new ServiceStack.Authenticate() { UserName = "admin", Password = "password" });

var debtorGETResponse = client.Get(new DebtorGETRequest { DebtorID = "0000000061000000001V" });

 C#

using (var webClient = new System.Net.WebClient())
{
    // Authenticate               
    webClient.QueryString.Add("username", "Admin");
    webClient.QueryString.Add("password", "password");
     
    string responsebody = webClient.DownloadString("https://api.jiwa.com.au/auth");
    // Deserialise response into a dynamic - below requires the Newtonsoft.Json nuget package - or you can use any other json serialisation library
	var authResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responsebody);
	var sessionId = authResponse.SessionId;

	webClient.Headers.Add(System.Net.HttpRequestHeader.Cookie, string.Format("ss-id={0}", sessionId));                
	responsebody = webClient.DownloadString("https://api.jiwa.com.au/Debtors/0000000061000000001V");         
}

 Curl

curl -H 'Accept: application/json' -H 'Content-Type: application/json' -X GET https://api.jiwa.com.au/auth -d '{"username":"Admin","password":"password"}'
curl -H 'Accept: application/json' -H 'Content-Type: application/json' --cookie 'ss-id=6w1nLX8r0sIrJHClX9Vj' -X GET https://api.jiwa.com.au/Debtors/0000000061000000001V

 Web Browser

Navigate to the auth URL and provide the username and password as parameters:

https://api.jiwa.com.au/auth?username=admin&password=password&format=json

Then navigate to the following URL:

https://api.jiwa.com.au/Debtors/0000000061000000001V

The response returned from the above request will be a json document representing the full debtor DTO model from the business logic - see the meta data page for the DebtorGETRequest for more detail.

 

Stateful Interaction


While typically interactions with a REST API would be stateless, it is possible to interact in a stateful way by passing the Request header "jiwa-stateful" with the value of "true".

When stateful requests are received, the server caches the appropriate business logic and subsequent requests will interact with that in-memory object.  This allows the consumer to perform actions like building a sales order without it being saved to the database until it is ready to save it.

Stateful requests will be committed to the database when a SAVE Request is received.  Pending changes can also be discarded with an ABANDON Request.

Below is an example of a stateful interaction with the Debtors - the object is statefully retrieved, and updated until a SAVE Request is sent.

stateful requests
var client = new ServiceStack.JsonServiceClient("http://localhost");
	var authResponse = client.Send<ServiceStack.AuthenticateResponse>(new ServiceStack.Authenticate()
	{
		provider = "credentials",
		UserName = "api",
		Password = "password",
		RememberMe = true
	});

	// Read a debtor
	client.Headers.Add("jiwa-stateful", "true");
	var debtorGETResponse = client.Get(new DebtorGETRequest { DebtorID = "0000000061000000001V" });

	// Update debtor 
	var debtorPATCHResponse  = client.Patch(new DebtorPATCHRequest() { DebtorID = "0000000061000000001V",  Name = "My new name", CreditLimit = 1000 });
 
    // Update some more fields
	debtorPATCHResponse  = client.Patch(new DebtorPATCHRequest() { DebtorID = "0000000061000000001V",  Address1 = "SE2L10 100 Walker Street" });

	// Save the changes
	var debtorSAVEResponse = client.Get(new DebtorSAVERequest() { DebtorID = "0000000061000000001V" });
}

 

When the server creates the business logic object, it is stored in a collection associated with the users session (this is actually a property of the Manager class - the ObjectDictionary).  Subsequent stateful requests for the same type (eg: Debtor Maintenance operations) will retrieve any existing business logic for the same record, otherwise a new business logic instance is created.

This means two subsequent stateful operations for different debtors will result in two business logic objects created by the server, and they will remain independent of each other.

Conversely, two subsequent stateful operations for the same debtor will result only one business logic object created by the server, and the second operation will be working on the same business logic instance as the first operation.


  • No labels