Skip to content

Commit d4ad37c

Browse files
Merge pull request #475 from codex-team/feat-release
feat(core): add releasesFactory to set releases list
2 parents 22f8ad7 + 300a877 commit d4ad37c

File tree

9 files changed

+209
-42
lines changed

9 files changed

+209
-42
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hawk.api",
3-
"version": "1.1.17",
3+
"version": "1.1.18",
44
"main": "index.ts",
55
"license": "UNLICENSED",
66
"scripts": {

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import PlansFactory from './models/plansFactory';
2626
import BusinessOperationsFactory from './models/businessOperationsFactory';
2727
import schema from './schema';
2828
import { graphqlUploadExpress } from 'graphql-upload';
29+
import ReleasesFactory from './models/releaseFactory';
2930

3031
/**
3132
* Option to enable playground
@@ -145,12 +146,16 @@ class HawkAPI {
145146
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
146147
const businessOperationsFactory = new BusinessOperationsFactory(mongo.databases.hawk!, dataLoaders);
147148

149+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
150+
const releasesFactory = new ReleasesFactory(mongo.databases.events!, dataLoaders);
151+
148152
return {
149153
usersFactory,
150154
workspacesFactory,
151155
projectsFactory,
152156
plansFactory,
153157
businessOperationsFactory,
158+
releasesFactory,
154159
};
155160
}
156161

src/models/releaseFactory.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { Collection, Db } from 'mongodb';
2+
import { ReleaseDBScheme, SourceMapFileChunk } from '@hawk.so/types';
3+
import DataLoaders from '../dataLoaders';
4+
5+
interface ReleaseWithFileDetails extends ReleaseDBScheme {
6+
fileDetails?: SourceMapFileChunk[];
7+
}
8+
9+
export default class ReleasesFactory {
10+
/**
11+
* Releases collection
12+
*/
13+
private collection: Collection<ReleaseDBScheme>;
14+
15+
/**
16+
* DataLoader for releases
17+
*/
18+
private dataLoaders: DataLoaders;
19+
20+
/**
21+
* Creates an instance of the releases factory
22+
* @param dbConnection - database connection
23+
* @param dataLoaders - DataLoaders instance for request batching
24+
*/
25+
constructor(dbConnection: Db, dataLoaders: DataLoaders) {
26+
this.collection = dbConnection.collection('releases');
27+
this.dataLoaders = dataLoaders;
28+
}
29+
30+
/**
31+
* Get releases by project identifier with file sizes
32+
* @param projectId - project identifier
33+
*/
34+
public async findManyByProjectId(projectId: string): Promise<ReleaseDBScheme[]> {
35+
try {
36+
const releases = await this.collection.aggregate<ReleaseWithFileDetails>([
37+
{
38+
$match: {
39+
projectId: projectId,
40+
},
41+
},
42+
{
43+
$lookup: {
44+
from: 'releases.files',
45+
let: { fileIds: '$files._id' },
46+
pipeline: [
47+
{
48+
$match: {
49+
$expr: {
50+
$in: ['$_id', '$$fileIds'],
51+
},
52+
},
53+
},
54+
{
55+
$project: {
56+
_id: 1,
57+
length: 1,
58+
chunkSize: 1,
59+
},
60+
},
61+
],
62+
as: 'fileDetails',
63+
},
64+
},
65+
]).toArray();
66+
67+
return releases.map(release => this.enrichReleaseWithFileSizes(release));
68+
} catch (error) {
69+
console.error(`[ReleasesFactory] Error in findManyByProjectId:`, error);
70+
throw error;
71+
}
72+
}
73+
74+
/**
75+
* Enriches release with file sizes from file details
76+
* @param release - release with file details
77+
* @returns enriched release
78+
*/
79+
private enrichReleaseWithFileSizes(release: ReleaseWithFileDetails): ReleaseDBScheme {
80+
const fileDetailsMap = new Map(
81+
release.fileDetails?.map(detail => [detail._id.toString(), detail.length]) || []
82+
);
83+
84+
return {
85+
...release,
86+
files: release.files?.map(file => ({
87+
...file,
88+
size: fileDetailsMap.get(file._id?.toString() || '') || 0,
89+
})),
90+
};
91+
}
92+
}

src/resolvers/project.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,26 @@ module.exports = {
265265
return event;
266266
},
267267

268+
/**
269+
* Returns project releases
270+
*
271+
* @param {ProjectDBScheme} project - result of parent resolver
272+
* @param {ContextFactories} context - Global GraphQL context with factories
273+
* @returns {Promise<Release[]>}
274+
*/
275+
async releases(project, _, { factories }) {
276+
if (!project._id) {
277+
throw new Error('projectId is required to fetch releases');
278+
}
279+
280+
try {
281+
return await factories.releasesFactory.findManyByProjectId(project._id.toString());
282+
} catch (error) {
283+
console.error('Error fetching releases:', error);
284+
throw new Error('Failed to get the releases');
285+
}
286+
},
287+
268288
/**
269289
* Find project events
270290
*

src/typeDefs/event.ts

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,6 @@ type SourceCodeLine {
1616
content: String
1717
}
1818
19-
"""
20-
Release commit
21-
"""
22-
type Commit {
23-
"""
24-
Hash of the commit
25-
"""
26-
hash: String!
27-
28-
"""
29-
Commit author
30-
"""
31-
author: String!
32-
33-
"""
34-
Commit title
35-
"""
36-
title: String!
37-
38-
"""
39-
Commit creation date
40-
"""
41-
date: DateTime!
42-
}
43-
44-
"""
45-
Release data of the corresponding event
46-
"""
47-
type Release {
48-
"""
49-
Release name
50-
"""
51-
releaseName: String! @renameFrom(name: "release")
52-
53-
"""
54-
Release commits
55-
"""
56-
commits: [Commit!]!
57-
}
58-
5919
"""
6020
Event backtrace representation
6121
"""

src/typeDefs/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import workspaceMutations from './workspaceMutations';
1717
import chart from './chart';
1818
import plans from './plans';
1919
import seed from './seed';
20+
import release from './release';
2021
import isE2E from '../utils/isE2E';
2122

2223
const rootSchema = gql`
@@ -100,6 +101,7 @@ const typeDefinitions = [
100101
workspaceMutations,
101102
chart,
102103
plans,
104+
release,
103105
projectEventGroupingPattern,
104106
projectEventGroupingPatternMutations,
105107
];

src/typeDefs/project.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ type Project {
109109
skip: Int = 0
110110
): [Event!]
111111
112+
"""
113+
Project releases
114+
"""
115+
releases: [Release!]!
116+
112117
"""
113118
Returns recent events grouped by day
114119
"""

src/typeDefs/release.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { gql } from 'apollo-server-express';
2+
3+
export default gql`
4+
"""
5+
Source map file details
6+
"""
7+
type SourceMapData {
8+
"""
9+
Source map filename
10+
"""
11+
mapFileName: String!
12+
13+
"""
14+
Original source filename
15+
"""
16+
originFileName: String!
17+
18+
"""
19+
File size in bytes
20+
"""
21+
size: Int!
22+
}
23+
24+
"""
25+
Release commit
26+
"""
27+
type Commit {
28+
"""
29+
Hash of the commit
30+
"""
31+
hash: String!
32+
33+
"""
34+
Commit author
35+
"""
36+
author: String!
37+
38+
"""
39+
Commit title
40+
"""
41+
title: String!
42+
43+
"""
44+
Commit creation date
45+
"""
46+
date: DateTime!
47+
}
48+
49+
"""
50+
Release data
51+
"""
52+
type Release {
53+
"""
54+
Release ID
55+
"""
56+
id: ID! @renameFrom(name: "_id")
57+
58+
"""
59+
Release name
60+
"""
61+
releaseName: String! @renameFrom(name: "release")
62+
63+
"""
64+
Project ID associated with the release
65+
"""
66+
projectId: ID!
67+
68+
"""
69+
Release commits
70+
"""
71+
commits: [Commit!]!
72+
73+
"""
74+
Source maps associated with the release
75+
"""
76+
files: [SourceMapData!]!
77+
}
78+
`;

src/types/graphql.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import UsersFactory from '../models/usersFactory';
22
import WorkspacesFactory from '../models/workspacesFactory';
33
import { GraphQLField } from 'graphql';
44
import ProjectsFactory from '../models/projectsFactory';
5-
// import Accounting from 'codex-accounting-sdk';
65
import PlansFactory from '../models/plansFactory';
76
import BusinessOperationsFactory from '../models/businessOperationsFactory';
7+
import ReleasesFactory from '../models/releaseFactory';
88

99
/**
1010
* Resolver's Context argument
@@ -79,6 +79,11 @@ export interface ContextFactories {
7979
* Allows to work with the Business Operations models
8080
*/
8181
businessOperationsFactory: BusinessOperationsFactory;
82+
83+
/**
84+
* Allows to work with releases
85+
*/
86+
releasesFactory: ReleasesFactory;
8287
}
8388

8489
/**

0 commit comments

Comments
 (0)