Repository: gitify-app/gitify (ts)

Issue:
Bad credentials after clicking on PR title
### 🔍 Is there already an issue for your problem?

- [X] I have checked older issues, open and closed

### 📝 Description

I'm using Gitify with personal token and GitHub Enterprise. I've added rights to `user:read`, `repo`, `notofication` as instructed on login screen. When I login, Gitify properly shows the PRs. When I click on PR title, it disappears from the list only to show up some time later. At the same time I can see failed request in console.

```
{
  "message": "Bad credentials",
  "documentation_url": "https://docs.github.com/enterprise-server@3.9/rest"
}
```

### 🪜 Steps To Reproduce

1. Use GH Enterprise, login and create personal access token with `user:read`, `repo`, `notofication` rights.
2. Use Gitify and login with the token.
3. Wait for PR appear on list.
4. Click PR title.

### Gitify Version

5.1.0

### Operating System

macOS

### GitHub Account

GitHub Enterprise

### 📸 Screenshots

_No response_

Pull request:
fix: ensure correct token is used
Fixes #944 

Previously we were not using the correct enterprise token for generating notification title url links.

This change extends the Notification type to pass the hostname along with it (previously was only stored adjacent), creates a new helper for getting the correct auth token, and uses this to make subsequent authenticated api calls. 

Patch:
diff --git a/src/__mocks__/mockedData.ts b/src/__mocks__/mockedData.ts
index a767bdd8..e8cfe782 100644
--- a/src/__mocks__/mockedData.ts
+++ b/src/__mocks__/mockedData.ts
@@ -5,6 +5,7 @@ import {
   GraphQLSearch,
   Discussion,
 } from '../typesGithub';
+import Constants from '../utils/constants';
 
 export const mockedEnterpriseAccounts: EnterpriseAccount[] = [
   {
@@ -21,6 +22,7 @@ export const mockedUser: GitifyUser = {
 
 // prettier-ignore
 export const mockedSingleNotification: Notification = {
+  hostname: Constants.DEFAULT_AUTH_OPTIONS.hostname,
   id: '138661096',
   unread: true,
   reason: 'subscribed',
diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts
index 0e8f26c6..d5360968 100644
--- a/src/hooks/useNotifications.ts
+++ b/src/hooks/useNotifications.ts
@@ -8,6 +8,7 @@ import {
   getEnterpriseAccountToken,
   generateGitHubAPIUrl,
   isEnterpriseHost,
+  getTokenForHost,
 } from '../utils/helpers';
 import { removeNotification } from '../utils/remove-notification';
 import {
@@ -97,7 +98,14 @@ export const useNotifications = (colors: boolean): NotificationsState => {
                 const { hostname } = new URL(accountNotifications.config.url);
                 return {
                   hostname,
-                  notifications: accountNotifications.data,
+                  notifications: accountNotifications.data.map(
+                    (notification) => {
+                      return {
+                        ...notification,
+                        hostname: hostname,
+                      };
+                    },
+                  ),
                 };
               },
             );
@@ -106,7 +114,14 @@ export const useNotifications = (colors: boolean): NotificationsState => {
                   ...enterpriseNotifications,
                   {
                     hostname: Constants.DEFAULT_AUTH_OPTIONS.hostname,
-                    notifications: gitHubNotifications.data,
+                    notifications: gitHubNotifications.data.map(
+                      (notification) => {
+                        return {
+                          ...notification,
+                          hostname: Constants.DEFAULT_AUTH_OPTIONS.hostname,
+                        };
+                      },
+                    ),
                   },
                 ]
               : [...enterpriseNotifications];
@@ -131,15 +146,10 @@ export const useNotifications = (colors: boolean): NotificationsState => {
                       .all<Notification>(
                         accountNotifications.notifications.map(
                           async (notification: Notification) => {
-                            const isEnterprise = isEnterpriseHost(
-                              accountNotifications.hostname,
+                            const token = getTokenForHost(
+                              notification.hostname,
+                              accounts,
                             );
-                            const token = isEnterprise
-                              ? getEnterpriseAccountToken(
-                                  accountNotifications.hostname,
-                                  accounts.enterpriseAccounts,
-                                )
-                              : accounts.token;
 
                             const additionalSubjectDetails =
                               await getGitifySubjectDetails(
diff --git a/src/typesGithub.ts b/src/typesGithub.ts
index 6ffdbe3b..46b5a496 100644
--- a/src/typesGithub.ts
+++ b/src/typesGithub.ts
@@ -79,6 +79,7 @@ export interface Notification {
   repository: Repository;
   url: string;
   subscription_url: string;
+  hostname: string; // This is not in the GitHub API, but we add it to the type to make it easier to work with
 }
 
 export type UserDetails = User & UserProfile;
diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts
index 5fc22957..4a503dca 100644
--- a/src/utils/helpers.ts
+++ b/src/utils/helpers.ts
@@ -13,6 +13,15 @@ import { openExternalLink } from '../utils/comms';
 import { Constants } from './constants';
 import { getWorkflowRunAttributes, getCheckSuiteAttributes } from './subject';
 
+export function getTokenForHost(hostname: string, accounts: AuthState): string {
+  const isEnterprise = isEnterpriseHost(hostname);
+  const token = isEnterprise
+    ? getEnterpriseAccountToken(hostname, accounts.enterpriseAccounts)
+    : accounts.token;
+
+  return token;
+}
+
 export function getEnterpriseAccountToken(
   hostname: string,
   accounts: EnterpriseAccount[],
@@ -244,14 +253,12 @@ export async function generateGitHubWebUrl(
   accounts: AuthState,
 ): Promise<string> {
   let url: string;
+  const token = getTokenForHost(notification.hostname, accounts);
 
   if (notification.subject.latest_comment_url) {
-    url = await getHtmlUrl(
-      notification.subject.latest_comment_url,
-      accounts.token,
-    );
+    url = await getHtmlUrl(notification.subject.latest_comment_url, token);
   } else if (notification.subject.url) {
-    url = await getHtmlUrl(notification.subject.url, accounts.token);
+    url = await getHtmlUrl(notification.subject.url, token);
   } else {
     // Perform any specific notification type handling (only required for a few special notification scenarios)
     switch (notification.subject.type) {
@@ -259,7 +266,7 @@ export async function generateGitHubWebUrl(
         url = getCheckSuiteUrl(notification);
         break;
       case 'Discussion':
-        url = await getDiscussionUrl(notification, accounts.token);
+        url = await getDiscussionUrl(notification, token);
         break;
       case 'RepositoryInvitation':
         url = `${notification.repository.html_url}/invitations`;


Test patch:
diff --git a/src/routes/__snapshots__/Notifications.test.tsx.snap b/src/routes/__snapshots__/Notifications.test.tsx.snap
index ab3a6744..7ca8cdc4 100644
--- a/src/routes/__snapshots__/Notifications.test.tsx.snap
+++ b/src/routes/__snapshots__/Notifications.test.tsx.snap
@@ -13,6 +13,7 @@ exports[`routes/Notifications.tsx should render itself & its children (with noti
     notifications={
       [
         {
+          "hostname": "github.com",
           "id": "138661096",
           "last_read_at": "2017-05-20T17:06:51Z",
           "reason": "subscribed",