Accessing Entity Relations

Entity Relationship URL’s

In the previous section we saw how model files can be used to define relationships between declared entities. We will now look at how to access and test the relationships via Postman tests.

This section uses the hasManyBelongsTo.json model from the jiffy repository. Rather than generate a new application from the model, you may choose to pull a pre-generated jiffy application source tree based on the model. To do so, switch to a sub-directory under your $GOPATH/src folder and run the following command in a terminal window.

    go get -u github.com/1414C/libraryapp

This will have the effect of pulling the application source code into a folder (libraryapp). Follow the instructions at the following link to start the application on your test machine.

To recap, our model contains two entities - Library and Book. A Library may have 0:n Books, and a Book must belong to one and only one Library.

{
    "entities":  [
        {
            "typeName": "Library",
            "properties": {
                "Name": {
                    "type": "string",
                    "format": "", 
                    "required": true,
                    "unique": false,
                    "index": "nonUnique",
                    "selectable": "eq,like"
                },
                "City": {
                    "type": "string",
                    "format": "", 
                    "required": true,
                    "unique": false,
                    "index": "",
                    "selectable": "eq,lt,gt,like"
                }
            },
            "compositeIndexes": [ 
                {"index": "name, city"}
            ],
            "relations": [
                    { 
                    "relName": "ToBooks",
                        "properties": {
                            "relType": "hasMany",
                            "toEntity": "Book"
                        }
                    }
                ],
                "ext_points": {
                    "gen_controller": true,
                    "gen_model": true
                }
    },
    {
        "typeName": "Book",
        "properties": {
            "Title": {
                "type": "string",
                "format": "", 
                "required": true,
                "default": "unknown title",
                "index": "nonUnique",
                "selectable": "eq,like"
            },
            "Author": {
                "type": "string",
                "format": "", 
                "required": false,
                "degault": "unknown author",
                "index": "nonUnique",
                "selectable": "eq,like"
            },
            "Hardcover": {
                "type": "bool",
                "format": "", 
                "required": true,
                "index": "",
                "selectable": "eq,ne"
            },
            "Copies": {
                "type": "uint64",
                "format": "", 
                "required": true,
                "index": "",
                "selectable": "eq,lt,gt"
            },
            "LibraryID": {
                "type": "uint64",
                "format": "", 
                "required": true,
                "index": "nonUnique",
                "selectable": "eq,like"
            }
        },
        "relations": [
                    { 
                    "relName": "ToLibrary",
                        "properties": {
                            "relType": "belongsTo",
                            "toEntity": "Library"
                        }
                    }
                ],
                "ext_points": {
                    "gen_controller": true,
                    "gen_model": true
                }
    }
    ]
}

Start the application.

    go run main.go -dev -rs   # use -rs the first time you start the app

Login

Launch Postman and create a new test specifying a target URL of: http://<your-ip-address:8080/usr/login making sure to select the http POST method. Maintain the request header as shown below to add the correct Content-Type.

set-content-type

Next, maintain the request body to provide a user-id and password as shown in the following JSON snippet. Typically the user-id for a Jiffy application is an email address, but we make an exception for the default administration user.

    {
        "email": "admin",
        "password": "initpass"
    }

When you have finished and your Postman (or other test utility) looks like the following image, click the ‘Send’ button to post your login request to the running application.

completed-login-req

If all goes well, you will get a http response code of 200 (status ok), and a block of JSON with a single ‘token’ tag containing a jumble of letters and numbers. This is the JSON Web Token (JWT) that will be used to validate your authorization to access the Person entity’s service end-points. If you want to read more about JWTs, jwt.io is a good place to start, or you can refer to the Access Control section of this document set.

login-jwt

Create a Library

Now that you have successfully logged into the application and received your first JWT, it is time to create a new ‘Person’ entity. Start by copying the content of the ‘token’ tag from the login response body to the clipboard. This JWT must henceforth be included in the http header of every subsequent request.

Create a new tab in Postman and specify a target URL of http://your-ip-address:8080/library with the http POST method. Next, add the following key-value pairs to the http header:

  • Content-Type : application\json
  • Authorization : Bearer *paste-your-JWT-here*

create-library-1

When you have finished maintaining the http header-values, click on ‘Body’ and maintain it using the ‘raw’ setting. This will allow you to paste the following JSON code snippet into the request’s body:

    {
        "name": "Bill the Cat Public Libary",
        "city": "Denver"
    }

When you have finished, the test session should look as follows and it is time to create your first ‘Library’ entity. Click ‘Send’ to post the new entity to the application.

create-library-2

Congratulations! You have created your first ‘Library’ entity!

create-library-3

What Just Happened?

The router matched the request URL to a route (service end-point), the middleware layer in the matched route examined the JWT, verified it was okay to proceed and then passed the raw JSON from the request body to the ‘Library’ entity’s controller. The controller unmarshalled the JSON into a ‘Library’ struct and then passed the result to the Create method in the model/validation layer. Validation of the ‘Library’ struct’s content occurred, and then a call was made to the underlying sqac ORM to create the entity on the database.

The sqac ORM-layer returned the new entity to the application’s model-layer, where it was checked and passed back to the controller layer, whereupon it was marshaled (struct content becomes JSON) and written to the the response-writer.

This is a high-level view of what transpired, but the general flow of things is accurate.

Notice that the entity passed back to us seems to have a couple of extra fields? All entities created via a Jiffy model file are injected with a primary-key of ‘id’ as well as a non-persistent ‘href’ field. In this example, our entity’s ‘id’ field was specified in the model file to be auto-incrementing with no starting value. ‘href’ is included in each entity’s GET responses, and acts as a self-reference providing the entity’s direct access URI to the consumer.

See the Annotated Simple Single Entity Model in the Model Maintenance section for details regarding key options.

Create a Second Library

Following the same steps as above, adjust the Body of the Library POST request to create another ‘Library’ entity as follows:

    {
        "name": "Opus the Penguin Public Library",
        "city": "Denver"
    }

Submission of the request should result in the creation of a new ‘Library’ entity:

create-library-4

Create a Book for Library 1

At the moment, your libraries don’t have any books but that is about to change. Create a new POST request in Postman, making sure to maintain the header as follows:

  • Content-Type : application\json
  • Authorization : Bearer *paste-your-JWT-here*

create-book-header

When you have finished maintaining the http header-values, click on ‘Body’ and maintain it using the ‘raw’ setting. This will allow you to paste the following JSON code snippet into the request’s body:

    {
        "title": "Peter Rabbit tricks Mr. Turtle",
        "author": "Beatrix Potter",
        "hardcover": true,
        "copies": 3,
        "library_id": 1
    }

When you have finished, the test session should look as follows and it is time to create your first ‘Book’ entity. Click ‘Send’ to post the new entity to the application.

create-book-1

If all went well, you will see something like this in the response:

create-book-2

Notice that we have allocated the new ‘Book’ entity to ‘Library’ entity 1.

Create More Books for Library 1

Following the same steps as above, adjust the Body of the Book POST request to create another ‘Book’ entity as follows:

    {
        "title": "Peter Rabbit tricks Mr. Fox",
        "author": "Beatrix Potter",
        "hardcover": false,
        "copies": 4,
        "library_id": 1
    }

Submission of the request should result in the creation of another new ‘Library’ entity:

create-book-3

Use the following JSON snippets to create a few more books for librarys 1 & 2:

Create Books for Library 1

    {
        "title": "Peter Rabbit tricks Mr. Wolf",
        "author": "Beatrix Potter",
        "hardcover": false,
        "copies": 2,
        "library_id": 1
    }
    {
        "title": "Peter Rabbit tricks Mr. Turtle",
        "author": "Beatrix Potter",
        "hardcover": true,
        "copies": 2,
        "library_id": 1
    }
    {
        "title": "Peter Rabbit tricks Mr. Turtle",
        "author": "Beatrix Potter",
        "hardcover": false,
        "copies": 5,
        "library_id": 1
    }
    {
        "title": "Peter Rabbit",
        "author": "Beatrix Potter",
        "hardcover": true,
        "copies": 2,
        "library_id": 1
    }

Create Books for Library 2

    {
        "title": "Peter Rabbit tricks Mr. Turtle",
        "author": "Beatrix Potter",
        "hardcover": false,
        "copies": 2,
        "library_id": 2
    }
    {
        "title": "Peter Rabbit",
        "author": "Beatrix Potter",
        "hardcover": true,
        "copies": 1,
        "library_id": 2
    }
    {
        "title": "Peter Rabbit Tricks Mr. Owl",
        "author": "Beatrix Potter",
        "hardcover": false,
        "copies": 4,
        "library_id": 2
    }

Get All of the Books For All Librarys

Create a new GET request in Postman specifying a target URL of http://your-ip-address:8080/books, making sure to maintain the header as follows:

  • Authorization : Bearer *paste-your-JWT-here*

When you have finished, the request should look as follows:

get-books-1

When you submit the request, the application should send a response containing all of the Book entities for both Libraries:

get-books-2

Get All of the Books for Library 1

Create a new GET request in Postman specifying a target URL of http://your-ip-address:8080/library/1/toBooks, making sure to maintain the header as follows:

  • Authorization : Bearer *paste-your-JWT-here*

When you have finished, the request should look as follows:

tobooks-1

When you submit the request, the application should send a response containing all of the Book entities that are owned by Library 1. As Libraries tend to have more than one title to lend, the tobooks relationship has been defined as a hasMany relationship (1:n). The end-point handler for the ./library/1/tobooks URI knows that there is a relationship between the Library and Book entities, and is therefore able to process the request.

    "relations": [
                    { 
                    "relName": "ToBooks",
                        "properties": {
                            "relType": "hasMany",
                            "toEntity": "Book"
                        }
                    }
                ],

The relations array in the Library block of the model file instructed jiffy to generate the end-point, end-point handler as well as database constraints to formally implement the relationship.

Try the same test to retrieve a list of all of the Book entities belonging to Library 2.

Get a Count of All Books

Rather than read potentially thousands of Books via a GET request, you may wish to simply get a count of how many Books exist. Use the following URI to create a new GET request to return the number of Books.

http://your-ip-address:8080/books/$count

The response should look similar to:

books-count-1

Jiffy applications return the result of a $count command in the response body rather than in the response header. In this case, the response body contains the number ‘9’ indicating that there are 9 Books in total.

Get a Count of Books Belonging to Library 1

Use the following URI to create a new GET request to return the number of Books that belong to Library 1.

http://your-ip-address:8080/library/1/tobooks/$count

The response should look similar to:

books-count-2

Get a Count of Books Belonging to Library 2

Use the following URI to create a new GET request to return the number of Books that belong to Library 2.

http://your-ip-address:8080/library/2/tobooks/$count

The response should look similar to:

books-count-3

Library 1 has 6 Books, while Library 2 has 3 giving a total of 9 Books.

Finding A Book’s Library

If the application has read a Book and needs details regarding that Book’s Library, the hasOne relationship can be leveraged to read the Library details. In the following example, a Book with an ID of 2 has been read from the database. The response body indicates that the Book belongsTo Library 1. The inverse of the hasMany relationship is the belongsTo relationship, which was defined in the model file that the application was generated from:

     "relations": [
                    { 
                    "relName": "ToLibrary",
                        "properties": {
                            "relType": "belongsTo",
                            "toEntity": "Library"
                        }
                    }
                ],

has-one-1

Use the following URI to create a new GET request to read a Book’s Library details from the database:

http://your-ip-address:8080/book/2/tolibrary

The response should look similar to:

book-to-library-1

What Else?

There are a number of suffixes that can be applied to the GET URI in order to influence what is returned to the caller. Refer to the CRUD Access, Filters and Relationships and Commands section in the Jiffy Application Overview section of the documentation for details.

As an example, a call to GET Books from Library 1 could be constructed with the following parameters:

https://your-ip-address:8080/library/1/tobooks/\$limit=3\$offset=2\$orderby=name$asc

  • return no more than 3 Books ($limit=3)
  • do not return the first 2 Books ($offset=2)
  • sort the returned list by name in ascending order

Offset and limit are common operations when you wish to return pages of results.