Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e18b1df744 | |||
| 595c172900 | |||
| cf225826c0 | |||
| 25c05bc2d8 | |||
| 4ec6295d1d | |||
| 2d18478110 | |||
| 8b95f10c65 | |||
| f3d96fd700 | |||
| 4762f6c0c3 | |||
| 7117a93376 | |||
| 085e34f062 | |||
| 32c388760b | |||
| 98b0bba398 |
@@ -0,0 +1,112 @@
|
||||
# Create User Story in YouTrack
|
||||
|
||||
Automated user-story creation workflow. Topic/hint: `$ARGUMENTS`
|
||||
|
||||
## Step 1 — Gather Context
|
||||
|
||||
Use `AskUserQuestion` tool to ask the user (max 4 questions at once):
|
||||
|
||||
1. **Domain** — Is this frontend (UI/UX) or backend/coordinator/systems work?
|
||||
2. **User type** — Who is the actor? (e.g. player, admin, bot, system)
|
||||
3. **Action** — What should the user be able to do?
|
||||
4. **Goal/value** — Why? What outcome does it enable?
|
||||
|
||||
If `$ARGUMENTS` already answers some of these, skip those questions.
|
||||
|
||||
## Step 2 — Research (if needed)
|
||||
|
||||
If the topic involves unfamiliar domain logic, game rules, or technical constraints:
|
||||
- Search the repo for relevant code (use `Grep`/`Bash` to find related files).
|
||||
- Use `WebSearch` if the topic involves external standards or protocols.
|
||||
- Do NOT guess. Surface findings before drafting.
|
||||
|
||||
## Step 3 — Draft Story
|
||||
|
||||
Compose the full story using this template:
|
||||
|
||||
```
|
||||
As a [type of user]
|
||||
I want to [perform an action]
|
||||
So that [achieve a goal or value]
|
||||
|
||||
|
||||
Description
|
||||
|
||||
[Additional context or business logic for this story.]
|
||||
|
||||
|
||||
Acceptance Criteria
|
||||
|
||||
[List the specific, measurable criteria that define when this story is done:]
|
||||
|
||||
- Criterion 1
|
||||
- Criterion 2
|
||||
- Criterion 3
|
||||
|
||||
|
||||
Implementation Notes
|
||||
|
||||
[Technical notes, design references, or constraints.]
|
||||
```
|
||||
|
||||
Rules:
|
||||
- User story line: plain English, present tense, from user's perspective.
|
||||
- Acceptance criteria: testable, unambiguous, one condition each.
|
||||
- Implementation notes: optional — only include if there are known constraints, related tickets, or design refs.
|
||||
|
||||
## Step 4 — Clarify Acceptance Criteria
|
||||
|
||||
Show the draft to the user.
|
||||
**Use `AskUserQuestion` tool to ask:**
|
||||
- Are the acceptance criteria complete and correct?
|
||||
- Any implementation constraints to add?
|
||||
- Priority (if known)?
|
||||
|
||||
Incorporate feedback. Repeat until user approves.
|
||||
|
||||
## Step 5 — Determine Project
|
||||
|
||||
> **Project routing rules (always apply these):**
|
||||
> - Backend code (game engine, bots, API, services, coordinator) → `NCS`
|
||||
> - Frontend code (UI, UX, web app) → `NCWF`
|
||||
> - Infrastructure (Kubernetes, pipelines, CI/CD, DB setup, cloud infra) → `NCI`
|
||||
> - If ambiguous, ask the user.
|
||||
|
||||
- Frontend / UI / UX → project: `NCWF`
|
||||
- Backend / coordinator / systems / bot / engine → project: `NCS`
|
||||
- Kubernetes, pipelines, CI/CD, DB setup, infrastructure → project: `NCI`
|
||||
|
||||
If still ambiguous, ask the user.
|
||||
|
||||
## Step 6 — Create Issue
|
||||
|
||||
Call `mcp__youtrack__create_issue` with:
|
||||
- `project`: determined in Step 5
|
||||
- `summary`: concise title derived from the "I want to" clause (≤72 chars, sentence case)
|
||||
- `description`: full formatted story from Step 3 (Markdown)
|
||||
- `type`: `Feature` (or `Task` if purely technical with no user-facing value)
|
||||
|
||||
## Step 7 — Link Issues
|
||||
|
||||
After creation, **automatically** ask the user (use `AskUserQuestion` if interactive, otherwise infer from context):
|
||||
|
||||
> Are there related issues to link? (skip if none)
|
||||
|
||||
Collect any issue IDs the user mentions. For each, determine the correct relation and call `mcp__youtrack__link_issues`:
|
||||
|
||||
| Situation | Relation to use |
|
||||
|-----------|----------------|
|
||||
| This story must be done before another | `blocks` |
|
||||
| Another story must be done before this | `is blocked by` |
|
||||
| Stories share domain or are related | `relates to` |
|
||||
| This is a child of an epic/story | `subtask of` |
|
||||
| This is a parent grouping subtasks | `parent for` |
|
||||
| This depends on another ticket's output | `depends on` |
|
||||
|
||||
If the user mentions an issue in the story description or implementation notes (e.g. "see NCS-42", "after NCS-12 is done"), auto-detect and suggest linking it — confirm before creating the link.
|
||||
|
||||
## Step 8 — Report
|
||||
|
||||
Display the created issue ID and URL.
|
||||
List any links created (relation type + linked issue ID).
|
||||
Ask if a linked sub-task or implementation ticket is needed.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Estimate Issue Time in YouTrack
|
||||
|
||||
Sprint planning time estimator. Issue ID or empty for full current sprint: `$ARGUMENTS`
|
||||
|
||||
## Step 1 — Determine Scope
|
||||
|
||||
**Single-issue mode** (`$ARGUMENTS` set):
|
||||
- Call `mcp__youtrack__get_issue` on `$ARGUMENTS`.
|
||||
- Proceed with that issue only.
|
||||
|
||||
**Sprint mode** (`$ARGUMENTS` empty):
|
||||
- Call `mcp__youtrack__search_issues` with query `project: NCS Sprints: {current sprint} #Unresolved`.
|
||||
- If query returns 0 results, use `AskUserQuestion` to ask for the sprint name, then retry with `project: NCS Sprints: {name}`.
|
||||
- Collect all returned issues.
|
||||
|
||||
## Step 2 — Build Issue Tree
|
||||
|
||||
For each top-level issue from Step 1:
|
||||
1. Fetch full details via `mcp__youtrack__get_issue`: summary, description, acceptance criteria, Type, existing `Zeitschätzung`, linked issues.
|
||||
2. Identify subtasks from links with relation `subtask of` (i.e. issues where the fetched issue is the parent).
|
||||
3. Recursively fetch subtasks until all leaves are known.
|
||||
4. Group into tree: Epic → Story → Task/Subtask.
|
||||
|
||||
**Leaf node** = issue with no subtask children.
|
||||
**Parent node** = issue that has at least one subtask child.
|
||||
|
||||
## Step 3 — Estimate Leaf Nodes
|
||||
|
||||
For each leaf node:
|
||||
1. Read: summary, description, acceptance criteria, implementation notes.
|
||||
2. If scope is unclear, search codebase (`Grep`/`Bash`) for related files to gauge complexity.
|
||||
3. Assign estimate using this scale:
|
||||
|
||||
| Size | Criteria | Estimate |
|
||||
|------|----------|----------|
|
||||
| Trivial | Config change, rename, 1-file tweak | 30m |
|
||||
| Small | 1–3 files, clear scope, no unknowns | 1h–2h |
|
||||
| Medium | 3–6 files, some design needed | 3h–5h |
|
||||
| Large | 6+ files, cross-module, non-trivial design | 1d–2d |
|
||||
| XL | New subsystem, research spike, major refactor | 3d–5d |
|
||||
|
||||
4. Record: estimate + one-line reasoning.
|
||||
5. Skip leaf if it already has `Zeitschätzung` set — note it as pre-estimated.
|
||||
|
||||
## Step 4 — Roll Up for Display
|
||||
|
||||
YouTrack auto-sums `Zeitschätzung` from subtasks up to parents — **do not write estimates to parent nodes**.
|
||||
|
||||
Compute display-only rolled-up totals:
|
||||
- Parent total = sum of all descendant leaf estimates (including pre-estimated ones).
|
||||
- Flag any branch where some leaves are missing estimates (partial roll-up).
|
||||
|
||||
## Step 5 — Show Summary + Confirm
|
||||
|
||||
Display full tree with estimates. Format:
|
||||
|
||||
```
|
||||
Epic NCS-10: Castling overhaul [4h 30m] ← rolled up
|
||||
Story NCS-11: Validate castling rights [2h 30m] ← rolled up
|
||||
Task NCS-12: Fix RuleSet check 1h 30m ← leaf (new)
|
||||
Task NCS-13: Add regression tests 1h ← leaf (new)
|
||||
Story NCS-14: UI feedback for castling [2h] ← rolled up
|
||||
Task NCS-15: Highlight invalid squares 2h ← leaf (pre-set, skipped)
|
||||
```
|
||||
|
||||
Legend: `[X]` = display-only roll-up (not written). Plain = will be written to YouTrack.
|
||||
|
||||
If sprint mode, show grand total at bottom:
|
||||
```
|
||||
Sprint total: Xd Yh Zm (N issues, M leaves to update)
|
||||
```
|
||||
|
||||
**Use `AskUserQuestion` tool:**
|
||||
- Does the breakdown look right?
|
||||
- Any estimates to adjust before writing to YouTrack?
|
||||
|
||||
Incorporate all feedback before proceeding.
|
||||
|
||||
## Step 6 — Write Estimates
|
||||
|
||||
On user approval, write estimates **only to leaf nodes** (bottom-up order):
|
||||
- For each leaf with a new estimate, call `mcp__youtrack__update_issue` with field `Zeitschätzung` = approved estimate.
|
||||
- YouTrack period format: `"30m"`, `"1h 30m"`, `"1d"`, `"2d 4h"`.
|
||||
- Skip leaves already pre-estimated.
|
||||
|
||||
## Step 7 — Report
|
||||
|
||||
List all updated issues with set estimates.
|
||||
Show final rolled-up totals per Epic/Story (read back from YouTrack via `mcp__youtrack__get_issue` if needed).
|
||||
In sprint mode, show total sprint estimate.
|
||||
@@ -0,0 +1,140 @@
|
||||
# Fix Defect from YouTrack
|
||||
|
||||
Automated defect-fix workflow. Ticket ID: `$ARGUMENTS`
|
||||
|
||||
## Step 1 — Fetch Ticket
|
||||
|
||||
Call `mcp__youtrack__get_issue` with ID `$ARGUMENTS`.
|
||||
Extract and display: summary, description, steps to reproduce, Priority, Subsystem.
|
||||
|
||||
## Step 2 — Create Worktree
|
||||
|
||||
Derive branch name from ticket:
|
||||
- `type` from YouTrack issue type: `bug` → `fix`, `feature`/`task` → `feat`, `refactor` → `refactor`, else `chore`
|
||||
- `scope` from affected module/component (kebab-case, omit if unclear)
|
||||
- `description` from ticket summary: lowercase, kebab-case, max 40 chars, drop articles
|
||||
|
||||
Branch format: `<type>/<ticket-id>-<description>`
|
||||
Example: `fix/NOW-123-castling-validation-failure`
|
||||
|
||||
Call `EnterWorktree` with that branch name.
|
||||
All subsequent file work happens inside this worktree.
|
||||
|
||||
## Step 3 — Identify Root Cause (read-only)
|
||||
|
||||
1. Run `./compile` — capture all errors and warnings.
|
||||
2. Run `./test` — capture all failures.
|
||||
3. Spawn `cavecrew-investigator` with: ticket description + compile/test output → locate root cause (files, line numbers, what's wrong).
|
||||
4. **If anything is ambiguous (reproduction unclear, scope uncertain, conflicting signals), use `AskUserQuestion` tool to ask — max 4 questions at once.**
|
||||
5. **Report findings to user. No file writes yet. Wait for acknowledgement before continuing.**
|
||||
|
||||
## Step 3b — Complexity Assessment + Subtasks
|
||||
|
||||
After root cause confirmed, assess scope:
|
||||
|
||||
**Simple** (1–2 files, single concern, < 1 hour estimated): proceed directly to Step 4.
|
||||
|
||||
**Complex** (3+ files, multiple concerns, or estimated > 1 hour): create subtasks before coding.
|
||||
|
||||
To create subtasks:
|
||||
1. Break fix into discrete, independently-completable tasks (e.g. "Fix validation in RuleSet", "Add regression test for castling edge case", "Update FenParser to handle X").
|
||||
2. For each subtask call `mcp__youtrack__create_issue` with:
|
||||
- `project`: based on subtask content — do **not** inherit from parent. Backend code → `NCS`; frontend/UI → `NCWF`; Kubernetes/pipelines/CI-CD/DB setup/infrastructure → `NCI`. If ambiguous, ask user.
|
||||
- `summary`: concise action-oriented title
|
||||
- `type`: `Task`
|
||||
- `description`: what to do and why
|
||||
3. Call `mcp__youtrack__link_issues` to link each subtask to `$ARGUMENTS` with relation `subtask of`.
|
||||
4. Check if the ticket description or comments mention other issue IDs. For each mentioned ID, suggest a link and confirm with user:
|
||||
- Fix depends on another fix finishing first → `is blocked by`
|
||||
- This fix blocks another ticket → `blocks`
|
||||
- Logically related but independent → `relates to`
|
||||
5. List created subtask IDs and any additional links to user.
|
||||
|
||||
Then proceed to Step 4, implementing subtasks in order.
|
||||
|
||||
## Step 4 — Fix
|
||||
|
||||
1. Implement fix (use `scala-implementer` agent for non-trivial changes; inline edits for small ones).
|
||||
2. Run `./compile` — must be green.
|
||||
3. Run `./test` — must be green.
|
||||
4. Run `./gradlew spotlessScalaApply` — **blocking, foreground only**. Wait for completion before continuing.
|
||||
5. Run `./lint` — **blocking, foreground only** (never `run_in_background`). Wait for exit code 0. Must be green.
|
||||
- If lint fails, fix all issues and re-run until exit code 0.
|
||||
- **Do NOT proceed to Step 5 until `./lint` has completed and returned exit code 0.**
|
||||
If any step fails, iterate until all pass.
|
||||
|
||||
## Step 5 — Review
|
||||
|
||||
Spawn `cavecrew-reviewer` on the full diff.
|
||||
Display findings grouped by severity.
|
||||
|
||||
## Step 5b — Apply Review Findings
|
||||
|
||||
If the review produced any findings (any severity):
|
||||
1. Implement all agreed fixes.
|
||||
2. Run `./compile` — must be green.
|
||||
3. Run `./test` — must be green.
|
||||
4. Run `./gradlew spotlessScalaApply` — **blocking, foreground only**. Wait for completion.
|
||||
5. Run `./lint` — **blocking, foreground only**. Wait for exit code 0.
|
||||
- If lint fails, fix all issues and re-run until exit code 0.
|
||||
6. Re-spawn `cavecrew-reviewer` on the updated diff to confirm all findings are resolved.
|
||||
|
||||
Repeat until review is clean or user explicitly accepts remaining findings.
|
||||
|
||||
## Step 6 — Confirm + Push
|
||||
|
||||
Show summary: ticket, branch, files changed, review findings.
|
||||
**Use `AskUserQuestion` tool to ask for explicit approval before pushing.** Include any open questions about commit message scope or body if unclear.
|
||||
|
||||
On approval, commit following Conventional Commits:
|
||||
|
||||
```
|
||||
<type>(<scope>): <short description, imperative, ≤50 chars>
|
||||
|
||||
<optional body: what changed and why, wrap at 72 chars>
|
||||
|
||||
Closes $ARGUMENTS
|
||||
https://knockoutwhist.youtrack.cloud/issue/$ARGUMENTS
|
||||
```
|
||||
|
||||
- `type`: same as branch type (`fix`, `feat`, `refactor`, `chore`, etc.)
|
||||
- `scope`: affected module (`core`, `rule`, `api`, `bot`, `io`)
|
||||
- Subject: imperative mood, no period, lowercase
|
||||
- Footer `Closes $ARGUMENTS` and ticket URL always present
|
||||
|
||||
Push branch to remote.
|
||||
|
||||
## Step 7 — Comment on Ticket
|
||||
|
||||
After successful push, call `mcp__youtrack__add_issue_comment` on `$ARGUMENTS` with:
|
||||
|
||||
```
|
||||
Branch `<branch-name>` pushed.
|
||||
|
||||
<one-sentence summary of what was changed and why>
|
||||
|
||||
Files changed:
|
||||
- <file1>
|
||||
- <file2>
|
||||
```
|
||||
|
||||
## Step 7b — Additional Links
|
||||
|
||||
After commenting, ask the user if `$ARGUMENTS` should be linked to any other issues not already linked:
|
||||
|
||||
| Situation | Relation |
|
||||
|-----------|---------|
|
||||
| This fix blocks another open ticket | `blocks` |
|
||||
| Another ticket must ship first | `is blocked by` |
|
||||
| Related defect or story | `relates to` |
|
||||
| Duplicate of another defect | `duplicates` |
|
||||
|
||||
Scan the ticket description and comments for any issue IDs that were mentioned but not yet linked. Suggest those automatically.
|
||||
|
||||
Call `mcp__youtrack__link_issues` for each confirmed link.
|
||||
|
||||
## Step 8 — Cleanup
|
||||
|
||||
Call `ExitWorktree` with `discard_changes: true` to delete the worktree.
|
||||
(Branch was pushed in step 6 — commits are safe on remote; `discard_changes: true` bypasses the local-ahead guard.)
|
||||
Report: branch pushed, ticket commented, links created, worktree deleted, done.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Implement Feature from YouTrack
|
||||
|
||||
Automated feature-implementation workflow. Ticket ID: `$ARGUMENTS`
|
||||
|
||||
## Step 1 — Fetch Ticket
|
||||
|
||||
Call `mcp__youtrack__get_issue` with ID `$ARGUMENTS`.
|
||||
Extract and display: summary, description, acceptance criteria, Priority, Subsystem.
|
||||
|
||||
## Step 2 — Create Worktree
|
||||
|
||||
Derive branch name from ticket:
|
||||
- `type` from YouTrack issue type: `feature`/`task` → `feat`, `refactor` → `refactor`, `bug` → `fix`, else `chore`
|
||||
- `scope` from affected module/component (kebab-case, omit if unclear)
|
||||
- `description` from ticket summary: lowercase, kebab-case, max 40 chars, drop articles
|
||||
|
||||
Branch format: `<type>/<ticket-id>-<description>`
|
||||
Example: `feat/NOW-456-add-bot-difficulty-slider`
|
||||
|
||||
Call `EnterWorktree` with that branch name.
|
||||
All subsequent file work happens inside this worktree.
|
||||
|
||||
## Step 3 — Understand Requirements (read-only)
|
||||
|
||||
1. Run `./compile` — confirm baseline is green.
|
||||
2. Run `./test` — confirm baseline is green.
|
||||
3. Spawn `cavecrew-investigator` with: ticket description + acceptance criteria → locate affected files, relevant types/interfaces, entry points, integration touch-points.
|
||||
4. **If anything is ambiguous (scope unclear, acceptance criteria missing, design decisions needed), use `AskUserQuestion` tool to ask — max 4 questions at once.**
|
||||
5. **Report plan to user: what will be added/changed, which files, which modules. No file writes yet. Wait for acknowledgement before continuing.**
|
||||
|
||||
## Step 4 — Implement
|
||||
|
||||
1. Implement feature (use `scala-implementer` agent for non-trivial changes; inline edits for small ones).
|
||||
2. Run `./compile` — must be green.
|
||||
3. Run `./test` — must be green (add new tests for new behaviour; do not modify existing tests unless requirements changed).
|
||||
4. Run `./gradlew spotlessScalaApply` — **blocking, foreground only**. Wait for completion before continuing.
|
||||
5. Run `./lint` — **blocking, foreground only** (never `run_in_background`). Wait for exit code 0. Must be green.
|
||||
- If lint fails, fix all issues and re-run until exit code 0.
|
||||
- **Do NOT proceed to Step 5 until `./lint` has completed and returned exit code 0.**
|
||||
If any step fails, iterate until all pass.
|
||||
|
||||
## Step 5 — Review
|
||||
|
||||
Spawn `cavecrew-reviewer` on the full diff.
|
||||
Display findings grouped by severity.
|
||||
|
||||
## Step 5b — Apply Review Findings
|
||||
|
||||
If the review produced any findings (any severity):
|
||||
1. Implement all agreed fixes.
|
||||
2. Run `./compile` — must be green.
|
||||
3. Run `./test` — must be green.
|
||||
4. Run `./gradlew spotlessScalaApply` — **blocking, foreground only**. Wait for completion.
|
||||
5. Run `./lint` — **blocking, foreground only**. Wait for exit code 0.
|
||||
- If lint fails, fix all issues and re-run until exit code 0.
|
||||
6. Re-spawn `cavecrew-reviewer` on the updated diff to confirm all findings are resolved.
|
||||
|
||||
Repeat until review is clean or user explicitly accepts remaining findings.
|
||||
|
||||
## Step 6 — Confirm + Push
|
||||
|
||||
Show summary: ticket, branch, files changed, review findings.
|
||||
**Use `AskUserQuestion` tool to ask for explicit approval before pushing.** Include any open questions about commit message scope or body if unclear.
|
||||
|
||||
On approval, commit following Conventional Commits:
|
||||
|
||||
```
|
||||
<type>(<scope>): <short description, imperative, ≤50 chars>
|
||||
|
||||
<optional body: what changed and why, wrap at 72 chars>
|
||||
|
||||
Closes $ARGUMENTS
|
||||
https://knockoutwhist.youtrack.cloud/issue/$ARGUMENTS
|
||||
```
|
||||
|
||||
- `type`: same as branch type (`feat`, `refactor`, `chore`, etc.)
|
||||
- `scope`: affected module (`core`, `rule`, `api`, `bot`, `io`)
|
||||
- Subject: imperative mood, no period, lowercase
|
||||
- Footer `Closes $ARGUMENTS` and ticket URL always present
|
||||
|
||||
Push branch to remote.
|
||||
|
||||
## Step 7 — Comment on Ticket
|
||||
|
||||
After successful push, call `mcp__youtrack__add_issue_comment` on `$ARGUMENTS` with:
|
||||
|
||||
```
|
||||
Branch `<branch-name>` pushed.
|
||||
|
||||
<one-sentence summary of what was added and why>
|
||||
|
||||
Files changed:
|
||||
- <file1>
|
||||
- <file2>
|
||||
```
|
||||
|
||||
## Step 8 — Cleanup
|
||||
|
||||
Call `ExitWorktree` with `discard_changes: true` to delete the worktree.
|
||||
(Branch was pushed in step 6 — commits are safe on remote; `discard_changes: true` bypasses the local-ahead guard.)
|
||||
Report: branch pushed, ticket commented, worktree deleted, done.
|
||||
@@ -0,0 +1,109 @@
|
||||
# Split Story into Subtasks in YouTrack
|
||||
|
||||
Split a user story into smaller, implementable subtasks. Story ID: `$ARGUMENTS`
|
||||
|
||||
## Step 1 — Fetch Story
|
||||
|
||||
Call `mcp__youtrack__get_issue` with ID `$ARGUMENTS`.
|
||||
Extract and display: summary, description, acceptance criteria, implementation notes.
|
||||
|
||||
## Step 2 — Research (if needed)
|
||||
|
||||
If the story involves unfamiliar domain logic or technical constraints:
|
||||
- Search repo for relevant code (`Grep`/`Bash`).
|
||||
- Use `WebSearch` for external standards or protocols.
|
||||
- Do NOT guess. Surface findings before proposing splits.
|
||||
|
||||
## Step 3 — Propose Split
|
||||
|
||||
Analyse the story and propose a set of subtasks. Rules:
|
||||
- Each subtask = one unit of work, completable independently or in sequence.
|
||||
- No subtask should exceed ~2 days of work.
|
||||
- Name subtasks in imperative mood (e.g. "Implement move validation endpoint").
|
||||
- Cover the full scope of the parent story — no gaps.
|
||||
|
||||
Show proposed subtask list to user (titles only) and ask:
|
||||
**Use `AskUserQuestion` tool:**
|
||||
- Does the split look right?
|
||||
- Any subtasks to add, remove, or merge?
|
||||
- Should any subtask be assigned to a specific person?
|
||||
|
||||
Incorporate feedback. Repeat until user approves the list.
|
||||
|
||||
## Step 4 — Draft Each Subtask
|
||||
|
||||
For each approved subtask, compose description using this template:
|
||||
|
||||
```
|
||||
[Brief description of what needs to be done for this subtask.]
|
||||
|
||||
|
||||
Steps / Tasks
|
||||
|
||||
- Task 1
|
||||
- Task 2
|
||||
- Task 3
|
||||
|
||||
|
||||
Definition of Done
|
||||
|
||||
What must be true for this subtask to be considered complete:
|
||||
|
||||
- Code implemented
|
||||
- Tests passed
|
||||
- Reviewed and merged
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Steps/Tasks: concrete, ordered where order matters.
|
||||
- Definition of Done: adjust per subtask — not all subtasks need the same criteria (e.g. a research spike has different DoD than an implementation task).
|
||||
- Keep description short — one paragraph max.
|
||||
|
||||
## Step 5 — Determine Project per Subtask
|
||||
|
||||
Assign each subtask's project based on its content — do **not** inherit blindly from parent:
|
||||
|
||||
- Backend code (game engine, bots, API, services, coordinator) → `NCS`
|
||||
- Frontend code (UI, UX, web app) → `NCWF`
|
||||
- Kubernetes, pipelines, CI/CD, DB setup, infrastructure → `NCI`
|
||||
|
||||
If a subtask's project is ambiguous, ask the user before creating it.
|
||||
|
||||
## Step 6 — Create Subtasks
|
||||
|
||||
For each subtask call `mcp__youtrack__create_issue` with:
|
||||
- `project`: from Step 5
|
||||
- `summary`: subtask title (≤72 chars, sentence case)
|
||||
- `description`: full formatted description from Step 4 (Markdown)
|
||||
- `type`: `Task`
|
||||
|
||||
Then call `mcp__youtrack__link_issues` to link each created subtask to `$ARGUMENTS` with relation `subtask of`.
|
||||
|
||||
## Step 6b — Inter-Subtask Links
|
||||
|
||||
If subtasks must be done in sequence (one depends on output of another), add ordering links:
|
||||
- For each dependency pair call `mcp__youtrack__link_issues` with relation `is blocked by` (subtask B is blocked by subtask A).
|
||||
|
||||
Ask the user to confirm sequencing before adding these links:
|
||||
|
||||
> Do any subtasks have ordering dependencies? (e.g. "Implement X must come before Add tests for X")
|
||||
|
||||
## Step 6c — External Links
|
||||
|
||||
Scan `$ARGUMENTS` description and implementation notes for any referenced issue IDs not already linked. For each:
|
||||
|
||||
| Situation | Relation |
|
||||
|-----------|---------|
|
||||
| Parent story blocks another epic/story | `blocks` |
|
||||
| Story depends on another epic completing | `is blocked by` |
|
||||
| Related story in same domain | `relates to` |
|
||||
| This story duplicates or supersedes | `duplicates` |
|
||||
|
||||
Suggest links to the user and call `mcp__youtrack__link_issues` on confirmation.
|
||||
|
||||
## Step 7 — Report
|
||||
|
||||
List all created subtask IDs and summaries.
|
||||
List all links created (subtask-of, blocking chains, external).
|
||||
Display parent story link.
|
||||
Ask if any subtask needs further splitting.
|
||||
@@ -48,3 +48,4 @@ graphify-out/
|
||||
.DS_Store
|
||||
/jacoco-reporter/.venv/
|
||||
/.claude/settings.local.json
|
||||
/.claude/worktrees/
|
||||
|
||||
@@ -57,6 +57,14 @@ Use consistently.
|
||||
- **Tests are the spec.** Don't modify to pass. Fix requirements/code. Update only if requirements change.
|
||||
- Never read build folders. Ask permission if needed.
|
||||
- Keep file current with decisions + conventions.
|
||||
- **NativeReflectionConfig (mandatory):** Every new type (class, case class, enum, sealed trait — anything serialized) must be registered in the `NativeReflectionConfig` of **every module that interacts with it**. Configs live at:
|
||||
- `modules/account/src/main/scala/de/nowchess/account/config/NativeReflectionConfig.scala`
|
||||
- `modules/coordinator/src/main/scala/de/nowchess/coordinator/config/NativeReflectionConfig.scala`
|
||||
- `modules/core/src/main/scala/de/nowchess/chess/config/NativeReflectionConfig.scala`
|
||||
- `modules/io/src/main/scala/de/nowchess/io/service/config/NativeReflectionConfig.scala`
|
||||
- `modules/rule/src/main/scala/de/nowchess/rules/config/NativeReflectionConfig.scala`
|
||||
- `modules/store/src/main/scala/de/nowchess/store/config/NativeReflectionConfig.scala`
|
||||
- `modules/ws/src/main/scala/de/nowchess/ws/config/NativeReflectionConfig.scala`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -348,3 +348,72 @@
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
## (2026-06-03)
|
||||
|
||||
### Features
|
||||
|
||||
* **account:** implement token pair handling for login and refresh endpoints ([9296db8](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9296db88b7131bbda9b9b0da65c327ef9063ee31))
|
||||
* add authentication permissions for metrics endpoints in application.yml ([04edd4d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/04edd4d6fd8a63196c36f6d67992832febc9bebb))
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add H2 database configuration for testing environment ([39c9e49](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39c9e492cef2515368c074da9406f95e9c0c9e64))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **logging:** add DEBUG/INFO/WARN logging across services (NCS-72) ([#41](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/41)) ([804a4bf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/804a4bf179e3dfb19e2be4390e7e543caf5237c6))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **account:** configure JDBC connection pool size to prevent exhaustion under load ([29072ef](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/29072efbfb1cfa1c3b1a85b4c1a587c971d245f9))
|
||||
* **auth:** add InternalClientHeadersFactory for custom client headers management ([e279c39](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e279c39246470156bf11e745ee72204018d4229d))
|
||||
* NCS-84 More Verbose Logging ([#51](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/51)) ([4ad92ab](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4ad92ab23698267f8faa59c4e18388d4a0042cca))
|
||||
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* **tests:** update token path to accessToken in ChallengeResourceTest ([354db11](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/354db11972342c47a1034303c11bccfb92e60109))
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
## (2026-06-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **account:** implement token pair handling for login and refresh endpoints ([9296db8](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9296db88b7131bbda9b9b0da65c327ef9063ee31))
|
||||
* add authentication permissions for metrics endpoints in application.yml ([04edd4d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/04edd4d6fd8a63196c36f6d67992832febc9bebb))
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **api:** define shared EventEnvelope and EventType for Redis EventBus ([#61](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/61)) ([595c172](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/595c172900da99de367c274488c3ccbeaef55882))
|
||||
* **config:** add H2 database configuration for testing environment ([39c9e49](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39c9e492cef2515368c074da9406f95e9c0c9e64))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **logging:** add DEBUG/INFO/WARN logging across services (NCS-72) ([#41](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/41)) ([804a4bf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/804a4bf179e3dfb19e2be4390e7e543caf5237c6))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **account:** configure JDBC connection pool size to prevent exhaustion under load ([29072ef](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/29072efbfb1cfa1c3b1a85b4c1a587c971d245f9))
|
||||
* **auth:** add InternalClientHeadersFactory for custom client headers management ([e279c39](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e279c39246470156bf11e745ee72204018d4229d))
|
||||
* NCS-84 More Verbose Logging ([#51](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/51)) ([4ad92ab](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4ad92ab23698267f8faa59c4e18388d4a0042cca))
|
||||
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* **tests:** update token path to accessToken in ChallengeResourceTest ([354db11](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/354db11972342c47a1034303c11bccfb92e60109))
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat: add authentication permissions for metrics endpoints in application.yml" ([a298417](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a298417b9e4d68dc73bbf40be63d9484536e9f83))
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
|
||||
@@ -45,6 +45,7 @@ dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
implementation(project(":modules:api"))
|
||||
implementation(project(":modules:security"))
|
||||
|
||||
implementation(platform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
|
||||
|
||||
@@ -12,10 +12,13 @@ import de.nowchess.account.domain.{
|
||||
UserAccount,
|
||||
}
|
||||
import de.nowchess.account.dto.*
|
||||
import de.nowchess.api.event.{EventEnvelope, EventType}
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
|
||||
@RegisterForReflection(
|
||||
targets = Array(
|
||||
classOf[EventEnvelope],
|
||||
classOf[EventType],
|
||||
classOf[UserAccount],
|
||||
classOf[BotAccount],
|
||||
classOf[OfficialBotAccount],
|
||||
|
||||
@@ -49,3 +49,5 @@ case class RotatedTokenDto(token: String)
|
||||
case class OfficialBotAccountDto(id: String, name: String, rating: Int, createdAt: String)
|
||||
|
||||
case class OfficialChallengeResponse(gameId: String, botName: String, difficulty: Int)
|
||||
|
||||
case class SyncOfficialBotsRequest(bots: List[String])
|
||||
|
||||
@@ -89,6 +89,13 @@ class OfficialBotAccountRepository:
|
||||
def findAll(): List[OfficialBotAccount] =
|
||||
em.createQuery("FROM OfficialBotAccount", classOf[OfficialBotAccount]).getResultList.asScala.toList
|
||||
|
||||
def findByName(name: String): Option[OfficialBotAccount] =
|
||||
em.createQuery("FROM OfficialBotAccount WHERE name = :name", classOf[OfficialBotAccount])
|
||||
.setParameter("name", name)
|
||||
.getResultList
|
||||
.asScala
|
||||
.headOption
|
||||
|
||||
def persist(bot: OfficialBotAccount): OfficialBotAccount =
|
||||
em.persist(bot)
|
||||
bot
|
||||
|
||||
@@ -4,6 +4,7 @@ import de.nowchess.account.domain.{BotAccount, OfficialBotAccount, UserAccount}
|
||||
import de.nowchess.account.dto.*
|
||||
import de.nowchess.account.error.AccountError
|
||||
import de.nowchess.account.service.AccountService
|
||||
import de.nowchess.security.InternalOnly
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
@@ -179,6 +180,13 @@ class AccountResource:
|
||||
createdAt = bot.createdAt.toString,
|
||||
)
|
||||
|
||||
@POST
|
||||
@Path("/official-bots/sync")
|
||||
@InternalOnly
|
||||
def syncOfficialBots(req: SyncOfficialBotsRequest): Response =
|
||||
accountService.syncOfficialBots(req.bots)
|
||||
Response.noContent().build()
|
||||
|
||||
@GET
|
||||
@Path("/official-bots")
|
||||
def getOfficialBots: Response =
|
||||
|
||||
@@ -206,6 +206,17 @@ class AccountService:
|
||||
officialBotAccountRepository.persist(bot)
|
||||
Right(bot)
|
||||
|
||||
@Transactional
|
||||
def syncOfficialBots(botNames: List[String]): Unit =
|
||||
botNames.foreach { name =>
|
||||
if officialBotAccountRepository.findByName(name).isEmpty then
|
||||
val bot = new OfficialBotAccount()
|
||||
bot.name = name
|
||||
bot.createdAt = Instant.now()
|
||||
officialBotAccountRepository.persist(bot)
|
||||
log.infof("Auto-registered official bot: %s", name)
|
||||
}
|
||||
|
||||
def getOfficialBotAccounts(): List[OfficialBotAccount] =
|
||||
officialBotAccountRepository.findAll()
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package de.nowchess.account.service
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import de.nowchess.account.config.RedisConfig
|
||||
import de.nowchess.api.event.{EventEnvelope, EventType}
|
||||
import io.quarkus.redis.datasource.RedisDataSource
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
@@ -10,22 +12,36 @@ import scala.compiletime.uninitialized
|
||||
class EventPublisher:
|
||||
|
||||
// scalafix:off DisableSyntax.var
|
||||
@Inject var redis: RedisDataSource = uninitialized
|
||||
@Inject var redisConfig: RedisConfig = uninitialized
|
||||
@Inject var redis: RedisDataSource = uninitialized
|
||||
@Inject var redisConfig: RedisConfig = uninitialized
|
||||
@Inject var objectMapper: ObjectMapper = uninitialized
|
||||
// scalafix:on DisableSyntax.var
|
||||
|
||||
def publishGameStart(botId: String, gameId: String, playingAs: String, difficulty: Int, botAccountId: String): Unit =
|
||||
val event =
|
||||
s"""{"type":"gameStart","gameId":"$gameId","playingAs":"$playingAs","difficulty":$difficulty,"botAccountId":"$botAccountId"}"""
|
||||
redis.pubsub(classOf[String]).publish(s"${redisConfig.prefix}:bot:$botId:events", event)
|
||||
()
|
||||
val payload = objectMapper.createObjectNode()
|
||||
payload.put("gameId", gameId)
|
||||
payload.put("playingAs", playingAs)
|
||||
payload.put("difficulty", difficulty)
|
||||
payload.put("botAccountId", botAccountId)
|
||||
publish(s"${redisConfig.prefix}:bot:$botId:events", EventType.GameStart, payload)
|
||||
|
||||
def publishChallengeCreated(destUserId: String, challengeId: String, challengerName: String): Unit =
|
||||
val event = s"""{"type":"challengeCreated","challengeId":"$challengeId","challengerName":"$challengerName"}"""
|
||||
redis.pubsub(classOf[String]).publish(s"${redisConfig.prefix}:user:$destUserId:events", event)
|
||||
()
|
||||
val payload = objectMapper.createObjectNode()
|
||||
payload.put("challengeId", challengeId)
|
||||
payload.put("challengerName", challengerName)
|
||||
publish(s"${redisConfig.prefix}:user:$destUserId:events", EventType.ChallengeCreated, payload)
|
||||
|
||||
def publishChallengeAccepted(challengerId: String, challengeId: String, gameId: String): Unit =
|
||||
val event = s"""{"type":"challengeAccepted","challengeId":"$challengeId","gameId":"$gameId"}"""
|
||||
redis.pubsub(classOf[String]).publish(s"${redisConfig.prefix}:user:$challengerId:events", event)
|
||||
val payload = objectMapper.createObjectNode()
|
||||
payload.put("challengeId", challengeId)
|
||||
payload.put("gameId", gameId)
|
||||
publish(s"${redisConfig.prefix}:user:$challengerId:events", EventType.ChallengeAccepted, payload)
|
||||
|
||||
private def publish(
|
||||
channel: String,
|
||||
eventType: EventType,
|
||||
payload: com.fasterxml.jackson.databind.node.ObjectNode,
|
||||
): Unit =
|
||||
val envelope = EventEnvelope.of(eventType, payload)
|
||||
redis.pubsub(classOf[String]).publish(channel, objectMapper.writeValueAsString(envelope))
|
||||
()
|
||||
|
||||
@@ -154,3 +154,35 @@ class AccountResourceTest:
|
||||
.post("/api/account/refresh")
|
||||
.`then`()
|
||||
.statusCode(401)
|
||||
|
||||
@Test
|
||||
def syncOfficialBotsCreatesNewBots(): Unit =
|
||||
givenRequest()
|
||||
.body("""{"bots":["sync-easy","sync-hard"]}""")
|
||||
.when()
|
||||
.post("/api/account/official-bots/sync")
|
||||
.`then`()
|
||||
.statusCode(204)
|
||||
RestAssured
|
||||
.`given`()
|
||||
.when()
|
||||
.get("/api/account/official-bots")
|
||||
.`then`()
|
||||
.statusCode(200)
|
||||
.body("name", hasItems("sync-easy", "sync-hard"))
|
||||
|
||||
@Test
|
||||
def syncOfficialBotsIsIdempotent(): Unit =
|
||||
val body = """{"bots":["idempotent-bot"]}"""
|
||||
givenRequest()
|
||||
.body(body)
|
||||
.when()
|
||||
.post("/api/account/official-bots/sync")
|
||||
.`then`()
|
||||
.statusCode(204)
|
||||
givenRequest()
|
||||
.body(body)
|
||||
.when()
|
||||
.post("/api/account/official-bots/sync")
|
||||
.`then`()
|
||||
.statusCode(204)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=18
|
||||
MINOR=20
|
||||
PATCH=0
|
||||
|
||||
@@ -145,3 +145,22 @@
|
||||
|
||||
* **dependencies:** correct Jackson databind dependency group ID ([008d72d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/008d72d826707c04205bac7de25170fae5fed861))
|
||||
* IO microservice ([#38](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/38)) ([a386f57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a386f57c21d34ead6cc6f92836c52b714597e289))
|
||||
## (2026-06-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** define shared EventEnvelope and EventType for Redis EventBus ([#61](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/61)) ([595c172](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/595c172900da99de367c274488c3ccbeaef55882))
|
||||
* **dto:** update GameWritebackEventDto for JSON deserialization and remove unused mixin ([576e3fe](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/576e3fea9bf1082549ea53efd3288474c42be93d))
|
||||
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([f088c4e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f088c4e9ffcc498d3d1b6f01e8f50042d5830d55))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([8744bee](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8744bee2dd20966dae90a09c21a43d5b06f59e00))
|
||||
* **rule:** Rules as a microservice ([#39](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/39)) ([093134d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/093134d36c6844ba02a36a28d5d044f09291cd1d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dependencies:** correct Jackson databind dependency group ID ([008d72d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/008d72d826707c04205bac7de25170fae5fed861))
|
||||
* IO microservice ([#38](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/38)) ([a386f57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a386f57c21d34ead6cc6f92836c52b714597e289))
|
||||
|
||||
@@ -50,6 +50,8 @@ dependencies {
|
||||
}
|
||||
}
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind:${versions["JACKSON"]!!}")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}")
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${versions["JACKSON"]!!}")
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.nowchess.api.event
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
final case class EventEnvelope(
|
||||
eventId: UUID,
|
||||
`type`: EventType,
|
||||
payload: JsonNode,
|
||||
timestamp: Instant,
|
||||
correlationId: Option[String],
|
||||
)
|
||||
|
||||
object EventEnvelope:
|
||||
def of(
|
||||
`type`: EventType,
|
||||
payload: JsonNode,
|
||||
correlationId: Option[String] = None,
|
||||
): EventEnvelope =
|
||||
EventEnvelope(
|
||||
eventId = UUID.randomUUID(),
|
||||
`type` = `type`,
|
||||
payload = payload,
|
||||
timestamp = Instant.now(),
|
||||
correlationId = correlationId,
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
package de.nowchess.api.event
|
||||
|
||||
enum EventType:
|
||||
case GameStart, GameCreationRequest, GameCreationResponse, BotGameStart, ChallengeCreated, ChallengeAccepted
|
||||
@@ -0,0 +1,50 @@
|
||||
package de.nowchess.api.event
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class EventEnvelopeTest extends AnyFunSuite with Matchers:
|
||||
|
||||
private val mapper =
|
||||
val m = new ObjectMapper()
|
||||
m.registerModule(DefaultScalaModule)
|
||||
m.findAndRegisterModules()
|
||||
m
|
||||
|
||||
test("EventEnvelope round-trips through JSON") {
|
||||
val payload = mapper.createObjectNode()
|
||||
payload.put("gameId", "game-123")
|
||||
payload.put("difficulty", 3)
|
||||
|
||||
val original = EventEnvelope.of(EventType.GameStart, payload, Some("corr-abc"))
|
||||
|
||||
val json = mapper.writeValueAsString(original)
|
||||
val decoded = mapper.readValue(json, classOf[EventEnvelope])
|
||||
|
||||
decoded.eventId shouldBe original.eventId
|
||||
decoded.`type` shouldBe original.`type`
|
||||
decoded.payload shouldBe original.payload
|
||||
decoded.timestamp shouldBe original.timestamp
|
||||
decoded.correlationId shouldBe Some("corr-abc")
|
||||
}
|
||||
|
||||
test("EventEnvelope serializes without correlationId") {
|
||||
val payload = mapper.createObjectNode()
|
||||
payload.put("challengeId", "ch-1")
|
||||
|
||||
val envelope = EventEnvelope.of(EventType.ChallengeCreated, payload)
|
||||
val json = mapper.writeValueAsString(envelope)
|
||||
val decoded = mapper.readValue(json, classOf[EventEnvelope])
|
||||
|
||||
decoded.`type` shouldBe EventType.ChallengeCreated
|
||||
decoded.correlationId shouldBe None
|
||||
}
|
||||
|
||||
test("EventEnvelope.of generates unique eventIds") {
|
||||
val payload = mapper.createObjectNode()
|
||||
val e1 = EventEnvelope.of(EventType.BotGameStart, payload)
|
||||
val e2 = EventEnvelope.of(EventType.BotGameStart, payload)
|
||||
e1.eventId should not equal e2.eventId
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=14
|
||||
MINOR=15
|
||||
PATCH=0
|
||||
|
||||
@@ -156,3 +156,26 @@
|
||||
### Reverts
|
||||
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
## (2026-06-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
|
||||
@@ -77,6 +77,8 @@ dependencies {
|
||||
implementation(project(":modules:api"))
|
||||
implementation(project(":modules:io"))
|
||||
implementation(project(":modules:rule"))
|
||||
implementation(project(":modules:security"))
|
||||
implementation("io.quarkus:quarkus-rest-client-jackson")
|
||||
implementation("com.microsoft.onnxruntime:onnxruntime:${versions["ONNXRUNTIME"]!!}")
|
||||
implementation("io.quarkus:quarkus-redis-client")
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@ quarkus:
|
||||
name: nowchess-official-bots
|
||||
redis:
|
||||
hosts: redis://${REDIS_HOST:localhost}:${REDIS_PORT:6379}
|
||||
rest-client:
|
||||
account-service:
|
||||
url: http://localhost:8083
|
||||
smallrye-jwt:
|
||||
enabled: true
|
||||
log:
|
||||
@@ -15,6 +18,8 @@ nowchess:
|
||||
host: localhost
|
||||
port: 6379
|
||||
prefix: nowchess
|
||||
internal:
|
||||
secret: 123abc
|
||||
|
||||
"%deployed":
|
||||
quarkus:
|
||||
@@ -28,8 +33,13 @@ nowchess:
|
||||
exporter:
|
||||
otlp:
|
||||
endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4317}
|
||||
rest-client:
|
||||
account-service:
|
||||
url: ${ACCOUNT_SERVICE_URL}
|
||||
nowchess:
|
||||
redis:
|
||||
host: ${REDIS_HOST:localhost}
|
||||
port: ${REDIS_PORT:6379}
|
||||
prefix: ${REDIS_PREFIX:nowchess}
|
||||
internal:
|
||||
secret: ${INTERNAL_SECRET}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package de.nowchess.bot.client
|
||||
|
||||
import de.nowchess.security.{InternalClientHeadersFactory, InternalSecretClientFilter}
|
||||
import jakarta.ws.rs.*
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
import org.eclipse.microprofile.rest.client.annotation.{RegisterClientHeaders, RegisterProvider}
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient
|
||||
|
||||
case class SyncOfficialBotsRequest(bots: List[String])
|
||||
|
||||
@Path("/api/account/official-bots")
|
||||
@RegisterRestClient(configKey = "account-service")
|
||||
@RegisterProvider(classOf[InternalSecretClientFilter])
|
||||
@RegisterClientHeaders(classOf[InternalClientHeadersFactory])
|
||||
trait AccountServiceClient:
|
||||
|
||||
@POST
|
||||
@Path("/sync")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
def syncBots(req: SyncOfficialBotsRequest): Unit
|
||||
+13
-1
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||
import de.nowchess.bot.BotController
|
||||
import de.nowchess.bot.BotDifficulty
|
||||
import de.nowchess.bot.client.{AccountServiceClient, SyncOfficialBotsRequest}
|
||||
import de.nowchess.bot.config.RedisConfig
|
||||
import de.nowchess.io.fen.FenParser
|
||||
import io.micrometer.core.instrument.MeterRegistry
|
||||
@@ -13,6 +14,8 @@ import jakarta.annotation.PostConstruct
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.enterprise.event.Observes
|
||||
import jakarta.inject.Inject
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient
|
||||
import org.jboss.logging.Logger
|
||||
import scala.compiletime.uninitialized
|
||||
import java.util.function.Consumer
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -20,12 +23,18 @@ import java.util.concurrent.TimeUnit
|
||||
@ApplicationScoped
|
||||
class OfficialBotService:
|
||||
|
||||
private val log = Logger.getLogger(classOf[OfficialBotService])
|
||||
|
||||
// scalafix:off DisableSyntax.var
|
||||
@Inject var redis: RedisDataSource = uninitialized
|
||||
@Inject var redisConfig: RedisConfig = uninitialized
|
||||
@Inject var objectMapper: ObjectMapper = uninitialized
|
||||
@Inject var botController: BotController = uninitialized
|
||||
@Inject var meterRegistry: MeterRegistry = uninitialized
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
var accountServiceClient: AccountServiceClient = uninitialized
|
||||
// scalafix:on DisableSyntax.var
|
||||
|
||||
private val terminalStatuses =
|
||||
@@ -39,7 +48,10 @@ class OfficialBotService:
|
||||
}
|
||||
|
||||
def onStart(@Observes event: StartupEvent): Unit =
|
||||
BotController.listBots.foreach(subscribeToEventChannel)
|
||||
val bots = BotController.listBots
|
||||
try accountServiceClient.syncBots(SyncOfficialBotsRequest(bots))
|
||||
catch case ex: Exception => log.errorf(ex, "Failed to auto-register official bots with account service")
|
||||
bots.foreach(subscribeToEventChannel)
|
||||
|
||||
private def subscribeToEventChannel(botName: String): Unit =
|
||||
val handler: Consumer[String] = msg => handleBotEvent(botName, msg)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=13
|
||||
MINOR=14
|
||||
PATCH=0
|
||||
|
||||
@@ -453,3 +453,36 @@
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* **store:** replace null check with Option for stream messages ([252851d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/252851de1cd715f797847e0660ee501c3a77237e))
|
||||
## (2026-06-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **config:** add GameWritebackEventDtoMixin for JSON deserialization ([381161f](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/381161f00345612a1789e08243746083dff884c5))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* **config:** update application.yml to nest HTTP port configuration ([3efebd5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3efebd5ed0493159c51f7246d18d59bac58cf875))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **dto:** update GameWritebackEventDto for JSON deserialization and remove unused mixin ([576e3fe](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/576e3fea9bf1082549ea53efd3288474c42be93d))
|
||||
* implement clock expiry scanning and handling for game records ([#53](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/53)) ([8f9eb12](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8f9eb12f663efabe4dc72b94394438652ad0ef02))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* **redis:** add @Startup annotation to GameWritebackStreamListener ([d61fe97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d61fe97b4c8e2db5e34b4a14d995297cc09f9435))
|
||||
* **redis:** implement game writeback stream processing with error handling and retries ([ae3ef76](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ae3ef766e8b7596a09e466cd4fb386119f17ca5c))
|
||||
* **redis:** use ManagedExecutor for asynchronous writeback processing ([af6b0ed](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/af6b0ed8b73724fcc8f20dfccbe6fe8f84fd792d))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
* update application.yml with new API root paths and add Micrometer and OpenTelemetry dependencies ([72ce262](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/72ce262bc491f94297700e6002fb5d0812e2cc2a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensure full hierarchy registration for reflection in NativeReflectionConfig ([ebba729](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/ebba729af3265df1619dfbe46fd1945b2a7e30b7))
|
||||
* NCS-85 Database Writeback fails without Logs ([#52](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/52)) ([7323908](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/73239088d985f01aa6b1067ed9097a845e471d4f))
|
||||
* **redis:** add log message for starting Writeback listener ([b610678](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b610678005de645115f48348e66aa9e6f5deb3d5))
|
||||
* **redis:** update Redis configuration with max pool size and waiting parameters ([5baf6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5baf6a7cdbea484fc49c02e2b5a1c3919b7fa2c4))
|
||||
* remove unused HTTP root-path configurations from application.yml ([3ed3e59](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ed3e59ee456d54cd3d65ece4f36623e256b9736))
|
||||
* **store:** cap game-writeback stream with MAXLEN trimming ([#58](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/58)) ([32c3887](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/32c388760b4f40f2937e4f3b4a04495313144fc0))
|
||||
* **store:** replace null check with Option for stream messages ([252851d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/252851de1cd715f797847e0660ee501c3a77237e))
|
||||
|
||||
+15
-6
@@ -5,7 +5,7 @@ import de.nowchess.api.dto.GameWritebackEventDto
|
||||
import de.nowchess.store.config.RedisConfig
|
||||
import de.nowchess.store.service.GameWritebackService
|
||||
import io.quarkus.redis.datasource.RedisDataSource
|
||||
import io.quarkus.redis.datasource.stream.{StreamMessage, XGroupCreateArgs, XReadGroupArgs}
|
||||
import io.quarkus.redis.datasource.stream.{StreamMessage, XAddArgs, XGroupCreateArgs, XReadGroupArgs}
|
||||
import io.quarkus.runtime.Startup
|
||||
import jakarta.annotation.PostConstruct
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
@@ -32,10 +32,11 @@ class GameWritebackStreamListener:
|
||||
private val log = Logger.getLogger(classOf[GameWritebackStreamListener])
|
||||
private val groupName = "store-writeback"
|
||||
|
||||
private def streamKey = s"${redisConfig.prefix}:game-writeback"
|
||||
private def dlqKey = s"${redisConfig.prefix}:game-writeback-dlq"
|
||||
private val maxRetries = 3
|
||||
private val consumerId = UUID.randomUUID().toString
|
||||
private def streamKey = s"${redisConfig.prefix}:game-writeback"
|
||||
private def dlqKey = s"${redisConfig.prefix}:game-writeback-dlq"
|
||||
private val maxRetries = 3
|
||||
private val consumerId = UUID.randomUUID().toString
|
||||
private val maxStreamLen = 1000L
|
||||
|
||||
@PostConstruct
|
||||
def startListening(): Unit =
|
||||
@@ -98,6 +99,14 @@ class GameWritebackStreamListener:
|
||||
case Success(_) => ()
|
||||
|
||||
private def xadd(key: String, json: String, attempt: Int): Unit =
|
||||
Try(redis.stream(classOf[String]).xadd(key, Map("data" -> json, "attempt" -> attempt.toString).asJava)) match
|
||||
Try(
|
||||
redis
|
||||
.stream(classOf[String])
|
||||
.xadd(
|
||||
key,
|
||||
new XAddArgs().maxlen(maxStreamLen).nearlyExactTrimming(),
|
||||
Map("data" -> json, "attempt" -> attempt.toString).asJava,
|
||||
),
|
||||
) match
|
||||
case Failure(ex) => log.errorf(ex, "Failed to publish to stream %s", key)
|
||||
case Success(_) => ()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=23
|
||||
MINOR=24
|
||||
PATCH=0
|
||||
|
||||
Reference in New Issue
Block a user