Skip to content

Commit e020b0d

Browse files
evanchoolyclaude
andauthored
Phase 1: Extract Mapper interface and add MapperType config option (#4193)
* Phase 1: extract Mapper interface, add MapperType config option - Convert Mapper.java from class to interface (@MorphiaInternal) - Extract AbstractMapper with shared entity registration/lookup logic - Create ReflectiveMapper extends AbstractMapper (current implementation) - Add MapperType enum (LEGACY, CRITTER) to dev.morphia.mapping - Add mapper() method to MorphiaConfig with default LEGACY - Update ManualMorphiaConfig to copy/store mapper field - Update MorphiaDatastore to use createMapper() factory (CRITTER branch throws UnsupportedOperationException until Phase 4) - Add invalid.mapper.config message to sofia.properties - Fix TestMapper to use mapper.copy() instead of new Mapper(mapper) All existing tests pass (1166 run, 1 skipped). Closes #4184 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix critter-core Mapper instantiation after interface extraction Generators.kt and TestEntityModelGenerator.kt were calling new Mapper(config) directly, which no longer compiles now that Mapper is an interface. Updated both to use ReflectiveMapper(config) instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix progress tracking formula: parent % based on all tasks, not phases Parent issue percentage is now (completed_tasks_across_all_phases / 53) * 100 instead of (completed_phases / 7) * 100. Clarifies that the parent title should be updated after every individual task completion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Address PR review comments - tryGetEntityModel: map actual (unwrapped) type not proxy type - isMappable: add null guard before isAssignableFrom to avoid NPE - createMapper: replace hard-coded string with Sofia.mapperNotYetAvailable() and throw MappingException instead of UnsupportedOperationException Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Remove unused invalid.mapper.config sofia key Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add update-progress skill and workflow for critter integration tracking - .github/scripts/update-critter-progress.sh: reusable script that fetches checkbox state from all 7 phase issues, computes phase % and parent % (total_done/53), updates issue titles, closes completed phases, and checks their boxes in #4179 - .github/workflows/update-critter-progress.yml: triggers on any merged PR or any edit to issues #4179/#4184-#4190 (e.g. checkbox ticked) - .claude/skills/update-progress/SKILL.md: Claude-invocable skill for manual progress updates with formula reference and phase table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix checkbox matching to be case-insensitive (- [X] and - [x]) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * update script updated --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 80b787d commit e020b0d

File tree

13 files changed

+712
-362
lines changed

13 files changed

+712
-362
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
name: update-progress
3+
description: >
4+
Update GitHub issue percentage titles for the critter integration plan (#4179).
5+
Use after checking off any task in a phase issue, or after merging a phase PR.
6+
Recomputes phase % and parent % from live checkbox state and updates all titles.
7+
allowed-tools: Bash(gh:*), Bash(awk:*), Bash(grep:*), Bash(sed:*)
8+
---
9+
10+
# Update Critter Integration Progress
11+
12+
## When to use
13+
14+
After completing any task in issues #4184#4190 — check the box in the issue first,
15+
then run this skill to recompute and update all percentages.
16+
17+
The GitHub workflow (`.github/workflows/update-critter-progress.yml`) handles this
18+
automatically on PR merges and issue edits. Use this skill for manual updates or
19+
to verify the current state.
20+
21+
## Formulas (source of truth: docs/critter-integration-plan.md)
22+
23+
```
24+
phase_percentage = (completed_tasks_in_phase / total_tasks_in_phase) * 100
25+
parent_percentage = (total_completed_tasks_across_all_phases / 53) * 100
26+
```
27+
28+
Both are rounded to the nearest integer.
29+
30+
## Phase reference
31+
32+
| Issue | Phase | Tasks |
33+
|-------|-------|-------|
34+
| #4184 | Phase 1: Extract Mapper interface and add config option | 9 |
35+
| #4185 | Phase 2: VarHandle accessor generator | 6 |
36+
| #4186 | Phase 3: Move critter-core into morphia-core | 8 |
37+
| #4187 | Phase 4: CritterMapper implementation | 9 |
38+
| #4188 | Phase 5: Wire CritterMapper into MorphiaDatastore | 5 |
39+
| #4189 | Phase 6: Test infrastructure and CI matrix | 7 |
40+
| #4190 | Phase 7: Cleanup and documentation | 9 |
41+
| **Total** | | **53** |
42+
43+
## Steps
44+
45+
Run the shared script:
46+
47+
```bash
48+
bash .github/scripts/update-critter-progress.sh
49+
```
50+
51+
The script:
52+
1. Fetches each phase issue body and counts `- [x]` entries
53+
2. Updates each phase issue title to `[XX%] Phase N: ...`
54+
3. Closes phase issues that reach 100% and checks their box in #4179
55+
4. Updates `#4179` title to `[XX%] Integrate critter-core into morphia-core`
56+
57+
## Manual commands (if running ad hoc)
58+
59+
```bash
60+
# Count checked tasks in a phase issue
61+
gh issue view 4184 --repo MorphiaOrg/morphia --json body -q '.body' \
62+
| grep -c '^\- \[x\]'
63+
64+
# Update a phase issue title
65+
gh issue edit 4184 --repo MorphiaOrg/morphia --title "[33%] Phase 1: Extract Mapper interface and add config option"
66+
67+
# Update parent
68+
gh issue edit 4179 --repo MorphiaOrg/morphia --title "[6%] Integrate critter-core into morphia-core"
69+
```
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env bash
2+
# Updates GitHub issue percentage titles for the critter integration plan.
3+
# Source of truth for formulas: docs/critter-integration-plan.md
4+
#
5+
# Usage: GH_TOKEN=<token> ./update-critter-progress.sh
6+
#
7+
# Derives subissues dynamically from the checkboxes in the parent issue body.
8+
# Phase percentages: (completed_tasks_in_phase / total_tasks_in_phase) * 100
9+
# Parent percentage: (total_completed_across_all_phases / total_across_all_phases) * 100
10+
11+
set -euo pipefail
12+
13+
REPO="MorphiaOrg/morphia"
14+
PARENT_ISSUE=4179
15+
16+
total_done=0
17+
total_tasks=0
18+
declare -a completed_phases=()
19+
20+
echo "Computing critter integration progress..."
21+
22+
# Fetch parent body and extract subissue numbers from its checkboxes
23+
parent_body=$(gh issue view "$PARENT_ISSUE" --repo "$REPO" --json body -q '.body')
24+
subissues=$(printf '%s' "$parent_body" | grep '^\- \[' | grep -o '#[0-9]*' | tr -d '#')
25+
26+
for issue in $subissues; do
27+
body=$(gh issue view "$issue" --repo "$REPO" --json body -q '.body')
28+
done_count=$(printf '%s' "$body" | grep -ci '^\- \[x\]' || true)
29+
tasks=$(printf '%s' "$body" | grep -ci '^\- \[' || true)
30+
31+
total_done=$((total_done + done_count))
32+
total_tasks=$((total_tasks + tasks))
33+
34+
pct=$(awk "BEGIN { printf \"%.0f\", ($done_count * 100) / ($tasks > 0 ? $tasks : 1) }")
35+
current_title=$(gh issue view "$issue" --repo "$REPO" --json title -q '.title')
36+
bare_title=$(printf '%s' "$current_title" | sed 's/^\[[0-9]*%\] //')
37+
new_title="[$pct%] $bare_title"
38+
39+
if [ "$current_title" != "$new_title" ]; then
40+
gh issue edit "$issue" --repo "$REPO" --title "$new_title"
41+
echo " #$issue: $current_title$new_title"
42+
else
43+
echo " #$issue: unchanged ($current_title)"
44+
fi
45+
46+
if [ "$done_count" -eq "$tasks" ] && [ "$tasks" -gt 0 ]; then
47+
completed_phases+=("$issue")
48+
state=$(gh issue view "$issue" --repo "$REPO" --json state -q '.state')
49+
if [ "$state" = "OPEN" ]; then
50+
gh issue close "$issue" --repo "$REPO"
51+
echo " #$issue: closed (all $tasks tasks complete)"
52+
fi
53+
fi
54+
done
55+
56+
# Update parent title
57+
parent_pct=$(awk "BEGIN { printf \"%.0f\", ($total_done * 100) / ($total_tasks > 0 ? $total_tasks : 1) }")
58+
current_parent_title=$(gh issue view "$PARENT_ISSUE" --repo "$REPO" --json title -q '.title')
59+
bare_parent_title=$(printf '%s' "$current_parent_title" | sed 's/^\[[0-9]*%\] //')
60+
new_parent_title="[$parent_pct%] $bare_parent_title"
61+
62+
if [ "$current_parent_title" != "$new_parent_title" ]; then
63+
gh issue edit "$PARENT_ISSUE" --repo "$REPO" --title "$new_parent_title"
64+
echo " #$PARENT_ISSUE: $current_parent_title$new_parent_title"
65+
else
66+
echo " #$PARENT_ISSUE: unchanged ($current_parent_title)"
67+
fi
68+
69+
# Check completed-phase boxes in parent body
70+
if [ ${#completed_phases[@]} -gt 0 ]; then
71+
updated_body="$parent_body"
72+
changed=false
73+
for issue in "${completed_phases[@]}"; do
74+
if printf '%s' "$updated_body" | grep -q "^- \[ \] #$issue"; then
75+
updated_body=$(printf '%s' "$updated_body" | sed "s/^- \[ \] #$issue/- [x] #$issue/")
76+
changed=true
77+
echo " #$PARENT_ISSUE: checked box for #$issue"
78+
fi
79+
done
80+
if [ "$changed" = "true" ]; then
81+
gh issue edit "$PARENT_ISSUE" --repo "$REPO" --body "$updated_body"
82+
fi
83+
fi
84+
85+
echo "Done: $total_done/$total_tasks tasks complete ($parent_pct%)"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Update Critter Integration Progress
2+
3+
# Runs whenever a PR is merged or a tracked issue is edited (e.g. checkbox ticked).
4+
# Recomputes phase and parent percentages using the formulas in docs/critter-integration-plan.md.
5+
6+
on:
7+
pull_request:
8+
types: [closed]
9+
issues:
10+
types: [edited]
11+
12+
jobs:
13+
update-progress:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
issues: write
17+
# Run on merged PRs, or edits to the parent/phase issues only
18+
if: |
19+
(github.event_name == 'pull_request' && github.event.pull_request.merged == true) ||
20+
(github.event_name == 'issues' && contains(
21+
fromJson('[4179, 4184, 4185, 4186, 4187, 4188, 4189, 4190]'),
22+
github.event.issue.number
23+
))
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 1
29+
30+
- name: Update issue percentages
31+
env:
32+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
run: bash .github/scripts/update-critter-progress.sh

core/src/main/java/dev/morphia/MorphiaDatastore.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import dev.morphia.mapping.EntityModelImporter;
5050
import dev.morphia.mapping.Mapper;
5151
import dev.morphia.mapping.MappingException;
52+
import dev.morphia.mapping.ReflectiveMapper;
5253
import dev.morphia.mapping.ShardKeyType;
5354
import dev.morphia.mapping.codec.EnumCodecProvider;
5455
import dev.morphia.mapping.codec.MorphiaCodecProvider;
@@ -124,7 +125,7 @@ public class MorphiaDatastore implements Datastore {
124125
@MorphiaInternal
125126
public MorphiaDatastore(MongoClient client, MorphiaConfig config) {
126127
this.mongoClient = client;
127-
this.mapper = new Mapper(config);
128+
this.mapper = createMapper(config);
128129
this.queryFactory = mapper.getConfig().queryFactory();
129130
importModels();
130131

@@ -234,6 +235,13 @@ public void applyDocumentValidations() {
234235
}
235236
}
236237

238+
private static Mapper createMapper(MorphiaConfig config) {
239+
return switch (config.mapper()) {
240+
case CRITTER -> throw new MappingException(Sofia.mapperNotYetAvailable("CRITTER"));
241+
case LEGACY -> new ReflectiveMapper(config);
242+
};
243+
}
244+
237245
/**
238246
* @return the Mapper used by this Datastore
239247
*/

0 commit comments

Comments
 (0)