From 1337d5f82f248b710fb1e351545ec47b26dc9216 Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Sun, 1 Oct 2023 09:38:38 +0100 Subject: [PATCH 1/8] chore: retries improvements --- .gitignore | 2 +- src/.DS_Store | Bin 6148 -> 6148 bytes src/Services/GithubApiService.ts | 40 +++--------------------- src/Services/request.ts | 51 +++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 37 deletions(-) create mode 100644 src/Services/request.ts diff --git a/.gitignore b/.gitignore index 91b922d4..663180a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .vscode .env .idea -.lock +deno.lock *.sh diff --git a/src/.DS_Store b/src/.DS_Store index dbcdcabe06f6c0b9f79fd341be39ab7c161065a8..6797d9ecb3f5fb28ed867fe631fdafb9841a8d89 100644 GIT binary patch delta 70 zcmZoMXfc@JFUrKgz`)4BAi%(o$&k#D%1~SuT$DHQqWWY55f(`fhG3vj5kncWWKMow O`o@lU_RZ`ZfB6A5W)e&Q delta 43 xcmZoMXfc@JFUrWkzyQPo3=EkJ$qYdZsSJ4xDHA`cZ(JS2K8aCbGdss$egMd~3atPD diff --git a/src/Services/GithubApiService.ts b/src/Services/GithubApiService.ts index 96627c1a..aff0a05d 100644 --- a/src/Services/GithubApiService.ts +++ b/src/Services/GithubApiService.ts @@ -12,13 +12,12 @@ import { queryUserPullRequest, queryUserRepository, } from "../Schemas/index.ts"; -import { soxa } from "../../deps.ts"; import { Retry } from "../Helpers/Retry.ts"; -import { GithubError, QueryDefaultResponse } from "../Types/index.ts"; import { CONSTANTS } from "../utils.ts"; import { EServiceKindError } from "../Types/EServiceKindError.ts"; import { ServiceError } from "../Types/ServiceError.ts"; import { Logger } from "../Helpers/Logger.ts"; +import { requestGithubData } from "./request.ts"; // Need to be here - Exporting from another file makes array of null export const TOKENS = [ @@ -89,27 +88,6 @@ export class GithubApiService extends GithubRepository { ); } - private handleError(responseErrors: GithubError[]): ServiceError { - const errors = responseErrors ?? []; - - const isRateLimitExceeded = errors.some((error) => - error.type.includes(EServiceKindError.RATE_LIMIT) || - error.message.includes("rate limit") - ); - - if (isRateLimitExceeded) { - throw new ServiceError( - "Rate limit exceeded", - EServiceKindError.RATE_LIMIT, - ); - } - - throw new ServiceError( - "unknown error", - EServiceKindError.NOT_FOUND, - ); - } - async executeQuery( query: string, variables: { [key: string]: string }, @@ -120,20 +98,10 @@ export class GithubApiService extends GithubRepository { CONSTANTS.DEFAULT_GITHUB_RETRY_DELAY, ); const response = await retry.fetch>(async ({ attempt }) => { - return await soxa.post("", {}, { - data: { query: query, variables }, - headers: { - Authorization: `bearer ${TOKENS[attempt]}`, - }, - }); - }) as QueryDefaultResponse<{ user: T }>; - - if (response?.data?.errors) { - return this.handleError(response?.data?.errors); - } + return await requestGithubData(query, variables, TOKENS[attempt]); + }); - return response?.data?.data?.user ?? - new ServiceError("not found", EServiceKindError.NOT_FOUND); + return response; } catch (error) { if (error instanceof ServiceError) { Logger.error(error); diff --git a/src/Services/request.ts b/src/Services/request.ts new file mode 100644 index 00000000..b51c3ec1 --- /dev/null +++ b/src/Services/request.ts @@ -0,0 +1,51 @@ +import { soxa } from "../../deps.ts"; +import { + EServiceKindError, + GithubError, + QueryDefaultResponse, + ServiceError, +} from "../Types/index.ts"; + +export async function requestGithubData( + query: string, + variables: { [key: string]: string }, + token = "", +) { + const data = await soxa.post("", {}, { + data: { query, variables }, + headers: { + Authorization: `bearer ${token}`, + }, + }) as QueryDefaultResponse<{ user: T }>; + + if (data?.data?.errors) { + throw handleError(data?.data?.errors); + } + + if (data?.data?.data?.user) { + return data.data.data.user; + } + + throw new ServiceError("not found", EServiceKindError.NOT_FOUND); +} + +function handleError(responseErrors: GithubError[]): ServiceError { + const errors = responseErrors ?? []; + + const isRateLimitExceeded = errors.some((error) => + error.type.includes(EServiceKindError.RATE_LIMIT) || + error.message.includes("rate limit") + ); + + if (isRateLimitExceeded) { + throw new ServiceError( + "Rate limit exceeded", + EServiceKindError.RATE_LIMIT, + ); + } + + throw new ServiceError( + "unknown error", + EServiceKindError.NOT_FOUND, + ); +} From ca5e3280bbd86dc93cf0dc90041782b414a8ec2a Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Sun, 1 Oct 2023 12:41:40 +0100 Subject: [PATCH 2/8] chore: types improvements feat: request azure fix: remove lock fix: check of rate limit fix: instances fix: instances --- .gitignore | 2 +- api/index.ts | 6 +- deps.ts | 9 +- src/Services/GithubApiService.ts | 1 - src/Services/GithubAzureService.ts | 128 ++++++++++++++++++++++++++ src/Services/index.ts | 23 +++++ src/StaticRenderRegeneration/index.ts | 4 +- 7 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 src/Services/GithubAzureService.ts create mode 100644 src/Services/index.ts diff --git a/.gitignore b/.gitignore index 91b922d4..663180a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .vscode .env .idea -.lock +deno.lock *.sh diff --git a/api/index.ts b/api/index.ts index 68d5215a..9935a744 100644 --- a/api/index.ts +++ b/api/index.ts @@ -4,13 +4,9 @@ import { COLORS, Theme } from "../src/theme.ts"; import { Error400 } from "../src/error_page.ts"; import "https://deno.land/x/dotenv@v0.5.0/load.ts"; import { staticRenderRegeneration } from "../src/StaticRenderRegeneration/index.ts"; -import { GithubRepositoryService } from "../src/Repository/GithubRepository.ts"; -import { GithubApiService } from "../src/Services/GithubApiService.ts"; import { ServiceError } from "../src/Types/index.ts"; import { ErrorPage } from "../src/pages/Error.ts"; - -const serviceProvider = new GithubApiService(); -const client = new GithubRepositoryService(serviceProvider).repository; +import { client } from "../src/Services/index.ts"; const defaultHeaders = new Headers( { diff --git a/deps.ts b/deps.ts index 4b51bfce..708f2e2c 100644 --- a/deps.ts +++ b/deps.ts @@ -9,9 +9,14 @@ import { spy, } from "https://deno.land/std@0.203.0/testing/mock.ts"; -import { CONSTANTS } from "./src/utils.ts"; +const api = new Map([ + ["github", Deno.env.get("GITHUB_API")], + ["azure", Deno.env.get("AZURE_API_URL")], +]); -const baseURL = Deno.env.get("GITHUB_API") || CONSTANTS.DEFAULT_GITHUB_API; +const DEFAULT_PROVIDER = Deno.env.get("provider") ?? "github"; + +const baseURL = api.get(DEFAULT_PROVIDER); const soxa = new ServiceProvider({ ...defaults, diff --git a/src/Services/GithubApiService.ts b/src/Services/GithubApiService.ts index 96627c1a..1a2a0f44 100644 --- a/src/Services/GithubApiService.ts +++ b/src/Services/GithubApiService.ts @@ -91,7 +91,6 @@ export class GithubApiService extends GithubRepository { private handleError(responseErrors: GithubError[]): ServiceError { const errors = responseErrors ?? []; - const isRateLimitExceeded = errors.some((error) => error.type.includes(EServiceKindError.RATE_LIMIT) || error.message.includes("rate limit") diff --git a/src/Services/GithubAzureService.ts b/src/Services/GithubAzureService.ts new file mode 100644 index 00000000..28900297 --- /dev/null +++ b/src/Services/GithubAzureService.ts @@ -0,0 +1,128 @@ +import { GithubRepository } from "../Repository/GithubRepository.ts"; +import { + GitHubUserActivity, + GitHubUserIssue, + GitHubUserPullRequest, + GitHubUserRepository, + UserInfo, +} from "../user_info.ts"; +import { + queryUserActivity, + queryUserIssue, + queryUserPullRequest, + queryUserRepository, +} from "../Schemas/index.ts"; +import { soxa } from "../../deps.ts"; +import { Retry } from "../Helpers/Retry.ts"; +import { + EServiceKindError, + QueryDefaultResponse, + ServiceError, +} from "../Types/index.ts"; +import { CONSTANTS } from "../utils.ts"; +import { Logger } from "../Helpers/Logger.ts"; + +const authentication = Deno.env.get("X_API_KEY"); + +export const TOKENS = [ + Deno.env.get("GITHUB_TOKEN1"), + Deno.env.get("GITHUB_TOKEN2"), +]; + +export class GithubAzureService extends GithubRepository { + async requestUserRepository( + username: string, + ): Promise { + return await this.executeQuery(queryUserRepository, { + username, + }, "userRepository"); + } + async requestUserActivity( + username: string, + ): Promise { + return await this.executeQuery(queryUserActivity, { + username, + }, "userActivity"); + } + async requestUserIssue( + username: string, + ): Promise { + return await this.executeQuery(queryUserIssue, { + username, + }, "userIssue"); + } + async requestUserPullRequest( + username: string, + ): Promise { + return await this.executeQuery( + queryUserPullRequest, + { username }, + "userPullRequest", + ); + } + async requestUserInfo(username: string): Promise { + // Avoid to call others if one of them is null + const repository = await this.requestUserRepository(username); + if (repository === null) { + return new ServiceError("not found", EServiceKindError.NOT_FOUND); + } + + const promises = Promise.allSettled([ + this.requestUserActivity(username), + this.requestUserIssue(username), + this.requestUserPullRequest(username), + ]); + const [activity, issue, pullRequest] = await promises; + const status = [ + activity.status, + issue.status, + pullRequest.status, + ]; + + if (status.includes("rejected")) { + Logger.error(`Can not find a user with username:' ${username}'`); + return new ServiceError("not found", EServiceKindError.NOT_FOUND); + } + + return new UserInfo( + (activity as PromiseFulfilledResult).value, + (issue as PromiseFulfilledResult).value, + (pullRequest as PromiseFulfilledResult).value, + repository as GitHubUserRepository, + ); + } + + async executeQuery( + query: string, + variables: { [key: string]: string }, + cache_ns?: string, + ) { + const retry = new Retry( + TOKENS.length, + CONSTANTS.DEFAULT_GITHUB_RETRY_DELAY, + ); + try { + const response = await retry.fetch>(async () => { + return await soxa.post("", {}, { + data: { query: query, variables }, + headers: { + cache_ns, + "x_api_key": authentication, + }, + }); + }) as QueryDefaultResponse<{ data: { user: T } }>; + + return response?.data?.data?.data?.user ?? + new ServiceError("not found", EServiceKindError.NOT_FOUND); + } catch (error) { + // TODO: Move this to a logger instance later + if (error instanceof Error && error.cause) { + Logger.error(JSON.stringify(error.cause, null, 2)); + } else { + Logger.error(error); + } + + return new ServiceError("not found", EServiceKindError.NOT_FOUND); + } + } +} diff --git a/src/Services/index.ts b/src/Services/index.ts new file mode 100644 index 00000000..3d9d41bd --- /dev/null +++ b/src/Services/index.ts @@ -0,0 +1,23 @@ +import { GithubRepositoryService } from "../Repository/GithubRepository.ts"; +import { GithubApiService } from "./GithubApiService.ts"; +import { GithubAzureService } from "./GithubAzureService.ts"; + +export type Provider = GithubApiService | GithubAzureService; + +const DEFAULT_PROVIDER = Deno.env.get("provider") ?? "github"; + +const providers = new Map([ + ["github", GithubApiService], + ["azure", GithubAzureService], +]); + +const serviceProvider = providers.get(DEFAULT_PROVIDER); + +if (serviceProvider === undefined) { + throw new Error("Invalid provider"); +} + +const provider = new serviceProvider(); +const repositoryClient = new GithubRepositoryService(provider).repository; + +export const client = repositoryClient; diff --git a/src/StaticRenderRegeneration/index.ts b/src/StaticRenderRegeneration/index.ts index bc846213..872e2276 100644 --- a/src/StaticRenderRegeneration/index.ts +++ b/src/StaticRenderRegeneration/index.ts @@ -1,6 +1,6 @@ import { CacheManager } from "./cache_manager.ts"; import { StaticRegenerationOptions } from "./types.ts"; -import { getUrl, hashString, readCache } from "./utils.ts"; +import { generateUUID, getUrl, readCache } from "./utils.ts"; export async function staticRenderRegeneration( request: Request, @@ -15,7 +15,7 @@ export async function staticRenderRegeneration( return await render(request); } - const cacheFile = await hashString(url.pathname + (url.search ?? "")); + const cacheFile = await generateUUID(url.pathname + (url.search ?? "")); const cacheManager = new CacheManager(options.revalidate ?? 0, cacheFile); if (cacheManager.isCacheValid) { const cache = readCache(cacheManager.cacheFilePath); From 6f59b888825485524609c28a8b7edc8e3a62d520 Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Sun, 1 Oct 2023 14:20:02 +0100 Subject: [PATCH 3/8] chore: improvements on retries --- deps.ts | 12 +- src/Helpers/Retry.ts | 6 + src/Services/GithubApiService.ts | 8 +- src/Services/__mocks__/rateLimitMock.json | 14 + .../__mocks__/successGithubResponse.json | 1535 +++++++++++++++++ src/Services/request.ts | 39 +- src/Types/Request.ts | 12 +- 7 files changed, 1610 insertions(+), 16 deletions(-) create mode 100644 src/Services/__mocks__/rateLimitMock.json create mode 100644 src/Services/__mocks__/successGithubResponse.json diff --git a/deps.ts b/deps.ts index 4b51bfce..691ef991 100644 --- a/deps.ts +++ b/deps.ts @@ -6,7 +6,9 @@ import { } from "https://deno.land/std@0.203.0/assert/mod.ts"; import { assertSpyCalls, + returnsNext, spy, + stub, } from "https://deno.land/std@0.203.0/testing/mock.ts"; import { CONSTANTS } from "./src/utils.ts"; @@ -18,4 +20,12 @@ const soxa = new ServiceProvider({ baseURL, }); -export { assertEquals, assertRejects, assertSpyCalls, soxa, spy }; +export { + assertEquals, + assertRejects, + assertSpyCalls, + returnsNext, + soxa, + spy, + stub, +}; diff --git a/src/Helpers/Retry.ts b/src/Helpers/Retry.ts index a7ce9a95..a1d79eaf 100644 --- a/src/Helpers/Retry.ts +++ b/src/Helpers/Retry.ts @@ -1,3 +1,4 @@ +import { ServiceError } from "../Types/index.ts"; import { Logger } from "./Logger.ts"; export type RetryCallbackProps = { @@ -17,6 +18,11 @@ async function* createAsyncIterable( yield data; return; } catch (e) { + if (e instanceof ServiceError && i < (retries - 1)) { + yield e; + return; + } + yield null; Logger.error(e); await new Promise((resolve) => setTimeout(resolve, delay)); diff --git a/src/Services/GithubApiService.ts b/src/Services/GithubApiService.ts index aff0a05d..fbaff484 100644 --- a/src/Services/GithubApiService.ts +++ b/src/Services/GithubApiService.ts @@ -58,6 +58,7 @@ export class GithubApiService extends GithubRepository { async requestUserInfo(username: string): Promise { // Avoid to call others if one of them is null const repository = await this.requestUserRepository(username); + if (repository instanceof ServiceError) { Logger.error(repository); return repository; @@ -98,7 +99,11 @@ export class GithubApiService extends GithubRepository { CONSTANTS.DEFAULT_GITHUB_RETRY_DELAY, ); const response = await retry.fetch>(async ({ attempt }) => { - return await requestGithubData(query, variables, TOKENS[attempt]); + return await requestGithubData( + query, + variables, + TOKENS[attempt], + ); }); return response; @@ -107,7 +112,6 @@ export class GithubApiService extends GithubRepository { Logger.error(error); return error; } - // TODO: Move this to a logger instance later if (error instanceof Error && error.cause) { Logger.error(JSON.stringify(error.cause, null, 2)); } else { diff --git a/src/Services/__mocks__/rateLimitMock.json b/src/Services/__mocks__/rateLimitMock.json new file mode 100644 index 00000000..1e943585 --- /dev/null +++ b/src/Services/__mocks__/rateLimitMock.json @@ -0,0 +1,14 @@ +{ + "exceeded": { + "documentation_url": "https://docs.github.com/en/free-pro-team@latest/rest/overview/resources-in-the-rest-api#secondary-rate-limits", + "message": "You have exceeded a secondary rate limit. Please wait a few minutes before you try again. If you reach out to GitHub Support for help, please include the request ID DBD8:FB98:31801A8:3222432:65195FDB." + }, + "rate_limit": { + "errors": [ + { + "type": "RATE_LIMITED", + "message": "API rate limit exceeded for user ID 10711649." + } + ] + } +} diff --git a/src/Services/__mocks__/successGithubResponse.json b/src/Services/__mocks__/successGithubResponse.json new file mode 100644 index 00000000..4940d309 --- /dev/null +++ b/src/Services/__mocks__/successGithubResponse.json @@ -0,0 +1,1535 @@ +{ + "repositories": { + "totalCount": 128, + "nodes": [ + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 23 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 11 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 9 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 6 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 6 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Java" + } + ] + }, + "stargazers": { + "totalCount": 5 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 5 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Jupyter Notebook" + } + ] + }, + "stargazers": { + "totalCount": 5 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 4 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 3 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 2 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 2 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 2 + } + }, + { + "languages": { + "nodes": [] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "PHP" + }, + { + "name": "Go" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "Dockerfile" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "Dockerfile" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Dart" + }, + { + "name": "Swift" + }, + { + "name": "Kotlin" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "HTML" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "Vue" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Jupyter Notebook" + }, + { + "name": "Python" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Dart" + }, + { + "name": "HTML" + }, + { + "name": "Swift" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "Vue" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "PHP" + } + ] + }, + "stargazers": { + "totalCount": 1 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "CSS" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "HTML" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "CSS" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "Shell" + }, + { + "name": "Dockerfile" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "CSS" + }, + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "Dockerfile" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "CSS" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "HTML" + }, + { + "name": "C#" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "PHP" + }, + { + "name": "Vue" + }, + { + "name": "Blade" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "CSS" + }, + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "C" + }, + { + "name": "C++" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "C++" + }, + { + "name": "Makefile" + }, + { + "name": "CMake" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "C++" + }, + { + "name": "Makefile" + }, + { + "name": "CMake" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "CSS" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "TypeScript" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Vue" + }, + { + "name": "TypeScript" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "CSS" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "PHP" + }, + { + "name": "JavaScript" + }, + { + "name": "Blade" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "Rust" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "Svelte" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "CSS" + }, + { + "name": "HTML" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "Dockerfile" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Rust" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "Dockerfile" + }, + { + "name": "Shell" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "Shell" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "HTML" + }, + { + "name": "JavaScript" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "CSS" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Dart" + }, + { + "name": "HTML" + }, + { + "name": "Swift" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Dart" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Rust" + }, + { + "name": "JavaScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "CSS" + }, + { + "name": "Swift" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "SCSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "Shell" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "SCSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "CSS" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "HTML" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "SCSS" + }, + { + "name": "JavaScript" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "JavaScript" + }, + { + "name": "SCSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "HTML" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "HTML" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "JavaScript" + }, + { + "name": "HTML" + }, + { + "name": "SCSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "Swift" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "HTML" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + }, + { + "languages": { + "nodes": [ + { + "name": "TypeScript" + }, + { + "name": "HTML" + }, + { + "name": "CSS" + } + ] + }, + "stargazers": { + "totalCount": 0 + } + } + ] + } +} diff --git a/src/Services/request.ts b/src/Services/request.ts index b51c3ec1..febb2073 100644 --- a/src/Services/request.ts +++ b/src/Services/request.ts @@ -1,7 +1,8 @@ import { soxa } from "../../deps.ts"; import { EServiceKindError, - GithubError, + GithubErrorResponse, + GithubExceedError, QueryDefaultResponse, ServiceError, } from "../Types/index.ts"; @@ -11,31 +12,45 @@ export async function requestGithubData( variables: { [key: string]: string }, token = "", ) { - const data = await soxa.post("", {}, { + const response = await soxa.post("", {}, { data: { query, variables }, headers: { Authorization: `bearer ${token}`, }, }) as QueryDefaultResponse<{ user: T }>; + const responseData = response.data; - if (data?.data?.errors) { - throw handleError(data?.data?.errors); + if (!responseData?.data?.user) { + throw handleError( + responseData as unknown as GithubErrorResponse | GithubExceedError, + ); } - if (data?.data?.data?.user) { - return data.data.data.user; + if (responseData.data.user) { + return responseData.data.user; } throw new ServiceError("not found", EServiceKindError.NOT_FOUND); } -function handleError(responseErrors: GithubError[]): ServiceError { - const errors = responseErrors ?? []; +function handleError( + reponseErrors: GithubErrorResponse | GithubExceedError, +): ServiceError { + let isRateLimitExceeded = false; + const arrayErrors = (reponseErrors as GithubErrorResponse)?.errors || []; + const objectError = (reponseErrors as GithubExceedError) || {}; - const isRateLimitExceeded = errors.some((error) => - error.type.includes(EServiceKindError.RATE_LIMIT) || - error.message.includes("rate limit") - ); + if (Array.isArray(arrayErrors)) { + isRateLimitExceeded = arrayErrors.some((error) => + error.type.includes(EServiceKindError.RATE_LIMIT) + ); + } + + if (objectError?.message) { + isRateLimitExceeded = objectError?.message.includes( + "rate limit", + ); + } if (isRateLimitExceeded) { throw new ServiceError( diff --git a/src/Types/Request.ts b/src/Types/Request.ts index 85f1362c..e0c97ada 100644 --- a/src/Types/Request.ts +++ b/src/Types/Request.ts @@ -3,9 +3,19 @@ export type GithubError = { type: string; }; +export type GithubErrorResponse = { + errors: GithubError[]; +}; + +export type GithubExceedError = { + documentation_url: string; + message: string; +}; + export type QueryDefaultResponse = { data: { data: T; - errors: GithubError[]; + errors?: GithubErrorResponse; + message?: string; }; }; From 433b2b1e31d656a91c386ae255bb7d26030d39a4 Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Sun, 1 Oct 2023 14:22:45 +0100 Subject: [PATCH 4/8] chore: improvements on retries --- src/Services/GithubApiService.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Services/GithubApiService.ts b/src/Services/GithubApiService.ts index f2f166f4..fbaff484 100644 --- a/src/Services/GithubApiService.ts +++ b/src/Services/GithubApiService.ts @@ -118,10 +118,7 @@ export class GithubApiService extends GithubRepository { Logger.error(error); } - return new ServiceError( - "Rate limit exceeded", - EServiceKindError.RATE_LIMIT, - ); + return new ServiceError("not found", EServiceKindError.NOT_FOUND); } } } From dfb6f099167fc0241d1932a3b47f3ed116fb81c4 Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Sun, 1 Oct 2023 15:59:25 +0100 Subject: [PATCH 5/8] fix: import --- src/StaticRenderRegeneration/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/StaticRenderRegeneration/index.ts b/src/StaticRenderRegeneration/index.ts index 872e2276..bc846213 100644 --- a/src/StaticRenderRegeneration/index.ts +++ b/src/StaticRenderRegeneration/index.ts @@ -1,6 +1,6 @@ import { CacheManager } from "./cache_manager.ts"; import { StaticRegenerationOptions } from "./types.ts"; -import { generateUUID, getUrl, readCache } from "./utils.ts"; +import { getUrl, hashString, readCache } from "./utils.ts"; export async function staticRenderRegeneration( request: Request, @@ -15,7 +15,7 @@ export async function staticRenderRegeneration( return await render(request); } - const cacheFile = await generateUUID(url.pathname + (url.search ?? "")); + const cacheFile = await hashString(url.pathname + (url.search ?? "")); const cacheManager = new CacheManager(options.revalidate ?? 0, cacheFile); if (cacheManager.isCacheValid) { const cache = readCache(cacheManager.cacheFilePath); From c2a12197627c344f64c01a33329efc99a6eef27e Mon Sep 17 00:00:00 2001 From: Alex Oliveira Date: Mon, 2 Oct 2023 18:21:20 +0100 Subject: [PATCH 6/8] chore: remove ds store --- .gitignore | 1 + src/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 src/.DS_Store diff --git a/.gitignore b/.gitignore index 663180a0..d59edcf0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .idea deno.lock *.sh +**/.DS_Store diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 6797d9ecb3f5fb28ed867fe631fdafb9841a8d89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKK~4iP3>-rbD{<+u$9#bwL{<4f_X8k`DkQX(3iP};zRlPUXsaz(4ltISNgPk2 z%wfF-VC(nYEwBX8(H-&O!`yt|eP&lJ;%ITkGam7T*HeESX4#hm&h5~Xb;Jo1{>x$5 z=ebyM{|ygx-`_HKz%Cd*-^bednQ_42_fPnjZ&p+aNC7Dz1*E|LRDdRxcxidH;h30o zSlrBW+RfHF6pPy#FOd$bi5jJV6gXGlI+shY|9A8s`u}s1R#HF;{3!)&vFSH!KB?8# y+2g#{7WzH?%a|MK9HJBxqZD)DrFb>TD?aCbZ8# Date: Mon, 2 Oct 2023 18:22:32 +0100 Subject: [PATCH 7/8] chore: remove ds store --- .gitignore | 1 + src/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 src/.DS_Store diff --git a/.gitignore b/.gitignore index 663180a0..d59edcf0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .idea deno.lock *.sh +**/.DS_Store diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 6797d9ecb3f5fb28ed867fe631fdafb9841a8d89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKK~4iP3>-rbD{<+u$9#bwL{<4f_X8k`DkQX(3iP};zRlPUXsaz(4ltISNgPk2 z%wfF-VC(nYEwBX8(H-&O!`yt|eP&lJ;%ITkGam7T*HeESX4#hm&h5~Xb;Jo1{>x$5 z=ebyM{|ygx-`_HKz%Cd*-^bednQ_42_fPnjZ&p+aNC7Dz1*E|LRDdRxcxidH;h30o zSlrBW+RfHF6pPy#FOd$bi5jJV6gXGlI+shY|9A8s`u}s1R#HF;{3!)&vFSH!KB?8# y+2g#{7WzH?%a|MK9HJBxqZD)DrFb>TD?aCbZ8# Date: Mon, 2 Oct 2023 18:41:59 +0100 Subject: [PATCH 8/8] fix: remove useless return --- src/Services/request.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Services/request.ts b/src/Services/request.ts index febb2073..9e356a77 100644 --- a/src/Services/request.ts +++ b/src/Services/request.ts @@ -20,17 +20,13 @@ export async function requestGithubData( }) as QueryDefaultResponse<{ user: T }>; const responseData = response.data; - if (!responseData?.data?.user) { - throw handleError( - responseData as unknown as GithubErrorResponse | GithubExceedError, - ); - } - if (responseData.data.user) { return responseData.data.user; } - throw new ServiceError("not found", EServiceKindError.NOT_FOUND); + throw handleError( + responseData as unknown as GithubErrorResponse | GithubExceedError, + ); } function handleError(