Skip to content

Commit

Permalink
feat: application repository kisely impl
Browse files Browse the repository at this point in the history
  • Loading branch information
0xnigir1 committed Oct 29, 2024
1 parent 9ad1569 commit 140c122
Show file tree
Hide file tree
Showing 13 changed files with 245 additions and 28 deletions.
13 changes: 12 additions & 1 deletion packages/repository/src/db/connection.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
import { CamelCasePlugin, Kysely, PostgresDialect } from "kysely";
import { CamelCasePlugin, ColumnType, Kysely, PostgresDialect } from "kysely";
import { Pool, PoolConfig } from "pg";

import {
Application,
PendingProjectRole as PendingProjectRoleTable,
PendingRoundRole as PendingRoundRoleTable,
ProjectRole as ProjectRoleTable,
Project as ProjectTable,
RoundRole as RoundRoleTable,
Round as RoundTable,
StatusSnapshot,
} from "../internal.js";

export interface DatabaseConfig extends PoolConfig {
connectionString: string;
}

type ApplicationTable = Omit<Application, "statusSnapshots"> & {
statusSnapshots: ColumnType<
StatusSnapshot[],
StatusSnapshot[] | string,
StatusSnapshot[] | string
>;
};

export interface Database {
rounds: RoundTable;
pendingRoundRoles: PendingRoundRoleTable;
roundRoles: RoundRoleTable;
projects: ProjectTable;
pendingProjectRoles: PendingProjectRoleTable;
projectRoles: ProjectRoleTable;
applications: ApplicationTable;
}

/**
Expand Down
8 changes: 7 additions & 1 deletion packages/repository/src/external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export type {
IRoundReadRepository,
IProjectRepository,
IProjectReadRepository,
IApplicationRepository,
IApplicationReadRepository,
DatabaseConfig,
} from "./internal.js";

Expand Down Expand Up @@ -35,6 +37,10 @@ export type {

export type { Changeset } from "./types/index.js";

export { KyselyRoundRepository, KyselyProjectRepository } from "./repositories/kysely/index.js";
export {
KyselyRoundRepository,
KyselyProjectRepository,
KyselyApplicationRepository,
} from "./repositories/kysely/index.js";

export { createKyselyPostgresDb as createKyselyDatabase } from "./internal.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Address, ChainId } from "@grants-stack-indexer/shared";

import { Application, NewApplication, PartialApplication } from "../types/application.types.js";

export interface IApplicationReadRepository {
/**
* Retrieves a specific application by its ID, chain ID, and round ID.
* @param id The ID of the application.
* @param chainId The chain ID of the application.
* @param roundId The round ID of the application.
* @returns A promise that resolves to an Application object if found, or undefined if not found.
*/
getApplicationById(
id: string,
chainId: ChainId,
roundId: string,
): Promise<Application | undefined>;

/**
* Retrieves a specific application by its chain ID, round ID, and project ID.
* @param chainId The chain ID of the application.
* @param roundId The round ID of the application.
* @param projectId The project ID of the application.
* @returns A promise that resolves to an Application object if found, or undefined if not found.
*/
getApplicationByProjectId(
chainId: ChainId,
roundId: string,
projectId: string,
): Promise<Application | undefined>;

/**
* Retrieves a specific application by its chain ID, round ID, and anchor address.
* @param chainId The chain ID of the application.
* @param roundId The round ID of the application.
* @param anchorAddress The anchor address of the application.
* @returns A promise that resolves to an Application object if found, or undefined if not found.
*/
getApplicationByAnchorAddress(
chainId: ChainId,
roundId: string,
anchorAddress: Address,
): Promise<Application | undefined>;

/**
* Retrieves all applications for a given chain ID and round ID.
* @param chainId The chain ID of the applications.
* @param roundId The round ID of the applications.
* @returns A promise that resolves to an array of Application objects.
*/
getApplicationsByRoundId(chainId: ChainId, roundId: string): Promise<Application[]>;
}

export interface IApplicationRepository extends IApplicationReadRepository {
/**
* Inserts a new application into the repository.
* @param application The new application to insert.
* @returns A promise that resolves when the insertion is complete.
*/
insertApplication(application: NewApplication): Promise<void>;

/**
* Updates an existing application in the repository.
* @param where An object containing the (id, chainId, and roundId) of the application to update.
* @param application The partial application data to update.
* @returns A promise that resolves when the update is complete.
*/
updateApplication(
where: { id: string; chainId: ChainId; roundId: string },
application: PartialApplication,
): Promise<void>;
}
1 change: 1 addition & 0 deletions packages/repository/src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./projectRepository.interface.js";
export * from "./roundRepository.interface.js";
export * from "./applicationRepository.interface.js";
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ export interface IRoundRepository extends IRoundReadRepository {
* @param round The partial round data to update.
* @returns A promise that resolves when the update is complete.
*/
updateRound(where: { id: string; chainId: ChainId }, round: PartialRound): Promise<void>;
updateRound(
where: { id: string; chainId: ChainId } | { chainId: ChainId; strategyAddress: Address },
round: PartialRound,
): Promise<void>;

/**
* Increments the funds for a specific round.
Expand Down
121 changes: 121 additions & 0 deletions packages/repository/src/repositories/kysely/application.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Kysely } from "kysely";

import { Address, ChainId, stringify } from "@grants-stack-indexer/shared";

import {
Application,
Database,
IApplicationRepository,
NewApplication,
PartialApplication,
} from "../../internal.js";

export class KyselyApplicationRepository implements IApplicationRepository {
constructor(
private readonly db: Kysely<Database>,
private readonly schemaName: string,
) {}

/* @inheritdoc */
async getApplicationById(
id: string,
chainId: ChainId,
roundId: string,
): Promise<Application | undefined> {
return this.db
.withSchema(this.schemaName)
.selectFrom("applications")
.where("id", "=", id)
.where("chainId", "=", chainId)
.where("roundId", "=", roundId)
.selectAll()
.executeTakeFirst();
}

/* @inheritdoc */
async getApplicationByProjectId(
chainId: ChainId,
roundId: string,
projectId: string,
): Promise<Application | undefined> {
return this.db
.withSchema(this.schemaName)
.selectFrom("applications")
.where("chainId", "=", chainId)
.where("roundId", "=", roundId)
.where("projectId", "=", projectId)
.selectAll()
.executeTakeFirst();
}

/* @inheritdoc */
async getApplicationByAnchorAddress(
chainId: ChainId,
roundId: string,
anchorAddress: Address,
): Promise<Application | undefined> {
return this.db
.withSchema(this.schemaName)
.selectFrom("applications")
.where("chainId", "=", chainId)
.where("roundId", "=", roundId)
.where("anchorAddress", "=", anchorAddress)
.selectAll()
.executeTakeFirst();
}

/* @inheritdoc */
async getApplicationsByRoundId(chainId: ChainId, roundId: string): Promise<Application[]> {
return this.db
.withSchema(this.schemaName)
.selectFrom("applications")
.where("chainId", "=", chainId)
.where("roundId", "=", roundId)
.selectAll()
.execute();
}

/* @inheritdoc */
async insertApplication(application: NewApplication): Promise<void> {
const _application = this.formatApplication(application);

await this.db
.withSchema(this.schemaName)
.insertInto("applications")
.values(_application)
.execute();
}

/* @inheritdoc */
async updateApplication(
where: { id: string; chainId: ChainId; roundId: string },
application: PartialApplication,
): Promise<void> {
const _application = this.formatApplication(application);

await this.db
.withSchema(this.schemaName)
.updateTable("applications")
.set(_application)
.where("id", "=", where.id)
.where("chainId", "=", where.chainId)
.where("roundId", "=", where.roundId)
.execute();
}

/**
* Formats the application to ensure that the statusSnapshots are stored as a JSON string.
* Also, properly handles BigInt stringification.
* @param application - The application to format.
* @returns The formatted application.
*/
private formatApplication<T extends NewApplication | PartialApplication>(application: T): T {
if (application?.statusSnapshots) {
application = {
...application,
statusSnapshots: stringify(application.statusSnapshots),
};
}
return application;
}
}
1 change: 1 addition & 0 deletions packages/repository/src/repositories/kysely/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./project.repository.js";
export * from "./round.repository.js";
export * from "./application.repository.js";
18 changes: 13 additions & 5 deletions packages/repository/src/repositories/kysely/round.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,22 @@ export class KyselyRoundRepository implements IRoundRepository {
await this.db.withSchema(this.schemaName).insertInto("rounds").values(round).execute();
}

async updateRound(where: { id: string; chainId: ChainId }, round: PartialRound): Promise<void> {
await this.db
/* @inheritdoc */
async updateRound(
where: { id: string; chainId: ChainId } | { chainId: ChainId; strategyAddress: Address },
round: PartialRound,
): Promise<void> {
const query = this.db
.withSchema(this.schemaName)
.updateTable("rounds")
.set(round)
.where("id", "=", where.id)
.where("chainId", "=", where.chainId)
.execute();
.where("chainId", "=", where.chainId);

if ("id" in where) {
await query.where("id", "=", where.id).execute();
} else {
await query.where("strategyAddress", "=", where.strategyAddress).execute();
}
}

/* @inheritdoc */
Expand Down
2 changes: 0 additions & 2 deletions packages/repository/src/types/application.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,3 @@ export type Application = {

export type NewApplication = Application;
export type PartialApplication = Partial<Application>;

//TODO: create the corresponding repository implementation
28 changes: 10 additions & 18 deletions packages/repository/src/types/changeset.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Address, ChainId } from "@grants-stack-indexer/shared";

import { NewApplication } from "./application.types.js";
import { NewApplication, PartialApplication } from "./application.types.js";
import {
NewPendingProjectRole,
NewProject,
Expand Down Expand Up @@ -92,14 +92,6 @@ export type Changeset =
fundedAmountInUsd: string;
};
}
| {
type: "IncrementRoundDonationStats";
args: {
chainId: ChainId;
roundId: Address;
amountInUsd: string;
};
}
| {
type: "IncrementRoundTotalDistributed";
args: {
Expand All @@ -108,15 +100,6 @@ export type Changeset =
amount: bigint;
};
}
| {
type: "IncrementApplicationDonationStats";
args: {
chainId: ChainId;
roundId: Address;
applicationId: string;
amountInUsd: number;
};
}
| {
type: "InsertPendingRoundRole";
args: {
Expand Down Expand Up @@ -144,4 +127,13 @@ export type Changeset =
| {
type: "InsertApplication";
args: NewApplication;
}
| {
type: "UpdateApplication";
args: {
chainId: ChainId;
roundId: string;
applicationId: string;
application: PartialApplication;
};
};
1 change: 1 addition & 0 deletions packages/repository/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./project.types.js";
export * from "./round.types.js";
export * from "./application.types.js";
export * from "./changeset.types.js";
2 changes: 2 additions & 0 deletions packages/shared/src/external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export type { BigNumberType } from "./internal.js";

export type { TokenCode, Token } from "./internal.js";
export { TOKENS, getToken } from "./tokens/tokens.js";

export { stringify } from "./internal.js";
1 change: 1 addition & 0 deletions packages/shared/src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type { Address } from "viem";
export { stringify } from "viem/utils";
export * from "./math/bignumber.js";
export * from "./types/index.js";
export * from "./constants/index.js";
Expand Down

0 comments on commit 140c122

Please sign in to comment.