Skip to content

Commit de296b6

Browse files
authoredOct 11, 2024
feat: project & rounds related repositories w/kysely (#7)
# 🤖 Linear Closes GIT-90 GIT-91 ## Description - `repositories` package - Project and Rounds repositories using Kysely Query Builder ## Checklist before requesting a review - [x] I have conducted a self-review of my code. - [x] I have conducted a QA. - [ ] If it is a core feature, I have included comprehensive tests.
1 parent 41ff0c0 commit de296b6

26 files changed

+1258
-5
lines changed
 

‎packages/metadata/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"test:cov": "vitest run --config vitest.config.ts --coverage"
2929
},
3030
"dependencies": {
31-
"@grants-stack-indexer/shared": "workspace:0.0.1",
31+
"@grants-stack-indexer/shared": "workspace:*",
3232
"axios": "1.7.7",
3333
"zod": "3.23.8"
3434
},

‎packages/pricing/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"test:cov": "vitest run --config vitest.config.ts --coverage"
2929
},
3030
"dependencies": {
31-
"@grants-stack-indexer/shared": "workspace:0.0.1",
31+
"@grants-stack-indexer/shared": "workspace:*",
3232
"axios": "1.7.7"
3333
},
3434
"devDependencies": {

‎packages/repository/README.md

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# grants-stack-indexer: repository package
2+
3+
This package provides a data access layer for the grants-stack-indexer project, implementing the Repository pattern to abstract database operations.
4+
5+
## Setup
6+
7+
1. Install dependencies by running `pnpm install` in the root directory of the project.
8+
9+
## Available Scripts
10+
11+
Available scripts that can be run using `pnpm`:
12+
13+
| Script | Description |
14+
| ------------- | ------------------------------------------------------- |
15+
| `build` | Build library using tsc |
16+
| `check-types` | Check for type issues using tsc |
17+
| `clean` | Remove `dist` folder |
18+
| `lint` | Run ESLint to check for coding standards |
19+
| `lint:fix` | Run linter and automatically fix code formatting issues |
20+
| `format` | Check code formatting and style using Prettier |
21+
| `format:fix` | Run formatter and automatically fix issues |
22+
| `test` | Run tests using Vitest |
23+
| `test:cov` | Run tests with coverage report |
24+
25+
## Usage
26+
27+
This package provides repository interfaces and implementations for projects and rounds. It uses Kysely as the query builder library.
28+
29+
### Creating a database connection
30+
31+
```typescript
32+
import { createKyselyPostgresDb, DatabaseConfig } from "@grants-stack-indexer/repository";
33+
34+
const dbConfig: DatabaseConfig = {
35+
connectionString: "postgresql://user:password@localhost:5432/mydb",
36+
};
37+
38+
const db = createKyselyPostgresDb(dbConfig);
39+
40+
// Instantiate a repository
41+
const projectRepository = new KyselyProjectRepository(db, "mySchema");
42+
43+
const projects = await projectRepository.getProjects(10);
44+
```
45+
46+
## API
47+
48+
### Repositories
49+
50+
This package provides the following repositories:
51+
52+
1. **IProjectRepository**: Manages project-related database operations, including project roles and pending roles.
53+
54+
2. **IRoundRepository**: Manages round-related database operations, including round roles and pending roles.
55+
56+
## References
57+
58+
- [Kysely](https://kysely.dev/)
59+
- [PostgreSQL](https://www.postgresql.org/)

‎packages/repository/package.json

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@grants-stack-indexer/repository",
3+
"version": "0.0.1",
4+
"private": true,
5+
"description": "",
6+
"license": "MIT",
7+
"author": "Wonderland",
8+
"type": "module",
9+
"main": "./dist/src/index.js",
10+
"types": "./dist/src/index.d.ts",
11+
"directories": {
12+
"src": "src"
13+
},
14+
"files": [
15+
"dist/*",
16+
"package.json",
17+
"!**/*.tsbuildinfo"
18+
],
19+
"scripts": {
20+
"build": "tsc -p tsconfig.build.json",
21+
"check-types": "tsc --noEmit -p ./tsconfig.json",
22+
"clean": "rm -rf dist/",
23+
"format": "prettier --check \"{src,test}/**/*.{js,ts,json}\"",
24+
"format:fix": "prettier --write \"{src,test}/**/*.{js,ts,json}\"",
25+
"lint": "eslint \"{src,test}/**/*.{js,ts,json}\"",
26+
"lint:fix": "pnpm lint --fix",
27+
"test": "vitest run --config vitest.config.ts --passWithNoTests",
28+
"test:cov": "vitest run --config vitest.config.ts --coverage"
29+
},
30+
"dependencies": {
31+
"@grants-stack-indexer/shared": "workspace:*",
32+
"kysely": "0.27.4",
33+
"pg": "8.13.0"
34+
},
35+
"devDependencies": {
36+
"@types/pg": "8.11.10"
37+
}
38+
}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { CamelCasePlugin, Kysely, PostgresDialect } from "kysely";
2+
import { Pool, PoolConfig } from "pg";
3+
4+
import {
5+
PendingProjectRole as PendingProjectRoleTable,
6+
PendingRoundRole as PendingRoundRoleTable,
7+
ProjectRole as ProjectRoleTable,
8+
Project as ProjectTable,
9+
RoundRole as RoundRoleTable,
10+
Round as RoundTable,
11+
} from "../internal.js";
12+
13+
export interface DatabaseConfig extends PoolConfig {
14+
connectionString: string;
15+
}
16+
17+
export interface Database {
18+
rounds: RoundTable;
19+
pendingRoundRoles: PendingRoundRoleTable;
20+
roundRoles: RoundRoleTable;
21+
projects: ProjectTable;
22+
pendingProjectRoles: PendingProjectRoleTable;
23+
projectRoles: ProjectRoleTable;
24+
}
25+
26+
/**
27+
* Creates and configures a Kysely database instance for PostgreSQL.
28+
*
29+
* @param config - The database configuration object extending PoolConfig.
30+
* @returns A configured Kysely instance for the Database.
31+
*
32+
* This function sets up a PostgreSQL database connection using Kysely ORM.
33+
*
34+
* @example
35+
* const dbConfig: DatabaseConfig = {
36+
* connectionString: 'postgresql://user:password@localhost:5432/mydb'
37+
* };
38+
* const db = createKyselyDatabase(dbConfig);
39+
*/
40+
export const createKyselyPostgresDb = (config: DatabaseConfig): Kysely<Database> => {
41+
const dialect = new PostgresDialect({
42+
pool: new Pool({
43+
max: 15,
44+
idleTimeoutMillis: 30_000,
45+
keepAlive: true,
46+
connectionTimeoutMillis: 5_000,
47+
...config,
48+
}),
49+
});
50+
51+
return new Kysely<Database>({ dialect, plugins: [new CamelCasePlugin()] });
52+
};

‎packages/repository/src/external.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Add your external exports here
2+
export type {
3+
IRoundRepository,
4+
IRoundReadRepository,
5+
IProjectRepository,
6+
IProjectReadRepository,
7+
DatabaseConfig,
8+
} from "./internal.js";
9+
10+
export type {
11+
Project,
12+
ProjectType,
13+
ProjectRoleNames,
14+
NewProject,
15+
PartialProject,
16+
ProjectRole,
17+
PendingProjectRole,
18+
} from "./types/project.types.js";
19+
20+
export type {
21+
Round,
22+
NewRound,
23+
PartialRound,
24+
RoundRole,
25+
PendingRoundRole,
26+
} from "./types/round.types.js";
27+
28+
export { KyselyRoundRepository, KyselyProjectRepository } from "./repositories/kysely/index.js";
29+
30+
export { createKyselyPostgresDb as createKyselyDatabase } from "./internal.js";

‎packages/repository/src/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from "./db/connection.js";
2+
export * from "./repositories/kysely/index.js";
3+
export * from "./interfaces/index.js";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./projectRepository.interface.js";
2+
export * from "./roundRepository.interface.js";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { Address, ChainId } from "@grants-stack-indexer/shared";
2+
3+
import {
4+
NewPendingProjectRole,
5+
NewProject,
6+
NewProjectRole,
7+
PartialProject,
8+
PendingProjectRole,
9+
Project,
10+
ProjectRoleNames,
11+
} from "../types/project.types.js";
12+
13+
export interface IProjectReadRepository {
14+
/**
15+
* Retrieves all projects for a given chain ID.
16+
* @param chainId The chain ID to filter projects by.
17+
* @returns A promise that resolves to an array of Project objects.
18+
*/
19+
getProjects(chainId: ChainId): Promise<Project[]>;
20+
21+
/**
22+
* Retrieves a specific project by its ID and chain ID.
23+
* @param chainId The chain ID of the project.
24+
* @param projectId The unique identifier of the project.
25+
* @returns A promise that resolves to a Project object if found, or undefined if not found.
26+
*/
27+
getProjectById(chainId: ChainId, projectId: string): Promise<Project | undefined>;
28+
29+
/**
30+
* Retrieves all pending project roles.
31+
* @returns A promise that resolves to an array of PendingProjectRole objects.
32+
*/
33+
getPendingProjectRoles(): Promise<PendingProjectRole[]>;
34+
35+
/**
36+
* Retrieves pending project roles for a specific chain ID and role.
37+
* @param chainId The chain ID to filter pending roles by.
38+
* @param role The role to filter pending roles by.
39+
* @returns A promise that resolves to an array of PendingProjectRole objects.
40+
*/
41+
getPendingProjectRolesByRole(chainId: ChainId, role: string): Promise<PendingProjectRole[]>;
42+
43+
/**
44+
* Retrieves a project by its anchor address and chain ID.
45+
* @param chainId The chain ID of the project.
46+
* @param anchorAddress The anchor address of the project.
47+
* @returns A promise that resolves to a Project object if found, or undefined if not found.
48+
*/
49+
getProjectByAnchor(chainId: ChainId, anchorAddress: Address): Promise<Project | undefined>;
50+
}
51+
52+
export interface IProjectRepository extends IProjectReadRepository {
53+
/**
54+
* Inserts a new project into the repository.
55+
* @param project The new project to be inserted.
56+
* @returns A promise that resolves when the insertion is complete.
57+
*/
58+
insertProject(project: NewProject): Promise<void>;
59+
60+
/**
61+
* Updates an existing project in the repository.
62+
* @param where An object containing the id and chainId to identify the project to update.
63+
* @param project The partial project data to update.
64+
* @returns A promise that resolves when the update is complete.
65+
*/
66+
updateProject(where: { id: string; chainId: ChainId }, project: PartialProject): Promise<void>;
67+
68+
/**
69+
* Inserts a new project role into the repository.
70+
* @param projectRole The new project role to be inserted.
71+
* @returns A promise that resolves when the insertion is complete.
72+
*/
73+
insertProjectRole(projectRole: NewProjectRole): Promise<void>;
74+
75+
/**
76+
* Deletes multiple project roles based on the provided criteria.
77+
* @param chainId The chain ID of the project roles to delete.
78+
* @param projectId The project ID of the roles to delete.
79+
* @param role The role type to delete.
80+
* @param address Optional address to further filter the roles to delete.
81+
* @returns A promise that resolves when the deletion is complete.
82+
*/
83+
deleteManyProjectRoles(
84+
chainId: ChainId,
85+
projectId: string,
86+
role: ProjectRoleNames,
87+
address?: Address,
88+
): Promise<void>;
89+
90+
/**
91+
* Inserts a new pending project role into the repository.
92+
* @param pendingProjectRole The new pending project role to be inserted.
93+
* @returns A promise that resolves when the insertion is complete.
94+
*/
95+
insertPendingProjectRole(pendingProjectRole: NewPendingProjectRole): Promise<void>;
96+
97+
/**
98+
* Deletes multiple pending project roles based on their IDs.
99+
* @param ids An array of IDs of the pending project roles to delete.
100+
* @returns A promise that resolves when the deletion is complete.
101+
*/
102+
deleteManyPendingProjectRoles(ids: number[]): Promise<void>;
103+
}

0 commit comments

Comments
 (0)
Please sign in to comment.