Skip to content

feat(github): enable button#2289

Merged
whutchinson98 merged 2 commits intomainfrom
hutch/macro-cyp4cgVvbdJJHQxGHGHei-connect-github-button-integration
Mar 31, 2026
Merged

feat(github): enable button#2289
whutchinson98 merged 2 commits intomainfrom
hutch/macro-cyp4cgVvbdJJHQxGHGHei-connect-github-button-integration

Conversation

@whutchinson98
Copy link
Copy Markdown
Member

No description provided.

@whutchinson98 whutchinson98 requested a review from a team as a code owner March 31, 2026 13:25
@macro-application
Copy link
Copy Markdown

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added GitHub account linking management to account settings: users can enable or disable GitHub connections from settings.
    • UI shows a loading state while link status is checked, presents an "Enable" button to start linking (redirects to GitHub), and a "Disable" button to unlink and refresh status.

Walkthrough

Added GitHub account-link management: the Account settings UI now checks link existence, shows loading/stateful Enable/Disable controls, and redirects to a service-provided authorization URL to initiate linking; the auth service client gained methods to init and delete GitHub links.

Changes

Cohort / File(s) Summary
Account Settings UI
js/app/packages/app/component/settings/Account.tsx
Added a createResource (githubLinkExists) to check GitHub link existence, handleGithubEnable to call authServiceClient.initGithubLink(window.location.href) and redirect (window.location.href = url), handleGithubDisable to call authServiceClient.deleteGithubLink() and refetch state, plus conditional Loading/Enable/Disable button rendering.
Authentication Service Client
js/app/packages/service-clients/service-auth/client.ts
Extended authServiceClient with initGithubLink(originalUrl?: string) (POST to /link/github, optional original_url query, returns authorization_url from InitGithubLinkResponse) and deleteGithubLink() (DELETE /link/github, maps OK to empty result); added InitGithubLinkResponse schema import.
🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess relatedness to the changeset. Add a pull request description explaining the changes, context, and purpose of implementing GitHub authentication linking functionality.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits format with 'feat:' prefix, is under 72 characters (27 chars), and accurately describes the main changes adding GitHub enable/disable functionality.

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


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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

Copy link
Copy Markdown
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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@js/app/packages/app/component/settings/Account.tsx`:
- Around line 93-96: The current createResource for githubLinkExists masks API
failures by returning false when response is missing; change the async loader
(the function passed to createResource that calls
authServiceClient.checkLinkExists) to stop defaulting to false and instead
propagate errors or return undefined/null so the resource can enter an error or
loading state (e.g., return response?.link_exists without the ?? false, or
rethrow when the RPC fails). Update any UI that consumes githubLinkExists (and
refetchGithubLink) to handle the error/undefined state instead of treating it as
"not linked."
- Around line 93-108: The component currently calls authServiceClient directly
(createResource/githubLinkExists, refetchGithubLink, handleGithubEnable calling
authServiceClient.initGithubLink, and handleGithubDisable calling
authServiceClient.deleteGithubLink); move these network calls into the queries
package by creating a TanStack Query read hook for checking GitHub link
existence (e.g., useGithubLinkExistsQuery) and mutation hooks for initGithubLink
and deleteGithubLink (e.g., useInitGithubLinkMutation,
useDeleteGithubLinkMutation), then replace the createResource and direct client
calls in Account.tsx with those hooks and call the mutations (and
invalidate/refetch the existence query) instead of using authServiceClient
directly.
- Line 101: The current window.open call (in Account.tsx) opens a URL with
target '_blank' allowing tabnabbing; update the open to use a safe pattern by
passing noopener,noreferrer in the features string or by capturing the returned
window object and setting its opener to null (e.g., use window.open(url,
'_blank', 'noopener,noreferrer') or const w = window.open(...); if (w) w.opener
= null) so the opened page cannot access window.opener.

In `@js/app/packages/service-clients/service-auth/client.ts`:
- Around line 471-477: The deleteGithubLink function currently calls
fetchWithAuth with a generic of {} and uses a no-op block mapper; change the
generic to use the imported EmptyResponse type and replace the mapper with an
explicit () => undefined so the return type is tightened and clearer; update the
call in deleteGithubLink (and its mapOk invocation) to use
fetchWithAuth<EmptyResponse>(...) and mapOk(..., () => undefined).
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 34513de8-19cd-42b0-8e18-fd2f8eef7c6f

📥 Commits

Reviewing files that changed from the base of the PR and between 87a58dd and 4a6c392.

📒 Files selected for processing (2)
  • js/app/packages/app/component/settings/Account.tsx
  • js/app/packages/service-clients/service-auth/client.ts

Comment on lines +93 to +108
const [githubLinkExists, { refetch: refetchGithubLink }] = createResource(async () => {
const [_, response] = await authServiceClient.checkLinkExists({ idp_name: 'github' });
return response?.link_exists ?? false;
});

const handleGithubEnable = async () => {
const [_, url] = await authServiceClient.initGithubLink();
if (url) {
window.open(url, '_blank');
}
};

const handleGithubDisable = async () => {
await authServiceClient.deleteGithubLink();
refetchGithubLink();
};
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.

⚠️ Potential issue | 🟠 Major

Move GitHub link API calls out of the component and into queries.

Lines 93-108 call authServiceClient directly from a TSX component. This bypasses the required TanStack Query boundary and should be moved to query/mutation hooks in the queries package.

As per coding guidelines, "All network calls to service clients MUST go through TanStack Query in the queries package. Do NOT call service clients directly from components or other packages."

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

In `@js/app/packages/app/component/settings/Account.tsx` around lines 93 - 108,
The component currently calls authServiceClient directly
(createResource/githubLinkExists, refetchGithubLink, handleGithubEnable calling
authServiceClient.initGithubLink, and handleGithubDisable calling
authServiceClient.deleteGithubLink); move these network calls into the queries
package by creating a TanStack Query read hook for checking GitHub link
existence (e.g., useGithubLinkExistsQuery) and mutation hooks for initGithubLink
and deleteGithubLink (e.g., useInitGithubLinkMutation,
useDeleteGithubLinkMutation), then replace the createResource and direct client
calls in Account.tsx with those hooks and call the mutations (and
invalidate/refetch the existence query) instead of using authServiceClient
directly.

const handleGithubEnable = async () => {
const [_, url] = await authServiceClient.initGithubLink();
if (url) {
window.open(url, '_blank');
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "Account.tsx" | grep -E "js/app/packages/app/component/settings"

Repository: macro-inc/macro

Length of output: 111


🏁 Script executed:

cat -n ./js/app/packages/app/component/settings/Account.tsx | sed -n '95,110p'

Repository: macro-inc/macro

Length of output: 538


🏁 Script executed:

# Check for other window.open calls in the same file
rg "window\.open" ./js/app/packages/app/component/settings/Account.tsx -n

Repository: macro-inc/macro

Length of output: 96


🏁 Script executed:

# Check if there are other similar patterns in the js/app directory
rg "window\.open\([^,]+,\s*['\"]_blank['\"](?!\s*,\s*['\"].*(?:noopener|noreferrer))" ./js/app --type tsx --type ts -A 1 | head -50

Repository: macro-inc/macro

Length of output: 376


🏁 Script executed:

# Search for all window.open calls in js/app
rg "window\.open\(" ./js/app --type tsx --type ts -B 2 -A 1

Repository: macro-inc/macro

Length of output: 86


🏁 Script executed:

# Search for all window.open calls in js/app
rg "window\.open\(" ./js/app -B 2 -A 1

Repository: macro-inc/macro

Length of output: 4185


Harden the new-tab redirect against tabnabbing.

Line 101 uses window.open(url, '_blank') without noopener,noreferrer, which allows the opened page to access window.opener.

🔒 Suggested fix
-      window.open(url, '_blank');
+      window.open(url, '_blank', 'noopener,noreferrer');
📝 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
window.open(url, '_blank');
window.open(url, '_blank', 'noopener,noreferrer');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/app/packages/app/component/settings/Account.tsx` at line 101, The current
window.open call (in Account.tsx) opens a URL with target '_blank' allowing
tabnabbing; update the open to use a safe pattern by passing noopener,noreferrer
in the features string or by capturing the returned window object and setting
its opener to null (e.g., use window.open(url, '_blank', 'noopener,noreferrer')
or const w = window.open(...); if (w) w.opener = null) so the opened page cannot
access window.opener.

Comment on lines +471 to +477
async deleteGithubLink() {
return mapOk(
await fetchWithAuth<{}>(`${authHost}/link/github`, {
method: 'DELETE',
}),
(_result) => {}
);
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.

🧹 Nitpick | 🔵 Trivial

Tighten delete-link response typing for clarity.

Use EmptyResponse (already imported) and an explicit () => undefined mapper instead of {} + empty block return.

♻️ Suggested refactor
   async deleteGithubLink() {
     return mapOk(
-      await fetchWithAuth<{}>(`${authHost}/link/github`, {
+      await fetchWithAuth<EmptyResponse>(`${authHost}/link/github`, {
         method: 'DELETE',
       }),
-      (_result) => {}
+      () => undefined
     );
   },
📝 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
async deleteGithubLink() {
return mapOk(
await fetchWithAuth<{}>(`${authHost}/link/github`, {
method: 'DELETE',
}),
(_result) => {}
);
async deleteGithubLink() {
return mapOk(
await fetchWithAuth<EmptyResponse>(`${authHost}/link/github`, {
method: 'DELETE',
}),
() => undefined
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/app/packages/service-clients/service-auth/client.ts` around lines 471 -
477, The deleteGithubLink function currently calls fetchWithAuth with a generic
of {} and uses a no-op block mapper; change the generic to use the imported
EmptyResponse type and replace the mapper with an explicit () => undefined so
the return type is tightened and clearer; update the call in deleteGithubLink
(and its mapOk invocation) to use fetchWithAuth<EmptyResponse>(...) and
mapOk(..., () => undefined).

@whutchinson98 whutchinson98 force-pushed the hutch/macro-cyp4cgVvbdJJHQxGHGHei-connect-github-button-integration branch from 4a6c392 to aa6755d Compare March 31, 2026 14:57
Copy link
Copy Markdown
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.

♻️ Duplicate comments (3)
js/app/packages/service-clients/service-auth/client.ts (1)

474-480: 🧹 Nitpick | 🔵 Trivial

Tighten delete-link response typing and mapper.

Use EmptyResponse + () => undefined instead of <{}> + empty block mapper for a clearer return contract.

♻️ Proposed refactor
   async deleteGithubLink() {
     return mapOk(
-      await fetchWithAuth<{}>(`${authHost}/link/github`, {
+      await fetchWithAuth<EmptyResponse>(`${authHost}/link/github`, {
         method: 'DELETE',
       }),
-      (_result) => {}
+      () => undefined
     );
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/app/packages/service-clients/service-auth/client.ts` around lines 474 -
480, The deleteGithubLink implementation should tighten its response typing and
mapper: in deleteGithubLink replace the fetchWithAuth generic from <{}> to
<EmptyResponse> and change the mapOk mapper from (_result) => {} to () =>
undefined so the function clearly returns void/undefined; also ensure
EmptyResponse is imported or available in this module and update any surrounding
types accordingly (symbols: deleteGithubLink, fetchWithAuth, mapOk,
EmptyResponse).
js/app/packages/app/component/settings/Account.tsx (2)

93-96: ⚠️ Potential issue | 🟡 Minor

Do not coerce failed GitHub link checks to false.

Line 95 maps missing/error responses to “not linked,” which can show the wrong action. Preserve error/undefined so UI can render loading/error distinctly.

💡 Minimal fix in current pattern
   const [githubLinkExists, { refetch: refetchGithubLink }] = createResource(async () => {
-    const [_, response] = await authServiceClient.checkLinkExists({ idp_name: 'github' });
-    return response?.link_exists ?? false;
+    const [error, response] = await authServiceClient.checkLinkExists({ idp_name: 'github' });
+    if (error) throw new Error(error[0]?.message ?? 'Failed to check GitHub link');
+    return response?.link_exists;
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@js/app/packages/app/component/settings/Account.tsx` around lines 93 - 96, The
current createResource for githubLinkExists coerces missing or errored responses
to false (returning response?.link_exists ?? false), which hides errors from the
UI; change the handler in the createResource used for githubLinkExists (and keep
refetchGithubLink) to return response?.link_exists without the "?? false" so the
value can be boolean | undefined, and avoid swallowing exceptions (let errors
propagate or rethrow instead of converting them to false) so the UI can render
loading/error states distinctly.

93-108: ⚠️ Potential issue | 🟠 Major

Move GitHub link network calls into queries (TanStack Query boundary).

createResource + direct authServiceClient usage here bypasses the required data-access layer. Please expose query/mutation hooks from queries and consume those in this component.

As per coding guidelines, "All network calls to service clients MUST go through TanStack Query in the queries package. Do NOT call service clients directly from components or other packages."

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

In `@js/app/packages/app/component/settings/Account.tsx` around lines 93 - 108,
The component currently uses createResource and calls authServiceClient directly
(see githubLinkExists + refetchGithubLink, handleGithubEnable,
handleGithubDisable), which bypasses the TanStack Query layer; move these
network operations into the queries package by adding a query hook (e.g.,
useGithubLinkExistsQuery) for checkLinkExists and mutation hooks (e.g.,
useInitGithubLinkMutation, useDeleteGithubLinkMutation) for initGithubLink and
deleteGithubLink, then replace createResource and direct calls in this component
with the new hooks (subscribe to the query for link state and call the mutations
in handleGithubEnable/handleGithubDisable, invoking refetch or relying on
mutation invalidate logic).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@js/app/packages/app/component/settings/Account.tsx`:
- Around line 93-96: The current createResource for githubLinkExists coerces
missing or errored responses to false (returning response?.link_exists ??
false), which hides errors from the UI; change the handler in the createResource
used for githubLinkExists (and keep refetchGithubLink) to return
response?.link_exists without the "?? false" so the value can be boolean |
undefined, and avoid swallowing exceptions (let errors propagate or rethrow
instead of converting them to false) so the UI can render loading/error states
distinctly.
- Around line 93-108: The component currently uses createResource and calls
authServiceClient directly (see githubLinkExists + refetchGithubLink,
handleGithubEnable, handleGithubDisable), which bypasses the TanStack Query
layer; move these network operations into the queries package by adding a query
hook (e.g., useGithubLinkExistsQuery) for checkLinkExists and mutation hooks
(e.g., useInitGithubLinkMutation, useDeleteGithubLinkMutation) for
initGithubLink and deleteGithubLink, then replace createResource and direct
calls in this component with the new hooks (subscribe to the query for link
state and call the mutations in handleGithubEnable/handleGithubDisable, invoking
refetch or relying on mutation invalidate logic).

In `@js/app/packages/service-clients/service-auth/client.ts`:
- Around line 474-480: The deleteGithubLink implementation should tighten its
response typing and mapper: in deleteGithubLink replace the fetchWithAuth
generic from <{}> to <EmptyResponse> and change the mapOk mapper from (_result)
=> {} to () => undefined so the function clearly returns void/undefined; also
ensure EmptyResponse is imported or available in this module and update any
surrounding types accordingly (symbols: deleteGithubLink, fetchWithAuth, mapOk,
EmptyResponse).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5ae60df3-d0fb-47bc-be00-d84a1b60f9ef

📥 Commits

Reviewing files that changed from the base of the PR and between 4a6c392 and aa6755d.

📒 Files selected for processing (2)
  • js/app/packages/app/component/settings/Account.tsx
  • js/app/packages/service-clients/service-auth/client.ts

@whutchinson98 whutchinson98 merged commit c9ea043 into main Mar 31, 2026
22 checks passed
@whutchinson98 whutchinson98 deleted the hutch/macro-cyp4cgVvbdJJHQxGHGHei-connect-github-button-integration branch March 31, 2026 15:06
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