Ben Langhinrichs

Photograph of Ben Langhinrichs

E-mail address - Ben Langhinrichs






January, 2021
SMTWTFS
     01 02
03 04 05 06 07 08 09
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

Search the weblog





























Genii Weblog


Civility in critiquing the ideas of others is no vice. Rudeness in defending your own ideas is no virtue.


Mon 11 Jan 2021, 10:37 AM
Continuing on the development challenge outlined in REST plus RPC: Do the demo backward and started in REST plus RPC: the right data in the right format, and no more, this post describes how we can build up the JavaScript necessary to invoke a batch of remote procedure calls with the JSON-RPC format. There are multiple ways to send HTTP requests and get back data in JavaScript. I find it easy to understand fetch, so that is what I am using, but it should be easy to translate to jQuery or Ajax or whatever. In this case, the devil is not in the details, but in the broad picture.
 
Note: I am not a JavaScript expert. Some of you will know more than I do, or have different ways to accomplish the task. If you'd like, comment and tell me how I could do differently or better.
 
Remote procedure calls with Exciton Boost use JSON-RPC. I did a Quick primer on JSON-RPC a few months ago if you are want to understand it better, but for our purposes, simply understand that we POST a message with a JSON body in a particular format to call procedure calls, and if an id is specified, we get a return value. There may be an array of methods called sequentially. For example, if I want to get the database title for the ReportIt.nsf database in our proposed demo, I would POST the following:
 
{
  "jsonrpc": "2.0",
  "method": "db.getTitle",
  "params": null,
  "id": 1
}
 
and would get back the JSON payload
 
{
    "jsonrpc""2.0",
    "result""Report It!",
    "id"1
}
 
Now, let's look at a sample JavaScript fetch way of POSTing that. Since I want to show a sequence of calls, I'll also get the default form.
 
const getTitle =  { jsonrpc: "2.0",  
                    method: "db.getTitle",
                    params: null,
                    id: 1
                  };
 
const getForm =   { jsonrpc: "2.0",  
                    method: "db.getDefaultForm",
                    params: null,
                    id: 2
                  };
 
 
function doIt() {
 
var data = [getTitle,getForm];
var stat = 0;
 
fetch('/ReportIt.nsf/api/boost/rpc', {
  method: 'POST', 
  mode: 'same-origin',
  cache: 'no-cache',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data),
})
.then(response => {stat = response.status; return response.json();})
.then(data => {
if (stat == 200) { 
  document.getElementById("demo").innerHTML = '<h1>'+data[0].result+'</h1>Default form: '+data[1].result;
} else { 
  document.getElementById("demo").innerHTML = '<h1>Status: '+stat+'</h1>'+data[0].error.message;
}});
 
 
};
 
For the crude purposes of this demo, I put a button in HTML that sets the value of a div with id='demo'.
 
<button type="button" style="border-radius: 6px;" onClick="doIt();">Get the database title</button><br>
<p id="demo"></p>
 
When we put this all together on a demo Page element in our database, we get the following. We can build on this for our demo in the next post.
 
Inline GIF image
 
=> Request a free Exciton Boost eval in January, and if you later purchase a license. we'll add in four hours of development assistance, a $700 value. <=
 
 

Copyright 2021 Genii Software Ltd.

Tags:

Wed 6 Jan 2021, 12:24 PM
Following up on the promise in yesterday's post, REST plus RPC: Do the demo backward, this post describes our first steps in building the nested tabbed table report demo.
 
My wife works in a preschool, and the first lesson for a successful day is: Rest first (well-rested and well-fed kids are happy kids). Most often, we find the same lesson is true when building apps which may require both the REST API and remote procedure calls: REST first (collect the necessary data, and only that data, in the most usable format).
 
Let's examine the data requirements by looking at a snippet of the table and marking up what data we'll need, some item values and other formulas.
 
Inline JPEG image
 
For those who have been following along, there are direct parallels  from my earlier post, Data from Domino: nuts and bolts of REST calls. That post is a good place to start when thinking about extracting data from any database using Exciton Boost's REST API. It is also a good post for showing many different ways to call the REST API from different languages. In this post, I'll stick to HTTP for clarity.
 
Task 1: Get list of accessible views
Returns a JSON array of objects, one for each view that is included, implicitly (all views) or explicitly (specified views), in the Exciton configuration db and marked as discoverable. Exciton is strict about data security, so views may be accessible but not discoverable. Views not included are inaccessible and will return a 404 error.
 
GET /ReportIt.nsf/api/boost/views HTTP/1.1
Accept: application/json
 
One of the accessible views is (AllOrdersByState) which has a UNID of 8F4CC3794598B864852586540074DE3F. We could access it using the UNID, but since using view names was added in Exciton Boost 4.6.0, we'll do that instead.
 
Task 2: Get view with columns & metadata
Returns a JSON array of objects, one for each document in the view. Each document includes a link to get the document from the documents collection as well as item values for each view column. Note that there are parameters to page through the view if desired.
 
GET /ReportIt.nsf/api/boost/views/(AllOrdersByState) HTTP/1.1
Accept: application/json
 
We get a JSON array of objects like that below. From this, we know we'll need State, City, and Qty, but won't need anything else.
 
Inline JPEG image
 
Task 3: Add selected items to view results 
Going back to the view, the request can include either additional or replacement items for the view columns using an item list via URL or HTTP header. This allows retrieval of any allowed items with the view results, including rendered rich text items.
 
GET /ReportIt.nsf/api/boost/views/(AllOrdersByState)?metadata=false HTTP/1.1
Accept: application/json
X-Items-List: State,City,LastName+", "+FirstName=Saleperson,Unit,Quantity,Color,DeliveryMethod,@Text(Price;"C,")=Price
 
And now we have the data we need in the format we need without the extra baggage. This call is the one REST call that is necessary, though it only returns 20 documents in the view at a time by default. That's a matter for another post as we process the data further
 
Inline JPEG image
 
 
=> Request a free Exciton Boost eval in January, and if you later purchase a license. we'll add in four hours of development assistance, a $700 value. <=
 
 
Continuing reading as I develop the solution. My follow up post, REST plus RPC: building the JavaScript for RPC calls, is now available.

Copyright 2021 Genii Software Ltd.

Tags:

Tue 5 Jan 2021, 05:59 PM
I usually wait until I have successfully completed a demo before I post about the results, but that doesn't match well with we actually face in development. Decades ago, we mocked up the final results using Dan Bricklin's Demo or sketched a "UI Design" on a whiteboard. Now, we have more modern techniques, but it's all basically the same idea. Design the goal before trying to achieve. It's a bit risky for a demo, but I thought I'd step through the entire process with you, including any fits and starts. Hence, this series of posts may not be very polished, but I hope they inform how you could proceed. All of these will use Exciton Boost 4.6, and if you are far more motivated than I expect of anyone, you can request an Exciton Boost evaluation license now and I'll post the db and code snippets so you can follow along. Or even tell me how I could should be doing it. Developers seem to love to do that.
 
I should note that I am not overly fond of Agile development or most other methodologies, and tend to go with whatever others are using. For this process, I'm just going seat-of-my-pants. If a formal methodology is important to you, remember what the philosopher on the deserted island said when faced with a cache of canned goods: "First, assume a can opener."
 
Goal: nested tabbed table that displays and works well in both Notes client and web/mobile browser
I decided to start with something familiar to Notes developers, but recognizable in modern web design as well.. Tabbed reports/interfaces were popular in Notes before they were popular on the web, but are now lots of places. (I don't care if you don't like them. Mutter that quietly to yourself, and then come up with something else you'd rather see and suggest it to me. Perhaps it can be my next demo.)
 
Inline PNG image
 
I won't show the web version, as I haven't developed it yet, but imagine it looks and acts basically the same. Our Bricklin's Demo mockup, our rules.
 
Source data
For the iteration of this demo, I'll use a Notes data source/view, as those are familiar to most of you. It also gives me a chance to use the REST API portion of Exciton Boost. There are 312 orders in the database, and a total of 68 nested tables what with all the cities and states.
 
Inline JPEG image
 
Bonus round
For the second iteration, I'm going to use a public REST API which I don't control. Imagine the nested tabbed table as looking kind of like the first. This is a bigger data set, I think, though I admit I haven't even checked carefully. It comes from Open Brewery DB and gives access to a bunch of brew pubs and such in the United States. We'll probably try different parameters as we work, but a sample from Ohio is included below. I don't quite know what I'll do with it, but we can play and you can make suggestions.
 
Inline JPEG image
 
 
=> Request a free Exciton Boost eval in January, and if you later purchase a license. we'll add in four hours of development assistance, a $700 value. <=
 
 
Continuing reading as I develop the solution. My follow up post, REST plus RPC: the right data in the right format, and no more, is now available.
 

Copyright 2021 Genii Software Ltd.

Tags:

Tue 5 Jan 2021, 03:24 PM
Inline JPEG image
 
Exciton Boost 4.6.0 was released on our website last week, and you can find the release notes online. We expect to release a few small point releases fairly regularly as we work with increasing numbers of customers and add requested features. Security is paramount, so there are likely to be slight tweaks to the Exciton Configuration template as we make sure we are providing the granular access people need without opening any vulnerabilities. 
 
Recent posts such as Data from Domino: nuts and bolts of REST calls have focused more on the REST API, as that has been the most pressing need identified by customers, but we hope to post some samples showing how the REST API can be complemented by and enhanced by use of the RPC (Remote Procedure Calls) API. Our most recent post on the RPC API is RPC - Empowering the server while retrieving to the client, but while interesting, most real-life uses have involved a combination of REST and RPC, so we'll get a couple of demos showing how they work together.
 
In the past, we have worked very hard to focus on product licenses, but as companies have trimmed developers, we have started doing more services in conjuction with our products when requested, We tend to refer to this as "development assistance" rather than consulting, but we are able to handle any level of work required by pulling in long-term colleagues and collaborators as needed or desired. As much or as little as you need. That's it, and no hard sell.
 
=> Request a free Exciton Boost eval in January, and if you later purchase a license. we'll add in four hours of development assistance, a $700 value. <=

Copyright 2021 Genii Software Ltd.

Tags:

Tue 29 Dec 2020, 06:06 PM
Inline JPEG image
 
Exciton Boost 4.6.0 was released on our website today. You can find the release notes online.
 
This is a low key announcement because there probably aren't many reading blog posts about software this close to the New Year, but if you want to get a jump on the competition, you can request an evaluation license today and give it a try.

Copyright 2020 Genii Software Ltd.

Wed 23 Dec 2020, 02:00 PM
Inline JPEG image
 
This is intended as a direct reference companion to my post earlier today, Getting data from Domino with Exciton Boost. Here, developers can see the exact requests made with parameters, HTTP headers, etc. in case anything in the video went by too quickly.
 
There are the nine tasks, but in some cases, I show multiple separate requests, as I used more than one in the video to explain different elements. For each request, I show the HTTP method and then one of different ways (e.g., JavaScript's fetch) to show how this code could be called. The second part is merely to get across the wide range of ways you could call Exciton Boost using different technologies. Finally, I include the video again in case you want to see these requests in action and see the results they generate.
 
Intro: Get core services
Returns a JSON array of objects, one for each view that is included, implicitly (all views) or explicitly (specified views), in the Exciton configuration db and marked as discoverable. Exciton is strict about data security, so views may be accessible but not discoverable. Views not included are inaccessible and will return a 404 error.
 
Intro) HTTP

GET /CurbAppeal.nsf/api/boost HTTP/1.1

Intro) cURL

curl --location --request GET '
https://geniisupport.com/CurbAppeal.nsf/api/boost'
 
 
Task 1: Get list of accessible views
Returns a JSON array of objects, one for each view that is included, implicitly (all views) or explicitly (specified views), in the Exciton configuration db and marked as discoverable. Exciton is strict about data security, so views may be accessible but not discoverable. Views not included are inaccessible and will return a 404 error.
 
1) HTTP
 
GET /CurbAppeal.nsf/api/boost/views HTTP/1.1
Accept: application/json
 
1) JavaScript - jQuery
 
var settings = {
  "url": "
https://geniisupport.com/CurbAppeal.nsf/api/boost/views",
  "method": "GET",
  "timeout": 0,
  "headers": {
    "Accept": "application/json"
  },
};

$.ajax(settings).done(function (response) {
  console.log(response);
});
 
 
Task 2: Get view with columns & metadata
Returns a JSON array of objects, one for each document in the view. Each document includes a link to get the document from the documents collection as well as item values for each view column. Note that there are parameters to page through the view if desired.
 
2) HTTP
 
GET /CurbAppeal.nsf/api/boost/views/76CA31CBCB19C70A8525694F0045C657 HTTP/1.1

2) Python - Request
 
import requests

payload={}
headers = {}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)

 
Task 3: Get filtered view
Returns a JSON array of objects, a subset of the full view filtered by a key match with the first view column. There are alternative ways to filter the view, but this is easiest.
 
3) HTTP
 
GET /CurbAppeal.nsf/api/boost/views/76CA31CBCB19C70A8525694F0045C657 HTTP/1.1
X-Key-Value: Chagrin Falls, OH

3) JavaScript - Fetch
 
var myHeaders = new Headers();
myHeaders.append("X-Key-Value", "Beachwood, OH");

var requestOptions = {
  method: 'GET',
  headers: myHeaders,
  redirect: 'follow'
};

fetch("
https://geniisupport.com/CurbAppeal.nsf/api/boost/views/76CA31CBCB19C70A8525694F0045C657", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));

 
Task 4: Get all items from the document
Returns a JSON object containing each item found on the document, or at least all allowed. Items may be explicitly allowed or disallowed in the configuration database, or all may be allowed by default. Some items such as time/date, author and reader fields, and rich text items will be represented by subobjects. Rich text fields are rendered to high fidelity roundtrippable HTML.
 
4) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B HTTP/1.1
 
4) Node JS - Request
 
var request = require('request');
var options = {
  'method': 'GET',
  'url': '
https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B',
  'headers': {
  }
};
request(options, function (error, response) {
  if (error) throw new Error(error);
  console.log(response.body);
});

 
Task 5: Get only selected items
Using an item list via URL or HTTP header, the request determines which items to returns in the JSON object. In addition to actual items, the request may use Notes formula language to create additional items or may use filtering to get subsets of rich text items.
 
5a) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B?metadata=false&items=StreetAddress,Bdrms,Full=Bathrooms,Half=Half_Baths,Photo HTTP/1.1
 
5a) JavaScript - XHR

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function() {
  if(this.readyState === 4) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "
https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B?metadata=false&items=StreetAddress,Bdrms,Full=Bathrooms,Half=Half_Baths,Photo");

xhr.send();

 
5b) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B?metadata=false HTTP/1.1
X-Items-List: StreetAddress+", "+City+", "+State=Address,Bdrms,Full=Bathrooms,Half=Half-Baths,Photo[Graphic 1]
 
5b) JavaScript - XHR
 
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function() {
  if(this.readyState === 4) {
    console.log(this.responseText);
  }
});

xhr.open("GET", "
https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B?metadata=false");
xhr.setRequestHeader("X-Items-List", "StreetAddress+\", \"+City+\", \"+State=Address,Bdrms,Full=Bathrooms,Half=Half-Baths,Photo[Graphic 1]");

xhr.send();

 
Task 6: Add selected items to view results
Going back to the view, the request can include either additional or replacement items for the view columns using an item list via URL or HTTP header. This allows retrieval of any allowed items with the view results, including rendered rich text items.
 
6) HTTP
 
GET /CurbAppeal.nsf/api/boost/views/76CA31CBCB19C70A8525694F0045C657?metadata=false&additems=Col1,Col2 HTTP/1.1
X-Key-Value: Beachwood, OH
 
6) Java - Unirest
 
Unirest.setTimeouts(0, 0);
HttpResponse<String> response = Unirest.get("
https://geniisupport.com/CurbAppeal.nsf/api/boost/views/76CA31CBCB19C70A8525694F0045C657?metadata=false&additems=Col1,Col2")
  .header("X-Key-Value", "Beachwood, OH")
  .asString();

 
Task 7: Get RTItems (as a list, as individual rtitems in JSON, as rendered HTML pages, or rendered HTML fragments)
Explores some of the different ways to retrieve rich text.
 
7a) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems HTTP/1.1
 
7a) PowerShell - RestMethod
 
$response = Invoke-RestMethod 'https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems' -Method 'GET' -Headers $headers
$response | ConvertTo-Json
 
7b) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Col1 HTTP/1.1
 
7b) PowerShell - RestMethod
 
$response = Invoke-RestMethod 'https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Col1' -Method 'GET' -Headers $headers
$response | ConvertTo-Json
 
7c) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Col1 HTTP/1.1
Accept: text/html
 
7c) PowerShell - RestMethod
 
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Accept", "text/html")

$response = Invoke-RestMethod '
https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Col1' -Method 'GET' -Headers $headers
$response | ConvertTo-Json
 
7d) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Col1?fragment=true HTTP/1.1
Accept: text/html
 
7d) PowerShell - RestMethod
 
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Accept", "text/html")

$response = Invoke-RestMethod '
https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Col1?fragment=true' -Method 'GET' -Headers $headers
$response | ConvertTo-Json

 
Task 8: Get image from rich text (either as an <img> link or as components in a JSON object, either allowing binary retrieval of the image from the server)
Depending on what exactly is needed, different ways to retrieve an image as a link back to the Domino db.
 
8a) HTTP [Image link as HTML fragment]
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Photo/chunks/Graphic 1?fragment=true HTTP/1.1
Accept: text/html
 
8a) Python - http.client
 
import http.client

conn = http.client.HTTPSConnection("geniisupport.com")
payload = ''
headers = {
  'Accept': 'text/html'
}
conn.request("GET", "/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Photo/chunks/Graphic 1?fragment=true", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
 
8b) HTTP [Image components as JSON]
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Photo/chunks/Graphic 1?metadata=false HTTP/1.1
X-Items-List: Photo[$GraphicOffset]=Offset,Photo[$GraphicFormat]=Fmt,Photo[%GraphicHeightPX]=height,Photo[%GraphicWidthPX]=width
 
8b) Python - http.client
 
import http.client

conn = http.client.HTTPSConnection("geniisupport.com")
payload = ''
headers = {
  'X-Items-List': 'Photo[$GraphicOffset]=Offset,Photo[$GraphicFormat]=Fmt,Photo[%GraphicHeightPX]=height,Photo[%GraphicWidthPX]=width'
}
conn.request("GET", "/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Photo/chunks/Graphic 1?metadata=false", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

 
Task 9: Get image data URI
Retrieves an image as an <img> link with the data URI included, so that the entire image is local. The dataimgs parameter shown can be used for any rich text or document retrieval so that the rendered HTML has the images locally available without any requirement for the Domino image to be accessible.
 
9) HTTP
 
GET /CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Photo/chunks/Graphic 1?fragment=true&dataimgs=max HTTP/1.1
Accept: text/html
 
9) Shell - wget
 
wget --no-check-certificate --quiet \
  --method GET \
  --timeout=0 \
  --header 'Accept: text/html' \
   '
https://geniisupport.com/CurbAppeal.nsf/api/boost/documents/524C08BF648BE77A85257B8600798F5B/rtitems/Photo/chunks/Graphic 1?fragment=true&dataimgs=max'

 
 

Copyright 2020 Genii Software Ltd.