Skip to content

Commit 00ee1f1

Browse files
committed
Merge branch 'master' of github.com:codex-team/hawk.api.nodejs into feat/opt-dupl
2 parents 076aea4 + b86fab1 commit 00ee1f1

4 files changed

Lines changed: 240 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ To execute the request, enter it in the input field on the left and click on the
2525
On the right side you will see the result of the query.
2626

2727
## GraphQL Voyager
28-
You can view API Schema visualization in `/voyager` page in your browser. To see current production schema go to [here](https://api.beta.hawk.so/voyager)
28+
You can view API Schema visualization in `/voyager` page in your browser.
29+
To see current production schema go to [here](https://api.beta.hawk.so/voyager)
2930

3031
## Migrations
3132

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.2.8",
3+
"version": "1.2.10",
44
"main": "index.ts",
55
"license": "BUSL-1.1",
66
"scripts": {

src/resolvers/project.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,79 @@ module.exports = {
177177
}
178178
},
179179

180+
/**
181+
* Update project rate limits settings
182+
*
183+
* @param {ResolverObj} _obj
184+
* @param {string} id - project id
185+
* @param {Object | null} rateLimitSettings - rate limit settings (null to remove)
186+
* @param {UserInContext} user - current authorized user {@see ../index.js}
187+
* @param {ContextFactories} factories - factories for working with models
188+
*
189+
* @returns {Project}
190+
*/
191+
async updateProjectRateLimits(_obj, { id, rateLimitSettings }, { user, factories }) {
192+
const project = await factories.projectsFactory.findById(id);
193+
194+
if (!project) {
195+
throw new ApolloError('There is no project with that id');
196+
}
197+
198+
if (project.workspaceId.toString() === '6213b6a01e6281087467cc7a') {
199+
throw new ApolloError('Unable to update demo project');
200+
}
201+
202+
// Validate rate limit settings if provided
203+
if (rateLimitSettings) {
204+
const { N, T } = rateLimitSettings;
205+
206+
// Validate that N and T exist
207+
if (!N || !T) {
208+
throw new UserInputError(
209+
'Rate limit settings must contain both N (threshold) and T (period) fields.'
210+
);
211+
}
212+
213+
// Validate N (threshold) - must be positive integer > 0
214+
if (typeof N !== 'number' || !Number.isInteger(N) || N <= 0) {
215+
throw new UserInputError(
216+
'Invalid rate limit threshold. Must be a positive integer greater than 0.'
217+
);
218+
}
219+
220+
// Validate T (period) - must be positive integer >= 60 (1 minute)
221+
if (typeof T !== 'number' || !Number.isInteger(T) || T < 60) {
222+
throw new UserInputError(
223+
'Invalid rate limit period. Must be a positive integer greater than or equal to 60 seconds.'
224+
);
225+
}
226+
227+
// Validate reasonable maximums (prevent extremely large values)
228+
const MAX_THRESHOLD = 1000000000; // 1 billion
229+
const MAX_PERIOD = 60 * 60 * 24 * 31; // 1 month in seconds
230+
231+
if (N > MAX_THRESHOLD) {
232+
throw new UserInputError(
233+
`Rate limit threshold cannot exceed ${MAX_THRESHOLD.toLocaleString()}.`
234+
);
235+
}
236+
237+
if (T > MAX_PERIOD) {
238+
throw new UserInputError(
239+
`Rate limit period cannot exceed ${MAX_PERIOD.toLocaleString()} seconds (1 month).`
240+
);
241+
}
242+
}
243+
244+
try {
245+
return project.updateProject({
246+
rateLimitSettings: rateLimitSettings || null,
247+
});
248+
} catch (err) {
249+
throw new ApolloError('Failed to update project rate limit settings', { originalError: err });
250+
}
251+
},
252+
180253
/**
181254
* Generates new project integration token by id
182255
*
@@ -374,5 +447,80 @@ module.exports = {
374447

375448
return factory.findChartData(days, timezoneOffset);
376449
},
450+
451+
/**
452+
* Returns list of not archived releases with number of events that were introduced in this release
453+
* We count events as new, cause payload.release only contain the same release name if the event is original
454+
*
455+
* @param {ProjectDBScheme} project - result of parent resolver
456+
* @returns {Promise<Array<{release: string, timestamp: number, newEventsCount: number, commitsCount: number, filesCount: number}>>}
457+
*/
458+
async releases(project) {
459+
const releasesCollection = mongo.databases.events.collection('releases');
460+
461+
const pipeline = [
462+
{ $match: { projectId: project._id.toString() } },
463+
{
464+
$project: {
465+
release: {
466+
$convert: {
467+
input: '$release',
468+
to: 'string',
469+
onError: '',
470+
onNull: '',
471+
},
472+
},
473+
commitsCount: { $size: { $ifNull: ['$commits', [] ] } },
474+
filesCount: { $size: { $ifNull: ['$files', [] ] } },
475+
_releaseIdSec: { $floor: { $divide: [ { $toLong: { $toDate: '$_id' } }, 1000] } },
476+
},
477+
},
478+
{
479+
$lookup: {
480+
from: 'events:' + project._id,
481+
let: { rel: '$release' },
482+
pipeline: [
483+
{
484+
$match: {
485+
$expr: {
486+
$eq: [ {
487+
$convert: {
488+
input: '$payload.release',
489+
to: 'string',
490+
onError: '',
491+
onNull: '',
492+
},
493+
}, '$$rel'],
494+
},
495+
},
496+
},
497+
{
498+
$group: {
499+
_id: null,
500+
count: { $sum: 1 },
501+
},
502+
},
503+
],
504+
as: 'eventAgg',
505+
},
506+
},
507+
{
508+
$project: {
509+
_id: 0,
510+
release: 1,
511+
commitsCount: 1,
512+
filesCount: 1,
513+
newEventsCount: { $ifNull: [ { $arrayElemAt: ['$eventAgg.count', 0] }, 0] },
514+
timestamp: '$_releaseIdSec',
515+
},
516+
},
517+
{ $sort: { _id: -1 } },
518+
];
519+
520+
const cursor = releasesCollection.aggregate(pipeline);
521+
const result = await cursor.toArray();
522+
523+
return result;
524+
},
377525
},
378526
};

src/typeDefs/project.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
11
import { gql } from 'apollo-server-express';
22

33
export default gql`
4+
"""
5+
Rate limits configuration input
6+
"""
7+
input RateLimitSettingsInput {
8+
"""
9+
Rate limit threshold (N events)
10+
"""
11+
N: Int!
12+
13+
"""
14+
Rate limit period in seconds (T seconds)
15+
"""
16+
T: Int!
17+
}
18+
19+
"""
20+
Rate limits configuration
21+
"""
22+
type RateLimitSettings {
23+
"""
24+
Rate limit threshold (N events)
25+
"""
26+
N: Int!
27+
28+
"""
29+
Rate limit period in seconds (T seconds)
30+
"""
31+
T: Int!
32+
}
433
534
"""
635
Possible events order
@@ -96,6 +125,36 @@ input EventsFiltersInput {
96125
ignored: Boolean
97126
}
98127
128+
"""
129+
Aggregated release info for project events
130+
"""
131+
type ProjectRelease {
132+
"""
133+
Release identifier
134+
"""
135+
release: String!
136+
137+
"""
138+
First occurrence timestamp
139+
"""
140+
timestamp: Float!
141+
142+
"""
143+
Number of new events introduced in this release
144+
"""
145+
newEventsCount: Int!
146+
147+
"""
148+
Number of commits in this release
149+
"""
150+
commitsCount: Int!
151+
152+
"""
153+
Number of files in this release
154+
"""
155+
filesCount: Int!
156+
}
157+
99158
"""
100159
Respose object with updated project and his id
101160
"""
@@ -253,6 +312,16 @@ type Project {
253312
Event grouping patterns
254313
"""
255314
eventGroupingPatterns: [ProjectEventGroupingPattern]
315+
316+
"""
317+
Rate limits configuration
318+
"""
319+
rateLimitSettings: RateLimitSettings
320+
321+
"""
322+
List of releases with unique events count, commits count and files count
323+
"""
324+
releases: [ProjectRelease!]!
256325
}
257326
258327
extend type Query {
@@ -305,6 +374,26 @@ extend type Mutation {
305374
Project image
306375
"""
307376
image: Upload @uploadImage
377+
378+
"""
379+
Rate limits configuration
380+
"""
381+
rateLimitSettings: RateLimitSettingsInput
382+
): Project! @requireAdmin
383+
384+
"""
385+
Update project rate limits settings
386+
"""
387+
updateProjectRateLimits(
388+
"""
389+
What project to update
390+
"""
391+
id: ID!
392+
393+
"""
394+
Rate limits configuration. Pass null to remove rate limits.
395+
"""
396+
rateLimitSettings: RateLimitSettingsInput
308397
): Project! @requireAdmin
309398
310399
"""

0 commit comments

Comments
 (0)