Skip to content
This repository was archived by the owner on Feb 29, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -943,12 +943,12 @@ Content-Length: 273

[Shea Valentine](https://github.com/sheavalentine-mdsol) and [Mark W. Foster](https://github.com/fosdev)

[*Data Objects*]: #7-data-objects
[*Data Object*]: #7-data-objects
[*Link Objects*]: #6-link-objects
[*Link Object*]: #6-link-objects
[*Reference Object*]: #5-reference-objects
[*Reference Objects*]: #5-reference-objects
[*Resource Object*]: #4-resource-objects
[*Resource Objects*]: #4-resource-objects
[*Data Objects*]: #5-data-objects
[*Data Object*]: #5-data-objects
[*Link Objects*]: #4-link-objects
[*Link Object*]: #4-link-objects
[*Reference Object*]: #7-reference-objects
[*Reference Objects*]: #7-reference-objects
[*Resource Object*]: #6-resource-objects
[*Resource Objects*]: #6-resource-objects
[I-D.wilde-profile-link]: http://tools.ietf.org/html/draft-wilde-profile-link-04
53 changes: 27 additions & 26 deletions tutorial.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Hale Tutorial
This document describes how the Hale serializer renders documents by providing examples using a ficitonal API, Coffee Bucks. The tutorial steps through every feature of Hale and provides concrete examples of how to use it.
This document describes how the Hale serializer renders documents by providing examples using a fictional API, Coffee Bucks. The tutorial steps through every feature of Hale and provides concrete examples of how to use it.

## An Overview of Coffee Bucks
The Coffee Bucks APIrepresents the Point of Sale (PoS) system for a coffee shop.
The Coffee Bucks API represents the Point of Sale (PoS) system for a coffee shop.

## The Basic Hale Document
The simplest valid Hale document is an empty object:
```json
{}
```
This is so because a hale document requires no elements. Obviously this document contains no information, but it nonetheless provides a canvas from which we can work and that we will use in the following sections.
This is so because a Hale document requires no elements. Obviously this document contains no information, but it nonetheless provides a canvas from which we can work and that we will use in the following sections.

## The Coffee Bucks Entry Point
The first thing we want to do is to describe an API entry point. This entry point is the place in the API that allows client servers to perform the basic actions that exist on a PoS API. In the entry point we anticipate a set of links that go to the other actions. So our basic structure looks like the following:
Expand Down Expand Up @@ -93,7 +93,7 @@ Let's start by specifying how to see a list of orders. Using HTTP this is a GET
}
}
```
The URL chosen here is the same as the `self` link because I've decided that another resource is superfluous. Still, this link will show all orders, regardless of their current state. What's probably more important for the business workflow is to show orders that are in a particular state.
The URL chosen here is the same as the `self` link because I've decided that another resource is superfluous. Still, this link will show all orders, regardless of their current state. What's probably more important for the business workflow is to show orders that are in a particular state.

Let's go ahead and add that capability into our Hale document as follows:
```json
Expand Down Expand Up @@ -127,7 +127,7 @@ Let's go ahead and add that capability into our Hale document as follows:
}
}
```
With this addition to the Hale document, we have added the `order_status` template variable in the `href` property, as well as a matching `order_status` data object that has two properties.
With this addition to the Hale document, we have added the `order_status` template variable in the `href` property, as well as a matching `order_status` data object that has two properties.
The `scope` property tells the client server whether the data belongs in the URL or in the request body. Since the `scope` value is `href`, the client knows that this `order_status` belongs in the URL.
The `options` property here specifies that valid values for `order_status` are `pending_payment`, `preparing`, or `fulfilled`. These are the "states" that an order can have. The `in` property specifies that those are the only possible fields.
We are also allowing multiple values to be set for `order_status`, which means that we are allowing URLs such as 'www.example.com/coffeebucks?order_status=preparing\&order_status=fulfilled' which would show a list of orders matching either of those.
Expand Down Expand Up @@ -170,7 +170,7 @@ Let's define each of these data objects one by one. For example, we define `drin
}
}
```
Here we have specified the `drink_type` data object. Like `order_status` before, `drink_type` specifies a set of possible orders and also specifies it only accepts values within that set. Unlike `order_status`, however, it is required, does not allow multiple values, and is in the request body instead of the URL.
Here we have specified the `drink_type` data object. Like `order_status` before, `drink_type` specifies a set of possible orders and also specifies it only accepts values within that set. Unlike `order_status`, however, it is required, does not allow multiple values, and is in the request body instead of the URL.
Let's add 'iced' next, as follows:
```json
{
Expand Down Expand Up @@ -199,7 +199,8 @@ Let's add 'iced' next, as follows:
}
}
```
The `iced` data object uses very different attributes. Here we specify `type`, which tells the client that the expected data type is a JSON Boolean type. The `value` property tells the client that if the `iced` property is not in the request body, the API server will assume that the value is `false`.
The `iced` data object uses very different attributes. Here we specify `type`, which tells the client that the expected data type is a JSON Boolean type. The `value` property tells the client that if the `iced` property is not in the request body, the API server will assume that the value is `false`.

Now let's add `size`, as follows:
```json
{
Expand Down Expand Up @@ -240,7 +241,7 @@ Now let's add `size`, as follows:
}
}
```
With the `size` data object we see `type` again show up. This time, though, it specifies a `primitive` type and a `data` type. Note that the the primitive type is specified before the colon (:) and that it specifies the JSON data type that it is supposed to be represented with. The data type is specified after the colon (':') and is intended to provide a _hint_ to the client about what sort of type this is. In this case, we specify that it is a range type, which is a well-defined HTML input type. It could be used by the client to understand that the elements in the `options` property have a rank ordering.
With the `size` data object we see `type` again show up. This time, though, it specifies a `primitive` type and a `data` type. Note that the primitive type is specified before the colon (:) and that it specifies the JSON data type that it is supposed to be represented with. The data type is specified after the colon (':') and is intended to provide a _hint_ to the client about what sort of type this is. In this case, we specify that it is a range type, which is a well-defined HTML input type. It could be used by the client to understand that the elements in the `options` property have a rank ordering.

We also specify a profile. This profile links to a specific element of the `coffeebucks` profile, which could specify how the numbers map to the names for those sizes. Alternatively, you could render this as follows:
```json
Expand Down Expand Up @@ -289,9 +290,9 @@ We also specify a profile. This profile links to a specific element of the `cof
}
}
```
This is a friendlier rendering. Note that this rendering specifies instead that its primitive type is integer, and its HTML data type is number.
NOTE: This data type could be anything; for example, it could be data type `coffeesizes`. But Hale anticipates that the client always understands HTML types, whereas `coffeesizes` would require specialized client knowledge.
For the options in the example, we defined a list of objects that give a name to each of the sizes and allows the client to "think" of the sizes either as numbers or as their name.
This is a friendlier rendering. Note that this rendering specifies instead that its primitive type is integer, and its HTML data type is number.
NOTE: This data type could be anything; for example, it could be data type `coffeesizes`. But Hale anticipates that the client always understands HTML types, whereas `coffeesizes` would require specialized client knowledge.
For the options in the example, we defined a list of objects that give a name to each of the sizes and allows the client to "think" of the sizes either as numbers or as their name.
Let's add the `shots` property next, as follows:
```json
{
Expand Down Expand Up @@ -343,8 +344,8 @@ Let's add the `shots` property next, as follows:
}
}
}
```
The shots data object is a more sensible range input. Here we define a minimum with `min` and we define a maximum with `max`. This tells the client that the most shots ever allowed is 16 and the least ever allowed is 0. Note that in this rendering we haven't specified `value`, nor have we specified `required`. This is because the API server will create a default number of shots based upon the size of the drink and the kind of drink that was requested.
```
The shots data object is a more sensible range input. Here we define a minimum with `min` and we define a maximum with `max`. This tells the client that the most shots ever allowed is 16 and the least ever allowed is 0. Note that in this rendering we haven't specified `value`, nor have we specified `required`. This is because the API server will create a default number of shots based upon the size of the drink and the kind of drink that was requested.
Finally, we add a similar object for `decaf`, as follows:
```json
{
Expand Down Expand Up @@ -405,7 +406,7 @@ Finally, we add a similar object for `decaf`, as follows:
}
}
}
```
```
The `decaf` data object looks almost exactly like the `shots` Data Object. But note that it uses a property called `lte`. This property is not defined in the Hale spec; instead it is using section 5.3 Constraint Extensions. In this case, we want to tell the client that `decaf` should be less than `shots` since 'decaf' is intended to specify which proportion of those shots should be made with 'decaf' coffee. However 'lte' is not in the spec, so if the client ignores this property it may happen to work - but should the client get an error from setting 'decaf' greater than 'shots', it can scrutinize the Data Objects, dereference the profile, and register the 'lte' Constraint Type.

####Putting It All Together
Expand Down Expand Up @@ -496,11 +497,11 @@ When we put the whole of the 'place_order' Link Object into the rest of our Hale
}
}
}
```
```
So far, we've added the basic links for our API, but this Hale document isn't just here for providing links. We also want to provide the set of orders in the system. This means we need to add some data and some more controls.

#### Adding the orders data
It is likely that you are going to want to tell the client how many total orders there are, how many orders are currently being displayed. You're also going to want to paginate the results.
It is likely that you are going to want to tell the client how many total orders there are, how many orders are currently being displayed. You're also going to want to paginate the results.
In the following, we'll add those.
```json
{
Expand Down Expand Up @@ -599,7 +600,7 @@ In the following, we'll add those.
"count": 3,
"total_count": 6
}
```
```
In this rendering, we add a `next` link that points to the current url with `?page=2` appended to the end. We also add a `page` attribute to the `orders` link, which enables the client to skip to a specific page. We also add the `count` and `total_count` attributes to the base document.
The client can understand these attributes by looking at the profile provided in the `profile` link.

Expand Down Expand Up @@ -717,7 +718,7 @@ Here you might be tempted to define an `orders` attribute in the base of the doc
}
```
In this rendering, we add an `order_list` element. We could call it `orders` and call the `orders` link `navigate` or something such as that. Remember that it doesn't really matter what you call them since their proper semantic meaning is described in the profile.
Unlike the other links, `order_list` is a JSON array rather than a JSON object. By specifying this array, you are saying that each link object within this array belongs to the `order_list` relation.
Unlike the other links, `order_list` is a JSON array rather than a JSON object. By specifying this array, you are saying that each link object within this array belongs to the `order_list` relation.
NOTE: This is most likely to come up when a resource acts as a container. But nothing prevents you from specifying two different ways to "search" using this structure.
#### Embedded Data
We now have a complete resource. However, given that our client is likely to be an interface, it might be worthwhile to have a way of including the order information in the resulting document. To do this, you first need to define an order resource as follows:
Expand Down Expand Up @@ -935,9 +936,9 @@ So, returning to our entry point, we can now embed these resources inside our ba
"total_count": 6
}
```
To summarize, to embed you create an array under `\_embedded` that is keyed by an attribute that matches the `\_link` attribute; in this case, `order_list`. Each object in the 'order_list' has its own state. So it provides different links that are contingent on that state.
To summarize, to embed you create an array under `_embedded` that is keyed by an attribute that matches the `_link` attribute; in this case, `order_list`. Each object in the 'order_list' has its own state. So it provides different links that are contingent on that state.

With this addition, you have successfully constructed a complex Hale document for a fully hypermedia, machine-driven, API to express a simple coffeebucks process.
With this addition, you have successfully constructed a complex Hale document for a fully hypermedia, machine-driven, API to express a simple coffeebucks process.

Now let's see what else we can do with a Hale document.

Expand Down Expand Up @@ -1016,7 +1017,7 @@ One of the first things you notice about the Coffee Bucks API is that it only al
```
This looks very much like the previously defined `place_order` attribute, but note several important changes.

First, notice that there's a different `request_encoding`. This is because the `form-urlencoded` attribute doesn't specify a way to POST complex data structures. Second, the `data` element contains two fields: `multi_order` and `orders`. "The `mutli_order` field specifies to the server computer that it is going to have more than a single order. The `orders` field actually contains all the orders.
First, notice that there's a different `request_encoding`. This is because the `form-urlencoded` attribute doesn't specify a way to POST complex data structures. Second, the `data` element contains two fields: `multi_order` and `orders`. The `mutli_order` field specifies to the server computer that it is going to have more than a single order. The `orders` field actually contains all the orders.

Looking at the `orders` field, note that its `type` is `object`. Being of an object type means that the `type` field is expecting a JSON object or its equivalent. It also specifies `multi` as true; this tells the client that the server anticipates in this case an array of objects.

Expand All @@ -1039,7 +1040,7 @@ The `orders` field specifies its own `data` field which then identifies the sub-
}
```

## _meta
## _meta

With the preceding additions included, the Hale document now looks has the following information:

Expand Down Expand Up @@ -1294,9 +1295,9 @@ With the preceding additions included, the Hale document now looks has the follo
}
```

As is, the document will work. While the document provides all of our functionality, however, it is needlessly verbose. To tweak and tighten up the document, we can use Hale's `_meta` feature. The `_meta` feature allows you to specify arbitrary information _about_ the current resource, information that is not itself a property of that resource. For example, you can specify `store_location` as an arbitrary meta property of the resource. Store location is relevant to the resource, but it is not a propery of an order itself.
As is, the document will work. While the document provides all of our functionality, however, it is needlessly verbose. To tweak and tighten up the document, we can use Hale's `_meta` feature. The `_meta` feature allows you to specify arbitrary information _about_ the current resource, information that is not itself a property of that resource. For example, you can specify `store_location` as an arbitrary meta property of the resource. Store location is relevant to the resource, but it is not a property of an order itself.

When a property is placed in `_meta` it automatically creates a _reference object_. Including a reference object in the definition of a state enables you to specify information that other resources in the document can use. In an endeavor to tighten up the document, you can use a `_meta` attribute consolidate the biggest chunk of the document, namely the common fields that the `place_order` and `multi_order` fields have.
When a property is placed in `_meta` it automatically creates a _reference object_. Including a reference object in the definition of a state enables you to specify information that other resources in the document can use. In an endeavor to tighten up the document, you can use a `_meta` attribute to consolidate the biggest chunk of the document, namely the common fields that the `place_order` and `multi_order` fields have.

The following changes reflect this use of the `_meta` feature to get this data.

Expand Down Expand Up @@ -1514,7 +1515,7 @@ The following changes reflect this use of the `_meta` feature to get this data.
In this emendation of the document, you'll note that we define a new reference object, `order_properties`, in the `_meta` section of the document. Once you define it, you can use `order_properties` throughout the document with the `_ref` tag. This `order_properties` tag always takes a list of tags which are interpreted from first to last item. These list tags instruct the client to replace that "_ref" tag with the listed properties.

## target
Now that the document is a little more terse, there is one other thing to consider. The `drink_type` element contains a menu. It may be out of the server's scope to know what that menu is and the menu may change. Therefore, instead of putting the menu inside the document explicitely, we can just reference an external resource to populate the document.
Now that the document is a little more terse, there is one other thing to consider. The `drink_type` element contains a menu. It may be out of the server's scope to know what that menu is and the menu may change. Therefore, instead of putting the menu inside the document explicitly, we can just reference an external resource to populate the document.

In the following rendering, we'll change `order_properties` to use a `_ref` object and get the list of menu items:

Expand Down Expand Up @@ -1687,4 +1688,4 @@ The following rendering shows what the Hale document now looks like:
"total_count": 6
}
```
With these changes, we have successfully rendered a Hale document that implements a Coffeebucks API.
With these changes, we have successfully rendered a Hale document that implements a Coffeebucks API.