What each app does
The hub
The hub handles as much as possible, so that job board apps are as thin as possible. Any functionality that is common to all or many job boards
is in the hub.
Handling installs
When the hub is installed, it checks for existing installed job board apps and updates its installed boards table.
Showing the settings page
The hub's settings page lets the admin user control the legacy source code field for each installed board.
The field defaults to defaultLegacySourceCode as passed by the board via
GET /jobBoards/forApp.
When legacy source code is set against a board, all linkouts for postings on that board will have a "source=" parameter appended with that value.
Some ATS's will be able to use the information in the tracker for sourcing purposes and won't need this field set.
Showing the job panel
The hub injects a panel for creating and updating postings into the job page in the ATS.
This shows a list of the existing postings for the job, across all boards, including:
- the board's icon (shrunk)
- the posting title (truncated)
- the status message (listing expired, job closed, etc.)
- menu/links to:
- Repost (re-open)
- Edit (shows the posting wizard again)
- Expire (close)
The panel also allows creating new postings by clicking a board's icon. The user then is redirected
to the posting form in a full window.
Showing the posting form
The posting wizard is a web page served by the hub to capture the user data (job title, categories etc.) for a posting.
In the fields section of the posting form, the hub captures the job board's
required fields (title, reference, description etc. and category values).
- To decide which common fields to display, the hub asks the board for it's settings
- To populate the category fields, the hub asks the job board for its categories
- To set defaults for the categories, the hub calls the category mapping API
- In the scheduled close date field, recruiter sees today + 30, though underneath the field is null
- The recruiter could also set scheduled close date to a value, e.g. so the posting is only open for 10 days. Or to a year in the future, in
which case the posting would remain active for a year, being re-billed every 30 days.
- Once the user has ok-ed the form, the hub:
- creates a posting object in its own database. The posting object holds all the data needed to post the job, including the fields.
- sends a "new posting" delta ping to the relevant job board app.
note right of poster: poster grabs common details:
note right of poster: get title, description
poster->ats: GET /jobs/byID/{}
note right of poster: get category drop down values
poster->board: GET /jobBoards/forApp/categories
poster->board: GET /jobBoards/forApp/categories/byID/{}
poster->board: GET /jobBoards/forApp/categories/byID/{}/values
note right of poster: get mappings (to set defaults)
poster->mapper: POST /jobBoards/forApp/{app}/mappings
note right of poster: poster pops UI so user can\nedit title, description, categories
note right of poster: get link to board's\niframe-ed custom fields UI (if any)
poster->board: GET /jobBoards/forApp/postingUI/byID/{posting}
note right of poster: Custom fields UI is shown.\n User edits, clicks OK. postMessage\npasses custom fields as json to hub.\nHub updates posting.
note right of poster: poster signals that the posting\nhas changed
poster->board: POST /jobBoards/postings/byID/{}/tenantDeltaPings
Handling expire, reopen requests
The panel allows the user to click to expire or reopen postings.
The hub responds by:
- Updating the posting object (actual close date, close date, status, last updated by email and date)
- sending a delta ping to the relevant job board app.
- displaying ajax spinning widget in the panel throughout
Building linkout urls, including modern (tracker) and legacy source tracking
For the posting, the hub builds the linkout url leading to the job on the careers site, including source tracking
information.
The hub builds this by:
- Call GET/BLAH to ask the primary career site for a link to the job
- Calling POST /trackers to create a campaign tracker and appending it to the linkout url
If the installed board has its legacy source code set, then the linkout url should have "&source= appended to it."
Processing postings that have expired
A dameon finds all postings that have arrived at their scheduled close date.
The daemon:
- sets status = closed
- sets actual close date = sysdate
- sends delta ping to relevant job board app
Listening for jobs closing in the ATS
The hub listens for jobs closing on the ATS. It closes the attached postings and notifies the relevant job board app.
- The hub learns via delta ping that a job has just closed in the ATS
- It iterates through all of the postings for that job doing this:
- set status = closed
- set close date = sysdate
- set status message = "Job closed"
- send delta ping to relevant job board app
Handling tenant reboots
When the hub starts up after maintenance, or the tenant is rebooted, the hub pulls all
category values from all installed job boards.
The job board's categories may have changed, making existing postings held in the
hub outdated.
If any existing open postings are found that are using a category value that no longer exists,
their stale flag is set. This causes the posting to be displayed e.g. in red in the job panel.
The posting form highlights to the user to which category needs fixing.
When the user successfully submits the posting form, the stale flag is cleared by the hub.
Handling uninstalls
The hub prevents uninstall (returns 400 to DELETE /tenants/{}) if there are installed boards.
When a job board app is uninstalled, the hub preserves all data by updating installed boards table:
- prior app shortcode = app shortcode
- app shortcode = null
So the tenant can reinstall the app (or another customer who picks up the same tenant shortcode can install it for the first time) and the
old data won't be visible.
However the old data can be shown (e.g. with strike through) in the hub's screens and in posting statistics etc.
Maintaining audit history
All changes to postings and installed boards, including who made them and when, are stored in audit tables or otherwise in some persistent store.
Some UI in the hub provides a UI to see and search posting audit data.
More functionality
More UI can be added to the hub that is useful across all job boards, e.g.:
- List of recent postings across all jobs and boards
- Show postings per user/category
- graphs of posting activity
- Tools like FreshChat
Hub database
Posting objects
- housekeeping:
- id
- installed board id
- created by email
- created by date
- last updated by email
- last updated by date
- status (draft, open, closed) - do we need error?
- status message e.g. "listing expired", "job closed", "error - missing blah", etc.)
- scheduled close date
- actual close date
- stale flag
- common fields
- set by user
- title
- description
- ref
- category values
- recruiter name
- recruiter email
- recruiter team
- contact person name
- contact person email
- application email
- linkout url
- set by board
- board-specific fields
- custom fields json (e.g. advertiser profile, template, premium features, video)
Installed boards
- housekeeping:
-
- id
- tenant
- app shortcode (set before uninstall)
- prior app shortcode (set after uninstall)
- legacy source code (when present, causes linkouts for this board to have source=CODE appended to them)
- setup error message (e.g. invalid credentials). When non-null:
- new postings can't be created
- the board appears in the hub's UI with warning icons, which links to the app's setup page
Audit data
Database tables/log files/s3 storage of who did what to which postings and when.
The category mapper
note right of mapper: user opens app
note right of mapper: mapper enumerates boards
mapper->board: GET /boards/forApp
mapper->board2: GET /boards/forApp
note right of mapper: mapper gets all nodes\nfrom tenant
mapper->board: GET /categories
mapper->board: GET /categories/byID/{}
mapper->board: GET /categories/byID/{}/values
note right of mapper: user starts mapping\nfor first board
note right of mapper: mapper gets all nodes from board
mapper->board: GET /jobBoards/forApp/categories
mapper->board: GET /jobBoards/forApp/categories/byID/{}
mapper->board: GET /jobBoards/forApp/categories/byID/{}/values
note right of mapper: user maps categories
note right of mapper: user maps nodes