Wednesday, 11 November 2020

More with Cloudant - Serverless web application and API

This popped into my inbox t'other day: -

In this tutorial, you will create a serverless web application using a bucket in Object Storage and implementing the application backend using IBM Cloud™ Functions.

As an event-driven platform, Cloud Functions supports a variety of use cases. Building web applications and APIs is one of them. With web apps, events are the interactions between the web browsers (or REST clients) and your web app, the HTTP requests. Instead of provisioning a virtual machine, a container or a Cloud Foundry runtime to deploy your backend, you can implement your backend API with a serverless platform. This can be a good solution to avoid paying for idle time and to let the platform scale as needed.

Any action (or function) in Cloud Functions can be turned into a HTTP endpoint ready to be consumed by web clients. When enabled for web, these actions are called web actions. Once you have web actions, you can assemble them into a full-featured API with API Gateway. API Gateway is a component of Cloud Functions to expose APIs. It comes with security, OAuth support, rate limiting, custom domain support.

Serverless web application and API

macOS Software Versioning and Updating from the command-line

Over and above the usual way to determine the Mac's software levels : -

 > About This Mac


and Software Update : -


we also have the command line: -

sw_vers 

ProductName: Mac OS X
ProductVersion: 10.15.7
BuildVersion: 19H15

and: -

softwareupdate -l

Software Update Tool
Finding available software
No new software available.

Getting ready for Big Sur, in case you couldn't tell 😂

Tuesday, 10 November 2020

Want to know a little more about Red Hat OpenShift Container Platform ?

 Red Hat have created a nice little set of self-paced tutorials / exercises for OpenShift Container Platform (OCP), which provide one with a set of exercises ( GUI and CLI steps ) to run against a ( very quickly provisioned ) OCP cluster.

I ran through a few of these yesterday afternoon, and can definitely recommend them to anyone looking to learn a little more about OCP.

More details here: -

OpenShift Playground

with an even more complete set of courses here: -

Red Hat Developer - Interactive Courses


Friday, 6 November 2020

Backslash broke my Cloudant

Well, the headline somewhat overhypes things BUT following on from my earlier post: -

Cloudy databases - Back with Cloudant on IBM Cloud

I noted that one can get the URL of a Cloudant instance from the Manage -> Overview page: -


and copy the External Endpoint (preferred) URL.

When this URL is copied to the clipboard, it includes a trailing slash ( / ), which is totally fine.

Right ?

Right ?

Well, not necessarily.

I then go on to describe how that URL can be added to a $URL environment variable: -

export URL="https://d66573ef-c567-4efc-6e59-c8ab7eb33de6-bluemix.cloudantnosqldb.appdomain.cloud/"

and then use that for various cURL commands such as: -

curl -s -k -X GET -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/music | json_pp

What I'd missed was.... it just does not work

That command fails with: -

{
   "reason" : "Database does not exist.",
   "error" : "not_found"
}

What was worse - I couldn't even create new databases: -

curl -s -k -X PUT -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/chocolate | json_pp

{
   "reason" : "Database does not exist.",
   "error" : "not_found"
}

curl -v -k -X PUT -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/cookies | json_pp

{
   "reason" : "Database does not exist.",
   "error" : "not_found"
}

which made absolutely no sense whatsoever.

It took me a while to work out what was going wrong ....

As mentioned, when I copied the URL from the Cloudant Overview page, it was coping the trailing slash.

This meant that the $URL variable included the slash ...

And then, when I specified the database name appended to the URL, I had: -

$URL/music

or: -

$URL/cookies

which, when the variable is expanded by the shell, turns into: -

https://d66573ef-c567-4efc-6e59-c8ab7eb33de6-bluemix.cloudantnosqldb.appdomain.cloud//music

or: -

https://d66573ef-c567-4efc-6e59-c8ab7eb33de6-bluemix.cloudantnosqldb.appdomain.cloud//cookies

In other words, we're trying to access / create a database called /music or /cookies.

Once I realised what was going wrong, I amended the $URL variable: -

export URL="https://d66573ef-c567-4efc-6e59-c8ab7eb33de6-bluemix.cloudantnosqldb.appdomain.cloud"

and all was well: -

curl -s -k -X PUT -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/chocolate | json_pp

{
   "ok" : true
}

curl -s -k -X GET -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/chocolate | json_pp

{
   "props" : {},
   "compact_running" : false,
   "update_seq" : "0-g1AAAAP3eJzLYWBgEMhgTmFQT0lKzi9KdUhJMjTQS8rVTU7WTS3VTUnVNTTWS87JL01JzCvRy0styQFqYMpjAZIMH4DUfyDISmQAmaAGN8GISAMeQAx4j2GAKZEGXIAYcB_DAAsiDTgAMeA81AAyAmEDxIT9ZAfCAogB68kOhAkQA-aTHQgNEAP6yQmEpAIgmVRPXipISgBpzsemmbDnkwJAmuOxaSbs8SQHkGZ_hGbSPG0A0m1PpqcVQJr1yfS0AEizPHmeTmRI4ofozAIACC1D2Q",
   "doc_del_count" : 0,
   "purge_seq" : 0,
   "instance_start_time" : "0",
   "doc_count" : 0,
   "sizes" : {
      "active" : 0,
      "external" : 0,
      "file" : 133940
   },
   "cluster" : {
      "w" : 2,
      "r" : 2,
      "n" : 3,
      "q" : 16
   },
   "disk_format_version" : 8,
   "db_name" : "chocolate"
}

Nice!

Thursday, 5 November 2020

More on Cloudant - things to read

Custom Indexers for Cloudant

Glynn Bird - CouchImport

IBM Cloud Docs - Cloudant - Creating an IBM Cloudant Query

IBM Cloud Docs - Cloudant - Authentication

Export & Import a Database with CouchDB

How to import JSON Document into a Cloudant NoSQL DB

Glynn Bird - Medium - Blogs for everything including Cloudant


Cloudy databases - Back with Cloudant on IBM Cloud

 A few years back, I was using Cloudant to store JSON data for a POC upon which my team and I were working, and wrote a few posts: -

IBM Integration Bus and Cloudant - Baby steps ...

Cloudant - Continuing to tinker

Doofus Alert - Using Cloudant queries via cURL

Fast forward to now ... I'm back in the game with Cloudant, and had to remind myself of some of the core concepts ...

So I created a Cloudant instance in my IBM Cloud account: -



Once my instance was created, I navigated to the Manage tab: -


( note that I've deliberately obscured the CRN and Endpoint details, for security )

However, I grabbed the External Endpoint (preferred) because that URL will be of use in a tick ...

Also note that Authentication methods defaults to IBM Cloud IAM which is important, in that it allows one to authenticate to the database using an API key / Access Token ...

Using the External Endpoint as an example, I've created a randomised sample here: -

https://d66573ef-c567-4efc-6e59-c8ab7eb33de6-bluemix.cloudantnosqldb.appdomain.cloud/

because ... SECURITY 😂

With that, and an Access Token, which I generated via my IBM Cloud API key, I'm able to access my Cloudant instance from the command-line: -

Generate Access Token

- This is a script that I use, via an alias, to parse the API key and generate an $ACCESS_TOKEN variable

source ~/genAccessToken.sh

Set the $URL variable

- Note, when copying/pasting the URL from the Manage -> Overview page, note that a trailing backslash ( / ) character will be copied. This must NOT be appended to the URL, or things go BOOM! ( I'll post about this shortly )

export URL="https://d66573ef-c567-4efc-6e59-c8ab7eb33de6-bluemix.cloudantnosqldb.appdomain.cloud"

Query the Cloudant instance

- Note that we don't need to explicitly authenticate to do this i.e. we don't present $ACCESS_TOKEN

curl -s -k -X GET $URL | json_pp

{
   "features" : [
      "geo",
      "access-ready",
      "iam",
      "partitioned",
      "pluggable-storage-engines",
      "scheduler"
   ],
   "version" : "2.1.1",
   "vendor" : {
      "version" : "8162",
      "name" : "IBM Cloudant",
      "variant" : "paas"
   },
   "features_flags" : [
      "partitioned"
   ],
   "couchdb" : "Welcome"
}

Create a database

- called movies

curl -s -k -X PUT $URL/movies | json_pp

{
   "error" : "unauthorized",
   "reason" : "one of _admin, server_admin is required for this request"
}

Goops, forgot to add the $ACCESS_TOKEN environment variable to the HTTP header

curl -s -k -X PUT -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/movies | json_pp

{
   "ok" : true
}

Query available databases

curl -s -k -X GET -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/_all_dbs | json_pp

[
   "movies"
]

Get the details on the movies database

curl -s -k -X GET -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/movies | json_pp

{
   "instance_start_time" : "0",
   "doc_count" : 0,
   "cluster" : {
      "q" : 16,
      "r" : 2,
      "w" : 2,
      "n" : 3
   },
   "compact_running" : false,
   "props" : {},
   "db_name" : "movies",
   "sizes" : {
      "external" : 0,
      "file" : 133940,
      "active" : 0
   },
   "purge_seq" : 0,
   "update_seq" : "0-g1AAAAP3eJzLYWBgEMhgTmFQT0lKzi9KdUhJMjTQS8rVTU7WTS3VTUnVNTTWS87JL01JzCvRy0styQFqYMpjAZIMH4DUfyDISmQAmaAGN8GISAMeQAx4j2GAKZEGXIAYcB_DAAsiDTgAMeA81AAyAmEDxIT9ZAfCAogB68kOhAkQA-aTHQgNEAP6yQmEpAIgmVRPXipISgBpzsemmbDnkwJAmuOxaSbs8SQHkGZ_hGbSPG0A0m1PpqcVQJr1yfS0AEizPHmeTmRI4ofozAIACC1D2Q",
   "disk_format_version" : 8,
   "doc_del_count" : 0
}

Create a JSON payload to populate the movies database

vi movies.json

{
  "docs":[
    {
      "_id":"1",
      "name":"War Games",
      "format":"DVD"
    },
    {
      "_id":"2",
      "name":"Top Gun",
      "format":"BluRay"
    },
    {
      "_id":"3",
      "name":"Rogue One",
      "format":"MP4"
    },
    {
      "_id":"4",
      "name":"Airplane",
      "format":"MP4"
    },
    {
      "_id":"5",
      "name":"Avengers",
      "format":"BluRay"
    },
    {
      "_id":"6",
      "name":"Mission Impossible",
      "format":"MP4"
    }
  ]
}

Populate the movies database

curl -s -k -X POST -H 'Authorization: Bearer '"$ACCESS_TOKEN" -H "Content-type: application/json" -d @movies.json $URL/movies/_bulk_docs | json_pp

[
   {
      "rev" : "1-48aa26577c7e2a4380df147095a1f592",
      "ok" : true,
      "id" : "1"
   },
   {
      "rev" : "1-cd93fc31e45e9379eb2137601ffaef38",
      "ok" : true,
      "id" : "2"
   },
   {
      "id" : "3",
      "rev" : "1-7bd2eb120d332170527a584897702e60",
      "ok" : true
   },
   {
      "ok" : true,
      "rev" : "1-5e930ff076df50b8cb9b09d354dc184b",
      "id" : "4"
   },
   {
      "rev" : "1-007906c513fc0c6d8c13c99e3ca4265f",
      "ok" : true,
      "id" : "5"
   },
   {
      "ok" : true,
      "rev" : "1-e3af283ebc1e44960cbc90690dde197c",
      "id" : "6"
   }
]

Query the movies database

curl -s -k -X GET -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/movies/_all_docs?include_docs=true | json_pp

{
   "rows" : [
      {
         "key" : "1",
         "value" : {
            "rev" : "1-48aa26577c7e2a4380df147095a1f592"
         },
         "id" : "1",
         "doc" : {
            "format" : "DVD",
            "_rev" : "1-48aa26577c7e2a4380df147095a1f592",
            "_id" : "1",
            "name" : "War Games"
         }
      },
      {
         "doc" : {
            "name" : "Top Gun",
            "format" : "BluRay",
            "_id" : "2",
            "_rev" : "1-cd93fc31e45e9379eb2137601ffaef38"
         },
         "id" : "2",
         "value" : {
            "rev" : "1-cd93fc31e45e9379eb2137601ffaef38"
         },
         "key" : "2"
      },
      {
         "doc" : {
            "_rev" : "1-7bd2eb120d332170527a584897702e60",
            "_id" : "3",
            "format" : "MP4",
            "name" : "Rogue One"
         },
         "key" : "3",
         "value" : {
            "rev" : "1-7bd2eb120d332170527a584897702e60"
         },
         "id" : "3"
      },
      {
         "id" : "4",
         "value" : {
            "rev" : "1-5e930ff076df50b8cb9b09d354dc184b"
         },
         "key" : "4",
         "doc" : {
            "name" : "Airplane",
            "_id" : "4",
            "_rev" : "1-5e930ff076df50b8cb9b09d354dc184b",
            "format" : "MP4"
         }
      },
      {
         "key" : "5",
         "value" : {
            "rev" : "1-007906c513fc0c6d8c13c99e3ca4265f"
         },
         "id" : "5",
         "doc" : {
            "_id" : "5",
            "_rev" : "1-007906c513fc0c6d8c13c99e3ca4265f",
            "format" : "BluRay",
            "name" : "Avengers"
         }
      },
      {
         "doc" : {
            "_rev" : "1-e3af283ebc1e44960cbc90690dde197c",
            "_id" : "6",
            "format" : "MP4",
            "name" : "Mission Impossible"
         },
         "key" : "6",
         "value" : {
            "rev" : "1-e3af283ebc1e44960cbc90690dde197c"
         },
         "id" : "6"
      }
   ],
   "offset" : 0,
   "total_rows" : 6
}

Query individual records

curl -s -k -X GET -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/movies/1 | json_pp

{
   "name" : "War Games",
   "_rev" : "1-48aa26577c7e2a4380df147095a1f592",
   "_id" : "1",
   "format" : "DVD"
}

curl -s -k -X GET -H 'Authorization: Bearer '"$ACCESS_TOKEN" $URL/movies/2 | json_pp

{
   "name" : "Top Gun",
   "format" : "BluRay",
   "_id" : "2",
   "_rev" : "1-cd93fc31e45e9379eb2137601ffaef38"
}

Query the database using search criteria

curl -s -k -X POST -H 'Authorization: Bearer '"$ACCESS_TOKEN" -H "Content-type: application/json" $URL/movies/_find -d '{
   "selector": {
     "name":"Airplane"
   }
 }' | json_pp

{
   "docs" : [
      {
         "name" : "Airplane",
         "format" : "MP4",
         "_id" : "4",
         "_rev" : "1-5e930ff076df50b8cb9b09d354dc184b"
      }
   ],
   "warning" : "No matching index found, create an index to optimize query time.",
   "bookmark" : "g1AAAAAyeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYozmoAkOGASEKEsAEr3DR8"
}

Create a text index on the name field

- This will allow us to mitigate the warnings shown previously

curl -s -k -X POST -H 'Authorization: Bearer '"$ACCESS_TOKEN" -H "Content-type: application/json" $URL/movies/_index -d '{
   "index": {
      "fields": [
         "name"
      ]
   },
   "name": "name-json-index",
   "type": "json"
}
' | json_pp

{
   "id" : "_design/700ae9eb4c3ddffe7f46e8b3140ee324aed53c0c",
   "result" : "created",
   "name" : "name-json-index"
}

Create a text index on the format field

curl -s -k -X POST -H 'Authorization: Bearer '"$ACCESS_TOKEN" -H "Content-type: application/json" $URL/movies/_index -d '{
   "index": {
      "fields": [
         "format"
      ]
   },
   "name": "format-json-index",
   "type": "json"
}
' | json_pp

{
   "name" : "format-json-index",
   "result" : "created",
   "id" : "_design/22dce09672c8b999fabd11c2cb7caffc9ee3ee37"
}

Query the database using the name field

curl -s -k -X POST -H 'Authorization: Bearer '"$ACCESS_TOKEN" -H "Content-type: application/json" $URL/movies/_find -d '{
   "selector": {
     "name":"Airplane"
   }
 }' | json_pp

{
   "bookmark" : "g1AAAAA_eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYozmoAkOGASOSAhkDiHY2ZRQU5iXmpWFgAI1RD1",
   "docs" : [
      {
         "name" : "Airplane",
         "_id" : "4",
         "_rev" : "1-5e930ff076df50b8cb9b09d354dc184b",
         "format" : "MP4"
      }
   ]
}

Query the database using the format field

curl -s -k -X POST -H 'Authorization: Bearer '"$ACCESS_TOKEN" -H "Content-type: application/json" $URL/movies/_find -d '{
   "selector": {
     "format":"MP4"
   }
 }' | json_pp

{
   "bookmark" : "g1AAAAA6eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYozmoEkOGASOSAhkDizb4BJVhYAt0cOlw",
   "docs" : [
      {
         "_rev" : "1-7bd2eb120d332170527a584897702e60",
         "format" : "MP4",
         "name" : "Rogue One",
         "_id" : "3"
      },
      {
         "_rev" : "1-5e930ff076df50b8cb9b09d354dc184b",
         "format" : "MP4",
         "name" : "Airplane",
         "_id" : "4"
      },
      {
         "name" : "Mission Impossible",
         "format" : "MP4",
         "_rev" : "1-e3af283ebc1e44960cbc90690dde197c",
         "_id" : "6"
      }
   ]
}

Just to close the loop, this is what I now see in the Cloudant Dashboard: -


and: -


It's been great to get back into Cloudant ... so much more to learn 🤣🤣🤣🤣🤣

Tuesday, 3 November 2020

UrbanCode Deploy - Using the query symbol to reference properties

 I was looking at an existing UrbanCode Deploy (UCD) element, known as a Component, and noticed that it made reference to an existing Property, but also included a query symbol ( ? ) within the configuration.

So, as an example, rather than referencing an existing Property via the conventional ( for UCD ) syntax of: -

${p:userName}

it instead referenced it using a query: -

${p?:userName}

which confused me initially.

Given that I am using UCD 6.2.7, I checked the documentation: -






Use the ? syntax if you are not sure about a property name:

${p?:propertyName}

This syntax returns a blank if the property is not found, and avoids the undefined property error.

Which is useful to know ....

Note to self - Firefox and local connections

 Whilst trying to hit my NAS from Firefox on my Mac, I kept seeing errors such as:- Unable to connect Firefox can’t establish a connection t...