Skip to content

refactor: decouple github.ts and enhance type safety + test coverage#19

Open
0xarchit wants to merge 9 commits into
mainfrom
fix-decouple
Open

refactor: decouple github.ts and enhance type safety + test coverage#19
0xarchit wants to merge 9 commits into
mainfrom
fix-decouple

Conversation

@0xarchit

@0xarchit 0xarchit commented Jun 25, 2026

Copy link
Copy Markdown
Owner

This PR resolves all 7 findings from the architectural review:

  • Decouples \src/lib/github.ts\ into a dedicated modular folder \src/lib/github/\ containing \http.ts, \profile.ts, \star-gate.ts, and \index.ts.
  • Deduplicates the \ orce\ / non-\ orce\ path inside \src/app/api/analyze/route.ts.
  • Enhances type safety with a \isValidSession\ type predicate guard on session loading and adds database row validation.
  • Replaces duplicate GraphQL fetching code in contributions route.
  • Migrates achievement cache to Redis.
  • Establishes a complete Vitest unit test suite (25 passing tests).
  • Adds JSDoc annotations to all public exports.

Summary by CodeRabbit

  • New Features

    • Added Vitest test scripts for running tests, watching, and generating coverage reports.
    • Added Vitest configuration and shared test setup to streamline local testing.
  • Bug Fixes

    • Improved profile analysis access control, including consistent starred/private-profile enforcement and correct cache usage.
    • Hardened session validation to reject missing or incorrectly typed sign-in data.
    • Improved AI analysis retry handling for non-success HTTP responses.
    • Enhanced GitHub fetching and star verification reliability via token fallback and safer request handling.
  • Tests

    • Added/expanded automated coverage for access control, AI parsing/retry behavior, session verification, and contribution streak calculations.

0xarchit added 7 commits June 25, 2026 12:16
…star-gate,index}.ts

- http.ts: shared fetch utilities, token pool, GraphQL/REST helpers
- profile.ts: getProfileSummary, calculateDetailedStreaks, rank/trophy/badge logic
- star-gate.ts: checkStarStatus, verifyAndInjectStar, getRepoStarCount
- index.ts: barrel re-export (import path unchanged: @/lib/github)
- Collapse 4 near-duplicate recursive paginators into 2 generic
  paginateForward/paginateBackward helpers (~250 lines removed)
- Move achievement cache from in-process Map to Redis (Finding 6 combined)

Breaking: none — all public exports preserved at @/lib/github
…hGitHubGraphQL

- Remove local GITHUB_TOKENS re-declaration
- Replace raw fetch() (no timeout, no retry) with fetchGitHubGraphQL from @/lib/github
- Use getFallbackToken() for consistent token selection
- Route now inherits 10s timeout and structured error logging automatically
auth.ts:
- Add isValidSession(p: JWTPayload) type predicate with runtime field checks
  (githubId: number, username: string, accessToken: string, avatarUrl: string)
- Replace 'payload as unknown as Session' double-cast with predicate guard
- Replace 'payload.username as string' cast with typeof guard

db.ts:
- Validate id, github_id (number/bigint) and username (string) in
  materializeUser() before casting raw DB row to User
- Returns null on schema mismatch instead of silently producing undefined fields
Before: populateAnalysis() + saveScan() + return were copy-pasted
in both the force=true and force=false branches (~40 duplicate lines).

After: force flag only controls whether the Redis cache read is skipped.
A single populateAnalysis() call, single saveScan() guard,
and single NextResponse.json(finalData) return cover both paths.

Also: standardise indentation to spaces throughout the file.
Setup:
- vitest.config.ts: node environment, @/ path alias, v8 coverage
- src/test-setup.ts: stubs required env vars (GITHUB_TOKENS, JWT_SECRET)
- package.json: add test / test:watch / test:coverage scripts

Tests (10 passing):
- src/lib/github/__tests__/profile.test.ts
  10 cases for calculateDetailedStreaks:
  empty input, no contributions, single day, consecutive days,
  gap mid-sequence, grace period (today=0), two-zero-day reset,
  weekly streak counts, weekly grace period, weekly two-zero-week reset

- src/lib/__tests__/auth.test.ts (pending env fixes for jose import)
- src/lib/__tests__/ai.test.ts  (pending fetch mock wiring)
ai.ts:    getAIAnalysis — documents userToken, alertCollector, CORRUPT_INTELLIGENCE error
db.ts:    updateUserSettings — documents normalizeSettingsPatch whitelist + INVALID_PRIMARY_SCAN
          saveScan — documents 10-scan rolling window retention
auth.ts:  createSession — documents cookie name (gitscore_session) and 7-day expiry
          verifySession — documents isValidSession predicate and return contract
          getSession — documents delegation to verifySession

Also: remove unused imports from auth.test.ts (flagged by tsc)
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff6daeeb-8f58-434e-8821-b72d1d164dc1

📥 Commits

Reviewing files that changed from the base of the PR and between c2627c7 and d25f1af.

📒 Files selected for processing (2)
  • src/lib/ai.ts
  • src/lib/github/profile.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/ai.ts
  • src/lib/github/profile.ts

📝 Walkthrough

Walkthrough

Adds Vitest tooling, hardens auth and DB validation, extracts shared GitHub helpers and modules, and updates the analyze and contributions API routes with tests.

Changes

GitHub analyzer backend

Layer / File(s) Summary
Vitest tooling
package.json, vitest.config.ts, src/test-setup.ts
package.json adds Vitest scripts and a devDependency; Vitest config sets Node globals, test globs, setup code, coverage, and the @ alias; test setup seeds GITHUB_TOKENS, JWT_SECRET, and ENCRYPTION_KEY.
Session and row validation
src/lib/auth.ts, src/lib/db.ts, src/lib/__tests__/auth.test.ts
JWT session payloads and database user rows now fail closed on malformed data, and the auth suite covers valid and invalid signed-session payloads.
AI analysis contract
src/lib/ai.ts, src/lib/__tests__/ai.test.ts
getAIAnalysis gains documentation, and its test suite covers parsing, missing developer_type, malformed or empty responses, and exhausted 429 retries.
GitHub HTTP helpers
src/lib/github/http.ts, src/lib/github/index.ts
Shared GitHub token, timeout, GraphQL, REST, error, and pagination helpers are added, along with the @/lib/github re-export entrypoint.
Profile summary
src/lib/github/profile.ts, src/lib/github/__tests__/profile.test.ts
The profile module now normalizes usernames, caches achievement checks, computes ranks and streaks, and assembles GitHub profile summaries; the streak tests cover empty, gap, and grace-period cases.
Star-gate verification
src/lib/github/star-gate.ts
The star-gate module adds username normalization, Redis-backed caching, bidirectional GraphQL and REST scans, direct REST verification, guest injection, and repo star-count lookup.
Contributions route
src/app/api/contributions/route.ts
The contributions route now uses shared GitHub token selection and GraphQL fetch helpers instead of building the request inline.
Analyze access control
src/app/api/analyze/route.ts, src/app/api/analyze/__tests__/access-control.test.ts
The analyze route now applies star-required denial, private/public visibility checks, cache-hit handling, and unified analysis/saveScan execution; the route tests cover guest denial, owner force access, non-owner rejection, public/private access, and cache hits.

Sequence Diagram(s)

sequenceDiagram
  participant AnalyzeRoute
  participant checkStarStatus
  participant getProfileSummary
  participant getAIAnalysis
  participant saveScan
  AnalyzeRoute->>checkStarStatus: verify star access
  alt cached analysis available
    AnalyzeRoute-->>AnalyzeRoute: return cached payload
  else cache miss
    AnalyzeRoute->>getProfileSummary: fetch profile snapshot
    AnalyzeRoute->>getAIAnalysis: generate analysis
    AnalyzeRoute->>saveScan: persist scan history when allowed
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I’m a rabbit with a Vitest nose,
I hop where the GitHub blossom grows.
Stars, scans, and sessions now dance in rows,
Whiskers twitch as the green test glow shows.
🐰✨ Hop on, code — the analyzer flows.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description does not follow the required template and omits most required sections. Add the template sections (Type of Change, How Has This Been Tested, Screenshots, Checklist, and Related Issues) and fill them out.
Docstring Coverage ⚠️ Warning Docstring coverage is 52.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely summarizes the main refactor, type-safety, and test-coverage changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-decouple

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.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 25, 2026

Copy link
Copy Markdown

Deploying github-profile-analyzer with  Cloudflare Pages  Cloudflare Pages

Latest commit: d25f1af
Status: ✅  Deploy successful!
Preview URL: https://63a6020b.github-profile-analyzer.pages.dev
Branch Preview URL: https://fix-decouple.github-profile-analyzer.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/api/contributions/route.ts (1)

81-100: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Move token selection inside the guarded request block.

getFallbackToken() can throw, but this call happens before the try, so token misconfiguration escapes the route’s JSON error handling.

Proposed fix
-  const token = getFallbackToken();
   const query = `
@@
   try {
+    const token = getFallbackToken();
     const res = await fetchGitHubGraphQL(token, query, { login: targetUser });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/contributions/route.ts` around lines 81 - 100, Move the token
lookup into the guarded request path so token misconfiguration is handled by the
route’s JSON error response. In the contributions route, `getFallbackToken()` is
currently called before the `try`, so wrap it with the `fetchGitHubGraphQL` call
inside the same `try` block (and keep the existing error handling) to ensure any
thrown token error is caught and returned consistently.
🧹 Nitpick comments (2)
src/app/api/analyze/__tests__/access-control.test.ts (1)

160-177: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the star-check principal and token arguments.

This public non-owner path should lock down whether the route verifies the viewer or the target. Add an expectation for checkStarStatus arguments so mismatched username/token principals are caught.

Also applies to: 189-197

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/analyze/__tests__/access-control.test.ts` around lines 160 - 177,
The public non-owner access-control test is missing an assertion for the
`checkStarStatus` call, so it can’t catch cases where the route checks the wrong
principal or token. Update the relevant `access-control.test.ts` cases around
`getSession`, `getUserByGithubId`, `getUserByUsername`, and `checkStarStatus` to
explicitly expect the viewer’s username and access token to be passed into
`checkStarStatus`, ensuring the route uses the viewer identity rather than the
target profile.
src/lib/github/profile.ts (1)

201-208: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove or consume the contributed-repos query.

repositoriesContributedTo is fetched with nested fields but never used in the returned ProfileSummary, so every profile call spends extra GraphQL cost for discarded data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/github/profile.ts` around lines 201 - 208, The
repositoriesContributedTo query is fetched in the profile summary GraphQL
request but its data is never used. Remove the repositoriesContributedTo
selection from the profile query in profile.ts, or wire its results into
ProfileSummary if they are actually needed, so the ProfileSummary builder only
requests fields it consumes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Around line 10-12: The coverage script is configured to use Vitest’s V8
provider, but the required companion package is missing from devDependencies.
Update package.json to add the coverage package needed by vitest.config.ts’s
coverage.provider setting, and keep the test:coverage script unchanged so it
works on a clean install.

In `@src/app/api/analyze/__tests__/access-control.test.ts`:
- Around line 84-93: The mocked return values in access-control.test.ts are
using unsafe as any casts, which triggers ESLint and can break CI. Replace the
casts on getProfileSummary and getAIAnalysis with properly typed fixtures, using
either shared fixture builders or an Awaited<ReturnType<typeof ...>> alias for
each mocked response shape. Update both mocked blocks in this test file so the
mocked objects satisfy the expected return types without any.

In `@src/lib/__tests__/ai.test.ts`:
- Around line 127-135: The “throws on HTTP 429 after all retries” test in
getAIAnalysis currently only checks the final rejection, so it can pass even if
the retry logic never runs. Update the test in ai.test.ts to assert the retry
path by verifying mockFetch is called the expected number of times and, if
possible, that the backoff/timer scheduling is triggered before the final throw.
Use the existing getAIAnalysis and mockFetch symbols to locate the relevant test
and make the assertion match the intended “after all retries” contract.

In `@src/lib/auth.ts`:
- Around line 89-90: The guest-session payload check in auth.ts is too loose
because `payload.verified` is treated as truthy instead of matching the exact
shape produced by `createGuestSession()`. Update the logic in the username
extraction path to require `payload.verified` to be the boolean `true` and keep
the `typeof payload.username === "string"` guard, so malformed tokens like
string-valued `verified` are rejected.

In `@src/lib/github/http.ts`:
- Around line 13-19: Move the GITHUB_TOKENS presence check out of module
initialization in the github/http module so imports do not fail when the env var
is missing; keep the validation inside getFallbackToken() where token selection
actually happens. While parsing GITHUB_TOKENS, trim each entry before filtering
so whitespace-only values are ignored, and ensure getFallbackToken() is the
place that throws the missing-token error.

In `@src/lib/github/profile.ts`:
- Around line 193-201: The GitHub profile aggregation in profile.ts is
incorrectly using repositories(first: 60) as the source for full-profile
metrics, which undercounts stats for users with more than 60 owned repos. Update
the aggregation logic around the repositories and profile totals so
authoredRepos remains capped if needed, but total_stars, public_repo_count,
rank, and language weighting are computed from complete profile-level data
rather than the first 60 nodes; make the same correction in the related
aggregation code paths referenced by repositoriesContributedTo and the
downstream stats/ranking helpers.
- Around line 59-75: Use a cacheable sentinel value for missing achievements in
getAchievementStatus so negative results can be read back from cache; since
getCachedData() treats null as a miss, replace the null writes in the successful
200 check and the catch path with a distinct stored value and map it back to
null when reading. Keep the fix localized to the cache read/write logic around
getCachedData, setCachedData, and githubFetchWithTimeout in profile.ts.

In `@src/lib/github/star-gate.ts`:
- Around line 437-459: The fallback in executeRestFallbackStarCheck is
under-scanning when repoStars/userStars are unavailable because it only checks
one page, which can miss valid stars beyond the first 100. Update the paging
logic in executeRestFallbackStarCheck (and the related call path at the other
referenced location) so unknown counts trigger a broader scan strategy instead
of defaulting to a single page, while still respecting STAR_REST_LIMIT and using
scanRepoStargazersRest and scanUserStarredRest to iterate through enough pages.

---

Outside diff comments:
In `@src/app/api/contributions/route.ts`:
- Around line 81-100: Move the token lookup into the guarded request path so
token misconfiguration is handled by the route’s JSON error response. In the
contributions route, `getFallbackToken()` is currently called before the `try`,
so wrap it with the `fetchGitHubGraphQL` call inside the same `try` block (and
keep the existing error handling) to ensure any thrown token error is caught and
returned consistently.

---

Nitpick comments:
In `@src/app/api/analyze/__tests__/access-control.test.ts`:
- Around line 160-177: The public non-owner access-control test is missing an
assertion for the `checkStarStatus` call, so it can’t catch cases where the
route checks the wrong principal or token. Update the relevant
`access-control.test.ts` cases around `getSession`, `getUserByGithubId`,
`getUserByUsername`, and `checkStarStatus` to explicitly expect the viewer’s
username and access token to be passed into `checkStarStatus`, ensuring the
route uses the viewer identity rather than the target profile.

In `@src/lib/github/profile.ts`:
- Around line 201-208: The repositoriesContributedTo query is fetched in the
profile summary GraphQL request but its data is never used. Remove the
repositoriesContributedTo selection from the profile query in profile.ts, or
wire its results into ProfileSummary if they are actually needed, so the
ProfileSummary builder only requests fields it consumes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2764f00b-8ddf-47e7-83d5-aad1bb2bf0df

📥 Commits

Reviewing files that changed from the base of the PR and between 299c8b6 and dd2326f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • package.json
  • src/app/api/analyze/__tests__/access-control.test.ts
  • src/app/api/analyze/route.ts
  • src/app/api/contributions/route.ts
  • src/lib/__tests__/ai.test.ts
  • src/lib/__tests__/auth.test.ts
  • src/lib/ai.ts
  • src/lib/auth.ts
  • src/lib/db.ts
  • src/lib/github.ts
  • src/lib/github/__tests__/profile.test.ts
  • src/lib/github/http.ts
  • src/lib/github/index.ts
  • src/lib/github/profile.ts
  • src/lib/github/star-gate.ts
  • src/test-setup.ts
  • vitest.config.ts
💤 Files with no reviewable changes (1)
  • src/lib/github.ts

Comment thread package.json
Comment thread src/app/api/analyze/__tests__/access-control.test.ts Outdated
Comment thread src/lib/__tests__/ai.test.ts Outdated
Comment thread src/lib/auth.ts Outdated
Comment thread src/lib/github/http.ts Outdated
Comment thread src/lib/github/profile.ts Outdated
Comment thread src/lib/github/profile.ts Outdated
Comment thread src/lib/github/star-gate.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/ai.ts (1)

181-203: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reset per-attempt response state to avoid stale error propagation.

response survives across retries. If a non-OK response happens on an earlier attempt and a later attempt aborts/throws before assignment, the stale response is reused after the loop, masking the final error path (including abort classification in handleFailure).

Proposed fix
   for (let attempt = 1; attempt <= AI_RETRY_ATTEMPTS; attempt++) {
+    response = null;
     try {
       console.log("[AI_ANALYSIS] Sending request to GitHub AI API", {
         attempt,
         url: "https://models.github.ai/inference/chat/completions",
         model: GITHUB_MODEL,
       });
-      response = await callAIWithTimeout(
+      response = await callAIWithTimeout(
         apiKey,
         systemPrompt,
         minified,
         attempt,
       );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/ai.ts` around lines 181 - 203, Reset the per-attempt response state
inside the retry loop in ai.ts so a failed response from an earlier attempt
cannot leak into later attempts or the post-loop failure handling. Update the
retry logic around the callAIWithTimeout flow to avoid reusing a stale
`response` value if a later attempt throws before assignment, and keep the final
error path in `handleFailure` based only on the last attempt’s outcome. Use the
`response` variable and the retry loop in the AI analysis request path as the
place to fix this.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/github/profile.ts`:
- Line 295: The allRepos binding in the profile-related GitHub repo collection
logic is never reassigned, so change it from a mutable binding to a constant
while keeping the array contents mutable. Update the declaration in the code
path that builds allRepos from initialRepos, and leave the rest of the
surrounding profile.ts logic unchanged.
- Around line 304-311: The repository pagination in the profile aggregation flow
is swallowing later-page failures by breaking out of the loop, which leaves
`total_stars`, language weights, trophies, and `original_repos` looking complete
when they are only partial. Update the pagination logic in `profile.ts` to
propagate the failure from the repository fetch/GraphQL response instead of
continuing with collected data, and make the aggregation path that builds the
summary from the repository pages fail fast with a clear error. Use the existing
`pageRes` / `pageBody` handling and the repository aggregation function that
computes the profile summary as the places to surface the error.

---

Outside diff comments:
In `@src/lib/ai.ts`:
- Around line 181-203: Reset the per-attempt response state inside the retry
loop in ai.ts so a failed response from an earlier attempt cannot leak into
later attempts or the post-loop failure handling. Update the retry logic around
the callAIWithTimeout flow to avoid reusing a stale `response` value if a later
attempt throws before assignment, and keep the final error path in
`handleFailure` based only on the last attempt’s outcome. Use the `response`
variable and the retry loop in the AI analysis request path as the place to fix
this.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 54e288d0-c30f-4b72-958a-aa3e3a2b747f

📥 Commits

Reviewing files that changed from the base of the PR and between dd2326f and c2627c7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json
  • src/app/api/analyze/__tests__/access-control.test.ts
  • src/app/api/analyze/route.ts
  • src/app/api/contributions/route.ts
  • src/lib/__tests__/ai.test.ts
  • src/lib/ai.ts
  • src/lib/auth.ts
  • src/lib/github/http.ts
  • src/lib/github/profile.ts
  • src/lib/github/star-gate.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • package.json
  • src/lib/tests/ai.test.ts
  • src/lib/auth.ts
  • src/app/api/analyze/tests/access-control.test.ts
  • src/lib/github/star-gate.ts
  • src/app/api/contributions/route.ts
  • src/app/api/analyze/route.ts

Comment thread src/lib/github/profile.ts Outdated
Comment thread src/lib/github/profile.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant