The DeLorean¶
Internal tooling platform for the Integrations Engineering and Doc Control teams. Two self-serve applications are served from a single Spring Boot process, both protected by Azure AD SSO.
Jira: CI-394 | Port: 8081 | Java 21 / Spring Boot 3.2.5 | Repo: the-delorean
| App | Path | Purpose |
|---|---|---|
| The DeLorean | / /queues/** /topics/** |
Azure Service Bus Dead Letter Queue manager |
| SKUdo | /skudo/** |
SKU resubmission through the Arena sync pipeline |
Users land on /home after login and navigate into either app. Tiles are shown only for apps the user has access to (controlled by Azure AD App Roles).
The DeLorean — DLQ Manager¶
Enables the Integrations Engineering team to browse Azure Service Bus queues and topics, inspect dead-letter messages, and repush or delete them — without needing direct Azure Portal access.
Features¶
Queue & Topic Browser¶
The left sidebar lists all queues and topics fetched from Azure Service Bus at startup. Selecting a queue or topic opens the Dead Letter sub-queue for that entity.
Dead Letter Message Inspector¶
Each DLQ entry shows:
- Message ID, sequence number, and enqueue time
- Dead-letter reason and error description
- Full message body (formatted JSON where applicable)
- Application properties
Repush¶
Repush sends the original message body back to the entity's primary queue/topic, preserving application properties. Available to ROLE_ADMIN users only.
- Repush single — repush one message
- Repush all — repush every message currently in the DLQ
Delete¶
Permanently removes a message from the DLQ without reprocessing it. Available to ROLE_ADMIN users only.
- Delete single — delete one message
- Delete all — purge every message in the DLQ
Navigation¶
The Bootstrap-based navbar shows the authenticated user's display name, their role badge (ADMIN / USER), an All Apps link back to /home, and a logout button.
Tech Stack¶
- Java 21 (Amazon Corretto 21)
- Spring Boot 3.2.5
- Thymeleaf 3.1 (server-side HTML rendering)
- Azure Service Bus SDK (
azure-messaging-servicebus) - Spring Security OAuth2 Client (Azure AD SSO via OIDC)
- Spring Data JPA + PostgreSQL
- Bootstrap 5
Configuration¶
# application.properties
spring.application.name=dlq-manager
server.port=8081
azure.servicebus.connection-string=<service-bus-connection-string>
Environment-specific connection strings are stored in the Spring Cloud Config Server.
SKUdo — SKU Resubmission¶
Enables the Doc Control team to resubmit one or more SKUs through the full Arena sync pipeline — covering OMS, Batching, Jackyun, and NAV — without escalating to the Integrations Engineering team.
Call Chain¶
The DeLorean / SKUdo UI (the-delorean :8081, path /skudo)
--> cm-int-service-exp (Spring Boot + Apache Camel passthrough)
--> cm-product-prc (core processing)
--> Arena API (item fetch)
--> OMS (cache invalidation)
--> Batching (BOM update)
--> Jackyun (product upsert)
--> NAV (NAV sync)
Component Responsibilities¶
| Component | Responsibility |
|---|---|
the-delorean (/skudo) |
Thymeleaf UI. Handles Azure AD auth, collects SKUs & system checkboxes, calls exp layer via RestTemplate, renders results and repush history. |
cm-int-service-exp |
Passthrough API layer. Validates APIM key, routes request via Apache Camel to cm-product-prc. |
cm-product-prc |
Core sync engine. Fetches each SKU from Arena and fans out to OMS, Batching, Jackyun, and/or NAV. |
| Spring Cloud Config Server | Provides environment-specific properties (URLs, APIM keys) at startup. |
Features¶
Environment Tabs¶
Tabs are driven by the active Spring profile:
nonprodprofile → DEV, QA, STAGE tabsprodprofile → PROD tab only
Each tab is colour-coded (DEV = blue, QA = green, STAGE = amber, PROD = red). Selecting a tab switches the target URL and APIM key.
SKU Input¶
Accepts flexible input formats:
- One SKU per line
- Comma-separated values
- JSON array (e.g.
["703434", "807718"])
SKUs are deduplicated and stripped of surrounding quotes automatically. Maximum 5 SKUs per request (enforced server-side in SkuDoController).
Per-System Targeting¶
Checkboxes allow Doc Control to target individual downstream systems:
| System | Operation |
|---|---|
| OMS | Cache invalidation |
| Batching | BOM update |
| Jackyun | Product upsert |
| NAV | NAV sync |
All systems are checked by default. Unchecking a system excludes it from the fan-out — useful when only one system failed and needs to be retried independently.
Repush History Panel¶
A history panel is shown to the right of the submission form, scoped to the active environment tab. It displays the last 30 resubmission entries for that environment, stored in PostgreSQL.
Each history entry shows:
- Timestamp of the submission
- SKUs that were submitted
- Target systems
- Per-SKU per-system result (OK / FAILED) shown as expandable badges
- Success and failure counts
- Name of the user who submitted
History is recorded automatically on every API call and trimmed to the most recent 30 entries per environment.
Results Table¶
After submission, a per-SKU results table is shown. Columns are rendered conditionally based on which systems were selected. Each cell shows OK (green) or FAILED (red) with the error message on failure. A summary badge at the top shows total SKUs, successes, and failures.
Submit Button Validation¶
- Disabled until at least one SKU is entered and at least one system checkbox is checked.
- Disables and shows
Processing...on submit to prevent double-submission.
SKUdo API Contract¶
POST /sku/resync on cm-int-service-exp:
{
"703434": { "oms": "OK", "batching": "OK", "jackyun": "OK" },
"807718": { "oms": "FAILED: HTTP 500 Internal Server Error", "batching": "OK", "jackyun": "OK" }
}
"OK"— system processed the SKU successfully."FAILED: <reason>"— system returned an error; reason contains HTTP status or exception message.
SKUdo Configuration¶
Properties stored in the Spring Cloud Config Server:
# dlq-manager-nonprod.yml
exp:
service:
environments:
DEV:
url: http://<cm-int-service-exp-dev-host>/sku/resync
apimKey: <nonprod-apim-key>
QA:
url: http://<cm-int-service-exp-qa-host>/sku/resync
apimKey: <nonprod-apim-key>
STAGE:
url: http://<cm-int-service-exp-stage-host>/sku/resync
apimKey: <nonprod-apim-key>
connectTimeout: 5000
readTimeout: 30000
Shared Platform¶
Authentication¶
Both apps use Azure AD SSO via Spring Security OAuth2 / OIDC. There is no local username/password login. Users click Sign in with Microsoft and authenticate with their PopSockets Microsoft account.
The login page is at /login. After successful authentication, users land on /home (the app selector).
Azure AD configuration (per profile):
spring:
security:
oauth2:
client:
registration:
azure:
client-id: <app-registration-client-id>
client-secret: <client-secret>
scope: openid, profile, email
redirect-uri: "http://<host>/login/oauth2/code/azure"
authorization-grant-type: authorization_code
provider:
azure:
issuer-uri: "https://login.microsoftonline.com/<tenant-id>/v2.0"
Access Management¶
Access to The DeLorean platform is controlled entirely through Azure AD App Roles, assigned per user in the Azure Portal App Registration. No credentials, roles, or user records are stored in the application — the app reads the roles claim from the OIDC token on every login.
The two apps are managed independently. A user can have access to one app, both apps, or neither. Within each app, the role (Admin vs User) is also managed independently — a user can be an Admin in one app and a regular User in the other.
App Roles Overview¶
| Azure AD App Role | App | Role Level | Spring Authorities |
|---|---|---|---|
DeLorean.Admin |
The DeLorean | Admin | ROLE_ADMIN + APP_DELOREAN |
DeLorean.User |
The DeLorean | User | ROLE_USER + APP_DELOREAN |
SkuDo.Admin |
SKUdo | Admin | ROLE_ADMIN + APP_SKUDO |
SkuDo.User |
SKUdo | User | ROLE_USER + APP_SKUDO |
The DeLorean — Role Permissions¶
| Action | DeLorean.User |
DeLorean.Admin |
|---|---|---|
| Browse queues and topics | ✅ | ✅ |
| View DLQ messages and message body | ✅ | ✅ |
| Repush single message | ❌ | ✅ |
| Repush all messages in a DLQ | ❌ | ✅ |
| Delete single message | ❌ | ✅ |
| Delete all messages in a DLQ | ❌ | ✅ |
SKUdo — Role Permissions¶
| Action | SkuDo.User |
SkuDo.Admin |
|---|---|---|
| View the SKU resubmission form | ✅ | ✅ |
| Submit SKUs for resubmission | ✅ | ✅ |
| View repush history panel | ✅ | ✅ |
| Access all environments (DEV / QA / STAGE / PROD) | ✅ | ✅ |
Access Rules¶
- A user with no recognised role is rejected at login with: "Sign-in failed. You may not have access. Contact your admin."
- App tiles on
/homeare only visible for apps the user has access to. A user with onlySkuDo.Userwill not see The DeLorean tile and cannot access/queues/**or/topics/**. - A user may hold roles for both apps simultaneously — for example
DeLorean.User+SkuDo.Admin. Each app's role level is independent. - Admin in one app does not grant admin in the other.
DeLorean.Adminhas no effect on SKUdo, and vice versa.
How to Assign Roles (Azure Portal)¶
- Go to Azure Portal → Azure Active Directory → App registrations
- Open the
popsockets-internal-toolsapp registration - Go to App roles — confirm the four roles are defined (
DeLorean.Admin,DeLorean.User,SkuDo.Admin,SkuDo.User) - Go to Enterprise applications → find the same app → Users and groups
- Click Add user/group → select the user → select the desired role → Assign
- To assign multiple roles to one user, repeat step 5 for each role
Note
A user must sign out and sign back in for role changes to take effect — the roles are read from the OIDC token issued at login time.
How Role Mapping Works (Technical)¶
AzureAdOAuth2UserService extends Spring Security's OidcUserService. After Azure AD authenticates the user, it reads the roles claim (a JSON array of role name strings) from the ID token and maps each to Spring Security GrantedAuthority objects:
DeLorean.Admin → ROLE_ADMIN + APP_DELOREAN
DeLorean.User → ROLE_USER + APP_DELOREAN
SkuDo.Admin → ROLE_ADMIN + APP_SKUDO
SkuDo.User → ROLE_USER + APP_SKUDO
SecurityConfig then enforces these authorities at the URL level:
/skudo/** → requires APP_SKUDO
/ , /queues/**, /topics/** → requires APP_DELOREAN
POST repush/delete endpoints → requires ROLE_ADMIN (DeLorean only)
/home → requires authenticated
No database lookup is performed during authentication.
Database¶
PostgreSQL, shared between both apps. JPA ddl-auto=update — tables are auto-created on first startup, no migration scripts needed for additive changes.
| Table | App | Purpose |
|---|---|---|
skudo_history |
SKUdo | Last 30 repush entries per environment. Columns: id, environment, skus (JSON), systems, results (JSON), success_count, fail_count, pushed_at, pushed_by. |
Spring Profiles¶
| Profile | Purpose |
|---|---|
local |
Local dev. Disables Spring Cloud Config, sets DB / Service Bus directly, dummy Azure AD placeholders. |
nonprod |
Pulls config from Spring Cloud Config Server. Shows DEV / QA / STAGE environments. |
prod |
Production. Shows PROD environment only. |
Project Structure¶
the-delorean/
+-- pom.xml
+-- src/main/java/com/popsockets/dlqmanager/
| +-- config/
| | +-- SecurityConfig.java (OAuth2 login, URL → authority rules)
| | +-- SkuDoProperties.java (@ConfigurationProperties env map)
| +-- controller/
| | +-- DlqController.java (DeLorean queue/topic/DLQ actions)
| | +-- HomeController.java (/home app selector)
| | +-- SkuDoController.java (GET /skudo, POST /skudo/resync)
| | +-- ProfileController.java (/profile — user info)
| +-- model/
| | +-- SkuDoHistory.java (JPA entity — repush history)
| +-- repository/
| | +-- SkuDoHistoryRepository.java (top-30-per-env query)
| +-- security/
| | +-- AzureAdOAuth2UserService.java (reads AD App Roles, builds authorities)
| +-- service/
| +-- DlqService.java (Azure Service Bus operations)
| +-- SkuDoService.java (RestTemplate call, history save/fetch)
+-- src/main/resources/
| +-- application.properties (base config)
| +-- application-local.yml (local dev overrides)
| +-- application-nonprod.yml (nonprod Azure AD config)
| +-- application-prod.yml (prod Azure AD config)
+-- src/main/resources/templates/
+-- login.html (Azure AD sign-in button)
+-- home.html (app selector — DeLorean + SKUdo tiles)
+-- layout.html (Bootstrap navbar used by DeLorean pages)
+-- skudo/
+-- index.html (two-column: form + history panel)
+-- results.html (per-SKU results table)
Key Class Descriptions¶
| Class | Responsibility |
|---|---|
AzureAdOAuth2UserService |
Extends OidcUserService. Reads roles claim from AD token, maps App Roles to Spring Security authorities. Rejects users with no recognised role. |
SecurityConfig |
Configures OAuth2 login (no form login). Maps URL patterns to required authorities. |
DlqService |
Wraps Azure Service Bus SDK. Browse entities, peek/receive DLQ messages, repush, delete. |
SkuDoController |
GET /skudo renders form + history. POST /skudo/resync validates (max 5 SKUs, ≥1 system), calls service, renders results. Gets logged-in user from @AuthenticationPrincipal OidcUser. |
SkuDoService |
Calls cm-int-service-exp via RestTemplate. Saves repush history to DB. Fetches history per environment. |
SkuDoHistoryRepository |
JPA repository. findTop30ByEnvironmentOrderByPushedAtDesc returns the last 30 entries per environment. |
SkuDoHistory |
JPA entity mapping to skudo_history. Stores SKUs and results as JSON strings. |
SkuDoProperties |
@ConfigurationProperties bean. Loads the environments map (URL + APIM key per env). Insertion order preserved via LinkedHashMap so tab order matches config order. |
How to Run Locally¶
Warning
Must use Amazon Corretto 21. Lombok's annotation processor crashes on other JDKs.
Warning
VPN must be connected for Spring Cloud Config Server and internal service endpoints.
# Build (skip tests — no test suite exists yet)
mvn clean package -DskipTests
# Run nonprod
mvn spring-boot:run -Dspring-boot.run.profiles=nonprod
# Run local (no Config Server required)
mvn spring-boot:run -Dspring-boot.run.profiles=local
For full SKUdo testing, also start:
- Spring Cloud Config Server (
cm-spring-cloud-config-server) on port 8888 cm-product-prcon port 8088 with thedevprofilecm-int-service-expon port 8081 with thedevprofile
Then navigate to http://localhost:8081/login, click Sign in with Microsoft, and authenticate with your PopSockets Microsoft account. The DeLorean and SKUdo tiles appear based on your Azure AD App Roles.
Future Expansion Options¶
Option 1 — Power Automate: Auto-Resync from Teams Failure Alerts¶
When SKUdo sends a failure alert to a Teams channel, a Power Automate flow can detect that message and automatically trigger the resync API for the failed SKUs — without any manual intervention.
| Attribute | Detail |
|---|---|
| Effort | Low — no code changes required; built entirely in Power Automate |
| Risk | Medium — risk of retry loop if alert format is inconsistent or cooldown is not applied |
| Prerequisites | Teams Incoming Webhook configured, APIM key available, Power Automate license |
| Priority | Medium |
Option 2 — Sync Status Dashboard¶
A read-only view showing the last known sync status for one or more SKUs across OMS, Batching, and Jackyun. Useful for verifying state before deciding whether to resubmit.
| Attribute | Detail |
|---|---|
| Effort | Medium — requires status query endpoints in cm-product-prc |
| Risk | Very low — read-only |
| Priority | High — pairs naturally with SKU resubmission |