Petstore: Users
The Users page (/console/users) is the petstore's server-mode CRUD example: a table of user accounts with a status
filter, an add dialog in the ribbon, and Edit and Delete row actions. Its one lesson beyond the plain table is the
password. It is write-only: it can be set from the form, but nothing the server sends ever contains it.
The table
The table pages, sorts and filters on the server. UsersRest.query runs the DataTables request through
DataTablesQuery.run and answers a malformed search expression with a 200 error envelope, as the other
server-mode pages do. The ribbon has a deselectable "User status" option group ($eq(ACTIVE) and $eq(INACTIVE)
on the userStatus column), the "+ Add user" dialog button and a refresh button. The row actions are Edit, which
opens the same form in a dialog, and Delete, which confirms first and is styled as dangerous.
The write-only password
Every read goes through a UserView record that has username, first name, last name and status, and no password
field. The query runs over UserView through its own InMemoryBeanQueryContext, so the password is neither
searchable nor serialized. GET /console/users/{username} returns the same view. The result toast of a create or an
update carries a UserView too.
The dialog is built by GET /console/users/form as a ModalDef with a FormDef:
| Case | Password field |
|---|---|
Add (no username query parameter) | Required, with the help text "Write-only; it is never shown again" |
Edit (?username=) | Optional, never pre-filled, with the help text "Leave blank to keep the current password" |
The field is a masked password input and never carries a value. On update, a field left out of the body keeps
its stored value, and a null or blank password keeps the stored password. Both the plain body and the dialog
envelope ({fields: {...}}) are accepted.
| Route | Does | Errors |
|---|---|---|
POST /console/users/query | One page of UserView rows | |
POST /console/users | Creates a user | 400 blank username, 409 username taken |
PUT /console/users/{username} | Updates a user | 404 unknown user, 400 unknown status |
DELETE /console/users/{username} | Deletes a user | 404 unknown user |
Each write calls ConsoleWrites.actor(req). With the petstore.secure setting on, a write without the demo bearer
token is a 401, and the audit entry names the actor. See Petstore: Secure for the
demo tokens.
Template
rowActions: [
{ id: 'edit', label: 'Edit', form: '/console/users/form?username={username}', present: 'dialog',
endpoint: '/console/users/{username}', method: 'PUT', onSuccess: 'redraw' },
{ id: 'delete', label: 'Delete', endpoint: '/console/users/{username}', method: 'DELETE',
confirm: "Delete user '{username}'?", confirmLabel: 'Delete', tone: 'danger', onSuccess: 'redraw' }
]
Builder twin
.rowActions(
RowAction.create("edit").label("Edit").form("/console/users/form?username={username}").present(Present.DIALOG)
.endpoint("/console/users/{username}").method(Method.PUT).onSuccess(OnSuccess.REDRAW),
RowAction.create("delete").label("Delete").endpoint("/console/users/{username}").method(Method.DELETE)
.confirm("Delete user '{username}'?").confirmLabel("Delete").tone(Tone.DANGER).onSuccess(OnSuccess.REDRAW))
Source: UsersRest in juneau-petstore/juneau-petstore-core/src/main/java/org/apache/juneau/petstore/console/users/;
UsersPageBuilders in .../console/pages/; users.ftlh under .../console/templates/.
Tests: UsersRest_Test.