Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dynamo db): integrate with dynamo db #770

Merged
Show file tree
Hide file tree
Changes from 13 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
41 changes: 41 additions & 0 deletions microservices/site-launch/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,44 @@ export interface SiteLaunchMessage {
status?: SiteLaunchStatus
statusMetadata?: string
}

export function isSiteLaunchMessage(obj: unknown): obj is SiteLaunchMessage {
if (!obj) {
return false
}

const message = obj as SiteLaunchMessage

return (
typeof message.repoName === "string" &&
kishore03109 marked this conversation as resolved.
Show resolved Hide resolved
typeof message.appId === "string" &&
typeof message.primaryDomainSource === "string" &&
typeof message.primaryDomainTarget === "string" &&
typeof message.domainValidationSource === "string" &&
typeof message.domainValidationTarget === "string" &&
typeof message.requestorEmail === "string" &&
typeof message.agencyEmail === "string" &&
(typeof message.githubRedirectionUrl === "undefined" ||
typeof message.githubRedirectionUrl === "string") &&
(typeof message.redirectionDomain === "undefined" ||
(Array.isArray(message.redirectionDomain) &&
message.redirectionDomain.every(
(rd) =>
typeof rd.source === "string" &&
typeof rd.target === "string" &&
typeof rd.type === "string"
))) &&
(typeof message.status === "undefined" ||
(typeof message.status === "object" &&
typeof message.status.state === "string" &&
(message.status.state === "success" ||
message.status.state === "failure" ||
message.status.state === "pending") &&
typeof message.status.message === "string" &&
Object.keys(SiteLaunchLambdaStatus).includes(
message.status.message as SiteLaunchLambdaStatus
))) &&
(typeof message.statusMetadata === "undefined" ||
typeof message.statusMetadata === "string")
)
}
16 changes: 16 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ const config = convict({
default: "site-launch",
},
},
stepFunctions: {
stepFunctionsArn: {
doc: "Amazon Resource Name (ARN) of the Step Functions state machine",
env: "STEP_FUNCTIONS_ARN",
format: "required-string",
default: "SiteLaunchStepFunctions-dev",
},
},
sqs: {
incomingQueueUrl: {
doc: "URL of the incoming SQS queue",
Expand All @@ -193,6 +201,14 @@ const config = convict({
format: "required-string",
default: "",
},
featureFlag: {
shouldDeprecateSiteQueues: {
doc: "Whether the queues are deprecated",
env: "FF_DEPRECATE_SITE_QUEUES",
format: "required-boolean",
default: false,
},
},
},
},
github: {
Expand Down
11 changes: 9 additions & 2 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { SubcollectionPageService } from "@root/services/fileServices/MdPageServ
import { UnlinkedPageService } from "@root/services/fileServices/MdPageServices/UnlinkedPageService"
import { CollectionYmlService } from "@root/services/fileServices/YmlFileServices/CollectionYmlService"
import { FooterYmlService } from "@root/services/fileServices/YmlFileServices/FooterYmlService"
import DynamoDBService from "@root/services/infra/DynamoDBService"
import { isomerRepoAxiosInstance } from "@services/api/AxiosInstance"
import { ResourceRoomDirectoryService } from "@services/directoryServices/ResourceRoomDirectoryService"
import { ConfigYmlService } from "@services/fileServices/YmlFileServices/ConfigYmlService"
Expand All @@ -58,6 +59,7 @@ import ReposService from "@services/identity/ReposService"
import SitesService from "@services/identity/SitesService"
import InfraService from "@services/infra/InfraService"
import { statsService } from "@services/infra/StatsService"
import StepFunctionsService from "@services/infra/StepFunctionsService"
import ReviewRequestService from "@services/review/ReviewRequestService"

import { apiLogger } from "./middleware/apiLogger"
Expand All @@ -71,6 +73,7 @@ import { PageService } from "./services/fileServices/MdPageServices/PageService"
import CollaboratorsService from "./services/identity/CollaboratorsService"
import LaunchClient from "./services/identity/LaunchClient"
import LaunchesService from "./services/identity/LaunchesService"
import DynamoDBDocClient from "./services/infra/DynamoDBClient"
import { rateLimiter } from "./services/utilServices/RateLimiter"
import { isSecure } from "./utils/auth-utils"

Expand Down Expand Up @@ -205,6 +208,8 @@ const launchesService = new LaunchesService({
launchClient,
})
const queueService = new QueueService()
const stepFunctionsService = new StepFunctionsService()
const dynamoDBService = new DynamoDBService(new DynamoDBDocClient())

const identityAuthService = getIdentityAuthService(gitHubService)
const collaboratorsService = new CollaboratorsService({
Expand All @@ -222,9 +227,11 @@ const infraService = new InfraService({
launchesService,
queueService,
collaboratorsService,
stepFunctionsService,
dynamoDBService,
})
// poller for incoming queue
infraService.pollQueue()
// poller site launch updates
infraService.pollMessages()

const authenticationMiddleware = getAuthenticationMiddleware()
const authorizationMiddleware = getAuthorizationMiddleware({
Expand Down
41 changes: 6 additions & 35 deletions src/services/infra/DynamoDBClient.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { DynamoDBClient, ScanCommandOutput } from "@aws-sdk/client-dynamodb"
import {
DynamoDBDocumentClient,
PutCommand,
GetCommand,
UpdateCommand,
DeleteCommand,
UpdateCommandInput,
PutCommandOutput,
UpdateCommandOutput,
DeleteCommandOutput,
GetCommandOutput,
ScanCommand,
} from "@aws-sdk/lib-dynamodb"
import autoBind from "auto-bind"

Expand Down Expand Up @@ -52,7 +48,7 @@ export default class DynamoDBDocClient {

private withLogger = async <T>(
promise: Promise<T>,
type: "create" | "get" | "delete" | "update"
type: "create" | "scan" | "delete" | "update"
): Promise<T> => {
try {
return await promise
Expand Down Expand Up @@ -82,38 +78,13 @@ export default class DynamoDBDocClient {
)
}

getItem = async (
tableName: string,
key: string
): Promise<GetCommandOutput> => {
getAllItems = async (tableName: string): Promise<ScanCommandOutput> => {
const params = {
TableName: tableName,
Key: { appId: key },
}

return this.withLogger(
this.dynamoDBDocClient.send(new GetCommand(params)),
"get"
)
}

updateItem = async ({
TableName,
Key,
UpdateExpression,
ExpressionAttributeNames,
ExpressionAttributeValues,
}: UpdateParams): Promise<UpdateCommandOutput> => {
const params: UpdateCommandInput = {
TableName,
Key,
UpdateExpression,
ExpressionAttributeNames,
ExpressionAttributeValues,
}
return this.withLogger(
this.dynamoDBDocClient.send(new UpdateCommand(params)),
"update"
this.dynamoDBDocClient.send(new ScanCommand(params)),
"scan"
kishore03109 marked this conversation as resolved.
Show resolved Hide resolved
)
}

Expand Down
80 changes: 24 additions & 56 deletions src/services/infra/DynamoDBService.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,50 @@
import {
DeleteCommandOutput,
GetCommandOutput,
UpdateCommandOutput,
} from "@aws-sdk/lib-dynamodb"
import { AttributeValue } from "@aws-sdk/client-dynamodb"
import { DeleteCommandOutput } from "@aws-sdk/lib-dynamodb"
import autoBind from "auto-bind"

import { config } from "@config/config"

import { SiteLaunchMessage } from "@root/../microservices/site-launch/shared/types"
import {
SiteLaunchMessage,
isSiteLaunchMessage,
} from "@root/../microservices/site-launch/shared/types"

import DynamoDBClient, { UpdateParams } from "./DynamoDBClient"
import DynamoDBClient from "./DynamoDBClient"

const MOCK_LAUNCH: SiteLaunchMessage = {
repoName: "my-repo",
appId: "my-app",
primaryDomainSource: "example.com",
primaryDomainTarget: "myapp.example.com",
domainValidationSource: "example.com",
domainValidationTarget: "myapp.example.com",
requestorEmail: "[email protected]",
agencyEmail: "[email protected]",
githubRedirectionUrl: "https://github.com/my-repo",
redirectionDomain: [
{
source: "example.com",
target: "myapp.example.com",
type: "A",
},
],
status: { state: "pending", message: "PENDING_DURING_SITE_LAUNCH" },
}
export default class DynamoDBService {
private readonly dynamoDBClient: DynamoDBClient

private readonly TABLE_NAME: string

constructor() {
this.dynamoDBClient = new DynamoDBClient()
constructor(dynamoDBClient: DynamoDBClient) {
this.dynamoDBClient = dynamoDBClient
this.TABLE_NAME = config.get("aws.dynamodb.siteLaunchTableName")
kishore03109 marked this conversation as resolved.
Show resolved Hide resolved
autoBind(this)
}

async createItem(message: SiteLaunchMessage): Promise<void> {
await this.dynamoDBClient.createItem(this.TABLE_NAME, MOCK_LAUNCH)
await this.dynamoDBClient.createItem(this.TABLE_NAME, message)
}

async getItem(message: SiteLaunchMessage): Promise<GetCommandOutput> {
return this.dynamoDBClient.getItem(this.TABLE_NAME, MOCK_LAUNCH.appId)
}
async getAllCompletedLaunches(): Promise<SiteLaunchMessage[]> {
const entries = ((
await this.dynamoDBClient.getAllItems(this.TABLE_NAME)
).Items?.filter(isSiteLaunchMessage) as unknown) as SiteLaunchMessage[]

const completedEntries =
entries?.filter(
(entry) =>
entry.status?.state === "success" || entry.status?.state === "failure"
) || []

async updateItem(message: SiteLaunchMessage): Promise<UpdateCommandOutput> {
// TODO: delete mocking after integration in IS-186
MOCK_LAUNCH.status = { state: "success", message: "SUCCESS_SITE_LIVE" }
const updateParams: UpdateParams = {
TableName: this.TABLE_NAME,
Key: { appId: MOCK_LAUNCH.appId },
// The update expression to apply to the item,
// in this case setting the "status" attribute to a value
UpdateExpression:
"set #status.#state = :state, #status.#message = :message",
ExpressionAttributeNames: {
"#status": "status",
"#state": "state",
"#message": "message",
},
// A map of expression attribute values used in the update expression,
// in this case mapping ":state" to the value of the Launch status state and ":message" to the value of the Launch status message
ExpressionAttributeValues: {
":state": MOCK_LAUNCH.status.state,
":message": MOCK_LAUNCH.status.message,
},
}
return this.dynamoDBClient.updateItem(updateParams)
// Delete after retrieving the items
Promise.all(completedEntries.map((entry) => this.deleteItem(entry)))
seaerchin marked this conversation as resolved.
Show resolved Hide resolved
return completedEntries
}

async deleteItem(message: SiteLaunchMessage): Promise<DeleteCommandOutput> {
return this.dynamoDBClient.deleteItem(this.TABLE_NAME, {
appId: MOCK_LAUNCH.appId,
appId: message.appId,
})
}
}
Loading