@shipfox/api-projects 11.0.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +39 -0
- package/README.md +83 -0
- package/dist/core/entities/project.d.ts +4 -0
- package/dist/core/entities/project.d.ts.map +1 -1
- package/dist/core/entities/project.js.map +1 -1
- package/dist/core/errors.d.ts +4 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +6 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/projects.d.ts +8 -0
- package/dist/core/projects.d.ts.map +1 -1
- package/dist/core/projects.js +62 -19
- package/dist/core/projects.js.map +1 -1
- package/dist/db/db.d.ts +138 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/projects.d.ts +53 -0
- package/dist/db/projects.d.ts.map +1 -1
- package/dist/db/projects.js +116 -18
- package/dist/db/projects.js.map +1 -1
- package/dist/db/schema/projects.d.ts +68 -0
- package/dist/db/schema/projects.d.ts.map +1 -1
- package/dist/db/schema/projects.js +12 -1
- package/dist/db/schema/projects.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/presentation/dto/project.d.ts +1 -0
- package/dist/presentation/dto/project.d.ts.map +1 -1
- package/dist/presentation/dto/project.js +1 -0
- package/dist/presentation/dto/project.js.map +1 -1
- package/dist/presentation/e2eRoutes/create-project.d.ts.map +1 -1
- package/dist/presentation/e2eRoutes/create-project.js +34 -8
- package/dist/presentation/e2eRoutes/create-project.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +4 -1
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +44 -3
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/routes/create-project.d.ts.map +1 -1
- package/dist/presentation/routes/create-project.js +8 -2
- package/dist/presentation/routes/create-project.js.map +1 -1
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +3 -1
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/update-project.d.ts +2 -0
- package/dist/presentation/routes/update-project.d.ts.map +1 -0
- package/dist/presentation/routes/update-project.js +53 -0
- package/dist/presentation/routes/update-project.js.map +1 -0
- package/dist/presentation/subscribers/index.d.ts +1 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -1
- package/dist/presentation/subscribers/index.js +1 -0
- package/dist/presentation/subscribers/index.js.map +1 -1
- package/dist/presentation/subscribers/on-source-repository-updated.d.ts +4 -0
- package/dist/presentation/subscribers/on-source-repository-updated.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-source-repository-updated.js +39 -0
- package/dist/presentation/subscribers/on-source-repository-updated.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0000_initial.sql +6 -0
- package/drizzle/meta/0000_snapshot.json +78 -0
- package/package.json +13 -12
- package/src/core/entities/project.ts +4 -0
- package/src/core/errors.ts +7 -0
- package/src/core/index.ts +3 -2
- package/src/core/projects.test.ts +10 -0
- package/src/core/projects.ts +90 -35
- package/src/db/db.ts +3 -0
- package/src/db/index.ts +6 -0
- package/src/db/projects.test.ts +44 -1
- package/src/db/projects.ts +242 -33
- package/src/db/schema/projects.ts +16 -0
- package/src/index.ts +9 -3
- package/src/presentation/dto/project.ts +1 -0
- package/src/presentation/e2eRoutes/create-project.test.ts +23 -0
- package/src/presentation/e2eRoutes/create-project.ts +39 -10
- package/src/presentation/inter-module.test.ts +309 -0
- package/src/presentation/inter-module.ts +76 -4
- package/src/presentation/routes/create-project.ts +10 -2
- package/src/presentation/routes/index.ts +7 -1
- package/src/presentation/routes/projects.test.ts +300 -4
- package/src/presentation/routes/update-project.ts +46 -0
- package/src/presentation/subscribers/index.ts +1 -0
- package/src/presentation/subscribers/on-source-commit-pushed.test.ts +2 -0
- package/src/presentation/subscribers/on-source-repository-updated.test.ts +108 -0
- package/src/presentation/subscribers/on-source-repository-updated.ts +50 -0
- package/test/factories/project.ts +6 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/dist/db/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
export { closeDb, db, schema } from './db.js';
|
|
4
4
|
export { pruneIntegrationEventDedup, recordIntegrationEventForProject } from './integration-event-dedup.js';
|
|
5
|
-
export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace } from './projects.js';
|
|
5
|
+
export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace, resolveCheckoutTarget, updateProject } from './projects.js';
|
|
6
6
|
export { projectsIntegrationEventDedup } from './schema/integration-event-dedup.js';
|
|
7
7
|
export { projectsOutbox } from './schema/outbox.js';
|
|
8
8
|
export const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');
|
package/dist/db/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport {closeDb, db, schema} from './db.js';\nexport {\n pruneIntegrationEventDedup,\n recordIntegrationEventForProject,\n} from './integration-event-dedup.js';\nexport type {\n AdminProjectSummary,\n CreateProjectParams,\n GetProjectBySourceParams,\n ListAdminProjectsParams,\n ListAdminProjectsResult,\n ListProjectsParams,\n ListProjectsResult,\n} from './projects.js';\nexport {\n createProject,\n getProjectById,\n getProjectBySource,\n getProjectCount,\n getWorkspaceProjectCounts,\n listAdminProjects,\n listProjects,\n requireProjectForWorkspace,\n} from './projects.js';\nexport {projectsIntegrationEventDedup} from './schema/integration-event-dedup.js';\nexport {projectsOutbox} from './schema/outbox.js';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","closeDb","db","schema","pruneIntegrationEventDedup","recordIntegrationEventForProject","createProject","getProjectById","getProjectBySource","getProjectCount","getWorkspaceProjectCounts","listAdminProjects","listProjects","requireProjectForWorkspace","projectsIntegrationEventDedup","projectsOutbox","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,SAAQC,OAAO,EAAEC,EAAE,EAAEC,MAAM,QAAO,UAAU;AAC5C,SACEC,0BAA0B,EAC1BC,gCAAgC,QAC3B,+BAA+B;
|
|
1
|
+
{"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport {closeDb, db, schema} from './db.js';\nexport {\n pruneIntegrationEventDedup,\n recordIntegrationEventForProject,\n} from './integration-event-dedup.js';\nexport type {\n AdminProjectSummary,\n CreateProjectParams,\n GetProjectBySourceParams,\n ListAdminProjectsParams,\n ListAdminProjectsResult,\n ListProjectsParams,\n ListProjectsResult,\n ResolveCheckoutTargetParams,\n ResolvedCheckoutTarget,\n UpdateProjectParams,\n UpdateProjectResult,\n} from './projects.js';\nexport {\n createProject,\n getProjectById,\n getProjectBySource,\n getProjectCount,\n getWorkspaceProjectCounts,\n listAdminProjects,\n listProjects,\n requireProjectForWorkspace,\n resolveCheckoutTarget,\n updateProject,\n} from './projects.js';\nexport {projectsIntegrationEventDedup} from './schema/integration-event-dedup.js';\nexport {projectsOutbox} from './schema/outbox.js';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","closeDb","db","schema","pruneIntegrationEventDedup","recordIntegrationEventForProject","createProject","getProjectById","getProjectBySource","getProjectCount","getWorkspaceProjectCounts","listAdminProjects","listProjects","requireProjectForWorkspace","resolveCheckoutTarget","updateProject","projectsIntegrationEventDedup","projectsOutbox","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,SAAQC,OAAO,EAAEC,EAAE,EAAEC,MAAM,QAAO,UAAU;AAC5C,SACEC,0BAA0B,EAC1BC,gCAAgC,QAC3B,+BAA+B;AActC,SACEC,aAAa,EACbC,cAAc,EACdC,kBAAkB,EAClBC,eAAe,EACfC,yBAAyB,EACzBC,iBAAiB,EACjBC,YAAY,EACZC,0BAA0B,EAC1BC,qBAAqB,EACrBC,aAAa,QACR,gBAAgB;AACvB,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,cAAc,QAAO,qBAAqB;AAElD,OAAO,MAAMC,iBAAiBnB,QAAQD,QAAQE,cAAc,YAAYmB,GAAG,IAAI,iBAAiB"}
|
package/dist/db/projects.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import type { Project } from '#core/entities/project.js';
|
|
2
|
+
import { db, type Executor } from './db.js';
|
|
3
|
+
type ProjectDatabase = ReturnType<typeof db>;
|
|
4
|
+
type ProjectTransaction = Parameters<Parameters<ProjectDatabase['transaction']>[0]>[0];
|
|
2
5
|
export interface CreateProjectParams {
|
|
3
6
|
workspaceId: string;
|
|
4
7
|
sourceConnectionId: string;
|
|
5
8
|
sourceExternalRepositoryId: string;
|
|
6
9
|
name: string;
|
|
10
|
+
slug: string;
|
|
11
|
+
}
|
|
12
|
+
export interface UpdateProjectParams {
|
|
13
|
+
projectId: string;
|
|
14
|
+
name?: string | undefined;
|
|
15
|
+
slug?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface UpdateProjectResult {
|
|
18
|
+
project: Project;
|
|
19
|
+
changed: boolean;
|
|
7
20
|
}
|
|
8
21
|
export interface ProjectCursor {
|
|
9
22
|
createdAt: Date;
|
|
@@ -31,6 +44,9 @@ export interface ListAdminProjectsResult {
|
|
|
31
44
|
nextCursor: ProjectCursor | null;
|
|
32
45
|
}
|
|
33
46
|
export declare function createProject(params: CreateProjectParams): Promise<Project>;
|
|
47
|
+
export declare function updateProject(params: UpdateProjectParams, options?: {
|
|
48
|
+
tx?: ProjectDatabase | ProjectTransaction | undefined;
|
|
49
|
+
}): Promise<UpdateProjectResult | undefined>;
|
|
34
50
|
export declare function getProjectById(id: string): Promise<Project | undefined>;
|
|
35
51
|
export interface GetProjectBySourceParams {
|
|
36
52
|
workspaceId: string;
|
|
@@ -38,10 +54,46 @@ export interface GetProjectBySourceParams {
|
|
|
38
54
|
sourceExternalRepositoryId: string;
|
|
39
55
|
}
|
|
40
56
|
export declare function getProjectBySource(params: GetProjectBySourceParams): Promise<Project | undefined>;
|
|
57
|
+
export interface UpdateProjectSourceRepositoryParams extends GetProjectBySourceParams {
|
|
58
|
+
tx?: Executor;
|
|
59
|
+
sourceRepositoryOwner: string;
|
|
60
|
+
sourceRepositoryName: string;
|
|
61
|
+
sourceDefaultBranch: string;
|
|
62
|
+
}
|
|
63
|
+
export declare function updateProjectSourceRepository(params: UpdateProjectSourceRepositoryParams): Promise<Project | undefined>;
|
|
41
64
|
export declare function requireProjectForWorkspace(params: {
|
|
42
65
|
projectId: string;
|
|
43
66
|
workspaceId: string;
|
|
44
67
|
}): Promise<Project>;
|
|
68
|
+
export interface ResolveCheckoutTargetParams {
|
|
69
|
+
workspaceId: string;
|
|
70
|
+
defaults: {
|
|
71
|
+
connectionId: string;
|
|
72
|
+
owner: string;
|
|
73
|
+
};
|
|
74
|
+
target: {
|
|
75
|
+
project: string;
|
|
76
|
+
} | {
|
|
77
|
+
connection?: string | undefined;
|
|
78
|
+
repository: string;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export interface ResolvedCheckoutTarget {
|
|
82
|
+
projectId: string;
|
|
83
|
+
connectionId: string;
|
|
84
|
+
externalRepositoryId: string;
|
|
85
|
+
}
|
|
86
|
+
export interface UpdateProjectSourceMetadataParams {
|
|
87
|
+
workspaceId: string;
|
|
88
|
+
projectId: string;
|
|
89
|
+
sourceConnectionId: string;
|
|
90
|
+
sourceExternalRepositoryId: string;
|
|
91
|
+
sourceRepositoryOwner: string;
|
|
92
|
+
sourceRepositoryName: string;
|
|
93
|
+
sourceDefaultBranch: string;
|
|
94
|
+
}
|
|
95
|
+
export declare function updateProjectSourceMetadata(params: UpdateProjectSourceMetadataParams): Promise<void>;
|
|
96
|
+
export declare function resolveCheckoutTarget(params: ResolveCheckoutTargetParams): Promise<ResolvedCheckoutTarget | undefined>;
|
|
45
97
|
export declare function listProjects(params: ListProjectsParams): Promise<ListProjectsResult>;
|
|
46
98
|
export declare function listAdminProjects(params: ListAdminProjectsParams): Promise<ListAdminProjectsResult>;
|
|
47
99
|
export declare function getProjectCount(): Promise<number>;
|
|
@@ -51,4 +103,5 @@ export declare function getWorkspaceProjectCounts(params: {
|
|
|
51
103
|
workspaceId: string;
|
|
52
104
|
count: number;
|
|
53
105
|
}>>;
|
|
106
|
+
export {};
|
|
54
107
|
//# sourceMappingURL=projects.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"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"}
|
package/dist/db/projects.js
CHANGED
|
@@ -1,33 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isUniqueViolation } from '@shipfox/node-drizzle';
|
|
2
|
+
import { and, count, desc, eq, ilike, inArray, lt, or, sql } from 'drizzle-orm';
|
|
3
|
+
import { ProjectAlreadyExistsError, ProjectNotFoundError, ProjectSlugConflictError } from '#core/errors.js';
|
|
3
4
|
import { recordProjectCreated } from '#metrics/instance.js';
|
|
4
5
|
import { db } from './db.js';
|
|
5
6
|
import { projects, toProject } from './schema/projects.js';
|
|
6
7
|
function escapeIlikePattern(value) {
|
|
7
8
|
return value.replace(/[\\%_]/g, (char)=>`\\${char}`);
|
|
8
9
|
}
|
|
10
|
+
const PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT = 'projects_workspace_slug_unique';
|
|
9
11
|
function cursorWhere(cursor) {
|
|
10
12
|
if (!cursor) return undefined;
|
|
11
13
|
return or(lt(projects.createdAt, cursor.createdAt), and(eq(projects.createdAt, cursor.createdAt), lt(projects.id, cursor.id)));
|
|
12
14
|
}
|
|
13
15
|
export async function createProject(params) {
|
|
14
16
|
const project = await db().transaction(async (tx)=>{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
let projectRow;
|
|
18
|
+
for(let attempt = 0; attempt < 2 && !projectRow; attempt += 1){
|
|
19
|
+
try {
|
|
20
|
+
[projectRow] = await tx.insert(projects).values({
|
|
21
|
+
workspaceId: params.workspaceId,
|
|
22
|
+
sourceConnectionId: params.sourceConnectionId,
|
|
23
|
+
sourceExternalRepositoryId: params.sourceExternalRepositoryId,
|
|
24
|
+
name: params.name,
|
|
25
|
+
slug: params.slug
|
|
26
|
+
}).onConflictDoNothing({
|
|
27
|
+
target: [
|
|
28
|
+
projects.sourceConnectionId,
|
|
29
|
+
projects.sourceExternalRepositoryId
|
|
30
|
+
]
|
|
31
|
+
}).returning();
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {
|
|
34
|
+
throw new ProjectSlugConflictError(params.slug);
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
if (!projectRow) {
|
|
39
|
+
const [conflict] = await tx.select().from(projects).where(and(eq(projects.sourceConnectionId, params.sourceConnectionId), eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId))).limit(1);
|
|
40
|
+
if (conflict) {
|
|
41
|
+
throw new ProjectAlreadyExistsError(conflict.id, params.sourceConnectionId, params.sourceExternalRepositoryId);
|
|
42
|
+
}
|
|
30
43
|
}
|
|
44
|
+
}
|
|
45
|
+
if (!projectRow) {
|
|
31
46
|
throw new Error('Insert returned no rows');
|
|
32
47
|
}
|
|
33
48
|
return toProject(projectRow);
|
|
@@ -35,6 +50,39 @@ export async function createProject(params) {
|
|
|
35
50
|
recordProjectCreated();
|
|
36
51
|
return project;
|
|
37
52
|
}
|
|
53
|
+
export async function updateProject(params, options = {}) {
|
|
54
|
+
const executor = options.tx ?? db();
|
|
55
|
+
const [existingRow] = await executor.select().from(projects).where(eq(projects.id, params.projectId)).limit(1);
|
|
56
|
+
if (!existingRow) return undefined;
|
|
57
|
+
const nextName = params.name ?? existingRow.name;
|
|
58
|
+
const nextSlug = params.slug ?? existingRow.slug;
|
|
59
|
+
if (nextName === existingRow.name && nextSlug === existingRow.slug) {
|
|
60
|
+
return {
|
|
61
|
+
project: toProject(existingRow),
|
|
62
|
+
changed: false
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const [row] = await executor.update(projects).set({
|
|
67
|
+
...params.name !== undefined ? {
|
|
68
|
+
name: params.name
|
|
69
|
+
} : {},
|
|
70
|
+
...params.slug !== undefined ? {
|
|
71
|
+
slug: params.slug
|
|
72
|
+
} : {},
|
|
73
|
+
updatedAt: new Date()
|
|
74
|
+
}).where(eq(projects.id, params.projectId)).returning();
|
|
75
|
+
return row ? {
|
|
76
|
+
project: toProject(row),
|
|
77
|
+
changed: true
|
|
78
|
+
} : undefined;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {
|
|
81
|
+
throw new ProjectSlugConflictError(nextSlug);
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
38
86
|
export async function getProjectById(id) {
|
|
39
87
|
const rows = await db().select().from(projects).where(eq(projects.id, id)).limit(1);
|
|
40
88
|
const row = rows[0];
|
|
@@ -47,12 +95,61 @@ export async function getProjectBySource(params) {
|
|
|
47
95
|
if (!row) return undefined;
|
|
48
96
|
return toProject(row);
|
|
49
97
|
}
|
|
98
|
+
export async function updateProjectSourceRepository(params) {
|
|
99
|
+
const executor = params.tx ?? db();
|
|
100
|
+
const rows = await executor.update(projects).set({
|
|
101
|
+
sourceRepositoryOwner: params.sourceRepositoryOwner,
|
|
102
|
+
sourceRepositoryName: params.sourceRepositoryName,
|
|
103
|
+
sourceDefaultBranch: params.sourceDefaultBranch,
|
|
104
|
+
updatedAt: new Date()
|
|
105
|
+
}).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.sourceConnectionId, params.sourceConnectionId), eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId))).returning();
|
|
106
|
+
const row = rows[0];
|
|
107
|
+
if (!row) return undefined;
|
|
108
|
+
return toProject(row);
|
|
109
|
+
}
|
|
50
110
|
export async function requireProjectForWorkspace(params) {
|
|
51
111
|
const project = await getProjectById(params.projectId);
|
|
52
112
|
if (!project) throw new ProjectNotFoundError(params.projectId);
|
|
53
113
|
if (project.workspaceId !== params.workspaceId) throw new ProjectNotFoundError(params.projectId);
|
|
54
114
|
return project;
|
|
55
115
|
}
|
|
116
|
+
export async function updateProjectSourceMetadata(params) {
|
|
117
|
+
await db().update(projects).set({
|
|
118
|
+
sourceConnectionId: params.sourceConnectionId,
|
|
119
|
+
sourceExternalRepositoryId: params.sourceExternalRepositoryId,
|
|
120
|
+
sourceRepositoryOwner: params.sourceRepositoryOwner,
|
|
121
|
+
sourceRepositoryName: params.sourceRepositoryName,
|
|
122
|
+
sourceDefaultBranch: params.sourceDefaultBranch,
|
|
123
|
+
updatedAt: new Date()
|
|
124
|
+
}).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.projectId)));
|
|
125
|
+
}
|
|
126
|
+
export async function resolveCheckoutTarget(params) {
|
|
127
|
+
const selection = {
|
|
128
|
+
projectId: projects.id,
|
|
129
|
+
connectionId: projects.sourceConnectionId,
|
|
130
|
+
externalRepositoryId: projects.sourceExternalRepositoryId
|
|
131
|
+
};
|
|
132
|
+
if ('project' in params.target) {
|
|
133
|
+
const [project] = await db().select(selection).from(projects).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.target.project))).limit(1);
|
|
134
|
+
return project;
|
|
135
|
+
}
|
|
136
|
+
const separator = params.target.repository.indexOf('/');
|
|
137
|
+
if (separator === 0 || separator === params.target.repository.length - 1) return undefined;
|
|
138
|
+
const repository = separator === -1 ? {
|
|
139
|
+
connectionId: params.target.connection ?? params.defaults.connectionId,
|
|
140
|
+
owner: params.defaults.owner,
|
|
141
|
+
name: params.target.repository
|
|
142
|
+
} : params.target.repository.indexOf('/', separator + 1) === -1 ? {
|
|
143
|
+
connectionId: params.target.connection ?? params.defaults.connectionId,
|
|
144
|
+
owner: params.target.repository.slice(0, separator),
|
|
145
|
+
name: params.target.repository.slice(separator + 1)
|
|
146
|
+
} : undefined;
|
|
147
|
+
if (repository === undefined) return undefined;
|
|
148
|
+
const matches = await db().select(selection).from(projects).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.sourceConnectionId, repository.connectionId), sql`lower(${projects.sourceRepositoryOwner}) = lower(${repository.owner})`, sql`lower(${projects.sourceRepositoryName}) = lower(${repository.name})`)).limit(2);
|
|
149
|
+
// Owner/name isn't a unique key: renamed or reconnected repositories can leave
|
|
150
|
+
// more than one project row matching. Fail closed instead of picking arbitrarily.
|
|
151
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
152
|
+
}
|
|
56
153
|
export async function listProjects(params) {
|
|
57
154
|
const conditions = [
|
|
58
155
|
eq(projects.workspaceId, params.workspaceId)
|
|
@@ -60,7 +157,8 @@ export async function listProjects(params) {
|
|
|
60
157
|
const cursorCondition = cursorWhere(params.cursor);
|
|
61
158
|
if (cursorCondition) conditions.push(cursorCondition);
|
|
62
159
|
if (params.search) {
|
|
63
|
-
|
|
160
|
+
const searchPattern = `%${escapeIlikePattern(params.search)}%`;
|
|
161
|
+
conditions.push(or(ilike(projects.name, searchPattern), ilike(projects.slug, searchPattern)));
|
|
64
162
|
}
|
|
65
163
|
const rows = await db().select().from(projects).where(and(...conditions)).orderBy(desc(projects.createdAt), desc(projects.id)).limit(params.limit + 1);
|
|
66
164
|
const hasMore = rows.length > params.limit;
|
package/dist/db/projects.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/projects.ts"],"sourcesContent":["import {and, count, desc, eq, ilike, inArray, lt, or, type SQL} from 'drizzle-orm';\nimport type {Project} from '#core/entities/project.js';\nimport {ProjectAlreadyExistsError, ProjectNotFoundError} from '#core/errors.js';\nimport {recordProjectCreated} from '#metrics/instance.js';\nimport {db} from './db.js';\nimport {projects, toProject} from './schema/projects.js';\n\nexport interface CreateProjectParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n name: string;\n}\n\nexport interface ProjectCursor {\n createdAt: Date;\n id: string;\n}\n\nexport interface ListProjectsParams {\n workspaceId: string;\n limit: number;\n cursor?: ProjectCursor | undefined;\n search?: string | undefined;\n}\n\nfunction escapeIlikePattern(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\n}\n\nexport interface ListProjectsResult {\n projects: Project[];\n nextCursor: ProjectCursor | null;\n}\n\nexport type AdminProjectSummary = Pick<\n Project,\n 'id' | 'workspaceId' | 'name' | 'createdAt' | 'updatedAt'\n>;\n\nexport interface ListAdminProjectsParams {\n limit: number;\n cursor?: ProjectCursor | undefined;\n projectId?: string | undefined;\n search?: string | undefined;\n}\n\nexport interface ListAdminProjectsResult {\n projects: AdminProjectSummary[];\n nextCursor: ProjectCursor | null;\n}\n\nfunction cursorWhere(cursor: ProjectCursor | undefined): SQL | undefined {\n if (!cursor) return undefined;\n return or(\n lt(projects.createdAt, cursor.createdAt),\n and(eq(projects.createdAt, cursor.createdAt), lt(projects.id, cursor.id)),\n );\n}\n\nexport async function createProject(params: CreateProjectParams): Promise<Project> {\n const project = await db().transaction(async (tx) => {\n const [projectRow] = await tx\n .insert(projects)\n .values({\n workspaceId: params.workspaceId,\n sourceConnectionId: params.sourceConnectionId,\n sourceExternalRepositoryId: params.sourceExternalRepositoryId,\n name: params.name,\n })\n .onConflictDoNothing({\n target: [projects.sourceConnectionId, projects.sourceExternalRepositoryId],\n })\n .returning();\n\n if (!projectRow) {\n const [conflict] = await tx\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n if (conflict) {\n throw new ProjectAlreadyExistsError(\n conflict.id,\n params.sourceConnectionId,\n params.sourceExternalRepositoryId,\n );\n }\n throw new Error('Insert returned no rows');\n }\n\n return toProject(projectRow);\n });\n recordProjectCreated();\n return project;\n}\n\nexport async function getProjectById(id: string): Promise<Project | undefined> {\n const rows = await db().select().from(projects).where(eq(projects.id, id)).limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport interface GetProjectBySourceParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n}\n\nexport async function getProjectBySource(\n params: GetProjectBySourceParams,\n): Promise<Project | undefined> {\n const rows = await db()\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.workspaceId, params.workspaceId),\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport async function requireProjectForWorkspace(params: {\n projectId: string;\n workspaceId: string;\n}): Promise<Project> {\n const project = await getProjectById(params.projectId);\n if (!project) throw new ProjectNotFoundError(params.projectId);\n if (project.workspaceId !== params.workspaceId) throw new ProjectNotFoundError(params.projectId);\n return project;\n}\n\nexport async function listProjects(params: ListProjectsParams): Promise<ListProjectsResult> {\n const conditions = [eq(projects.workspaceId, params.workspaceId)];\n const cursorCondition = cursorWhere(params.cursor);\n if (cursorCondition) conditions.push(cursorCondition);\n if (params.search) {\n conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));\n }\n\n const rows = await db()\n .select()\n .from(projects)\n .where(and(...conditions))\n .orderBy(desc(projects.createdAt), desc(projects.id))\n .limit(params.limit + 1);\n\n const hasMore = rows.length > params.limit;\n const pageRows = hasMore ? rows.slice(0, params.limit) : rows;\n const last = pageRows.at(-1);\n\n return {\n projects: pageRows.map(toProject),\n nextCursor: hasMore && last ? {createdAt: last.createdAt, id: last.id} : null,\n };\n}\n\nexport async function listAdminProjects(\n params: ListAdminProjectsParams,\n): Promise<ListAdminProjectsResult> {\n const conditions: SQL[] = [];\n const cursorCondition = cursorWhere(params.cursor);\n if (cursorCondition) conditions.push(cursorCondition);\n if (params.projectId) conditions.push(eq(projects.id, params.projectId));\n if (params.search) {\n conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));\n }\n\n const rows = await db()\n .select({\n id: projects.id,\n workspaceId: projects.workspaceId,\n name: projects.name,\n createdAt: projects.createdAt,\n updatedAt: projects.updatedAt,\n })\n .from(projects)\n .where(conditions.length > 0 ? and(...conditions) : undefined)\n .orderBy(desc(projects.createdAt), desc(projects.id))\n .limit(params.limit + 1);\n\n const hasMore = rows.length > params.limit;\n const pageRows = hasMore ? rows.slice(0, params.limit) : rows;\n const last = pageRows.at(-1);\n\n return {\n projects: pageRows,\n nextCursor: hasMore && last ? {createdAt: last.createdAt, id: last.id} : null,\n };\n}\n\nexport async function getProjectCount(): Promise<number> {\n const [row] = await db().select({value: count()}).from(projects);\n return row?.value ?? 0;\n}\n\nexport async function getWorkspaceProjectCounts(params: {\n workspaceIds: string[];\n}): Promise<Array<{workspaceId: string; count: number}>> {\n const rows = await db()\n .select({workspaceId: projects.workspaceId, count: count()})\n .from(projects)\n .where(inArray(projects.workspaceId, params.workspaceIds))\n .groupBy(projects.workspaceId);\n\n const countsByWorkspace = new Map(rows.map((row) => [row.workspaceId, Number(row.count)]));\n return params.workspaceIds.map((workspaceId) => ({\n workspaceId,\n count: countsByWorkspace.get(workspaceId) ?? 0,\n }));\n}\n"],"names":["and","count","desc","eq","ilike","inArray","lt","or","ProjectAlreadyExistsError","ProjectNotFoundError","recordProjectCreated","db","projects","toProject","escapeIlikePattern","value","replace","char","cursorWhere","cursor","undefined","createdAt","id","createProject","params","project","transaction","tx","projectRow","insert","values","workspaceId","sourceConnectionId","sourceExternalRepositoryId","name","onConflictDoNothing","target","returning","conflict","select","from","where","limit","Error","getProjectById","rows","row","getProjectBySource","requireProjectForWorkspace","projectId","listProjects","conditions","cursorCondition","push","search","orderBy","hasMore","length","pageRows","slice","last","at","map","nextCursor","listAdminProjects","updatedAt","getProjectCount","getWorkspaceProjectCounts","workspaceIds","groupBy","countsByWorkspace","Map","Number","get"],"mappings":"AAAA,SAAQA,GAAG,EAAEC,KAAK,EAAEC,IAAI,EAAEC,EAAE,EAAEC,KAAK,EAAEC,OAAO,EAAEC,EAAE,EAAEC,EAAE,QAAiB,cAAc;AAEnF,SAAQC,yBAAyB,EAAEC,oBAAoB,QAAO,kBAAkB;AAChF,SAAQC,oBAAoB,QAAO,uBAAuB;AAC1D,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,QAAQ,EAAEC,SAAS,QAAO,uBAAuB;AAqBzD,SAASC,mBAAmBC,KAAa;IACvC,OAAOA,MAAMC,OAAO,CAAC,WAAW,CAACC,OAAS,CAAC,EAAE,EAAEA,MAAM;AACvD;AAwBA,SAASC,YAAYC,MAAiC;IACpD,IAAI,CAACA,QAAQ,OAAOC;IACpB,OAAOb,GACLD,GAAGM,SAASS,SAAS,EAAEF,OAAOE,SAAS,GACvCrB,IAAIG,GAAGS,SAASS,SAAS,EAAEF,OAAOE,SAAS,GAAGf,GAAGM,SAASU,EAAE,EAAEH,OAAOG,EAAE;AAE3E;AAEA,OAAO,eAAeC,cAAcC,MAA2B;IAC7D,MAAMC,UAAU,MAAMd,KAAKe,WAAW,CAAC,OAAOC;QAC5C,MAAM,CAACC,WAAW,GAAG,MAAMD,GACxBE,MAAM,CAACjB,UACPkB,MAAM,CAAC;YACNC,aAAaP,OAAOO,WAAW;YAC/BC,oBAAoBR,OAAOQ,kBAAkB;YAC7CC,4BAA4BT,OAAOS,0BAA0B;YAC7DC,MAAMV,OAAOU,IAAI;QACnB,GACCC,mBAAmB,CAAC;YACnBC,QAAQ;gBAACxB,SAASoB,kBAAkB;gBAAEpB,SAASqB,0BAA0B;aAAC;QAC5E,GACCI,SAAS;QAEZ,IAAI,CAACT,YAAY;YACf,MAAM,CAACU,SAAS,GAAG,MAAMX,GACtBY,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CACJzC,IACEG,GAAGS,SAASoB,kBAAkB,EAAER,OAAOQ,kBAAkB,GACzD7B,GAAGS,SAASqB,0BAA0B,EAAET,OAAOS,0BAA0B,IAG5ES,KAAK,CAAC;YACT,IAAIJ,UAAU;gBACZ,MAAM,IAAI9B,0BACR8B,SAAShB,EAAE,EACXE,OAAOQ,kBAAkB,EACzBR,OAAOS,0BAA0B;YAErC;YACA,MAAM,IAAIU,MAAM;QAClB;QAEA,OAAO9B,UAAUe;IACnB;IACAlB;IACA,OAAOe;AACT;AAEA,OAAO,eAAemB,eAAetB,EAAU;IAC7C,MAAMuB,OAAO,MAAMlC,KAAK4B,MAAM,GAAGC,IAAI,CAAC5B,UAAU6B,KAAK,CAACtC,GAAGS,SAASU,EAAE,EAAEA,KAAKoB,KAAK,CAAC;IAEjF,MAAMI,MAAMD,IAAI,CAAC,EAAE;IACnB,IAAI,CAACC,KAAK,OAAO1B;IACjB,OAAOP,UAAUiC;AACnB;AAQA,OAAO,eAAeC,mBACpBvB,MAAgC;IAEhC,MAAMqB,OAAO,MAAMlC,KAChB4B,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CACJzC,IACEG,GAAGS,SAASmB,WAAW,EAAEP,OAAOO,WAAW,GAC3C5B,GAAGS,SAASoB,kBAAkB,EAAER,OAAOQ,kBAAkB,GACzD7B,GAAGS,SAASqB,0BAA0B,EAAET,OAAOS,0BAA0B,IAG5ES,KAAK,CAAC;IAET,MAAMI,MAAMD,IAAI,CAAC,EAAE;IACnB,IAAI,CAACC,KAAK,OAAO1B;IACjB,OAAOP,UAAUiC;AACnB;AAEA,OAAO,eAAeE,2BAA2BxB,MAGhD;IACC,MAAMC,UAAU,MAAMmB,eAAepB,OAAOyB,SAAS;IACrD,IAAI,CAACxB,SAAS,MAAM,IAAIhB,qBAAqBe,OAAOyB,SAAS;IAC7D,IAAIxB,QAAQM,WAAW,KAAKP,OAAOO,WAAW,EAAE,MAAM,IAAItB,qBAAqBe,OAAOyB,SAAS;IAC/F,OAAOxB;AACT;AAEA,OAAO,eAAeyB,aAAa1B,MAA0B;IAC3D,MAAM2B,aAAa;QAAChD,GAAGS,SAASmB,WAAW,EAAEP,OAAOO,WAAW;KAAE;IACjE,MAAMqB,kBAAkBlC,YAAYM,OAAOL,MAAM;IACjD,IAAIiC,iBAAiBD,WAAWE,IAAI,CAACD;IACrC,IAAI5B,OAAO8B,MAAM,EAAE;QACjBH,WAAWE,IAAI,CAACjD,MAAMQ,SAASsB,IAAI,EAAE,CAAC,CAAC,EAAEpB,mBAAmBU,OAAO8B,MAAM,EAAE,CAAC,CAAC;IAC/E;IAEA,MAAMT,OAAO,MAAMlC,KAChB4B,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CAACzC,OAAOmD,aACbI,OAAO,CAACrD,KAAKU,SAASS,SAAS,GAAGnB,KAAKU,SAASU,EAAE,GAClDoB,KAAK,CAAClB,OAAOkB,KAAK,GAAG;IAExB,MAAMc,UAAUX,KAAKY,MAAM,GAAGjC,OAAOkB,KAAK;IAC1C,MAAMgB,WAAWF,UAAUX,KAAKc,KAAK,CAAC,GAAGnC,OAAOkB,KAAK,IAAIG;IACzD,MAAMe,OAAOF,SAASG,EAAE,CAAC,CAAC;IAE1B,OAAO;QACLjD,UAAU8C,SAASI,GAAG,CAACjD;QACvBkD,YAAYP,WAAWI,OAAO;YAACvC,WAAWuC,KAAKvC,SAAS;YAAEC,IAAIsC,KAAKtC,EAAE;QAAA,IAAI;IAC3E;AACF;AAEA,OAAO,eAAe0C,kBACpBxC,MAA+B;IAE/B,MAAM2B,aAAoB,EAAE;IAC5B,MAAMC,kBAAkBlC,YAAYM,OAAOL,MAAM;IACjD,IAAIiC,iBAAiBD,WAAWE,IAAI,CAACD;IACrC,IAAI5B,OAAOyB,SAAS,EAAEE,WAAWE,IAAI,CAAClD,GAAGS,SAASU,EAAE,EAAEE,OAAOyB,SAAS;IACtE,IAAIzB,OAAO8B,MAAM,EAAE;QACjBH,WAAWE,IAAI,CAACjD,MAAMQ,SAASsB,IAAI,EAAE,CAAC,CAAC,EAAEpB,mBAAmBU,OAAO8B,MAAM,EAAE,CAAC,CAAC;IAC/E;IAEA,MAAMT,OAAO,MAAMlC,KAChB4B,MAAM,CAAC;QACNjB,IAAIV,SAASU,EAAE;QACfS,aAAanB,SAASmB,WAAW;QACjCG,MAAMtB,SAASsB,IAAI;QACnBb,WAAWT,SAASS,SAAS;QAC7B4C,WAAWrD,SAASqD,SAAS;IAC/B,GACCzB,IAAI,CAAC5B,UACL6B,KAAK,CAACU,WAAWM,MAAM,GAAG,IAAIzD,OAAOmD,cAAc/B,WACnDmC,OAAO,CAACrD,KAAKU,SAASS,SAAS,GAAGnB,KAAKU,SAASU,EAAE,GAClDoB,KAAK,CAAClB,OAAOkB,KAAK,GAAG;IAExB,MAAMc,UAAUX,KAAKY,MAAM,GAAGjC,OAAOkB,KAAK;IAC1C,MAAMgB,WAAWF,UAAUX,KAAKc,KAAK,CAAC,GAAGnC,OAAOkB,KAAK,IAAIG;IACzD,MAAMe,OAAOF,SAASG,EAAE,CAAC,CAAC;IAE1B,OAAO;QACLjD,UAAU8C;QACVK,YAAYP,WAAWI,OAAO;YAACvC,WAAWuC,KAAKvC,SAAS;YAAEC,IAAIsC,KAAKtC,EAAE;QAAA,IAAI;IAC3E;AACF;AAEA,OAAO,eAAe4C;IACpB,MAAM,CAACpB,IAAI,GAAG,MAAMnC,KAAK4B,MAAM,CAAC;QAACxB,OAAOd;IAAO,GAAGuC,IAAI,CAAC5B;IACvD,OAAOkC,KAAK/B,SAAS;AACvB;AAEA,OAAO,eAAeoD,0BAA0B3C,MAE/C;IACC,MAAMqB,OAAO,MAAMlC,KAChB4B,MAAM,CAAC;QAACR,aAAanB,SAASmB,WAAW;QAAE9B,OAAOA;IAAO,GACzDuC,IAAI,CAAC5B,UACL6B,KAAK,CAACpC,QAAQO,SAASmB,WAAW,EAAEP,OAAO4C,YAAY,GACvDC,OAAO,CAACzD,SAASmB,WAAW;IAE/B,MAAMuC,oBAAoB,IAAIC,IAAI1B,KAAKiB,GAAG,CAAC,CAAChB,MAAQ;YAACA,IAAIf,WAAW;YAAEyC,OAAO1B,IAAI7C,KAAK;SAAE;IACxF,OAAOuB,OAAO4C,YAAY,CAACN,GAAG,CAAC,CAAC/B,cAAiB,CAAA;YAC/CA;YACA9B,OAAOqE,kBAAkBG,GAAG,CAAC1C,gBAAgB;QAC/C,CAAA;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../src/db/projects.ts"],"sourcesContent":["import {isUniqueViolation} from '@shipfox/node-drizzle';\nimport {and, count, desc, eq, ilike, inArray, lt, or, type SQL, sql} from 'drizzle-orm';\nimport type {Project} from '#core/entities/project.js';\nimport {\n ProjectAlreadyExistsError,\n ProjectNotFoundError,\n ProjectSlugConflictError,\n} from '#core/errors.js';\nimport {recordProjectCreated} from '#metrics/instance.js';\nimport {db, type Executor} from './db.js';\nimport {projects, toProject} from './schema/projects.js';\n\ntype ProjectDatabase = ReturnType<typeof db>;\ntype ProjectTransaction = Parameters<Parameters<ProjectDatabase['transaction']>[0]>[0];\n\nexport interface CreateProjectParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n name: string;\n slug: string;\n}\n\nexport interface UpdateProjectParams {\n projectId: string;\n name?: string | undefined;\n slug?: string | undefined;\n}\n\nexport interface UpdateProjectResult {\n project: Project;\n changed: boolean;\n}\n\nexport interface ProjectCursor {\n createdAt: Date;\n id: string;\n}\n\nexport interface ListProjectsParams {\n workspaceId: string;\n limit: number;\n cursor?: ProjectCursor | undefined;\n search?: string | undefined;\n}\n\nfunction escapeIlikePattern(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\n}\n\nexport interface ListProjectsResult {\n projects: Project[];\n nextCursor: ProjectCursor | null;\n}\n\nexport type AdminProjectSummary = Pick<\n Project,\n 'id' | 'workspaceId' | 'name' | 'createdAt' | 'updatedAt'\n>;\n\nexport interface ListAdminProjectsParams {\n limit: number;\n cursor?: ProjectCursor | undefined;\n projectId?: string | undefined;\n search?: string | undefined;\n}\n\nexport interface ListAdminProjectsResult {\n projects: AdminProjectSummary[];\n nextCursor: ProjectCursor | null;\n}\n\nconst PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT = 'projects_workspace_slug_unique';\n\nfunction cursorWhere(cursor: ProjectCursor | undefined): SQL | undefined {\n if (!cursor) return undefined;\n return or(\n lt(projects.createdAt, cursor.createdAt),\n and(eq(projects.createdAt, cursor.createdAt), lt(projects.id, cursor.id)),\n );\n}\n\nexport async function createProject(params: CreateProjectParams): Promise<Project> {\n const project = await db().transaction(async (tx) => {\n let projectRow: typeof projects.$inferSelect | undefined;\n for (let attempt = 0; attempt < 2 && !projectRow; attempt += 1) {\n try {\n [projectRow] = await tx\n .insert(projects)\n .values({\n workspaceId: params.workspaceId,\n sourceConnectionId: params.sourceConnectionId,\n sourceExternalRepositoryId: params.sourceExternalRepositoryId,\n name: params.name,\n slug: params.slug,\n })\n .onConflictDoNothing({\n target: [projects.sourceConnectionId, projects.sourceExternalRepositoryId],\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {\n throw new ProjectSlugConflictError(params.slug);\n }\n throw error;\n }\n\n if (!projectRow) {\n const [conflict] = await tx\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n if (conflict) {\n throw new ProjectAlreadyExistsError(\n conflict.id,\n params.sourceConnectionId,\n params.sourceExternalRepositoryId,\n );\n }\n }\n }\n\n if (!projectRow) {\n throw new Error('Insert returned no rows');\n }\n\n return toProject(projectRow);\n });\n recordProjectCreated();\n return project;\n}\n\nexport async function updateProject(\n params: UpdateProjectParams,\n options: {tx?: ProjectDatabase | ProjectTransaction | undefined} = {},\n): Promise<UpdateProjectResult | undefined> {\n const executor = options.tx ?? db();\n\n const [existingRow] = await executor\n .select()\n .from(projects)\n .where(eq(projects.id, params.projectId))\n .limit(1);\n if (!existingRow) return undefined;\n\n const nextName = params.name ?? existingRow.name;\n const nextSlug = params.slug ?? existingRow.slug;\n if (nextName === existingRow.name && nextSlug === existingRow.slug) {\n return {project: toProject(existingRow), changed: false};\n }\n\n try {\n const [row] = await executor\n .update(projects)\n .set({\n ...(params.name !== undefined ? {name: params.name} : {}),\n ...(params.slug !== undefined ? {slug: params.slug} : {}),\n updatedAt: new Date(),\n })\n .where(eq(projects.id, params.projectId))\n .returning();\n\n return row ? {project: toProject(row), changed: true} : undefined;\n } catch (error) {\n if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {\n throw new ProjectSlugConflictError(nextSlug);\n }\n throw error;\n }\n}\n\nexport async function getProjectById(id: string): Promise<Project | undefined> {\n const rows = await db().select().from(projects).where(eq(projects.id, id)).limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport interface GetProjectBySourceParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n}\n\nexport async function getProjectBySource(\n params: GetProjectBySourceParams,\n): Promise<Project | undefined> {\n const rows = await db()\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.workspaceId, params.workspaceId),\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport interface UpdateProjectSourceRepositoryParams extends GetProjectBySourceParams {\n tx?: Executor;\n sourceRepositoryOwner: string;\n sourceRepositoryName: string;\n sourceDefaultBranch: string;\n}\n\nexport async function updateProjectSourceRepository(\n params: UpdateProjectSourceRepositoryParams,\n): Promise<Project | undefined> {\n const executor = params.tx ?? db();\n const rows = await executor\n .update(projects)\n .set({\n sourceRepositoryOwner: params.sourceRepositoryOwner,\n sourceRepositoryName: params.sourceRepositoryName,\n sourceDefaultBranch: params.sourceDefaultBranch,\n updatedAt: new Date(),\n })\n .where(\n and(\n eq(projects.workspaceId, params.workspaceId),\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .returning();\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport async function requireProjectForWorkspace(params: {\n projectId: string;\n workspaceId: string;\n}): Promise<Project> {\n const project = await getProjectById(params.projectId);\n if (!project) throw new ProjectNotFoundError(params.projectId);\n if (project.workspaceId !== params.workspaceId) throw new ProjectNotFoundError(params.projectId);\n return project;\n}\n\nexport interface ResolveCheckoutTargetParams {\n workspaceId: string;\n defaults: {\n connectionId: string;\n owner: string;\n };\n target: {project: string} | {connection?: string | undefined; repository: string};\n}\n\nexport interface ResolvedCheckoutTarget {\n projectId: string;\n connectionId: string;\n externalRepositoryId: string;\n}\n\nexport interface UpdateProjectSourceMetadataParams {\n workspaceId: string;\n projectId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n sourceRepositoryOwner: string;\n sourceRepositoryName: string;\n sourceDefaultBranch: string;\n}\n\nexport async function updateProjectSourceMetadata(\n params: UpdateProjectSourceMetadataParams,\n): Promise<void> {\n await db()\n .update(projects)\n .set({\n sourceConnectionId: params.sourceConnectionId,\n sourceExternalRepositoryId: params.sourceExternalRepositoryId,\n sourceRepositoryOwner: params.sourceRepositoryOwner,\n sourceRepositoryName: params.sourceRepositoryName,\n sourceDefaultBranch: params.sourceDefaultBranch,\n updatedAt: new Date(),\n })\n .where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.projectId)));\n}\n\nexport async function resolveCheckoutTarget(\n params: ResolveCheckoutTargetParams,\n): Promise<ResolvedCheckoutTarget | undefined> {\n const selection = {\n projectId: projects.id,\n connectionId: projects.sourceConnectionId,\n externalRepositoryId: projects.sourceExternalRepositoryId,\n };\n\n if ('project' in params.target) {\n const [project] = await db()\n .select(selection)\n .from(projects)\n .where(\n and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.target.project)),\n )\n .limit(1);\n return project;\n }\n\n const separator = params.target.repository.indexOf('/');\n if (separator === 0 || separator === params.target.repository.length - 1) return undefined;\n\n const repository =\n separator === -1\n ? {\n connectionId: params.target.connection ?? params.defaults.connectionId,\n owner: params.defaults.owner,\n name: params.target.repository,\n }\n : params.target.repository.indexOf('/', separator + 1) === -1\n ? {\n connectionId: params.target.connection ?? params.defaults.connectionId,\n owner: params.target.repository.slice(0, separator),\n name: params.target.repository.slice(separator + 1),\n }\n : undefined;\n if (repository === undefined) return undefined;\n\n const matches = await db()\n .select(selection)\n .from(projects)\n .where(\n and(\n eq(projects.workspaceId, params.workspaceId),\n eq(projects.sourceConnectionId, repository.connectionId),\n sql`lower(${projects.sourceRepositoryOwner}) = lower(${repository.owner})`,\n sql`lower(${projects.sourceRepositoryName}) = lower(${repository.name})`,\n ),\n )\n .limit(2);\n\n // Owner/name isn't a unique key: renamed or reconnected repositories can leave\n // more than one project row matching. Fail closed instead of picking arbitrarily.\n return matches.length === 1 ? matches[0] : undefined;\n}\n\nexport async function listProjects(params: ListProjectsParams): Promise<ListProjectsResult> {\n const conditions = [eq(projects.workspaceId, params.workspaceId)];\n const cursorCondition = cursorWhere(params.cursor);\n if (cursorCondition) conditions.push(cursorCondition);\n if (params.search) {\n const searchPattern = `%${escapeIlikePattern(params.search)}%`;\n conditions.push(\n or(ilike(projects.name, searchPattern), ilike(projects.slug, searchPattern)) as SQL,\n );\n }\n\n const rows = await db()\n .select()\n .from(projects)\n .where(and(...conditions))\n .orderBy(desc(projects.createdAt), desc(projects.id))\n .limit(params.limit + 1);\n\n const hasMore = rows.length > params.limit;\n const pageRows = hasMore ? rows.slice(0, params.limit) : rows;\n const last = pageRows.at(-1);\n\n return {\n projects: pageRows.map(toProject),\n nextCursor: hasMore && last ? {createdAt: last.createdAt, id: last.id} : null,\n };\n}\n\nexport async function listAdminProjects(\n params: ListAdminProjectsParams,\n): Promise<ListAdminProjectsResult> {\n const conditions: SQL[] = [];\n const cursorCondition = cursorWhere(params.cursor);\n if (cursorCondition) conditions.push(cursorCondition);\n if (params.projectId) conditions.push(eq(projects.id, params.projectId));\n if (params.search) {\n conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));\n }\n\n const rows = await db()\n .select({\n id: projects.id,\n workspaceId: projects.workspaceId,\n name: projects.name,\n createdAt: projects.createdAt,\n updatedAt: projects.updatedAt,\n })\n .from(projects)\n .where(conditions.length > 0 ? and(...conditions) : undefined)\n .orderBy(desc(projects.createdAt), desc(projects.id))\n .limit(params.limit + 1);\n\n const hasMore = rows.length > params.limit;\n const pageRows = hasMore ? rows.slice(0, params.limit) : rows;\n const last = pageRows.at(-1);\n\n return {\n projects: pageRows,\n nextCursor: hasMore && last ? {createdAt: last.createdAt, id: last.id} : null,\n };\n}\n\nexport async function getProjectCount(): Promise<number> {\n const [row] = await db().select({value: count()}).from(projects);\n return row?.value ?? 0;\n}\n\nexport async function getWorkspaceProjectCounts(params: {\n workspaceIds: string[];\n}): Promise<Array<{workspaceId: string; count: number}>> {\n const rows = await db()\n .select({workspaceId: projects.workspaceId, count: count()})\n .from(projects)\n .where(inArray(projects.workspaceId, params.workspaceIds))\n .groupBy(projects.workspaceId);\n\n const countsByWorkspace = new Map(rows.map((row) => [row.workspaceId, Number(row.count)]));\n return params.workspaceIds.map((workspaceId) => ({\n workspaceId,\n count: countsByWorkspace.get(workspaceId) ?? 0,\n }));\n}\n"],"names":["isUniqueViolation","and","count","desc","eq","ilike","inArray","lt","or","sql","ProjectAlreadyExistsError","ProjectNotFoundError","ProjectSlugConflictError","recordProjectCreated","db","projects","toProject","escapeIlikePattern","value","replace","char","PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT","cursorWhere","cursor","undefined","createdAt","id","createProject","params","project","transaction","tx","projectRow","attempt","insert","values","workspaceId","sourceConnectionId","sourceExternalRepositoryId","name","slug","onConflictDoNothing","target","returning","error","conflict","select","from","where","limit","Error","updateProject","options","executor","existingRow","projectId","nextName","nextSlug","changed","row","update","set","updatedAt","Date","getProjectById","rows","getProjectBySource","updateProjectSourceRepository","sourceRepositoryOwner","sourceRepositoryName","sourceDefaultBranch","requireProjectForWorkspace","updateProjectSourceMetadata","resolveCheckoutTarget","selection","connectionId","externalRepositoryId","separator","repository","indexOf","length","connection","defaults","owner","slice","matches","listProjects","conditions","cursorCondition","push","search","searchPattern","orderBy","hasMore","pageRows","last","at","map","nextCursor","listAdminProjects","getProjectCount","getWorkspaceProjectCounts","workspaceIds","groupBy","countsByWorkspace","Map","Number","get"],"mappings":"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"}
|
|
@@ -71,6 +71,57 @@ export declare const projects: import("drizzle-orm/pg-core").PgTableWithColumns<
|
|
|
71
71
|
identity: undefined;
|
|
72
72
|
generated: undefined;
|
|
73
73
|
}, {}, {}>;
|
|
74
|
+
sourceRepositoryOwner: import("drizzle-orm/pg-core").PgColumn<{
|
|
75
|
+
name: "source_repository_owner";
|
|
76
|
+
tableName: "projects";
|
|
77
|
+
dataType: "string";
|
|
78
|
+
columnType: "PgText";
|
|
79
|
+
data: string;
|
|
80
|
+
driverParam: string;
|
|
81
|
+
notNull: false;
|
|
82
|
+
hasDefault: false;
|
|
83
|
+
isPrimaryKey: false;
|
|
84
|
+
isAutoincrement: false;
|
|
85
|
+
hasRuntimeDefault: false;
|
|
86
|
+
enumValues: [string, ...string[]];
|
|
87
|
+
baseColumn: never;
|
|
88
|
+
identity: undefined;
|
|
89
|
+
generated: undefined;
|
|
90
|
+
}, {}, {}>;
|
|
91
|
+
sourceRepositoryName: import("drizzle-orm/pg-core").PgColumn<{
|
|
92
|
+
name: "source_repository_name";
|
|
93
|
+
tableName: "projects";
|
|
94
|
+
dataType: "string";
|
|
95
|
+
columnType: "PgText";
|
|
96
|
+
data: string;
|
|
97
|
+
driverParam: string;
|
|
98
|
+
notNull: false;
|
|
99
|
+
hasDefault: false;
|
|
100
|
+
isPrimaryKey: false;
|
|
101
|
+
isAutoincrement: false;
|
|
102
|
+
hasRuntimeDefault: false;
|
|
103
|
+
enumValues: [string, ...string[]];
|
|
104
|
+
baseColumn: never;
|
|
105
|
+
identity: undefined;
|
|
106
|
+
generated: undefined;
|
|
107
|
+
}, {}, {}>;
|
|
108
|
+
sourceDefaultBranch: import("drizzle-orm/pg-core").PgColumn<{
|
|
109
|
+
name: "source_default_branch";
|
|
110
|
+
tableName: "projects";
|
|
111
|
+
dataType: "string";
|
|
112
|
+
columnType: "PgText";
|
|
113
|
+
data: string;
|
|
114
|
+
driverParam: string;
|
|
115
|
+
notNull: false;
|
|
116
|
+
hasDefault: false;
|
|
117
|
+
isPrimaryKey: false;
|
|
118
|
+
isAutoincrement: false;
|
|
119
|
+
hasRuntimeDefault: false;
|
|
120
|
+
enumValues: [string, ...string[]];
|
|
121
|
+
baseColumn: never;
|
|
122
|
+
identity: undefined;
|
|
123
|
+
generated: undefined;
|
|
124
|
+
}, {}, {}>;
|
|
74
125
|
name: import("drizzle-orm/pg-core").PgColumn<{
|
|
75
126
|
name: "name";
|
|
76
127
|
tableName: "projects";
|
|
@@ -88,6 +139,23 @@ export declare const projects: import("drizzle-orm/pg-core").PgTableWithColumns<
|
|
|
88
139
|
identity: undefined;
|
|
89
140
|
generated: undefined;
|
|
90
141
|
}, {}, {}>;
|
|
142
|
+
slug: import("drizzle-orm/pg-core").PgColumn<{
|
|
143
|
+
name: "slug";
|
|
144
|
+
tableName: "projects";
|
|
145
|
+
dataType: "string";
|
|
146
|
+
columnType: "PgText";
|
|
147
|
+
data: string;
|
|
148
|
+
driverParam: string;
|
|
149
|
+
notNull: true;
|
|
150
|
+
hasDefault: false;
|
|
151
|
+
isPrimaryKey: false;
|
|
152
|
+
isAutoincrement: false;
|
|
153
|
+
hasRuntimeDefault: false;
|
|
154
|
+
enumValues: [string, ...string[]];
|
|
155
|
+
baseColumn: never;
|
|
156
|
+
identity: undefined;
|
|
157
|
+
generated: undefined;
|
|
158
|
+
}, {}, {}>;
|
|
91
159
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
92
160
|
name: "created_at";
|
|
93
161
|
tableName: "projects";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/db/schema/projects.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/db/schema/projects.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAGvD,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BpB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAC;AACrD,MAAM,MAAM,eAAe,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAC;AAE3D,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAcjD"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { uuidv7PrimaryKey } from '@shipfox/node-drizzle';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
2
3
|
import { index, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
|
|
3
4
|
import { pgTable } from './common.js';
|
|
4
5
|
export const projects = pgTable('projects', {
|
|
@@ -6,7 +7,11 @@ export const projects = pgTable('projects', {
|
|
|
6
7
|
workspaceId: uuid('workspace_id').notNull(),
|
|
7
8
|
sourceConnectionId: uuid('source_connection_id').notNull(),
|
|
8
9
|
sourceExternalRepositoryId: text('source_external_repository_id').notNull(),
|
|
10
|
+
sourceRepositoryOwner: text('source_repository_owner'),
|
|
11
|
+
sourceRepositoryName: text('source_repository_name'),
|
|
12
|
+
sourceDefaultBranch: text('source_default_branch'),
|
|
9
13
|
name: text('name').notNull(),
|
|
14
|
+
slug: text('slug').notNull(),
|
|
10
15
|
createdAt: timestamp('created_at', {
|
|
11
16
|
withTimezone: true
|
|
12
17
|
}).notNull().defaultNow(),
|
|
@@ -15,7 +20,9 @@ export const projects = pgTable('projects', {
|
|
|
15
20
|
}).notNull().defaultNow()
|
|
16
21
|
}, (table)=>[
|
|
17
22
|
uniqueIndex('projects_source_unique').on(table.sourceConnectionId, table.sourceExternalRepositoryId),
|
|
18
|
-
|
|
23
|
+
uniqueIndex('projects_workspace_slug_unique').on(table.workspaceId, table.slug),
|
|
24
|
+
index('projects_workspace_created_id_idx').on(table.workspaceId, table.createdAt, table.id),
|
|
25
|
+
index('projects_source_repository_lookup_idx').on(table.workspaceId, table.sourceConnectionId, sql`lower(${table.sourceRepositoryOwner})`, sql`lower(${table.sourceRepositoryName})`)
|
|
19
26
|
]);
|
|
20
27
|
export function toProject(row) {
|
|
21
28
|
return {
|
|
@@ -23,7 +30,11 @@ export function toProject(row) {
|
|
|
23
30
|
workspaceId: row.workspaceId,
|
|
24
31
|
sourceConnectionId: row.sourceConnectionId,
|
|
25
32
|
sourceExternalRepositoryId: row.sourceExternalRepositoryId,
|
|
33
|
+
sourceRepositoryOwner: row.sourceRepositoryOwner,
|
|
34
|
+
sourceRepositoryName: row.sourceRepositoryName,
|
|
35
|
+
sourceDefaultBranch: row.sourceDefaultBranch,
|
|
26
36
|
name: row.name,
|
|
37
|
+
slug: row.slug,
|
|
27
38
|
createdAt: row.createdAt,
|
|
28
39
|
updatedAt: row.updatedAt
|
|
29
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/db/schema/projects.ts"],"sourcesContent":["import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {index, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport type {Project} from '#core/entities/project.js';\nimport {pgTable} from './common.js';\n\nexport const projects = pgTable(\n 'projects',\n {\n id: uuidv7PrimaryKey(),\n workspaceId: uuid('workspace_id').notNull(),\n sourceConnectionId: uuid('source_connection_id').notNull(),\n sourceExternalRepositoryId: text('source_external_repository_id').notNull(),\n name: text('name').notNull(),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),\n },\n (table) => [\n uniqueIndex('projects_source_unique').on(\n table.sourceConnectionId,\n table.sourceExternalRepositoryId,\n ),\n index('projects_workspace_created_id_idx').on(table.workspaceId, table.createdAt, table.id),\n ],\n);\n\nexport type ProjectDb = typeof projects.$inferSelect;\nexport type ProjectCreateDb = typeof projects.$inferInsert;\n\nexport function toProject(row: ProjectDb): Project {\n return {\n id: row.id,\n workspaceId: row.workspaceId,\n sourceConnectionId: row.sourceConnectionId,\n sourceExternalRepositoryId: row.sourceExternalRepositoryId,\n name: row.name,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n"],"names":["uuidv7PrimaryKey","index","text","timestamp","uniqueIndex","uuid","pgTable","projects","id","workspaceId","notNull","sourceConnectionId","sourceExternalRepositoryId","name","createdAt","withTimezone","defaultNow","updatedAt","table","on","toProject","row"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,IAAI,QAAO,sBAAsB;AAE9E,SAAQC,OAAO,QAAO,cAAc;AAEpC,OAAO,MAAMC,WAAWD,QACtB,YACA;IACEE,
|
|
1
|
+
{"version":3,"sources":["../../../src/db/schema/projects.ts"],"sourcesContent":["import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {sql} from 'drizzle-orm';\nimport {index, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport type {Project} from '#core/entities/project.js';\nimport {pgTable} from './common.js';\n\nexport const projects = pgTable(\n 'projects',\n {\n id: uuidv7PrimaryKey(),\n workspaceId: uuid('workspace_id').notNull(),\n sourceConnectionId: uuid('source_connection_id').notNull(),\n sourceExternalRepositoryId: text('source_external_repository_id').notNull(),\n sourceRepositoryOwner: text('source_repository_owner'),\n sourceRepositoryName: text('source_repository_name'),\n sourceDefaultBranch: text('source_default_branch'),\n name: text('name').notNull(),\n slug: text('slug').notNull(),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),\n },\n (table) => [\n uniqueIndex('projects_source_unique').on(\n table.sourceConnectionId,\n table.sourceExternalRepositoryId,\n ),\n uniqueIndex('projects_workspace_slug_unique').on(table.workspaceId, table.slug),\n index('projects_workspace_created_id_idx').on(table.workspaceId, table.createdAt, table.id),\n index('projects_source_repository_lookup_idx').on(\n table.workspaceId,\n table.sourceConnectionId,\n sql`lower(${table.sourceRepositoryOwner})`,\n sql`lower(${table.sourceRepositoryName})`,\n ),\n ],\n);\n\nexport type ProjectDb = typeof projects.$inferSelect;\nexport type ProjectCreateDb = typeof projects.$inferInsert;\n\nexport function toProject(row: ProjectDb): Project {\n return {\n id: row.id,\n workspaceId: row.workspaceId,\n sourceConnectionId: row.sourceConnectionId,\n sourceExternalRepositoryId: row.sourceExternalRepositoryId,\n sourceRepositoryOwner: row.sourceRepositoryOwner,\n sourceRepositoryName: row.sourceRepositoryName,\n sourceDefaultBranch: row.sourceDefaultBranch,\n name: row.name,\n slug: row.slug,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n"],"names":["uuidv7PrimaryKey","sql","index","text","timestamp","uniqueIndex","uuid","pgTable","projects","id","workspaceId","notNull","sourceConnectionId","sourceExternalRepositoryId","sourceRepositoryOwner","sourceRepositoryName","sourceDefaultBranch","name","slug","createdAt","withTimezone","defaultNow","updatedAt","table","on","toProject","row"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,GAAG,QAAO,cAAc;AAChC,SAAQC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,IAAI,QAAO,sBAAsB;AAE9E,SAAQC,OAAO,QAAO,cAAc;AAEpC,OAAO,MAAMC,WAAWD,QACtB,YACA;IACEE,IAAIT;IACJU,aAAaJ,KAAK,gBAAgBK,OAAO;IACzCC,oBAAoBN,KAAK,wBAAwBK,OAAO;IACxDE,4BAA4BV,KAAK,iCAAiCQ,OAAO;IACzEG,uBAAuBX,KAAK;IAC5BY,sBAAsBZ,KAAK;IAC3Ba,qBAAqBb,KAAK;IAC1Bc,MAAMd,KAAK,QAAQQ,OAAO;IAC1BO,MAAMf,KAAK,QAAQQ,OAAO;IAC1BQ,WAAWf,UAAU,cAAc;QAACgB,cAAc;IAAI,GAAGT,OAAO,GAAGU,UAAU;IAC7EC,WAAWlB,UAAU,cAAc;QAACgB,cAAc;IAAI,GAAGT,OAAO,GAAGU,UAAU;AAC/E,GACA,CAACE,QAAU;QACTlB,YAAY,0BAA0BmB,EAAE,CACtCD,MAAMX,kBAAkB,EACxBW,MAAMV,0BAA0B;QAElCR,YAAY,kCAAkCmB,EAAE,CAACD,MAAMb,WAAW,EAAEa,MAAML,IAAI;QAC9EhB,MAAM,qCAAqCsB,EAAE,CAACD,MAAMb,WAAW,EAAEa,MAAMJ,SAAS,EAAEI,MAAMd,EAAE;QAC1FP,MAAM,yCAAyCsB,EAAE,CAC/CD,MAAMb,WAAW,EACjBa,MAAMX,kBAAkB,EACxBX,GAAG,CAAC,MAAM,EAAEsB,MAAMT,qBAAqB,CAAC,CAAC,CAAC,EAC1Cb,GAAG,CAAC,MAAM,EAAEsB,MAAMR,oBAAoB,CAAC,CAAC,CAAC;KAE5C,EACD;AAKF,OAAO,SAASU,UAAUC,GAAc;IACtC,OAAO;QACLjB,IAAIiB,IAAIjB,EAAE;QACVC,aAAagB,IAAIhB,WAAW;QAC5BE,oBAAoBc,IAAId,kBAAkB;QAC1CC,4BAA4Ba,IAAIb,0BAA0B;QAC1DC,uBAAuBY,IAAIZ,qBAAqB;QAChDC,sBAAsBW,IAAIX,oBAAoB;QAC9CC,qBAAqBU,IAAIV,mBAAmB;QAC5CC,MAAMS,IAAIT,IAAI;QACdC,MAAMQ,IAAIR,IAAI;QACdC,WAAWO,IAAIP,SAAS;QACxBG,WAAWI,IAAIJ,SAAS;IAC1B;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AuthInterModuleClient } from '@shipfox/api-auth-dto/inter-module';
|
|
|
2
2
|
import type { IntegrationsModuleClient } from '@shipfox/api-integration-core-dto/inter-module';
|
|
3
3
|
import { type ShipfoxModule } from '@shipfox/node-module';
|
|
4
4
|
export type { Project } from '#core/index.js';
|
|
5
|
-
export { createProjectFromSource, ProjectAccessDeniedError, ProjectAlreadyExistsError, ProjectNotFoundError, } from '#core/index.js';
|
|
5
|
+
export { createProjectFromSource, ProjectAccessDeniedError, ProjectAlreadyExistsError, ProjectNotFoundError, ProjectSlugConflictError, updateProjectDetails, } from '#core/index.js';
|
|
6
6
|
export type { GetProjectBySourceParams } from '#db/index.js';
|
|
7
7
|
export { createProject, db, getProjectById, getProjectBySource, getWorkspaceProjectCounts, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace, } from '#db/index.js';
|
|
8
8
|
export { createProjectRoutes, requireProjectAccess } from '#presentation/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAM9E,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,gDAAgD,CAAC;AAE7F,OAAO,EAAC,KAAK,aAAa,EAAoB,MAAM,sBAAsB,CAAC;AAe3E,YAAY,EAAC,OAAO,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAC,wBAAwB,EAAC,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,aAAa,EACb,EAAE,EACF,cAAc,EACd,kBAAkB,EAClB,yBAAyB,EACzB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAEjF,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,wBAAwB,CAAC;IACvC,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,IAAI,GACL,EAAE,2BAA2B,GAAG,aAAa,CA4B7C"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dirname, resolve } from 'node:path';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { INTEGRATION_SOURCE_COMMIT_PUSHED } from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import { INTEGRATION_SOURCE_COMMIT_PUSHED, INTEGRATION_SOURCE_REPOSITORY_UPDATED } from '@shipfox/api-integration-core-dto';
|
|
4
4
|
import { projectsEventSchemas } from '@shipfox/api-projects-dto';
|
|
5
5
|
import { subscriberFactory } from '@shipfox/node-module';
|
|
6
6
|
import { db, migrationsPath, projectsOutbox } from '#db/index.js';
|
|
@@ -8,13 +8,13 @@ import { registerProjectsServiceMetrics } from '#metrics/index.js';
|
|
|
8
8
|
import { projectsE2eRoutes } from '#presentation/e2eRoutes/index.js';
|
|
9
9
|
import { createProjectRoutes } from '#presentation/index.js';
|
|
10
10
|
import { createProjectsInterModulePresentation } from '#presentation/inter-module.js';
|
|
11
|
-
import { onSourceCommitPushed } from '#presentation/subscribers/index.js';
|
|
11
|
+
import { onSourceCommitPushed, onSourceRepositoryUpdated } from '#presentation/subscribers/index.js';
|
|
12
12
|
import { createProjectsMaintenanceActivities } from '#temporal/activities/index.js';
|
|
13
13
|
import { PROJECTS_MAINTENANCE_TASK_QUEUE } from '#temporal/constants.js';
|
|
14
14
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
15
|
const maintenanceWorkflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');
|
|
16
16
|
const subscriber = subscriberFactory();
|
|
17
|
-
export { createProjectFromSource, ProjectAccessDeniedError, ProjectAlreadyExistsError, ProjectNotFoundError } from '#core/index.js';
|
|
17
|
+
export { createProjectFromSource, ProjectAccessDeniedError, ProjectAlreadyExistsError, ProjectNotFoundError, ProjectSlugConflictError, updateProjectDetails } from '#core/index.js';
|
|
18
18
|
export { createProject, db, getProjectById, getProjectBySource, getWorkspaceProjectCounts, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace } from '#db/index.js';
|
|
19
19
|
export { createProjectRoutes, requireProjectAccess } from '#presentation/index.js';
|
|
20
20
|
export function createProjectsModule({ integrations, auth }) {
|
|
@@ -39,10 +39,13 @@ export function createProjectsModule({ integrations, auth }) {
|
|
|
39
39
|
}
|
|
40
40
|
],
|
|
41
41
|
interModulePresentations: [
|
|
42
|
-
createProjectsInterModulePresentation(
|
|
42
|
+
createProjectsInterModulePresentation({
|
|
43
|
+
integrations
|
|
44
|
+
})
|
|
43
45
|
],
|
|
44
46
|
subscribers: [
|
|
45
|
-
subscriber(INTEGRATION_SOURCE_COMMIT_PUSHED, onSourceCommitPushed)
|
|
47
|
+
subscriber(INTEGRATION_SOURCE_COMMIT_PUSHED, onSourceCommitPushed),
|
|
48
|
+
subscriber(INTEGRATION_SOURCE_REPOSITORY_UPDATED, onSourceRepositoryUpdated)
|
|
46
49
|
],
|
|
47
50
|
workers: [
|
|
48
51
|
{
|