Skip to content

Commit

Permalink
chore: Move GH requests to the server (All-Hands-AI#6217)
Browse files Browse the repository at this point in the history
  • Loading branch information
amanape authored and AlexCuadron committed Jan 13, 2025
1 parent 3663c08 commit 0a93ffe
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 171 deletions.
47 changes: 0 additions & 47 deletions frontend/__tests__/api/github.test.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "test-utils";
import { GitHubRepositorySelector } from "#/components/features/github/github-repo-selector";
import OpenHands from "#/api/open-hands";
import * as GitHubAPI from "#/api/github";

describe("GitHubRepositorySelector", () => {
const onInputChangeMock = vi.fn();
Expand Down Expand Up @@ -60,8 +59,8 @@ describe("GitHubRepositorySelector", () => {
];

const searchPublicRepositoriesSpy = vi.spyOn(
GitHubAPI,
"searchPublicRepositories",
OpenHands,
"searchGitHubRepositories",
);
searchPublicRepositoriesSpy.mockResolvedValue(mockSearchedRepos);

Expand Down
82 changes: 0 additions & 82 deletions frontend/src/api/github.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
import { extractNextPageFromLink } from "#/utils/extract-next-page-from-link";
import { github } from "./github-axios-instance";
import { openHands } from "./open-hands-axios";

/**
* Given the user, retrieves app installations IDs for OpenHands Github App
* Uses user access token for Github App
*/
export const retrieveGitHubAppInstallations = async (): Promise<number[]> => {
const response = await github.get<GithubAppInstallation>(
"/user/installations",
);

return response.data.installations.map((installation) => installation.id);
};

/**
* Retrieves repositories where OpenHands Github App has been installed
* @param installationIndex Pagination cursor position for app installation IDs
Expand Down Expand Up @@ -82,72 +69,3 @@ export const retrieveGitHubUserRepositories = async (

return { data: response.data, nextPage };
};

/**
* Given a GitHub token, retrieves the authenticated user
* @returns The authenticated user or an error response
*/
export const retrieveGitHubUser = async () => {
const response = await github.get<GitHubUser>("/user");

const { data } = response;

const user: GitHubUser = {
id: data.id,
login: data.login,
avatar_url: data.avatar_url,
company: data.company,
name: data.name,
email: data.email,
};

return user;
};

export const searchPublicRepositories = async (
query: string,
per_page = 5,
sort: "" | "updated" | "stars" | "forks" = "stars",
order: "desc" | "asc" = "desc",
): Promise<GitHubRepository[]> => {
const response = await github.get<{ items: GitHubRepository[] }>(
"/search/repositories",
{
params: {
q: query,
per_page,
sort,
order,
},
},
);
return response.data.items;
};

export const retrieveLatestGitHubCommit = async (
repository: string,
): Promise<GitHubCommit | null> => {
try {
const response = await github.get<GitHubCommit[]>(
`/repos/${repository}/commits`,
{
params: {
per_page: 1,
},
},
);
return response.data[0] || null;
} catch (error) {
if (!error || typeof error !== "object") {
throw new Error("Unknown error occurred");
}
const axiosError = error as { response?: { status: number } };
if (axiosError.response?.status === 409) {
// Repository is empty, no commits yet
return null;
}
throw new Error(
error instanceof Error ? error.message : "Unknown error occurred",
);
}
};
39 changes: 39 additions & 0 deletions frontend/src/api/open-hands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,45 @@ class OpenHands {
const data = await openHands.post("/api/settings", settings);
return data.status === 200;
}

static async getGitHubUser(): Promise<GitHubUser> {
const response = await openHands.get<GitHubUser>("/api/github/user");

const { data } = response;

const user: GitHubUser = {
id: data.id,
login: data.login,
avatar_url: data.avatar_url,
company: data.company,
name: data.name,
email: data.email,
};

return user;
}

static async getGitHubUserInstallationIds(): Promise<number[]> {
const response = await openHands.get<number[]>("/api/github/installations");
return response.data;
}

static async searchGitHubRepositories(
query: string,
per_page = 5,
): Promise<GitHubRepository[]> {
const response = await openHands.get<{ items: GitHubRepository[] }>(
"/api/github/search/repositories",
{
params: {
query,
per_page,
},
},
);

return response.data.items;
}
}

export default OpenHands;
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function GitHubRepositoriesSuggestionBox({
<GitHubRepositorySelector
onInputChange={setSearchQuery}
onSelect={handleSubmit}
publicRepositories={searchedRepos}
publicRepositories={searchedRepos || []}
userRepositories={repositories}
/>
) : (
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/hooks/query/use-app-installations.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { useQuery } from "@tanstack/react-query";
import { useAuth } from "#/context/auth-context";
import { useConfig } from "./use-config";
import { retrieveGitHubAppInstallations } from "#/api/github";
import OpenHands from "#/api/open-hands";

export const useAppInstallations = () => {
const { data: config } = useConfig();
const { gitHubToken } = useAuth();

return useQuery({
queryKey: ["installations", gitHubToken, config?.GITHUB_CLIENT_ID],
queryFn: async () => {
const data = await retrieveGitHubAppInstallations();
return data;
},
queryFn: OpenHands.getGitHubUserInstallationIds,
enabled:
!!gitHubToken &&
!!config?.GITHUB_CLIENT_ID &&
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/hooks/query/use-github-user.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { useQuery } from "@tanstack/react-query";
import React from "react";
import posthog from "posthog-js";
import { retrieveGitHubUser } from "#/api/github";
import { useAuth } from "#/context/auth-context";
import { useConfig } from "./use-config";
import OpenHands from "#/api/open-hands";

export const useGitHubUser = () => {
const { gitHubToken, setUserId } = useAuth();
const { data: config } = useConfig();

const user = useQuery({
queryKey: ["user", gitHubToken],
queryFn: retrieveGitHubUser,
queryFn: OpenHands.getGitHubUser,
enabled: !!gitHubToken && !!config?.APP_MODE,
retry: false,
});
Expand Down
17 changes: 0 additions & 17 deletions frontend/src/hooks/query/use-latest-repo-commit.ts

This file was deleted.

5 changes: 2 additions & 3 deletions frontend/src/hooks/query/use-search-repositories.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { useQuery } from "@tanstack/react-query";
import { searchPublicRepositories } from "#/api/github";
import OpenHands from "#/api/open-hands";

export function useSearchRepositories(query: string) {
return useQuery({
queryKey: ["repositories", query],
queryFn: () => searchPublicRepositories(query, 3),
queryFn: () => OpenHands.searchGitHubRepositories(query, 3),
enabled: !!query,
select: (data) => data.map((repo) => ({ ...repo, is_public: true })),
initialData: [],
});
}
2 changes: 2 additions & 0 deletions openhands/server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import openhands.agenthub # noqa F401 (we import this to get the agents registered)
from openhands.server.middleware import (
AttachConversationMiddleware,
GitHubTokenMiddleware,
InMemoryRateLimiter,
LocalhostCORSMiddleware,
NoCacheMiddleware,
Expand Down Expand Up @@ -44,6 +45,7 @@ async def _lifespan(app: FastAPI):
allow_headers=['*'],
)

app.add_middleware(GitHubTokenMiddleware)
app.add_middleware(NoCacheMiddleware)
app.add_middleware(
RateLimitMiddleware, rate_limiter=InMemoryRateLimiter(requests=10, seconds=1)
Expand Down
13 changes: 13 additions & 0 deletions openhands/server/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,16 @@ async def __call__(self, request: Request, call_next: Callable):
await self._detach_session(request)

return response


class GitHubTokenMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if request.url.path.startswith('/api/github'):
github_token = request.headers.get('X-GitHub-Token')
if not github_token:
return JSONResponse(
status_code=400,
content={'error': 'Missing X-GitHub-Token header'},
)
request.state.github_token = github_token
return await call_next(request)
Loading

0 comments on commit 0a93ffe

Please sign in to comment.