The walk-through on this page will help you create your first expression-backed record. Expression-backed records differ from other types of record types in that they use web services as their source data.
Use the provided API to understand how the configurations work. Then, try it with your own data or API. Keep in mind, the final configurations will need to change to fit your data type's field names and types.
For this tutorial, you will be required to create several objects in your environment. If you do not have permissions to do so, speak with your system administrator. Also, you will need to create these object in an application. If you have a sandbox application available, you can build object in there. Similarly, you can follow the Application Building Tutorial and create object within that.
The content below assumes a basic familiarity with interfaces and focuses more on the specifics of configuring the data source and list view. Consider going through the Interface Tutorial and Process-Backed and Entity-Backed Record Tutorial. Additionally, Record Design and Create a Record Type pages provide more consideration and how-to examples related to record creation.
The content below also assumes that you have a basic familiarity with using APIs. Note that you will need to register for a freely available, open source API to complete this tutorial.
Before creating the record type, you will need to register for API access, create the integrations that will be the data source, and create a reference CDT.
We will be using The Movie Database (DB) as our data source. Their API will let us retrieve movies released in the United State over the past two years.
Before using The Movie DB API, please read their terms of use before continuing.
Once your developer key has been approved, you should see your API Key in your user profile settings. Take note of the API Key (v3 auth) as we will be using it when we create our source integration object
The Movie DB supports, among other standards, Open API. Download the OAS file (oas.json
) found in their API documentation, which can be found in the header of the API page.
Once you have your account, API key, and Open API file, we can now begin creating our integrations objects.
To create a connected system:
oas.json
file downloaded from the Movie DB API.
The first integration we build will populate the record's list view. We will be using the discover/movie
operation to return an array of movies.
To create the integration to return movies:
Notice that the integration is pre-populated with all the necessary things to make an HTTP request to The Movie DB. If you'd like you can test the integration and see the result that come back from the integration.
Before we use this integration however, we will want to restrict the movies that come back to a more manageable data set. To do so, we will add several query parameters and a rule input to our integration.
Parameter | Value | Notes |
---|---|---|
primary_release_date.gte |
text(today()-1095,"yyyy-mm-dd") |
Allows us to limit our results by showing only releases for the last three years. Using text function to pass in date in the format desired by the Movie DB |
sort_by |
"revenue.desc" |
Sorting the movie by highest grossing |
region |
"US" |
Limit to US movies only. |
with_release_type |
"3|2" |
Returns only movies with a Theatrical or Theatrical (limited) release. |
page |
ri!page |
Chooses which page to use. |
The second integration we will create will generate the record's view. When a user click on a link in the record list, this integration will call back to The Movie DB to get specific movie information.
The last object we need to create is data type to map to our source expression. This will be used as our record fields.
:EXAMPLE
to the end of the default namespace.movies
. Optionally add a descriptiongenre_ids
field in the Movie DB is an array, however, to keep this example simple, we will only store the first genre published for each movie.When creating your CDT, follow the order above. This will ensure the columns in your list view matches what's are presented later in this example.
Now that we have all of our setup complete, let create and configure our new record type.
We will take a step-wise approach to configuring the record type, starting with source configuration, then setting up filters, followed by the record list, and finally the record type's summary view.
To begin:
Three things are needed when configuring a record type to accept expressions: a data type, a list view source expression, and a record view source expression. To access these fields, click on the Source & Default Filters page. From the Source dropdown select Expression.
Once expression is selected you will see the fields to define the data source.
Copy the expression below and paste in the List View Source Expression. Replace the name of the integration rule below if needed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a!localVariables(
local!result: rule!EXAMPLE_DiscoverMoveSourceExpression(
page: floor(rsp!pagingInfo.startIndex / rsp!pagingInfo.batchSize) + 1,
genres: local!genreIds
),
local!movies: cast(
'type!{urn:com:appian:types:EXAMPLE}movies?list',
local!result.result.body.results
),
a!dataSubset(
startIndex: rsp!pagingInfo.startIndex,
batchSize: 20,
sort: rsp!pagingInfo.sort,
totalCount: local!result.result.body.total_results,
data: local!movies,
identifiers: local!movies.id
)
)
Copy the expression below and paste in the Record View Source Expression. Replace the name of the integration rule below if needed.
1
rule!GetMovieByID(rp!id).result.body
At this point, we have a fully functioning albeit limited record type. You can go to the record type to preview by clicking on the Record List URL.
Before we add functionality to improve the usability of the records, let's review what we did. In our expression for the List View Source Expression we are creating a datasubset from our first integration. The one rule input we have, page gets the right page number by comparing the start index to the existing batch size.
The data subset is created using the a!datasubset() function. To create it, record source parameters (rsp!
) are used to populate parameters of the data subset. Now that we have our source expressions configured, lets continue to make improvements to the record list by creating user filters, enabling search, and creating the record list.
We are going to configure a user filter that will return a subset of movies based on genre. To do this, we will create another integration, setup the user filter expression, and finally update the List View Source Expression to accept user filter selections.
You can test this integration and see that it returns a series of movie genres.
Once we have the genre movie
In the User Filters field, copy the expression below and paste in the expression editor. Replace the name of the integration rule below if needed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
a!localVariables(
local!genres: rule!EXAMPLE_GetMovieGenres().result.body.genres,
a!facet(
name: "Genres",
allowMultipleSelections: false,
options: {
a!forEach(
local!genres,
a!facetOption(
id: fv!index,
name: fv!item.name,
filter: a!queryFilter(
field: "genre_ids",
operator: "=",
value: tointeger(fv!item.id)
)
)
}
)
)
This expression will create an array of filter options, which will compare the genre_id
of a record to the value of the genre filter.
The last thing we need to do is update the List View Source Expression to include filter selection. To do this, we will add to new local variables, local!genreIndex
and local!genreIds
to the source expression. In addition to the source expression, we must also update our source expression integration.
ri!genres
.In the record type, update the List View Source Expression with the example expression below. The final expression will look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
a!localVariables(
local!genreIndex: wherecontains("genre_ids", index(rsp!filters, "field", "")),
local!genreIds: if(
length(local!genreIndex) > 0,
index(rsp!filters, "value", {}),
{}
),
local!result: rule!EXAMPLE_DiscoverMoveSourceExpression(
page: floor(rsp!pagingInfo.startIndex / rsp!pagingInfo.batchSize) + 1,
genres: local!genreIds
),
local!movies: cast(
'type!{urn:com:appian:types:EXAMPLE}movies?list',
local!result.result.body.results
),
a!dataSubset(
startIndex: rsp!pagingInfo.startIndex,
batchSize: 20,
sort: rsp!pagingInfo.sort,
totalCount: local!result.result.body.total_results,
data: local!movies,
identifiers: local!movies.id
)
)
After saving the record type, you can check out the user filter operation on the movie's list view.
With this service we can search for keywords. To configure search results, we need to create an integration to the Movie DB's keyword search operation
ri!keyword
.Just as we updated the List View Source Expression for our user filter, we need to do the same for enabling search. In addition to the source expression, we must also update our source expression integration.
ri!keywords
.In the record type, update the Live View Source Expression with the example expression below. The final expression will look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
a!localVariables(
local!search: rule!EXAMPLE_SearchMovieKeywords(rsp!searchText),
local!genreIndex: wherecontains("genre_ids", index(rsp!filters, "field", "")),
local!genreIds: if(
length(local!genreIndex) > 0,
index(rsp!filters, "value", {}),
{}
),
local!result: rule!EXAMPLE_DiscoverMoveSourceExpression(
page: floor(rsp!pagingInfo.startIndex / rsp!pagingInfo.batchSize) + 1,
genres: local!genreIds,
keywords: if(
isnull(rsp!searchText),
"",
joinarray(local!search.result.body.results.id[1],",")
)
),
local!movies: cast(
'type!{urn:com:appian:types:EXAMPLE}movies?list',
local!result.result.body.results
),
a!dataSubset(
startIndex: rsp!pagingInfo.startIndex,
batchSize: 20,
sort: rsp!pagingInfo.sort,
totalCount: local!result.result.body.total_results,
data: local!movies,
identifiers: local!movies.id
)
)
Now that our search and filter is created, lets populate our record list with more meaningful data.
You should end up with a list view that looks like this:
A summary view for an expression-backed record is no different, structurally, from a summary view for any other type of record. What may be different, however, is how you acquire the data. For this example, our service calls will be coming from different web service operations, not the /discover/movies
source that populated our list.
Copy the expression below and paste in the interface definition.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
a!localVariables(
local!results: a!fromJson(rule!EXAMPLE_GetMovieByID(ri!id).result.body),
local!imagePath: "https://image.tmdb.org/t/p/original/",
{
a!billboardLayout(
backgroundMedia: a!webImage(
source: local!imagePath & local!results.backdrop_path
),
overlay: a!barOverlay(
contents: {
a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextItem(
text: index(local!results, "tagline", ""),
size: "MEDIUM",
style: "STRONG"
),
align: "CENTER"
)
}
),
height: "TALL",
marginBelow: "NONE"
),
a!columnsLayout(
columns: {
a!columnLayout(
contents: {
a!paragraphField(
label: "Synopsis",
labelPosition: "ADJACENT",
value: local!results.overview,
readonly: true,
height: "MEDIUM"
),
a!paragraphField(
label: "Genres",
labelPosition: "ADJACENT",
value: substitute(joinarray(local!results.genres.name, "|"), "|", char(10)),
readonly: true,
height: "MEDIUM"
),
a!textField(
label: "Release Date",
labelPosition: "ADJACENT",
value: local!results.release_date,
readonly: true
),
a!textField(
label: "Budget",
labelPosition: "ADJACENT",
value: dollar(local!results.budget),
readonly: true
),
a!textField(
label: "Box Office Revenue",
labelPosition: "ADJACENT",
value: dollar(local!results.revenue),
readonly: true
)
}
),
a!columnLayout(
contents: {
a!boxLayout(
label: "Theatrical Poster",
contents: {
a!imageField(
labelPosition: "COLLAPSED",
images: {
a!webImage(
source: local!imagePath & local!results.poster_path
)
},
size: "LARGE",
isThumbnail: false,
style: "STANDARD",
align: "CENTER"
),
a!imageField(
images: a!webImage(
source: "https://www.themoviedb.org/assets/1/v4/logos/powered-by-rectangle-green-dcada16968ed648d5eb3b36bbcfdd8cdf804f723dcca775c8f2bf4cea025aad6.svg",
size: "SMALL",
align: "END"
)
)
},
style: "STANDARD",
marginBelow: "STANDARD"
)
}
)
}
)
}
)
The last steps are to get the record's title and summary view configured.
rf!title
to give each record the title of the movie.In the Interface section, call the interface in the expression editor and pass in rp!id
. You're expression should look something like the example below.
1
rule!EXAMPLE_SummaryView(rp!id)
Your record is complete. It should look something like:
If you want to use a record picker with your expression-backed record, it should just work as expected. You should not need to change your record definition. But, in case something is not working as desired, here are a few notes on how the record picker works and how you can tweak your record design to optimize for a record picker.
If this picker does not meet your needs in some way, use a custom picker.
On This Page