Kathuko › Oracle APEX › Part 13

Part 13

Quality and shipping

Turnout works. This part makes it something you can hand to real clubs: every change tracked, every rule tested, the obvious security holes closed, the slow parts measured, and a checklist for the day it goes live.

Do it in this order, because each step makes the next one safe:

  1. Source control first, so every later change can be undone.
  2. Tests next, so you know a change didn't break a rule.
  3. Security review, while the app is still small enough to read end to end.
  4. Performance, measured, never guessed.
  5. Go live, from a checklist, not from memory.

Before you start

You need Turnout as it was at the end of Part 12. If you don't have it, import the Start here app, turnout-app-part-12.zip, from the downloads at the end of this page.

Most of this part is reading and checking rather than building. The one thing you add to the app is in Lesson 13.5: phone icons. Download the five icons from this page's downloads before you get there.

Refresh the sample data. The sample events are dated from the day you last ran the seed, and the early ones are only a few days out. In SQL Workshop › SQL Scripts, run turnout_seed.sql again so they're upcoming again. It resets the sample data, including anything you changed while testing.

Lesson 13.1 The app as source files

APEX 26.2 can describe a whole application as APEXlang: plain text files, one per page and one per kind of shared component. That's how Turnout lives in Git:

turnout/
  sql/          schema, seed, migrations (part5_login_codes.sql)
  packages/     the business logic and its tests
  apexlang/
    turnout/
      application.apx
      pages/                 p00001-discover.apx ... p09999-login.apx
      shared-components/     lists, authentications, email templates, automations
      workspace-components/  web credentials (no secrets)
tools/          validate and deploy scripts

You've had these files all along: every Finished download in this course is an APEXlang app in a zip. Unzip turnout-app-part-13.zip and open turnout/pages/p00020-event.apx in any text editor. You'll recognize every region, item and process you built.

Text files mean real code review: a diff shows exactly which button moved or which query changed, and a pull request can be read like any other code.

Validate before every import

SQLcl checks APEXlang without touching a database:

sql /nolog
SQL> apex validate -input turnout
SQL> exit

Install SQLcl from oracle.com/sqlcl (it needs Java 17 or later), unzip an app download, and run that from the folder that holds turnout/. "Validation successful." means the import will accept it. In a project you'd wrap the line in a small script, which is what tools/validate-apex-app.sh is in Turnout's repository.

It catches a misspelled property, a reference to a list that doesn't exist, or a missing confirmation message, in seconds. Turnout's history shows why it's worth it: the first validation found four errors that would each have failed the import.

Two lessons from building Turnout:

  • The compiler version matters. .apex/apexlang.json (a hidden folder: show hidden files to see it) names the APEXlang version, and it must match the compiler inside your SQLcl. When SQLcl is upgraded, update it.
  • Secrets never go in Git. Web credentials in workspace-components/ carry names only. Client IDs and secrets are typed into each workspace.

Lesson 13.2 Tests you run every time

Turnout has two test scripts, both plain PL/SQL so they run anywhere:

Script Checks Expect
turnout_api_tests.sql Security, capacity and waitlist, guests, membership, invitations, events 29 PASS, all rolled back
turnout_auth_tests.sql Email codes: hashing, expiry, attempts, rate limits; account linking 13 PASS, test rows removed

Run both after every package change, and before every import to production. A failure is a gift: it means a rule changed before a club found out.

Tests need a known starting point. The API tests expect the seed's Tuesday Track Intervals: three going, Grace then Lucas waiting. Click around that event in the app and a test fails, not because the code is wrong but because the data moved. To get back to the seed, run turnout_seed.sql again, then the tests. If your tables themselves are in a mess, run turnout_reset.sql and import the Finished app and click Install Supporting Objects: that recreates the tables, the seed and the latest packages, but it replaces your own app and its Spanish translation. In a team, give tests their own schema so nobody's clicking gets in the way.

Lesson 13.3 The security review

Read the app once, end to end, with this list:

Check Turnout Where
Every non-public page needs sign-in Yes: only 1, 2, 10, 20 and 9999 are public Page › Security
Every organizer and admin page has a scheme Is Organizer, Is Site Admin Part 6
Every page with ids in the URL has a checksum Arguments Must Have Checksum, except the public pages 10 and 20 (Unrestricted on purpose, so links can be shared; everything they show is public) Page › Security
Every change goes through an API that checks permissions to_security.require_* Part 3
Text people type is escaped when shown apex_escape.html in messages; APEX escapes report columns by default Pages 130, 9999
No secrets in the export Web credentials hold names only Part 5
Codes and tokens are random enough dbms_random today See below
APEX Advisor is clean Run it App Builder › Utilities › Advisor

One upgrade before production. Sign-in codes and invite tokens use dbms_random, which is fine for a course but predictable in principle. In production, use the cryptographic generator:

l_code := lpad(mod(to_number(rawtohex(dbms_crypto.randombytes(4)), 'XXXXXXXX'), 1000000), 6, '0');

DBMS_CRYPTO needs an execute grant from your DBA, which is why the course doesn't depend on it.

Turnout's approach and Deep Data Security

APEX 26.2 can use Deep Data Security, a feature of Oracle AI Database 26ai that enforces row and column rules in the database itself. You declare data roles and data grants (for example, "a member may select the RSVPs of events in their own clubs"), and the database applies them to every query from every client. APEX passes the signed-in person's identity to the database, so a report with no where clause at all still returns only what that person may see.

Turnout today Deep Data Security
Where the read rule lives In every query: where r.user_id = to_security.current_user_id Once, in a data grant
Where the change rule lives In every API procedure: to_security.require_club_role Still in the API: data grants govern what can be read and changed, but rules like "the waitlist moves up in order" are business logic
Forget a filter That page shows too much The database still filters it
Who sees the rule Anyone reading the page or package A DBA reading the grants; developers need to know they exist
Needs Any Oracle Database and APEX version Oracle AI Database 26ai, recent enough to include it, and configuration of end users and roles
Testing Plain PL/SQL tests acting as a person (act_as) Tests must connect as, or impersonate, an end user

What to choose. For Turnout as a course app, the explicit filters stay: you can read every rule, they run on apex.oracle.com and any database, and the tests prove them. For a new production app on 26ai with many reports and several ways in (APEX, REST, AI agents, reporting tools), Deep Data Security is worth the setup: one policy covers every path, and a forgotten filter stops being a data leak. Keep the API checks either way. Read rules and change rules are different jobs, and the database can't know that only an owner may hand over a club.

Lesson 13.4 Performance: measure first

With 248 people and about 120 events, every Turnout page is fast. A city with 50,000 members would find the slow parts. Find them before they do:

  1. Debug mode. Developer toolbar › Debug, reload, then View Debug. It lists every region with its time.
  2. The plan. Paste a slow query into SQL Commands and press Explain. A full scan of TO_RSVPS is the warning sign.

Three fixes cover most of what you'll find in an app like this:

Pattern In Turnout Fix when it's slow
A PL/SQL function called once per row Discover calls to_rsvp_api.spots_left for every card Compute it in SQL with a join and group by, or keep a counter column updated by the API
A scalar subquery per row My clubs counts upcoming events per club Join to a grouped subquery once
A missing index on a filter column TO_RSVPS is filtered by user_id on every personal page Already indexed in Part 2 (TO_RSVPS_USER_IX): check yours are too

Measure again after each change. A fix you can't measure isn't a fix.

Lesson 13.5 Installable on phones

Turnout is now a progressive web app: on a phone, "Add to Home Screen" gives it its own icon, and it opens full screen like a native app. Two settings and five images do it:

progressiveWebApp {
    enable: true
    installable: true
    display: standalone
    themeColor: #c2410c
}

APEX finds the icons by name in Static Application Files: icons/app-icon-32.png, -192.png, -512.png, and rounded 144 and 256 versions. They're the Turnout mark from Part 9, rendered at each size. The square versions fill the whole tile, because phones round the corners themselves.

Build it.

  1. Shared Components › Static Application Files › Create File, Directory icons. Upload all five icons from this page's downloads in one go: app-icon-32.png, app-icon-192.png, app-icon-512.png, app-icon-144-rounded.png and app-icon-256-rounded.png. Keep the names exactly as they are: APEX looks for them by name.
  2. Shared Components › Progressive Web App. Switch Enable Progressive Web App and Installable on, Display Standalone, Screen Orientation Any, Theme Color Custom and Custom Theme Color #c2410c. Apply Changes.

Check: run Turnout and look at the browser tab: the Turnout mark is its icon. On a phone, open the app and use Add to Home Screen; it opens full screen with the mark as its icon.

Lesson 13.6 The go-live checklist

  • DEMO_MODE set to N (Application Settings). Codes now go by email only.
  • MAIL_FROM set to an address your mail server may send from.
  • Email sending works from your instance: send yourself a code.
  • Social sign-in credentials filled in for each provider you use, and their settings set to Y.
  • At least two site admins assigned in Application Access Control.
  • Seed data not loaded. Create categories by hand, or with a small script.
  • dbms_crypto swapped in for codes and tokens (Lesson 13.3).
  • Both test scripts pass against the production schema before the first real person signs in.
  • APEX Advisor run, warnings read.
  • Debug off, and the developer toolbar hidden from end users (it is, for anyone without a workspace login).
  • A backup or export taken, and the restore tried once.
  • The Spanish translation re-seeded and published after the final import (Part 12).

Checkpoint

Turnout is ready for real clubs. You can change it safely (Git and validation), prove it still works (two test scripts), say why it's secure (the review), know where it'll be slow (measured), and launch it without forgetting a step (the checklist).

File Change
apexlang/turnout/application.apx Progressive web app on
apexlang/turnout/shared-components/static-files/icons/ Five app icons
apexlang/turnout/shared-components/static-files.apx Lists the icons

The capstone

Build one feature on your own, end to end, using everything in the course:

Recurring events. An organizer creates "Tuesday Track Intervals, every Tuesday at 6:30 PM for 8 weeks" once, and Turnout creates the eight events.

You'll need a table change with reasons (Part 2), an API procedure with tests (Part 3), a page and a dialog (Part 7), an authorization check (Part 6), a dynamic action or two (Part 8), and Spanish text (Part 12). Ship it with the checklist in Lesson 13.6.

Downloads for Part 13

Everything this part uses. Each file is plain text you can open in any editor before you run it.

Part 13 quiz

5 questions. Get 4 right to pass. You can retake it as often as you like.