Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/renderer/utils/forges/gitea/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { KeyIcon } from '@primer/octicons-react';
import { mockGiteaAccount } from '../../../__mocks__/account-mocks';
import { mockPartialGitifyNotification } from '../../../__mocks__/notifications-mocks';

import type { Hostname, Link, SettingsState, Token } from '../../../types';
import { useSettingsStore } from '../../../stores';

import type { Hostname, Link, Token } from '../../../types';

import { giteaAdapter } from './adapter';
import * as client from './client';
Expand Down Expand Up @@ -129,10 +131,9 @@ describe('renderer/utils/forges/gitea/adapter.ts', () => {
},
]);

const result = await giteaAdapter.listNotifications(mockGiteaAccount, {
fetchAllNotifications: false,
fetchReadNotifications: false,
} as SettingsState);
useSettingsStore.setState({ fetchAllNotifications: false, fetchReadNotifications: false });

const result = await giteaAdapter.listNotifications(mockGiteaAccount);

expect(result).toHaveLength(1);
expect(result[0].id).toBe('99');
Expand Down
16 changes: 3 additions & 13 deletions src/renderer/utils/forges/gitea/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { KeyIcon, ServerIcon } from '@primer/octicons-react';

import type {
Account,
Hostname,
Link,
RawGitifyNotification,
SettingsState,
Token,
} from '../../../types';
import type { Account, Hostname, Link, RawGitifyNotification, Token } from '../../../types';
import type {
ForgeAdapter,
ForgeCapabilities,
Expand Down Expand Up @@ -43,11 +36,8 @@ async function fetchAuthenticatedUser(account: Account): Promise<RefreshAccountD
};
}

async function listNotifications(
account: Account,
settings: SettingsState,
): Promise<RawGitifyNotification[]> {
const raw = await listGiteaNotifications(account, settings);
async function listNotifications(account: Account): Promise<RawGitifyNotification[]> {
const raw = await listGiteaNotifications(account);
return transformGiteaNotifications(raw, account);
}

Expand Down
43 changes: 18 additions & 25 deletions src/renderer/utils/forges/gitea/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { mockGiteaAccount } from '../../../__mocks__/account-mocks';

import type { Hostname, SettingsState } from '../../../types';
import { useSettingsStore } from '../../../stores';

import type { Hostname } from '../../../types';

import * as comms from '../../system/comms';
import {
Expand Down Expand Up @@ -37,10 +39,9 @@ describe('renderer/utils/forges/gitea/client.ts', () => {
it('fetches a single page when fetchAllNotifications is false', async () => {
fetchMock().mockResolvedValueOnce(jsonResponse([{ id: 1 }]));

const result = await listGiteaNotifications(mockGiteaAccount, {
fetchAllNotifications: false,
fetchReadNotifications: false,
} as SettingsState);
useSettingsStore.setState({ fetchAllNotifications: false, fetchReadNotifications: false });

const result = await listGiteaNotifications(mockGiteaAccount);

expect(result).toEqual([{ id: 1 }]);
expect(fetchMock()).toHaveBeenCalledTimes(1);
Expand All @@ -54,10 +55,9 @@ describe('renderer/utils/forges/gitea/client.ts', () => {
it('includes read status when fetchReadNotifications is true', async () => {
fetchMock().mockResolvedValueOnce(jsonResponse([]));

await listGiteaNotifications(mockGiteaAccount, {
fetchAllNotifications: false,
fetchReadNotifications: true,
} as SettingsState);
useSettingsStore.setState({ fetchAllNotifications: false, fetchReadNotifications: true });

await listGiteaNotifications(mockGiteaAccount);

const calledUrl = fetchMock().mock.calls[0][0] as string;
expect(calledUrl).toContain('status-types=unread');
Expand All @@ -70,10 +70,9 @@ describe('renderer/utils/forges/gitea/client.ts', () => {
.mockResolvedValueOnce(jsonResponse([{ id: 100 }]))
.mockResolvedValueOnce(jsonResponse([]));

const result = await listGiteaNotifications(mockGiteaAccount, {
fetchAllNotifications: true,
fetchReadNotifications: false,
} as SettingsState);
useSettingsStore.setState({ fetchAllNotifications: true, fetchReadNotifications: false });

const result = await listGiteaNotifications(mockGiteaAccount);

expect(result).toHaveLength(101);
expect(fetchMock()).toHaveBeenCalledTimes(2);
Expand All @@ -87,20 +86,14 @@ describe('renderer/utils/forges/gitea/client.ts', () => {
}),
);

await expect(
listGiteaNotifications(mockGiteaAccount, {
fetchAllNotifications: false,
fetchReadNotifications: false,
} as SettingsState),
).rejects.toThrow(/^Gitea API 403 Forbidden$/);
useSettingsStore.setState({ fetchAllNotifications: false, fetchReadNotifications: false });

await expect(listGiteaNotifications(mockGiteaAccount)).rejects.toThrow(
/^Gitea API 403 Forbidden$/,
);
// The thrown error must not include the response body — a hostile
// server could echo back the Authorization header into logs.
await expect(
listGiteaNotifications(mockGiteaAccount, {
fetchAllNotifications: false,
fetchReadNotifications: false,
} as SettingsState),
).rejects.toThrow();
await expect(listGiteaNotifications(mockGiteaAccount)).rejects.toThrow();
});
});

Expand Down
17 changes: 9 additions & 8 deletions src/renderer/utils/forges/gitea/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { Account, Hostname, SettingsState } from '../../../types';
import useSettingsStore from '../../../stores/useSettingsStore';

import type { Account, Hostname } from '../../../types';
import type { GiteaNotificationThread, GiteaUser } from './types';

import { isValidHostname } from '../../auth/utils';
Expand Down Expand Up @@ -53,7 +55,9 @@ async function giteaRequest<T>(account: Account, pathname: string, init?: Reques
return response.json() as Promise<T>;
}

function buildNotificationQuery(settings: SettingsState): string {
function buildNotificationQuery(): string {
const settings = useSettingsStore.getState();

const params = new URLSearchParams();
params.set('limit', String(PAGE_SIZE));

Expand All @@ -66,13 +70,10 @@ function buildNotificationQuery(settings: SettingsState): string {
return params.toString();
}

export async function listGiteaNotifications(
account: Account,
settings: SettingsState,
): Promise<GiteaNotificationThread[]> {
const query = buildNotificationQuery(settings);
export async function listGiteaNotifications(account: Account): Promise<GiteaNotificationThread[]> {
const query = buildNotificationQuery();

if (!settings.fetchAllNotifications) {
if (!useSettingsStore.getState().fetchAllNotifications) {
return giteaRequest<GiteaNotificationThread[]>(account, `notifications?${query}&page=1`);
}

Expand Down
5 changes: 2 additions & 3 deletions src/renderer/utils/forges/github/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AppsIcon, KeyIcon, PersonIcon } from '@primer/octicons-react';

import { mockGitHubCloudAccount } from '../../../__mocks__/account-mocks';

import type { Hostname, Link, SettingsState, Token } from '../../../types';
import type { Hostname, Link, Token } from '../../../types';

import { githubAdapter } from './adapter';
import * as client from './client';
Expand Down Expand Up @@ -102,7 +102,6 @@ describe('renderer/utils/forges/github/adapter.ts', () => {

describe('listNotifications', () => {
it('lists notifications and transforms them to GitifyNotification', async () => {
const settings = {} as SettingsState;
vi.spyOn(client, 'listNotificationsForAuthenticatedUser').mockResolvedValue([
{
id: '1',
Expand All @@ -128,7 +127,7 @@ describe('renderer/utils/forges/github/adapter.ts', () => {
},
] as unknown as Awaited<ReturnType<typeof client.listNotificationsForAuthenticatedUser>>);

const result = await githubAdapter.listNotifications(mockGitHubCloudAccount, settings);
const result = await githubAdapter.listNotifications(mockGitHubCloudAccount);

expect(result).toHaveLength(1);
expect(result[0].id).toBe('1');
Expand Down
9 changes: 3 additions & 6 deletions src/renderer/utils/forges/github/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AppsIcon, KeyIcon, MarkGithubIcon, PersonIcon } from '@primer/octicons-

import { Constants } from '../../../constants';

import type { Account, Link, RawGitifyNotification, SettingsState } from '../../../types';
import type { Account, Link, RawGitifyNotification } from '../../../types';
import type { AuthMethod } from '../../auth/types';
import type { ForgeAdapter, NotificationDisplayHelpers, RefreshAccountData } from '../types';

Expand Down Expand Up @@ -52,11 +52,8 @@ async function fetchAuthenticatedUser(account: Account): Promise<RefreshAccountD
};
}

async function listNotifications(
account: Account,
settings: SettingsState,
): Promise<RawGitifyNotification[]> {
const raw = await listNotificationsForAuthenticatedUser(account, settings);
async function listNotifications(account: Account): Promise<RawGitifyNotification[]> {
const raw = await listNotificationsForAuthenticatedUser(account);
return transformNotifications(raw, account);
}

Expand Down
40 changes: 15 additions & 25 deletions src/renderer/utils/forges/github/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {

import { Constants } from '../../../constants';

import type { Link, SettingsState } from '../../../types';
import { useSettingsStore } from '../../../stores';

import type { Link } from '../../../types';

import {
fetchAuthenticatedUserDetails,
Expand Down Expand Up @@ -125,16 +127,13 @@ describe('renderer/utils/forges/github/client.ts', () => {

describe('listNotificationsForAuthenticatedUser', () => {
it('should list only participating notifications for user', async () => {
const mockSettings: Partial<SettingsState> = {
useSettingsStore.setState({
participating: true,
fetchReadNotifications: false,
fetchAllNotifications: false,
};
});

await listNotificationsForAuthenticatedUser(
mockGitHubCloudAccount,
mockSettings as SettingsState,
);
await listNotificationsForAuthenticatedUser(mockGitHubCloudAccount);

expect(createOctokitClientSpy).toHaveBeenCalledWith(mockGitHubCloudAccount, 'rest');
expect(mockOctokit.rest.activity.listNotificationsForAuthenticatedUser).toHaveBeenCalledWith({
Expand All @@ -148,16 +147,13 @@ describe('renderer/utils/forges/github/client.ts', () => {
});

it('should list participating and watching notifications for user', async () => {
const mockSettings: Partial<SettingsState> = {
useSettingsStore.setState({
participating: false,
fetchReadNotifications: false,
fetchAllNotifications: false,
};
});

await listNotificationsForAuthenticatedUser(
mockGitHubCloudAccount,
mockSettings as SettingsState,
);
await listNotificationsForAuthenticatedUser(mockGitHubCloudAccount);

expect(createOctokitClientSpy).toHaveBeenCalledWith(mockGitHubCloudAccount, 'rest');
expect(mockOctokit.rest.activity.listNotificationsForAuthenticatedUser).toHaveBeenCalledWith({
Expand All @@ -171,16 +167,13 @@ describe('renderer/utils/forges/github/client.ts', () => {
});

it('should list read and done notifications for user', async () => {
const mockSettings: Partial<SettingsState> = {
useSettingsStore.setState({
participating: false,
fetchReadNotifications: true,
fetchAllNotifications: false,
};
});

await listNotificationsForAuthenticatedUser(
mockGitHubCloudAccount,
mockSettings as SettingsState,
);
await listNotificationsForAuthenticatedUser(mockGitHubCloudAccount);

expect(createOctokitClientSpy).toHaveBeenCalledWith(mockGitHubCloudAccount, 'rest');
expect(mockOctokit.rest.activity.listNotificationsForAuthenticatedUser).toHaveBeenCalledWith({
Expand All @@ -194,16 +187,13 @@ describe('renderer/utils/forges/github/client.ts', () => {
});

it('should unpaginate notifications list for user', async () => {
const mockSettings: Partial<SettingsState> = {
useSettingsStore.setState({
participating: false,
fetchReadNotifications: false,
fetchAllNotifications: true,
};
});

await listNotificationsForAuthenticatedUser(
mockGitHubCloudAccount,
mockSettings as SettingsState,
);
await listNotificationsForAuthenticatedUser(mockGitHubCloudAccount);

expect(createOctokitClientSpy).toHaveBeenCalledWith(mockGitHubCloudAccount, 'rest');
expect(mockOctokit.paginate).toHaveBeenCalledWith(
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/utils/forges/github/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Constants } from '../../../constants';

import type { Account, Link, RawGitifyNotification, SettingsState } from '../../../types';
import useSettingsStore from '../../../stores/useSettingsStore';

import type { Account, Link, RawGitifyNotification } from '../../../types';
import type {
GetCommitCommentResponse,
GetCommitResponse,
Expand Down Expand Up @@ -49,8 +51,8 @@ export async function fetchAuthenticatedUserDetails(account: Account) {
*/
export async function listNotificationsForAuthenticatedUser(
account: Account,
settings: SettingsState,
): Promise<ListNotificationsForAuthenticatedUserResponse> {
const settings = useSettingsStore.getState();
const octokit = await createOctokitClient(account, 'rest');

if (settings.fetchAllNotifications) {
Expand Down
3 changes: 1 addition & 2 deletions src/renderer/utils/forges/github/enrich.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { mockPartialGitifyNotification } from '../../../__mocks__/notifications-mocks';
import { mockSettings } from '../../../__mocks__/state-mocks';

import type { GitifyRepository, Link } from '../../../types';

Expand Down Expand Up @@ -45,7 +44,7 @@ describe('renderer/utils/forges/github/enrich.ts', () => {

vi.mocked(client.fetchIssueByNumber).mockRejectedValue(mockError);

const [result] = await enrichGitHubNotifications([mockNotification], mockSettings);
const [result] = await enrichGitHubNotifications([mockNotification]);

expect(result).toBeDefined();
expect(rendererLogErrorSpy).toHaveBeenCalledWith(
Expand Down
10 changes: 3 additions & 7 deletions src/renderer/utils/forges/github/enrich.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GitifySubject, RawGitifyNotification, SettingsState } from '../../../types';
import type { GitifySubject, RawGitifyNotification } from '../../../types';

import { rendererLogError, rendererLogWarn, toError } from '../../core/logger';
import { fetchNotificationDetailsForList } from './client';
Expand All @@ -23,14 +23,11 @@ export const GITHUB_API_MERGE_BATCH_SIZE = 100;
*/
export async function enrichGitHubNotifications(
notifications: RawGitifyNotification[],
settings: SettingsState,
): Promise<RawGitifyNotification[]> {
const fragments = await fetchInBatches(notifications);

return Promise.all(
notifications.map((notification) =>
enrichSingle(notification, settings, fragments.get(notification)),
),
notifications.map((notification) => enrichSingle(notification, fragments.get(notification))),
);
}

Expand Down Expand Up @@ -67,14 +64,13 @@ async function fetchInBatches(

async function enrichSingle(
notification: RawGitifyNotification,
settings: SettingsState,
fetchedData: FetchMergedDetailsTemplateQuery['repository'] | undefined,
): Promise<RawGitifyNotification> {
let additionalSubjectDetails: Partial<GitifySubject> = {};

try {
const handler = createNotificationHandler(notification);
additionalSubjectDetails = await handler.enrich(notification, settings, fetchedData);
additionalSubjectDetails = await handler.enrich(notification, fetchedData);
} catch (err) {
rendererLogError(
'enrichGitHubNotifications',
Expand Down
Loading