Skip to content

refactor: adminjourneys modern override#8814

Open
mikeallisonJS wants to merge 2 commits intomainfrom
00-00-MA-refactor-adminjourneys-modern-override
Open

refactor: adminjourneys modern override#8814
mikeallisonJS wants to merge 2 commits intomainfrom
00-00-MA-refactor-adminjourneys-modern-override

Conversation

@mikeallisonJS
Copy link
Collaborator

@mikeallisonJS mikeallisonJS commented Mar 6, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a new admin query for retrieving journeys with filtering by status, template type, and team selection.
    • Included optional parameter to filter by last active team membership.
    • Implemented access control validation for secure retrieval of journey data.

@mikeallisonJS mikeallisonJS requested a review from csiyang March 6, 2026 00:14
@mikeallisonJS mikeallisonJS self-assigned this Mar 6, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

Walkthrough

The changes introduce a new adminJourneys GraphQL query endpoint in the api-journeys-modern service. The API Gateway routes this endpoint with an override directive. The implementation includes authentication, dynamic filtering capabilities, access control checks, and a comprehensive test suite.

Changes

Cohort / File(s) Summary
API Gateway Routing
apis/api-gateway/schema.graphql
Updated adminJourneys resolver to route from API_JOURNEYS_MODERN graph instead of API_JOURNEYS with an override parameter for field resolution.
Query Schema Definition
apis/api-journeys-modern/schema.graphql
Added new adminJourneys field to Query type with optional filters for status, template, teamId, and useLastActiveTeamId, returning an array of Journey objects.
Query Implementation
apis/api-journeys-modern/src/schema/journey/adminJourneys.query.ts
Implemented adminJourneys resolver with authentication, dynamic Prisma filtering based on useLastActiveTeamId and teamId parameters, role-based access control, and conditional template/status filtering.
Query Tests
apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts
Added comprehensive test suite covering authenticated/unauthenticated access, last-active team resolution, template filtering, publisher role behavior, and ACL enforcement.
Access Control & Configuration
apis/api-journeys-modern/src/schema/journey/journey.acl.ts, apis/api-journeys-modern/src/schema/journey/index.ts, apis/api-journeys-modern/src/yoga.ts
Added journeyReadAccessWhere helper for role-based access control, imported new query module, and configured response cache with TTL 0 for the adminJourneys query.

Sequence Diagram

sequenceDiagram
    participant Client
    participant GraphQL API
    participant Auth Service
    participant Prisma ORM
    participant Access Control

    Client->>GraphQL API: Query adminJourneys(status, template, teamId, useLastActiveTeamId)
    GraphQL API->>Auth Service: Authenticate & extract userId
    alt User Authenticated
        Auth Service-->>GraphQL API: Return userId & currentUser
        GraphQL API->>Prisma ORM: Fetch journeyProfile (if useLastActiveTeamId)
        alt useLastActiveTeamId true
            alt Journey Profile Exists
                Prisma ORM-->>GraphQL API: Return lastActiveTeamId
                GraphQL API->>GraphQL API: Build filter with lastActiveTeamId
            else Journey Profile Missing
                Prisma ORM-->>GraphQL API: Not found
                GraphQL API-->>Client: GraphQL Error (NOT_FOUND)
            end
        else useLastActiveTeamId false
            GraphQL API->>GraphQL API: Use provided teamId or apply default ACL
        end
        GraphQL API->>Access Control: Get journeyReadAccessWhere(userId, currentUser)
        Access Control-->>GraphQL API: Return access filter (teams, user journeys, published, publisher templates)
        GraphQL API->>Prisma ORM: findMany with combined filters (access + status + template)
        Prisma ORM-->>GraphQL API: Return Journey array
        GraphQL API-->>Client: Return adminJourneys result
    else User Not Authenticated
        Auth Service-->>GraphQL API: Authentication failed
        GraphQL API-->>Client: Authentication error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: adminjourneys modern override' is directly related to the main change in the changeset, which involves refactoring the adminJourneys resolver to use the API_JOURNEYS_MODERN graph with an override directive.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 00-00-MA-refactor-adminjourneys-modern-override

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Mar 6, 2026

View your CI Pipeline Execution ↗ for commit 3ebc9ff

Command Status Duration Result
nx run watch-e2e:e2e ✅ Succeeded 2m 24s View ↗
nx run journeys-e2e:e2e ✅ Succeeded 1m 29s View ↗
nx run resources-e2e:e2e ✅ Succeeded 1m 13s View ↗
nx run journeys-admin-e2e:e2e ✅ Succeeded 40s View ↗
nx run player-e2e:e2e ✅ Succeeded 4s View ↗
nx run short-links-e2e:e2e ✅ Succeeded 14s View ↗
nx run videos-admin-e2e:e2e ✅ Succeeded 6s View ↗
nx run-many --target=vercel-alias --projects=watch ✅ Succeeded 2s View ↗
Additional runs (20) ✅ Succeeded ... View ↗

☁️ Nx Cloud last updated this comment at 2026-03-06 00:30:42 UTC

@github-actions github-actions bot temporarily deployed to Preview - short-links March 6, 2026 00:18 Inactive
@github-actions github-actions bot temporarily deployed to Preview - player March 6, 2026 00:18 Inactive
@github-actions github-actions bot temporarily deployed to Preview - videos-admin March 6, 2026 00:18 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys-admin March 6, 2026 00:18 Inactive
@github-actions github-actions bot temporarily deployed to Preview - resources March 6, 2026 00:18 Inactive
@github-actions github-actions bot temporarily deployed to Preview - watch March 6, 2026 00:18 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys March 6, 2026 00:18 Inactive
@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys ✅ Ready journeys preview Fri Mar 6 13:20:34 NZDT 2026

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
player ✅ Ready player preview Fri Mar 6 13:20:44 NZDT 2026

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
videos-admin ✅ Ready videos-admin preview Fri Mar 6 13:20:51 NZDT 2026

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
short-links ✅ Ready short-links preview Fri Mar 6 13:21:01 NZDT 2026

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
resources ✅ Ready resources preview Fri Mar 6 13:21:12 NZDT 2026

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys-admin ✅ Ready journeys-admin preview Fri Mar 6 13:21:14 NZDT 2026

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
watch ✅ Ready watch preview Fri Mar 6 13:21:19 NZDT 2026

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
apis/api-journeys-modern/src/schema/journey/journey.acl.ts (1)

37-60: Extract these ACL predicates into a shared source of truth.

journeyReadAccessWhere now maintains the same role/template access matrix in a second place alongside journeyAcl(). Pulling the team/user/template predicates into shared helpers/constants will make the list filter and object-level checks much less likely to drift apart later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-journeys-modern/src/schema/journey/journey.acl.ts` around lines 37 -
60, The ACL predicates used in journeyReadAccessWhere are duplicated with
journeyAcl; extract the common role/template predicates into a shared helper
(e.g., export a constant or function like buildJourneyAccessPredicates or
journeyAccessPredicates) that returns the array of OR conditions for team
manager/member, team member, userJourneys owner/editor, template: true &
published, and the optional template-only when isPublisher; then replace the
inline OR array in journeyReadAccessWhere and the predicate list in journeyAcl
to import and use that shared helper so both list filters and object-level
checks derive from the same source of truth.
apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts (2)

34-53: Add a status filter case.

status is part of the new query contract, but this suite never passes it, so a broken status predicate in the resolver would still go green.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts`
around lines 34 - 53, The test never exercises the new status argument of the
ADMIN_JOURNEYS_QUERY, so add a case that passes a status variable into the
adminJourneys GraphQL query to validate the resolver predicate; update the spec
that uses ADMIN_JOURNEYS_QUERY to invoke the query with variables like { status:
['PUBLISHED'] } (or another JourneyStatus enum value used in fixtures), and
assert the returned journeys match the expected filtered set (or that none are
returned) so a broken status filter will fail the test.

193-197: Avoid asserting on AND[1] === {}.

This couples the test to the resolver's current filter order and its use of a no-op placeholder. Assert on the absence of the personal-fallback predicate instead so harmless refactors do not break the test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts`
around lines 193 - 197, The test currently asserts andFilters[1] === {}, which
couples it to filter ordering and a no-op placeholder; instead, update the
assertion on prismaMock.journey.findMany (inspect lastFindManyCall?.where?.AND
via andFilters) to ensure the resolver's personal-fallback predicate is absent:
assert that no element in andFilters matches the personal-fallback predicate
(e.g., no element is an empty object and none contain the specific
personal-fallback key your resolver adds), using a check like
Array.prototype.every to confirm no AND entry equals {} or contains the
personal-fallback field rather than asserting at a fixed index.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts`:
- Around line 240-273: The test "should allow publishers to read templates
regardless of status" uses mockJourney with status 'published', so change it to
an unpublished status (e.g., set mockJourney.status = 'draft' or use a dedicated
unpublished template fixture) before calling prismaMock.journey.findMany so the
resolved journey is an unpublished template; keep the template flag true and the
same assertions so the test demonstrates that publisher users
(mockPublisherUser) can read templates regardless of status (references:
mockJourney, mockPublisherUser, prismaMock.journey.findMany,
ADMIN_JOURNEYS_QUERY).

In `@apis/api-journeys-modern/src/schema/journey/adminJourneys.query.ts`:
- Around line 37-57: Reorder and tighten the team-id logic so explicit
args.teamId takes precedence: first check if args.teamId != null and set
filter.teamId = args.teamId (and skip any profile lookup), otherwise if
args.useLastActiveTeamId === true then call
prisma.journeyProfile.findUnique(...) and only set filter.teamId and
lastActiveApplied = true when profile != null and profile.lastActiveTeamId !=
null; do not flip lastActiveApplied when profile.lastActiveTeamId is null so the
subsequent owner/editor fallback (the branch guarded by !lastActiveApplied and
journeyReadAccessWhere(...)) still runs. Reference: args.teamId,
args.useLastActiveTeamId, prisma.journeyProfile.findUnique,
profile.lastActiveTeamId, lastActiveApplied, filter.teamId,
journeyReadAccessWhere.

---

Nitpick comments:
In `@apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts`:
- Around line 34-53: The test never exercises the new status argument of the
ADMIN_JOURNEYS_QUERY, so add a case that passes a status variable into the
adminJourneys GraphQL query to validate the resolver predicate; update the spec
that uses ADMIN_JOURNEYS_QUERY to invoke the query with variables like { status:
['PUBLISHED'] } (or another JourneyStatus enum value used in fixtures), and
assert the returned journeys match the expected filtered set (or that none are
returned) so a broken status filter will fail the test.
- Around line 193-197: The test currently asserts andFilters[1] === {}, which
couples it to filter ordering and a no-op placeholder; instead, update the
assertion on prismaMock.journey.findMany (inspect lastFindManyCall?.where?.AND
via andFilters) to ensure the resolver's personal-fallback predicate is absent:
assert that no element in andFilters matches the personal-fallback predicate
(e.g., no element is an empty object and none contain the specific
personal-fallback key your resolver adds), using a check like
Array.prototype.every to confirm no AND entry equals {} or contains the
personal-fallback field rather than asserting at a fixed index.

In `@apis/api-journeys-modern/src/schema/journey/journey.acl.ts`:
- Around line 37-60: The ACL predicates used in journeyReadAccessWhere are
duplicated with journeyAcl; extract the common role/template predicates into a
shared helper (e.g., export a constant or function like
buildJourneyAccessPredicates or journeyAccessPredicates) that returns the array
of OR conditions for team manager/member, team member, userJourneys
owner/editor, template: true & published, and the optional template-only when
isPublisher; then replace the inline OR array in journeyReadAccessWhere and the
predicate list in journeyAcl to import and use that shared helper so both list
filters and object-level checks derive from the same source of truth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2ca2e287-ba73-46ea-8aef-dc6d48907cc3

📥 Commits

Reviewing files that changed from the base of the PR and between e1b8086 and 3ebc9ff.

📒 Files selected for processing (7)
  • apis/api-gateway/schema.graphql
  • apis/api-journeys-modern/schema.graphql
  • apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts
  • apis/api-journeys-modern/src/schema/journey/adminJourneys.query.ts
  • apis/api-journeys-modern/src/schema/journey/index.ts
  • apis/api-journeys-modern/src/schema/journey/journey.acl.ts
  • apis/api-journeys-modern/src/yoga.ts

Comment on lines +37 to +57
if (args.useLastActiveTeamId === true) {
const profile = await prisma.journeyProfile.findUnique({
where: { userId }
})
if (profile == null)
throw new GraphQLError('journey profile not found', {
extensions: { code: 'NOT_FOUND' }
})
lastActiveApplied = true
if (profile.lastActiveTeamId != null) {
filter.teamId = profile.lastActiveTeamId
}
}

if (args.teamId != null) {
filter.teamId = args.teamId
} else if (
args.template !== true &&
filter.teamId == null &&
!lastActiveApplied
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle explicit teamId before the last-active lookup.

When both args are set, Lines 37-44 can still throw NOT_FOUND before Line 52 gives teamId precedence. Also, Line 45 flips lastActiveApplied even when profile.lastActiveTeamId is null, which skips the Lines 53-57 owner/editor fallback and can widen the query to whatever journeyReadAccessWhere(...) allows.

Possible fix if the intended precedence is teamId → last-active team → fallback filter
-      if (args.useLastActiveTeamId === true) {
+      if (args.teamId != null) {
+        filter.teamId = args.teamId
+      } else if (args.useLastActiveTeamId === true) {
         const profile = await prisma.journeyProfile.findUnique({
           where: { userId }
         })
         if (profile == null)
           throw new GraphQLError('journey profile not found', {
             extensions: { code: 'NOT_FOUND' }
           })
-        lastActiveApplied = true
         if (profile.lastActiveTeamId != null) {
           filter.teamId = profile.lastActiveTeamId
+          lastActiveApplied = true
         }
-      }
-
-      if (args.teamId != null) {
-        filter.teamId = args.teamId
       } else if (
         args.template !== true &&
         filter.teamId == null &&
         !lastActiveApplied
       ) {

Based on learnings, "journey data is public and does not require authorization checks for read access in GraphQL queries like journeySimpleGet."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (args.useLastActiveTeamId === true) {
const profile = await prisma.journeyProfile.findUnique({
where: { userId }
})
if (profile == null)
throw new GraphQLError('journey profile not found', {
extensions: { code: 'NOT_FOUND' }
})
lastActiveApplied = true
if (profile.lastActiveTeamId != null) {
filter.teamId = profile.lastActiveTeamId
}
}
if (args.teamId != null) {
filter.teamId = args.teamId
} else if (
args.template !== true &&
filter.teamId == null &&
!lastActiveApplied
) {
if (args.teamId != null) {
filter.teamId = args.teamId
} else if (args.useLastActiveTeamId === true) {
const profile = await prisma.journeyProfile.findUnique({
where: { userId }
})
if (profile == null)
throw new GraphQLError('journey profile not found', {
extensions: { code: 'NOT_FOUND' }
})
if (profile.lastActiveTeamId != null) {
filter.teamId = profile.lastActiveTeamId
lastActiveApplied = true
}
} else if (
args.template !== true &&
filter.teamId == null &&
!lastActiveApplied
) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-journeys-modern/src/schema/journey/adminJourneys.query.ts` around
lines 37 - 57, Reorder and tighten the team-id logic so explicit args.teamId
takes precedence: first check if args.teamId != null and set filter.teamId =
args.teamId (and skip any profile lookup), otherwise if args.useLastActiveTeamId
=== true then call prisma.journeyProfile.findUnique(...) and only set
filter.teamId and lastActiveApplied = true when profile != null and
profile.lastActiveTeamId != null; do not flip lastActiveApplied when
profile.lastActiveTeamId is null so the subsequent owner/editor fallback (the
branch guarded by !lastActiveApplied and journeyReadAccessWhere(...)) still
runs. Reference: args.teamId, args.useLastActiveTeamId,
prisma.journeyProfile.findUnique, profile.lastActiveTeamId, lastActiveApplied,
filter.teamId, journeyReadAccessWhere.

@stage-branch-merger
Copy link

I see you added the "on stage" label, I'll get this merged to the stage branch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant