Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
A service is a component of an information system that exposes functionality and data from a context, to let users or information systems interact by creating, reading, updating, and deleting data.
A service is a component of an information system. Its life starts when it is deployed and ends when it is pulled back. A typical instance is a user interface based on HTML-CSS that runs in a browser. But an application program interface (API) that serves other computers with web services is a perfectly valid instance as well.
The definition of a service specifies which data is presented to which users. For every different use of the system a different service can be defined. This may lead to a substantial amount of services for large and complex systems. However, one device will show one service only at any given moment in time.
This page gives syntactic details of services. Some more explanations are found here.
Please note that the keyword INTERFACE
is still used. That may be confusing. In a future release of Ampersand the keyword INTERFACE
will become obsolete and the word SERVICE
will be used.
This example specifies three tabs. One shows students, one shows courses and one shows modules. This is what it looks like when run in a browser:
Due to the complexity of services, its syntax and meaning are discussed in a separate section.
On the user screen each atom is displayed in some form as data. If a service exists for that atom, that is shown to the user as a hyperlink to which you can navigate.
When running an application in your browser, you are watching one user interface at any given moment in time. Each hyperlink on your screen represents an atom to which some service applies. To navigate to that user interface, you click on the hyperlink. You will see the service being applied solely to the atom you just clicked. To determine the atom(s) to which a service applies, each service has an interface term.
The next sections contain two examples:
a client service to allow clients of a web shop to change their name and address and show them status information of their orders;
a login service to demonstrate how to get different interface structures under varying conditions.
Suppose we have a delivery-hub that distributes orders over vendors and registers the subsequent deliveries. Let us define a service for clients, to allow clients to change their name and address and display their orders.
The service has a header, which is the first line in this example:
The word ClientInfo
is the name of this service. This name identifies the service , so it must be unique throughout the entire context.
This service is shown only to users with roles Client
or Vendor
. That is indicated by the restriction FOR Client,Vendor
. Without that restriction, the service is available for every user in any role.
The term to the right of the colon (:
) symbol is called the interface term. A service is called from an atom which must be in the domain of this term. Let, for example, Peter
be a Client
. As Peter
is an element of the domain of I[Client]
, the service can be called from that atom.
The same term, I[Client]
, is also used as box term for the box that follows the header. For every element in the codomain of the box term, a container (in HTML: <div>
) will be drawn on the user screen. That box serves as a subinterface, which is called with precisely one atom. With I[Client]
as box term, the codomain will contain just one atom, which is precisely the atom from which the service was called.
In this example, the outermost box contains seven box items and the innermost box two. Each box item has a label and an term. For example the box item "Name" : clientName
has "Name"
as its label and clientName
as term. The atom a
from which the box was called is used to select the pairs (a
,x
) from the term. All x
-es for which (a
,x
) is in clientName
will be displayed. Supposing that the relation clientName
associates only one name to a client, this specific box item displays just one name. However, in the fifth box item, the term orderedBy~ - V; orderAccepted~
may contain an arbitrary number of orders to be accepted by provider, all of which are shown.
The syntax of a service is best explained by means of examples. However, if you want to understand the syntax in detail, this section is what you are looking for.
A service specification has the following structure. It is identical for user interfaces (INTERFACE
) and application programming interfaces (API
).
The name of a service must be unique within the context. The term defines the atoms to which the interface can be applied. The (optional) crud annotation constrains the possible interactions a user can do. The (optional) views determine what the service will look like. If no view is specified, the service will look like the screenshot above. Finally the sub-interface contains all the contents, i.e. the fields, field names and the constraints on them.
The hierarchy of boxes in a service comes from the following (recursive) syntax of <subinterface>
.
A sub-interface may be defined on the spot (by <boxKey> <box>
) or it may link to another service to reuse its structure:
The boxKey is meant to tell the front-end application what the service looks like. The compiler uses templates to adapt an interface to specific needs regarding its HTML structure. Please read the documentation of templates for details.
If no htmlname is specified, Ampersand uses BOX <FORM>
by default.
A box is simply a list of service items (ifcItem
) separated by commas. Each service item specifies a field in the service or a sub-interface.
Each service item has a label that must be unique within the box. After the colon there is either a term or a text. The term specifies which data is related to the field it specifies if it has no sub-interface. If it does, it specifies the atoms on which the box is applied.
Ampersand is meant for back-end design. It offers no features for front-end design. For that purpose we advise you use contemporary front-end tools for web-based applications. Your Ampersand application is designed to be adaptable, especially for this purpose.
However, Ampersand offers a few layout features that let you place items. It has three built-in layout options, colums, rows and tabs, which you can mix freely.
The column layout uses BOX <TABLE>
to instruct the front-end application to use a tabular layout in user interfaces. Here is an example of a service, which uses the table layout.
This service shows three columns in the user interface, Students, Course and Modules. The first column is not readable, because the CRUD annotation blocks this column for reading. It would have shown students in each row, because the target of V[SESSION*Student]
is Student
. The second column shows courses in two columns, Course and Modules. The third column shows modules in three columns. This is what the user will see on the screen.
The row layout uses BOX <FORM>
to instruct the front-end application to layout the user interface row by row. Here is an example of a service, which uses the row layout on the top level.
This service shows three rows in the user interface, Students, Course and Modules. The first column shows students in each of its rows. Each student is shown in the column layout. The second row shows courses in two columns, Course and Modules. Please read about templates if you are curious which other ways of displaying information there are besides BOX <FORM>
. Please read the explanation of CRUD annotations if you are curious about the CRUD annotations. This is what the user will see on the screen.
The column layout uses BOX <TABS>
to instruct the front-end application to tabs in the user interface. Here is an example of a service, which uses the column layout.
This service shows three tabs in the user interface, Students, Course and Modules. Only one tab is shown at a time, to avoid cluttered data. This is what the user will see on the screen.
We have discussed the COLS
, ROWS
, and TABS
layout options. Please note that these options do not change the semantics; whatever your options, Ampersand displays the same data in the same fields.
If these options are not enough, you can enhance your application with your own layouts.
TODO: This example is subject to bitrot. It has to be redone.
This example defines a login/logout service, because it is familiar. We show this example to demonstrate how to get different interface structures under varying conditions.
The compiler uses templates to adapt an interface to specific needs regarding its HTML structure. Please read the documentation of templates first for details.
To link system activities to a person or organisation, we use the notion of Account
. To log in means to associate a session with the Account
of the user. This association is made in the relation sessionAccount
. To log out means to break that link, i.e. to remove the session/account pair from relation sessionAccount
. When logging in, it is customary that the user identifies herself. In this example we do this with a UserID
and Password
.
A UserId
is used to identify the user by a unique name. In this way, the (system generated) key of the user in the database is kept within the database.
To make it more difficult to use an other person's Account
, the system registers passwords. A Password
is a string of characters known to the user only. For this reason, the login service must not expose the password while the user is typing it.
To isolate a data space for one specific user, we use the notion of session. A SESSION
corresponds with the notion of session as used in browsers. Ampersand links the session called '_SESSION'
to the current browser session, which results in the behaviour one would expect of a browser session.
A login service allows a user to log in and log out of the system. Here is what it looks like in a browser:
Wonder what the 25al1rdkdfvmapkkqvuf5sroc5 means? Well this is the session number of the actual browser session. It is the value for which the atom
"_SESSION"
stands in your script.
When you type your name, it shows up in the field Userid, but when you type in the password it is obscured by dots
(
as we would expect
)
:
When we then type
<enter>
, the login functionality disappears and the logout functionality appears:
When you click the checkbox, you have logged out and will return to the first screen
To understand how it all works, let us discuss the code for this service:
If you analyse this code, notice the nested structure of BOX
-es. The service is a box on the top level with two sub-boxes labeled "Login"
and "Logout"
.
The top-level box has "_SESSION"[SESSION]
as its box-term. What you must remember is that the every atom of the codomain of that term causes one contain (HTML: <div>
). In this example, the codomain of "_SESSION"[SESSION]
is just one atom, which is the session identifier. That is shown in the title of the outmost box in the browser.
<HROWS>
We would expect to see two subboxes, one labeled "Login"
and another labeled "Logout"
. However, the outmost box was annotated with <HROWS>
. The H
stands for hidden. It means that empty subboxes will be hidden from the user. Now look at the box-terms of the "Login"
- and "Logout"
subboxes. Which elements are in the codomain of the box-term of the "Login"
subbox? It says: "All sessions without an account associated with it". Since there is only one session (i.e. the browser-session) this comes down to "the current session, provided there is no account associated with it." So if nobody is logged in in the current browser session, the session atom is the only atom. Otherwise there is no atom and the "Login"
subbox is not shown. Similarly, which elements are there to make the the "Logout"
subbox appear? That box-term shows all sessions associated with an account. Which would be the account of the current session, provided someone is logged in.
That explains why the "Login"
subbox is shown when nobody is logged in and the "Logout"
subbox is shown when someone is logged in.
So let us do the following experiment: change the
<HROWS>
annotation to
<ROWS>
. Then we will see both boxes:
Notice that both subboxes have the
H
in their annotations, so in the screeshot above the
"Logout"
subbox remains empty. However, when logged in, the other subbox remains empty:
Services are meant to expose functionality and data from a context, to let users or information systems interact with the system by creating, reading, updating, and deleting data.
Note: The service definition must be outside a pattern
The following figure is an example of a user interface, which shows the name, status, e-mail and co-workers of a person called "J. Lovell".
The specification of this user interface is given in the following service definition:
To understand this fragment, take notice of:
The name of this service is Person
. This name immediately follows the keyword INTERFACE
.
The term following the colon, I[Person]
, is the interface term of this service.
The service can be applied to any atom from the domain of the interface term. So this particular service is applicable to any atom of type Person
. In the screenshot, it applies to "J. Lovell"
.
The labels "Name", "Status", "Email", and "Works with" correspond to field names in the user interface.
Each term at the right of a field name specifies which data is presented in the field. For this reason it is called the field term for that field. Field name and field term are separated by a colon.
Of all pairs <"J. Lovell", x>
from the field term, the field displays the right atom x
. A field term always works on one specific atom on the left, which is "J. Lovell"
in this example.
Field terms are subject to type checking. The following relations provide an example for getting a type-correct service:
The source concepts of a field term must match the target concept of the interface term.
Looking at the screenshot, we can tell that "J. Lovell"
has one personName (which is "J. Lovell"
), it has no personStatus, one personEmail and three persons to work with in RELATION workswith
.
You can create structure in a service by nesting. Here is an example:
The specification of this service is given in the following code fragment.
Notice the following features:
1. The structure of a service is hierarchical. It consists of boxes within a box. This is because a field term may be followed by a BOX
with a list of subservices. Without it, it is just a field term. 2. When a field term is followed by a BOX
, every atom in the codomain of the field term is displayed in a box of its own on the screen. That box behaves like a service with the field term serving as interface term of that subservice. 3. By this mechanism, the hierarchical structure of the entire service translates directly to the hierarchical structure of the web-page in which it is displayed. 4. The source concept of a field term must match with the target concept of the field term outside the box.
5. The target concept of a field term that has a box, must match with the source concepts of each field inside that box.
Especially in more complicated services, you will find it nice to adapt the layout of the fields in the user interface. For this purpose, you can substitute the word BOX
by COLS
, ROWS
, or TABS
, as in the following code fragment. Note that these annotation have no meaning other than to change what the user interface looks like.
Notice the effect that these changes have on the user interface.
Notice the following features:
1. The keyword BOX <TABS>
turns the box into a layout with tabs.
2. The keyword BOX <TABLE>
turns the layout 90 degrees into columns.
3. The keyword BOX <FORM>
is default for any box. It does not change the effect of BOX
.
Compile and run the script Project Administration Example. Start by reproducing everything that is shown above. It is quite likely that you will be trying out your own ideas before you get to the end... Have fun!
After finishing your assignment, you have learned:
to explain how a service definition is displayed on the screen of a user.
to predict which data items a service applies to, if you know which pairs are in an interface term.
to predict which data items are displayed, if you know which pairs are in a field term.
to explain which atoms are used in a sub-interface.
to understand what the keywords TABS
, COLS
, and ROWS
do to your display.
More than one service may apply to the same atom. That gives you a choice on runtime to which service you want to navigate. If no service applies, that atom is not navigable.
You don't have to put up with the if they don't suit your purpose. You can change most anything by including your own code snippets. (to be done...).
CRUD annotations are used in services to constrain the functionality of fields and boxes in an INTERFACE
-statement. This allows you to minimize the functionality for your users, to design for easy learning.
Each CRUD annotation comes right after a term, so we can always refer to "the term" to which a CRUD annotation belongs. A CRUD annotation constrains the things your user can do with the target atoms and the pairs of its term.
The CRUD-annotation specifies Create, Read, Update, and Delete rights for the term it follows. Capital = allowed, Non-capital = not allowed. CRUD is the default, so if you specify nothing, everything is allowed. The following service definition illustrates this.
The user interface defined by this service is shown as a screenshot below. Notice that the lowercase r in the annotation of the Students box prevents showing the underlying box. The full CRUD functionality in Course yields 'create' functionality (the green plus-button), 'remove pair' functionality (red minus button), and 'delete atom' functionality (the red trash can button). The lowercase c, u, and d in the Modules box prevents displaying that functionality in the user interface.
The next sections give some more detailed information on the run time semantics for CRUD annotations as implemented in Ampersand.
A top-level Update or Create are common in my own scripts, e.g. to create an overview of People and be able to create a new Person: INTERFACE "People" : V[SESSION*Person] CRud COLS []
. And update is also possible.
The red minus is enabled by U
. It unlinks an atom (by deleting a pair from a relation) and leaves the atom alone.
The red trash bin is enabled by D
. It removes an atom and all pairs in which that atom is used.
Motivations for CRUD-functionality are found in the GitHub discussions on CRUD functionality.
Read
CRUD for boxes
CRUD for fields
R
Read is allowed
Read is allowed
r
Read is not allowed
Read is not allowed
CRUD
for a box
for a field.
C
c
Atoms cannot be created
Atoms cannot be created
Update
CRUD for boxes
CRUD for fields
U
u
Update is not allowed
Update is not allowed
Delete
CRUD for boxes
CRUD for fields
D
d
delete not allowed
delete not allowed
A + (plus) button is displayed that lets you create a new atom, but only if the box-expression is editable.
Enter a new atom and a +
button appears. Click the + to add that atom to the listed set of atoms. If you enter an atom that exists (Peter), you can select it.
Removing and/or adding a pair (src,tgt) is allowed if expr is editable and the atom exists. Deleting a pair is done with the - button; the atom will NOT be deleted.
Removing and/or adding a pair (src,tgt) is allowed if expr is editable and the atom exists. Deleting a pair is done with the - button; the atom will NOT be deleted.
Deleting a pair is done with the - (minus) button. Deleting an atom is done with the trash bin.
Delete atom (tgt) and all pairs in which it is used.