Skip to content
This repository has been archived by the owner on Mar 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #18 from nekochans/feature/issue5
Browse files Browse the repository at this point in the history
GET /lgtm-images に通信するエンドポイントを実装
  • Loading branch information
keitakn authored Dec 24, 2022
2 parents faa69e2 + 981759b commit 4051847
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 2 deletions.
3 changes: 2 additions & 1 deletion src/api/issueAccessToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export const issueAccessToken = async (

if (isCognitoTokenResponseBody(responseBody)) {
await dto.cacheClient.put(dto.cognitoClientId, responseBody.access_token, {
expirationTtl: 3600,
// トークンの有効期限が3600秒なのでそれよりも10分早い3000秒をcacheの有効期限とする
expirationTtl: 3000,
});

const issueAccessTokenResponse = {
Expand Down
9 changes: 9 additions & 0 deletions src/bindings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type Bindings = {
APP_ENV: 'staging' | 'production';
COGNITO_CLIENT_ID: string;
COGNITO_CLIENT_SECRET: string;
COGNITO_TOKEN_ENDPOINT: `https://${string}`;
IMAGE_RECOGNITION_API_URL: `https://${string}`;
LGTMEOW_API_URL: `https://${string}`;
COGNITO_TOKEN: KVNamespace;
};
93 changes: 93 additions & 0 deletions src/handlers/handleFetchLgtmImagesInRandom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { CacheClient } from '../api/cacheClient';
import { fetchLgtmImagesInRandom } from '../api/fetchLgtmImages';
import { issueAccessToken } from '../api/issueAccessToken';
import { isValidationErrorResponse } from '../api/validationErrorResponse';
import { httpStatusCode } from '../httpStatusCode';
import { isFailureResult } from '../result';
import {
createErrorResponse,
createSuccessResponse,
createValidationErrorResponse,
ResponseHeader,
} from './handlerResponse';

type Dto = {
env: {
cognitoTokenEndpoint: string;
cognitoClientId: string;
cognitoClientSecret: string;
apiBaseUrl: string;
cacheClient: CacheClient;
};
};

export const handleFetchLgtmImagesInRandom = async (
dto: Dto
): Promise<Response> => {
const issueTokenRequest = {
endpoint: dto.env.cognitoTokenEndpoint,
cognitoClientId: dto.env.cognitoClientId,
cognitoClientSecret: dto.env.cognitoClientSecret,
cacheClient: dto.env.cacheClient,
};

const issueAccessTokenResult = await issueAccessToken(issueTokenRequest);
if (isFailureResult(issueAccessTokenResult)) {
const problemDetails = {
title: 'failed to issue access token',
type: 'InternalServerError',
status: httpStatusCode.internalServerError,
} as const;

return createErrorResponse(
problemDetails,
httpStatusCode.internalServerError
);
}

const fetchLgtmImagesRequest = {
apiBaseUrl: dto.env.apiBaseUrl,
accessToken: issueAccessTokenResult.value.jwtAccessToken,
};

const fetchLgtmImagesResult = await fetchLgtmImagesInRandom(
fetchLgtmImagesRequest
);

const headers: ResponseHeader = {
'Content-Type': 'application/json',
};

if (fetchLgtmImagesResult.value.xRequestId != null) {
headers['X-Request-Id'] = fetchLgtmImagesResult.value.xRequestId;
}

if (fetchLgtmImagesResult.value.xLambdaRequestId != null) {
headers['X-Lambda-Request-Id'] =
fetchLgtmImagesResult.value.xLambdaRequestId;
}

if (isFailureResult(fetchLgtmImagesResult)) {
if (isValidationErrorResponse(fetchLgtmImagesResult.value)) {
return createValidationErrorResponse(
fetchLgtmImagesResult.value.invalidParams,
headers
);
}

const problemDetails = {
title: 'failed to fetch lgtm images in random',
type: 'InternalServerError',
status: httpStatusCode.internalServerError,
} as const;

return createErrorResponse(
problemDetails,
httpStatusCode.internalServerError
);
}

const responseBody = { lgtmImages: fetchLgtmImagesResult.value.lgtmImages };

return createSuccessResponse(responseBody, httpStatusCode.ok, headers);
};
56 changes: 56 additions & 0 deletions src/handlers/handlerResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { HttpStatusCode, httpStatusCode } from '../httpStatusCode';
import { InvalidParams } from '../validator';

export type ResponseHeader = {
'Content-Type': 'application/json';
'X-Request-Id'?: string;
'X-Lambda-Request-Id'?: string;
};

export const createSuccessResponse = (
body: unknown,
statusCode: HttpStatusCode = httpStatusCode.ok,
headers: ResponseHeader = { 'Content-Type': 'application/json' }
): Response => {
const jsonBody = JSON.stringify(body);

return new Response(jsonBody, { headers, status: statusCode });
};

export type ProblemDetails = {
title: string;
type: 'ResourceNotFound' | 'InternalServerError';
status?: HttpStatusCode;
detail?: string;
};

export type ValidationProblemDetails = {
title: 'unprocessable entity';
type: 'ValidationError';
status: HttpStatusCode;
invalidParams: InvalidParams;
};

export const createErrorResponse = (
problemDetails: ProblemDetails,
statusCode: HttpStatusCode = httpStatusCode.internalServerError,
headers: ResponseHeader = { 'Content-Type': 'application/json' }
): Response => createSuccessResponse(problemDetails, statusCode, headers);

export const createValidationErrorResponse = (
invalidParams: InvalidParams,
headers: ResponseHeader = { 'Content-Type': 'application/json' }
): Response => {
const validationProblemDetails: ValidationProblemDetails = {
title: 'unprocessable entity',
type: 'ValidationError',
status: httpStatusCode.unprocessableEntity,
invalidParams,
} as const;

return createSuccessResponse(
validationProblemDetails,
httpStatusCode.unprocessableEntity,
headers
);
};
26 changes: 25 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { Bindings } from './bindings';
import { handleFetchLgtmImagesInRandom } from './handlers/handleFetchLgtmImagesInRandom';

const app = new Hono();
const app = new Hono<{ Bindings: Bindings }>();

app.get('/', (c) => c.text('Hello! Hono!'));

app.use('*', async (c, next) => {
const handler =
c.env.APP_ENV === 'production'
? cors({ origin: 'https://lgtmeow.com' })
: cors();

await handler(c, next);
});

app.get('/lgtm-images', async (c) => {
return await handleFetchLgtmImagesInRandom({
env: {
cognitoTokenEndpoint: c.env.COGNITO_TOKEN_ENDPOINT,
cognitoClientId: c.env.COGNITO_CLIENT_ID,
cognitoClientSecret: c.env.COGNITO_CLIENT_SECRET,
apiBaseUrl: c.env.LGTMEOW_API_URL,
cacheClient: c.env.COGNITO_TOKEN,
},
});
});

export default app;
6 changes: 6 additions & 0 deletions wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
name = "lgtm-cat-bff"
main = "src/index.ts"
compatibility_date = "2022-12-15"
kv_namespaces = [
{ binding = "COGNITO_TOKEN", id = "79245f90d047421bb54d2d0cb0d4eeb5", preview_id = "09863c096f884e9d8009368614f9590b" }
]

[env.staging]
name = "staging-lgtm-cat-bff"
kv_namespaces = [
{ binding = "COGNITO_TOKEN", id = "79245f90d047421bb54d2d0cb0d4eeb5", preview_id = "09863c096f884e9d8009368614f9590b" }
]

0 comments on commit 4051847

Please sign in to comment.