Skip to content

Commit

Permalink
feat: Support full URLs when adding repo to insights page (#1470)
Browse files Browse the repository at this point in the history
  • Loading branch information
dominicduffin1 authored Aug 17, 2023
1 parent 2ce44bd commit ee53414
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {

import useSupabaseAuth from "lib/hooks/useSupabaseAuth";
import {
generateApiPrUrl,
generateRepoParts,
getAvatarByUsername,
getOwnerAndRepoNameFromUrl,
isValidIssueUrl,
Expand Down Expand Up @@ -219,7 +219,7 @@ const ContributorHighlightCard = ({
}

if (isValidPullRequestUrl(pullrequestLink) || isValidIssueUrl(pullrequestLink)) {
const { apiPaths } = generateApiPrUrl(highlight.prLink);
const { apiPaths } = generateRepoParts(highlight.prLink);
const { repoName, orgName, issueId } = apiPaths;
setLoading(true);
const isIssue = highlight.prLink.includes("issues");
Expand Down
6 changes: 3 additions & 3 deletions components/molecules/HighlightInput/highlight-input-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Tooltip from "components/atoms/Tooltip/tooltip";

import { createHighlights } from "lib/hooks/createHighlights";
import {
generateApiPrUrl,
generateRepoParts,
getGithubIssueDetails,
getGithubIssueComments,
getPullRequestCommitMessageFromUrl,
Expand Down Expand Up @@ -112,9 +112,9 @@ const HighlightInputForm = ({ refreshCallback }: HighlightInputFormProps): JSX.E
const highlight = bodyText;

if (isValidPullRequestUrl(pullrequestLink) || isValidIssueUrl(pullrequestLink)) {
// generateApiPrUrl will return an object with repoName, orgName and issueId
// generateRepoParts will return an object with repoName, orgName and issueId
// it can work with both issue and pull request links
const { apiPaths } = generateApiPrUrl(pullrequestLink);
const { apiPaths } = generateRepoParts(pullrequestLink);
const { repoName, orgName, issueId } = apiPaths;
setLoading(true);
// Api validation to check validity of github pull request link match
Expand Down
18 changes: 14 additions & 4 deletions components/organisms/InsightPage/InsightPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import RepoNotIndexed from "components/organisms/Repositories/repository-not-ind
import useRepositories from "lib/hooks/api/useRepositories";

import useSupabaseAuth from "lib/hooks/useSupabaseAuth";
import { getAvatarById, getAvatarByUsername } from "lib/utils/github";
import { generateRepoParts, getAvatarById, getAvatarByUsername } from "lib/utils/github";
import useStore from "lib/store";
import Error from "components/atoms/Error/Error";
import Search from "components/atoms/Search/search";
Expand Down Expand Up @@ -202,10 +202,20 @@ const InsightPage = ({ edit, insight, pageRepos }: InsightPageProps) => {
return;
}

const { apiPaths, isValidUrl } = generateRepoParts(repoToAdd);

if (!isValidUrl) {
setAddRepoError(RepoLookupError.Invalid);
return;
}

const { repoFullName } = apiPaths;

setAddRepoLoading({ repoName: repoToAdd, isAddedFromCart, isLoading: true });
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/repos/${repoToAdd}`);
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/repos/${repoFullName}`);
setAddRepoLoading({ repoName: repoToAdd, isAddedFromCart, isLoading: false });

if (response.ok) {
const addedRepo = (await response.json()) as DbRepo;

Expand All @@ -215,7 +225,7 @@ const InsightPage = ({ edit, insight, pageRepos }: InsightPageProps) => {
setAddRepoError(RepoLookupError.Initial);
setRepoSearchTerm("");
} else {
const publicRepoResponse = await fetch(`https://api.github.com/repos/${repoToAdd}`);
const publicRepoResponse = await fetch(`https://api.github.com/repos/${repoFullName}`);

if (publicRepoResponse.ok) {
const publicRepo = await publicRepoResponse.json();
Expand Down Expand Up @@ -390,7 +400,7 @@ const InsightPage = ({ edit, insight, pageRepos }: InsightPageProps) => {
</Title>
<Search
isLoading={createLoading}
placeholder="Repository Full Name (ex: open-sauced/open-sauced)"
placeholder="Repository URL or Full Name (ex: open-sauced/open-sauced)"
className="!w-full text-md text-gra"
name={"query"}
suggestions={suggestions}
Expand Down
41 changes: 34 additions & 7 deletions lib/utils/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,56 @@ const getProfileLink = (username: string | null) => `https://github.com/${userna

const getRepoIssuesLink = (repoName: string | null) => `https://github.com/${(repoName && `${repoName}/issues`) || ""}`;

const generateApiPrUrl = (
const generateRepoParts = (
url: string
): { isValidUrl: boolean; apiPaths: { orgName: string | null; repoName: string | null; issueId: string | null } } => {
): {
isValidUrl: boolean;
apiPaths: { orgName: string | null; repoName: string | null; repoFullName: string | null; issueId: string | null };
} => {
try {
const trimmedUrl = url.trim();

if (
!(trimmedUrl.includes("https://") || trimmedUrl.includes("http://") || trimmedUrl.includes("www.")) &&
trimmedUrl.split("/").length === 2
) {
const [orgName, repoName] = trimmedUrl.split("/");

const repoFullName = `${orgName}/${repoName}`;

return {
isValidUrl: true,
apiPaths: { orgName, repoName, repoFullName, issueId: null },
};
}

const githubUrl = new URL(trimmedUrl.includes("https://") ? trimmedUrl : `https://${trimmedUrl}`);
const { pathname } = githubUrl;

const [, orgName, repoName, , issueId] = pathname.split("/");

if (githubUrl.hostname !== "github.com") {
const repoFullName = `${orgName}/${repoName}`;

if (githubUrl.hostname !== "github.com" || !orgName || !repoName) {
return {
isValidUrl: false,
apiPaths: { orgName: null, repoName: null, issueId: null },
apiPaths: { orgName: null, repoName: null, repoFullName: null, issueId: null },
};
}

if (!issueId) {
return {
isValidUrl: true,
apiPaths: { orgName, repoName, repoFullName, issueId: null },
};
}

return {
isValidUrl: true,
apiPaths: { orgName, repoName, issueId },
apiPaths: { orgName, repoName, repoFullName, issueId },
};
} catch (err) {
return { isValidUrl: false, apiPaths: { orgName: null, repoName: null, issueId: null } };
return { isValidUrl: false, apiPaths: { orgName: null, repoName: null, repoFullName: null, issueId: null } };
}
};

Expand Down Expand Up @@ -144,7 +171,7 @@ export {
getAvatarByUsername,
getProfileLink,
getRepoIssuesLink,
generateApiPrUrl,
generateRepoParts,
generateGhOgImage,
isValidPullRequestUrl,
isValidIssueUrl,
Expand Down
49 changes: 49 additions & 0 deletions tests/lib/utils/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getProfileLink,
getRepoIssuesLink,
generateGhOgImage,
generateRepoParts,
isValidIssueUrl,
} from "lib/utils/github";

Expand Down Expand Up @@ -35,6 +36,54 @@ describe("[lib] github methods", () => {
const result = generateGhOgImage("https://gitub.com/open-sauced/hot/pull/448");
expect(result).toEqual({ isValid: false, url: "" });
});
it("Should return an object with valid org name, repo name and issue", () => {
const result = generateRepoParts("https://github.com/open-sauced/insights/pull/1470");
expect(result.isValidUrl).toEqual(true);
expect(result.apiPaths).toEqual({
orgName: "open-sauced",
repoName: "insights",
repoFullName: "open-sauced/insights",
issueId: "1470",
});
});
it("Should return an object with valid org name, repo name and issue", () => {
const result = generateRepoParts("github.com/open-sauced/insights/pull/1470");
expect(result.isValidUrl).toEqual(true);
expect(result.apiPaths).toEqual({
orgName: "open-sauced",
repoName: "insights",
repoFullName: "open-sauced/insights",
issueId: "1470",
});
});
it("Should return an object with a valid org name and repo name", () => {
const result = generateRepoParts("https://github.com/open-sauced/insights");
expect(result.isValidUrl).toEqual(true);
expect(result.apiPaths).toEqual({
orgName: "open-sauced",
repoName: "insights",
repoFullName: "open-sauced/insights",
issueId: null,
});
});
it("Should return an object with a valid org name and repo name", () => {
const result = generateRepoParts("open-sauced/insights");
expect(result.isValidUrl).toEqual(true);
expect(result.apiPaths).toEqual({
orgName: "open-sauced",
repoName: "insights",
repoFullName: "open-sauced/insights",
issueId: null,
});
});
it("Should return an object with isValidUrl set to false", () => {
const result = generateRepoParts("https://insights.opensauced.pizza/hub/insights/new");
expect(result.isValidUrl).toBeFalsy();
});
it("Should return an object with isValidUrl set to false", () => {
const result = generateRepoParts("🍕");
expect(result.isValidUrl).toBeFalsy();
});
it("Should return false", () => {
const result = isValidIssueUrl("https://gitub.com/open-sauced/hot/pull/448");
expect(result).toEqual(false);
Expand Down

0 comments on commit ee53414

Please sign in to comment.