@juki-team/commons 0.1.45 → 0.1.47
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/dist/cjs/index.cjs +115 -9
- package/dist/esm/index.js +108 -9
- package/dist/types/constants/websocket.d.ts +18 -5
- package/dist/types/dto/socket.d.ts +37 -3
- package/dist/types/helpers/socket.d.ts +12 -1
- package/dist/types/types/socket.d.ts +23 -2
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -594,6 +594,8 @@ exports.WebSocketSubscriptionEvent = void 0;
|
|
|
594
594
|
WebSocketSubscriptionEvent["UNSUBSCRIBE_CONTEST_CHANGES"] = "UNSUBSCRIBE_CONTEST_CHANGES";
|
|
595
595
|
WebSocketSubscriptionEvent["SUBSCRIBE_USER_NOTIFICATION"] = "SUBSCRIBE_USER_NOTIFICATION";
|
|
596
596
|
WebSocketSubscriptionEvent["UNSUBSCRIBE_USER_NOTIFICATION"] = "UNSUBSCRIBE_USER_NOTIFICATION";
|
|
597
|
+
WebSocketSubscriptionEvent["SUBSCRIBE_SUBMISSIONS_JUDGING"] = "SUBSCRIBE_SUBMISSIONS_JUDGING";
|
|
598
|
+
WebSocketSubscriptionEvent["UNSUBSCRIBE_SUBMISSIONS_JUDGING"] = "UNSUBSCRIBE_SUBMISSIONS_JUDGING";
|
|
597
599
|
})(exports.WebSocketSubscriptionEvent || (exports.WebSocketSubscriptionEvent = {}));
|
|
598
600
|
exports.WebSocketResponseEvent = void 0;
|
|
599
601
|
(function (WebSocketResponseEvent) {
|
|
@@ -612,6 +614,7 @@ exports.WebSocketResponseEvent = void 0;
|
|
|
612
614
|
WebSocketResponseEvent["USER_NOTIFICATION_"] = "USER_NOTIFICATION_";
|
|
613
615
|
WebSocketResponseEvent["USER_NOTIFICATION_SUBMISSION"] = "USER_NOTIFICATION_SUBMISSION";
|
|
614
616
|
WebSocketResponseEvent["USER_NOTIFICATION_CONTEST_CLARIFICATION"] = "USER_NOTIFICATION_CONTEST_CLARIFICATION";
|
|
617
|
+
WebSocketResponseEvent["SUBMISSION_JUDGING"] = "SUBMISSION_JUDGING";
|
|
615
618
|
})(exports.WebSocketResponseEvent || (exports.WebSocketResponseEvent = {}));
|
|
616
619
|
/**
|
|
617
620
|
* Roles of a contest, with the same names as the contest members in the DB.
|
|
@@ -627,6 +630,26 @@ exports.ContestChannelRole = void 0;
|
|
|
627
630
|
ContestChannelRole["GUESTS"] = "guests";
|
|
628
631
|
ContestChannelRole["SPECTATORS"] = "spectators";
|
|
629
632
|
})(exports.ContestChannelRole || (exports.ContestChannelRole = {}));
|
|
633
|
+
/**
|
|
634
|
+
* Roles of a problem, the ones computed by `getProblemAccessPermissions` (backend).
|
|
635
|
+
* Each role has its own Ably channel: `problem:{problemKey}:{role}` (see `CHANNEL_PROBLEM`).
|
|
636
|
+
*/
|
|
637
|
+
exports.ProblemChannelRole = void 0;
|
|
638
|
+
(function (ProblemChannelRole) {
|
|
639
|
+
ProblemChannelRole["ADMINISTRATORS"] = "administrators";
|
|
640
|
+
ProblemChannelRole["MANAGERS"] = "managers";
|
|
641
|
+
ProblemChannelRole["SPECTATORS"] = "spectators";
|
|
642
|
+
})(exports.ProblemChannelRole || (exports.ProblemChannelRole = {}));
|
|
643
|
+
/**
|
|
644
|
+
* Roles of the public channels of a user: `user:{userKey}:{role}` (see `CHANNEL_USER_ROLE`), by its public key
|
|
645
|
+
* (`getUserKey(nickname, organizationKey)`) like problems and contests.
|
|
646
|
+
* There are no per-user permissions: `spectators` is everyone who can list the submissions of the user.
|
|
647
|
+
* The private channel of the user is `user:{userId}` (see `CHANNEL_USER`).
|
|
648
|
+
*/
|
|
649
|
+
exports.UserChannelRole = void 0;
|
|
650
|
+
(function (UserChannelRole) {
|
|
651
|
+
UserChannelRole["SPECTATORS"] = "spectators";
|
|
652
|
+
})(exports.UserChannelRole || (exports.UserChannelRole = {}));
|
|
630
653
|
|
|
631
654
|
exports.DataViewMode = void 0;
|
|
632
655
|
(function (DataViewMode) {
|
|
@@ -2234,12 +2257,18 @@ const EMPTY_USER_PERMISSIONS = {
|
|
|
2234
2257
|
const segment = (value) => encodeURIComponent(value);
|
|
2235
2258
|
/** private: events of one session (code runs, requested data) */
|
|
2236
2259
|
const CHANNEL_SESSION = (sessionId) => `session:${segment(sessionId)}`;
|
|
2237
|
-
/** private: events of one user (own submissions while the contest is frozen, notifications) */
|
|
2260
|
+
/** private: events of one user (own submissions while the contest is frozen, notifications, own problem crawls) */
|
|
2238
2261
|
const CHANNEL_USER = (userId) => `user:${segment(userId)}`;
|
|
2262
|
+
/**
|
|
2263
|
+
* per role: public events of one user (judging of its submissions visible to everyone, see `UserChannelRole`).
|
|
2264
|
+
* By its public key (`getUserKey(nickname, organizationKey)`), the one the clients know; the private channel
|
|
2265
|
+
* (`CHANNEL_USER`) is by id. They never collide: the role channel has one more segment.
|
|
2266
|
+
*/
|
|
2267
|
+
const CHANNEL_USER_ROLE = (userKey, role) => `user:${segment(userKey)}:${role}`;
|
|
2239
2268
|
/** public: status of a submission when it is visible to everyone */
|
|
2240
2269
|
const CHANNEL_SUBMISSION = (submitId) => `submission:${segment(submitId)}`;
|
|
2241
|
-
/**
|
|
2242
|
-
const CHANNEL_PROBLEM = (problemKey) => `problem:${segment(problemKey)}`;
|
|
2270
|
+
/** per role: events of a problem for one role (judging of its submissions, see `ProblemChannelRole`) */
|
|
2271
|
+
const CHANNEL_PROBLEM = (problemKey, role) => `problem:${segment(problemKey)}:${role}`;
|
|
2243
2272
|
/** per role: events of a contest for one role (and the roles above it, see `ContestChannelRole`) */
|
|
2244
2273
|
const CHANNEL_CONTEST = (contestKey, role) => `contest:${segment(contestKey)}:${role}`;
|
|
2245
2274
|
/**
|
|
@@ -2250,9 +2279,12 @@ const CHANNEL_CONTEST = (contestKey, role) => `contest:${segment(contestKey)}:${
|
|
|
2250
2279
|
* The identity of a member is its `clientId` (`sessionId|uiId`, verified by Ably), never its data.
|
|
2251
2280
|
*/
|
|
2252
2281
|
const CHANNEL_ORGANIZATION_PRESENCE = (organizationKey) => `presence:organization:${segment(organizationKey)}`;
|
|
2253
|
-
// capability resources (wildcards) of the public channels
|
|
2282
|
+
// capability resources (wildcards) of the public channels.
|
|
2283
|
+
// Ably: a trailing `*` replaces one or more segments, any other `*` exactly one segment. So `user:*:spectators`
|
|
2284
|
+
// matches every `user:{userKey}:spectators` but never the private `user:{userId}`.
|
|
2285
|
+
// There is no public `problem:*`: it would also match the role channels `problem:{problemKey}:{role}`.
|
|
2254
2286
|
const CHANNEL_SUBMISSION_ALL = 'submission:*';
|
|
2255
|
-
const
|
|
2287
|
+
const CHANNEL_USER_SPECTATORS_ALL = `user:*:${exports.UserChannelRole.SPECTATORS}`;
|
|
2256
2288
|
/**
|
|
2257
2289
|
* Returns the contest and role of a `CHANNEL_CONTEST` channel name, or null if it is not one.
|
|
2258
2290
|
*/
|
|
@@ -2263,6 +2295,16 @@ const getContestChannelParams = (channel) => {
|
|
|
2263
2295
|
}
|
|
2264
2296
|
return { contestKey: decodeURIComponent(contestKey), role: role };
|
|
2265
2297
|
};
|
|
2298
|
+
/**
|
|
2299
|
+
* Returns the problem and role of a `CHANNEL_PROBLEM` channel name, or null if it is not one.
|
|
2300
|
+
*/
|
|
2301
|
+
const getProblemChannelParams = (channel) => {
|
|
2302
|
+
const [namespace, problemKey, role, ...rest] = channel.split(':');
|
|
2303
|
+
if (namespace !== 'problem' || !problemKey || rest.length || !Object.values(exports.ProblemChannelRole).includes(role)) {
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2306
|
+
return { problemKey: decodeURIComponent(problemKey), role: role };
|
|
2307
|
+
};
|
|
2266
2308
|
|
|
2267
2309
|
const NEW_PAGE_SHEET = () => ({
|
|
2268
2310
|
id: uuid.v4(),
|
|
@@ -2974,6 +3016,23 @@ const isUnsubscribeUserNotificationWebSocketEventDto = (event) => {
|
|
|
2974
3016
|
return isWebsocketSubscription(event)
|
|
2975
3017
|
&& event?.event === exports.WebSocketSubscriptionEvent.UNSUBSCRIBE_USER_NOTIFICATION;
|
|
2976
3018
|
};
|
|
3019
|
+
const isNonEmptyString = (value) => typeof value === 'string' && !!value;
|
|
3020
|
+
/**
|
|
3021
|
+
* Exactly one of `problemKey` / `contestKey` / `userKey` is set.
|
|
3022
|
+
*/
|
|
3023
|
+
const isSubmissionsJudgingScope = (scope) => {
|
|
3024
|
+
return [scope?.problemKey, scope?.contestKey, scope?.userKey].filter(isNonEmptyString).length === 1;
|
|
3025
|
+
};
|
|
3026
|
+
const isSubscribeSubmissionsJudgingWebSocketEventDto = (event) => {
|
|
3027
|
+
return isWebsocketSubscription(event)
|
|
3028
|
+
&& event?.event === exports.WebSocketSubscriptionEvent.SUBSCRIBE_SUBMISSIONS_JUDGING
|
|
3029
|
+
&& isSubmissionsJudgingScope(event);
|
|
3030
|
+
};
|
|
3031
|
+
const isUnsubscribeSubmissionsJudgingWebSocketEventDto = (event) => {
|
|
3032
|
+
return isWebsocketSubscription(event)
|
|
3033
|
+
&& event?.event === exports.WebSocketSubscriptionEvent.UNSUBSCRIBE_SUBMISSIONS_JUDGING
|
|
3034
|
+
&& isSubmissionsJudgingScope(event);
|
|
3035
|
+
};
|
|
2977
3036
|
// is WebSocketResponseEventDto, CHANNEL_SUBSCRIBE_CLIENT
|
|
2978
3037
|
const isWebSocketResponseEventDto = (event) => {
|
|
2979
3038
|
return Object.values(exports.WebSocketResponseEvent).includes(event?.event)
|
|
@@ -2995,6 +3054,18 @@ const isSubmissionRunStatusMessageWebSocketResponseEventDto = (event) => {
|
|
|
2995
3054
|
&& event?.verdict in exports.ProblemVerdict
|
|
2996
3055
|
&& typeof event?.points === 'number';
|
|
2997
3056
|
};
|
|
3057
|
+
const isSubmissionJudgingWebSocketResponseEventDto = (event) => {
|
|
3058
|
+
return isWebSocketResponseEventDto(event)
|
|
3059
|
+
&& event?.event === exports.WebSocketResponseEvent.SUBMISSION_JUDGING
|
|
3060
|
+
&& isNonEmptyString(event?.submitId)
|
|
3061
|
+
&& isNonEmptyString(event?.problemKey)
|
|
3062
|
+
&& (event?.contestKey === null || isNonEmptyString(event?.contestKey))
|
|
3063
|
+
&& isNonEmptyString(event?.userKey)
|
|
3064
|
+
&& typeof event?.judging === 'boolean'
|
|
3065
|
+
&& event?.status in exports.SubmissionRunStatus
|
|
3066
|
+
&& event?.verdict in exports.ProblemVerdict
|
|
3067
|
+
&& typeof event?.points === 'number';
|
|
3068
|
+
};
|
|
2998
3069
|
const isUserMessageWebSocketResponseEventDto = (event) => {
|
|
2999
3070
|
return isWebSocketResponseEventDto(event)
|
|
3000
3071
|
&& event?.event === exports.WebSocketResponseEvent.USER_MESSAGE
|
|
@@ -3100,6 +3171,11 @@ const getWebSocketResponseEventKeyOfResponse = (response, clientId) => {
|
|
|
3100
3171
|
if (isUserNotificationWebSocketResponseEventDto(response)) {
|
|
3101
3172
|
return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.USER_NOTIFICATION_, clientId, '*');
|
|
3102
3173
|
}
|
|
3174
|
+
if (isSubmissionJudgingWebSocketResponseEventDto(response)) {
|
|
3175
|
+
// one key for every table: the same event is published on several channels (problem / contest / user) and the
|
|
3176
|
+
// client deduplicates it, so it is dispatched once; each table filters by the submissions it lists
|
|
3177
|
+
return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.SUBMISSION_JUDGING, clientId, '*');
|
|
3178
|
+
}
|
|
3103
3179
|
return null;
|
|
3104
3180
|
};
|
|
3105
3181
|
/**
|
|
@@ -3111,12 +3187,23 @@ const getChannelOfWebSocketSubscribeEvent = (event) => {
|
|
|
3111
3187
|
if (isSubscribeSubmissionRunStatusWebSocketEventDto(event)) {
|
|
3112
3188
|
return CHANNEL_SUBMISSION(event.submitId);
|
|
3113
3189
|
}
|
|
3114
|
-
|
|
3115
|
-
return CHANNEL_PROBLEM(event.problemKey);
|
|
3116
|
-
}
|
|
3190
|
+
// SUBSCRIBE_PROBLEM_CRAWLED: the event arrives by the private user channel of who requested the crawl
|
|
3117
3191
|
if (isSubscribeContestChangesWebSocketEventDto(event) || isSubscribeSubmissionsCrawlWebSocketEventDto(event)) {
|
|
3118
3192
|
return CHANNEL_CONTEST(event.contestKey, exports.ContestChannelRole.SPECTATORS);
|
|
3119
3193
|
}
|
|
3194
|
+
if (isSubscribeSubmissionsJudgingWebSocketEventDto(event)) {
|
|
3195
|
+
// the judging start/end is published on the spectators channel when it is visible to everyone; the hidden ones
|
|
3196
|
+
// (frozen / quiet contests) arrive by the role channels of the contest (managers) and the private user channel
|
|
3197
|
+
if ('problemKey' in event && event.problemKey) {
|
|
3198
|
+
return CHANNEL_PROBLEM(event.problemKey, exports.ProblemChannelRole.SPECTATORS);
|
|
3199
|
+
}
|
|
3200
|
+
if ('contestKey' in event && event.contestKey) {
|
|
3201
|
+
return CHANNEL_CONTEST(event.contestKey, exports.ContestChannelRole.SPECTATORS);
|
|
3202
|
+
}
|
|
3203
|
+
if ('userKey' in event && event.userKey) {
|
|
3204
|
+
return CHANNEL_USER_ROLE(event.userKey, exports.UserChannelRole.SPECTATORS);
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3120
3207
|
return null;
|
|
3121
3208
|
};
|
|
3122
3209
|
/**
|
|
@@ -3126,6 +3213,18 @@ const getContestKeyOfWebSocketSubscribeEvent = (event) => {
|
|
|
3126
3213
|
if (isSubscribeContestChangesWebSocketEventDto(event) || isSubscribeSubmissionsCrawlWebSocketEventDto(event)) {
|
|
3127
3214
|
return event.contestKey;
|
|
3128
3215
|
}
|
|
3216
|
+
if (isSubscribeSubmissionsJudgingWebSocketEventDto(event) && 'contestKey' in event && event.contestKey) {
|
|
3217
|
+
return event.contestKey;
|
|
3218
|
+
}
|
|
3219
|
+
return null;
|
|
3220
|
+
};
|
|
3221
|
+
/**
|
|
3222
|
+
* Problem whose role channels the client needs (the API grants them when the token is requested with this problem).
|
|
3223
|
+
*/
|
|
3224
|
+
const getProblemKeyOfWebSocketSubscribeEvent = (event) => {
|
|
3225
|
+
if (isSubscribeSubmissionsJudgingWebSocketEventDto(event) && 'problemKey' in event && event.problemKey) {
|
|
3226
|
+
return event.problemKey;
|
|
3227
|
+
}
|
|
3129
3228
|
return null;
|
|
3130
3229
|
};
|
|
3131
3230
|
|
|
@@ -3327,11 +3426,12 @@ exports.AssignmentType = AssignmentType;
|
|
|
3327
3426
|
exports.CHANNEL_CONTEST = CHANNEL_CONTEST;
|
|
3328
3427
|
exports.CHANNEL_ORGANIZATION_PRESENCE = CHANNEL_ORGANIZATION_PRESENCE;
|
|
3329
3428
|
exports.CHANNEL_PROBLEM = CHANNEL_PROBLEM;
|
|
3330
|
-
exports.CHANNEL_PROBLEM_ALL = CHANNEL_PROBLEM_ALL;
|
|
3331
3429
|
exports.CHANNEL_SESSION = CHANNEL_SESSION;
|
|
3332
3430
|
exports.CHANNEL_SUBMISSION = CHANNEL_SUBMISSION;
|
|
3333
3431
|
exports.CHANNEL_SUBMISSION_ALL = CHANNEL_SUBMISSION_ALL;
|
|
3334
3432
|
exports.CHANNEL_USER = CHANNEL_USER;
|
|
3433
|
+
exports.CHANNEL_USER_ROLE = CHANNEL_USER_ROLE;
|
|
3434
|
+
exports.CHANNEL_USER_SPECTATORS_ALL = CHANNEL_USER_SPECTATORS_ALL;
|
|
3335
3435
|
exports.CODE_LANGUAGE = CODE_LANGUAGE;
|
|
3336
3436
|
exports.CONTEST_ROLE = CONTEST_ROLE;
|
|
3337
3437
|
exports.CONTEST_STATUS = CONTEST_STATUS;
|
|
@@ -3470,8 +3570,10 @@ exports.getMonths = getMonths;
|
|
|
3470
3570
|
exports.getParamsOfClientId = getParamsOfClientId;
|
|
3471
3571
|
exports.getParamsOfUserKey = getParamsOfUserKey;
|
|
3472
3572
|
exports.getPlainText = getPlainText;
|
|
3573
|
+
exports.getProblemChannelParams = getProblemChannelParams;
|
|
3473
3574
|
exports.getProblemCommentKey = getProblemCommentKey;
|
|
3474
3575
|
exports.getProblemJudgeKey = getProblemJudgeKey;
|
|
3576
|
+
exports.getProblemKeyOfWebSocketSubscribeEvent = getProblemKeyOfWebSocketSubscribeEvent;
|
|
3475
3577
|
exports.getRandomString = getRandomString;
|
|
3476
3578
|
exports.getSeconds = getSeconds;
|
|
3477
3579
|
exports.getSummaryWorksheetsInPages = getSummaryWorksheetsInPages;
|
|
@@ -3532,14 +3634,17 @@ exports.isSendDataRunCommandWebSocketResponseEventDto = isSendDataRunCommandWebS
|
|
|
3532
3634
|
exports.isSendDataSsmSessionsListWebSocketResponseEventDto = isSendDataSsmSessionsListWebSocketResponseEventDto;
|
|
3533
3635
|
exports.isSendDataWebSocketResponseEventDto = isSendDataWebSocketResponseEventDto;
|
|
3534
3636
|
exports.isStringJson = isStringJson;
|
|
3637
|
+
exports.isSubmissionJudgingWebSocketResponseEventDto = isSubmissionJudgingWebSocketResponseEventDto;
|
|
3535
3638
|
exports.isSubmissionRunStatusMessageWebSocketResponseEventDto = isSubmissionRunStatusMessageWebSocketResponseEventDto;
|
|
3536
3639
|
exports.isSubmissionsCrawlWebSocketResponseEventDto = isSubmissionsCrawlWebSocketResponseEventDto;
|
|
3640
|
+
exports.isSubmissionsJudgingScope = isSubmissionsJudgingScope;
|
|
3537
3641
|
exports.isSubscribeCodeRunStatusWebSocketEventDto = isSubscribeCodeRunStatusWebSocketEventDto;
|
|
3538
3642
|
exports.isSubscribeContestChangesWebSocketEventDto = isSubscribeContestChangesWebSocketEventDto;
|
|
3539
3643
|
exports.isSubscribeGetDataWebSocketEventDto = isSubscribeGetDataWebSocketEventDto;
|
|
3540
3644
|
exports.isSubscribeProblemCrawledWebSocketEventDto = isSubscribeProblemCrawledWebSocketEventDto;
|
|
3541
3645
|
exports.isSubscribeSubmissionRunStatusWebSocketEventDto = isSubscribeSubmissionRunStatusWebSocketEventDto;
|
|
3542
3646
|
exports.isSubscribeSubmissionsCrawlWebSocketEventDto = isSubscribeSubmissionsCrawlWebSocketEventDto;
|
|
3647
|
+
exports.isSubscribeSubmissionsJudgingWebSocketEventDto = isSubscribeSubmissionsJudgingWebSocketEventDto;
|
|
3543
3648
|
exports.isSubscribeUserNotificationWebSocketEventDto = isSubscribeUserNotificationWebSocketEventDto;
|
|
3544
3649
|
exports.isTeamMember = isTeamMember;
|
|
3545
3650
|
exports.isToday = isToday;
|
|
@@ -3549,6 +3654,7 @@ exports.isUnsubscribeGetDataWebSocketEventDto = isUnsubscribeGetDataWebSocketEve
|
|
|
3549
3654
|
exports.isUnsubscribeProblemCrawledWebSocketEventDto = isUnsubscribeProblemCrawledWebSocketEventDto;
|
|
3550
3655
|
exports.isUnsubscribeSubmissionRunStatusWebSocketEventDto = isUnsubscribeSubmissionRunStatusWebSocketEventDto;
|
|
3551
3656
|
exports.isUnsubscribeSubmissionsCrawlWebSocketEventDto = isUnsubscribeSubmissionsCrawlWebSocketEventDto;
|
|
3657
|
+
exports.isUnsubscribeSubmissionsJudgingWebSocketEventDto = isUnsubscribeSubmissionsJudgingWebSocketEventDto;
|
|
3552
3658
|
exports.isUnsubscribeUserNotificationWebSocketEventDto = isUnsubscribeUserNotificationWebSocketEventDto;
|
|
3553
3659
|
exports.isUserMember = isUserMember;
|
|
3554
3660
|
exports.isUserMessageWebSocketResponseEventDto = isUserMessageWebSocketResponseEventDto;
|
package/dist/esm/index.js
CHANGED
|
@@ -592,6 +592,8 @@ var WebSocketSubscriptionEvent;
|
|
|
592
592
|
WebSocketSubscriptionEvent["UNSUBSCRIBE_CONTEST_CHANGES"] = "UNSUBSCRIBE_CONTEST_CHANGES";
|
|
593
593
|
WebSocketSubscriptionEvent["SUBSCRIBE_USER_NOTIFICATION"] = "SUBSCRIBE_USER_NOTIFICATION";
|
|
594
594
|
WebSocketSubscriptionEvent["UNSUBSCRIBE_USER_NOTIFICATION"] = "UNSUBSCRIBE_USER_NOTIFICATION";
|
|
595
|
+
WebSocketSubscriptionEvent["SUBSCRIBE_SUBMISSIONS_JUDGING"] = "SUBSCRIBE_SUBMISSIONS_JUDGING";
|
|
596
|
+
WebSocketSubscriptionEvent["UNSUBSCRIBE_SUBMISSIONS_JUDGING"] = "UNSUBSCRIBE_SUBMISSIONS_JUDGING";
|
|
595
597
|
})(WebSocketSubscriptionEvent || (WebSocketSubscriptionEvent = {}));
|
|
596
598
|
var WebSocketResponseEvent;
|
|
597
599
|
(function (WebSocketResponseEvent) {
|
|
@@ -610,6 +612,7 @@ var WebSocketResponseEvent;
|
|
|
610
612
|
WebSocketResponseEvent["USER_NOTIFICATION_"] = "USER_NOTIFICATION_";
|
|
611
613
|
WebSocketResponseEvent["USER_NOTIFICATION_SUBMISSION"] = "USER_NOTIFICATION_SUBMISSION";
|
|
612
614
|
WebSocketResponseEvent["USER_NOTIFICATION_CONTEST_CLARIFICATION"] = "USER_NOTIFICATION_CONTEST_CLARIFICATION";
|
|
615
|
+
WebSocketResponseEvent["SUBMISSION_JUDGING"] = "SUBMISSION_JUDGING";
|
|
613
616
|
})(WebSocketResponseEvent || (WebSocketResponseEvent = {}));
|
|
614
617
|
/**
|
|
615
618
|
* Roles of a contest, with the same names as the contest members in the DB.
|
|
@@ -625,6 +628,26 @@ var ContestChannelRole;
|
|
|
625
628
|
ContestChannelRole["GUESTS"] = "guests";
|
|
626
629
|
ContestChannelRole["SPECTATORS"] = "spectators";
|
|
627
630
|
})(ContestChannelRole || (ContestChannelRole = {}));
|
|
631
|
+
/**
|
|
632
|
+
* Roles of a problem, the ones computed by `getProblemAccessPermissions` (backend).
|
|
633
|
+
* Each role has its own Ably channel: `problem:{problemKey}:{role}` (see `CHANNEL_PROBLEM`).
|
|
634
|
+
*/
|
|
635
|
+
var ProblemChannelRole;
|
|
636
|
+
(function (ProblemChannelRole) {
|
|
637
|
+
ProblemChannelRole["ADMINISTRATORS"] = "administrators";
|
|
638
|
+
ProblemChannelRole["MANAGERS"] = "managers";
|
|
639
|
+
ProblemChannelRole["SPECTATORS"] = "spectators";
|
|
640
|
+
})(ProblemChannelRole || (ProblemChannelRole = {}));
|
|
641
|
+
/**
|
|
642
|
+
* Roles of the public channels of a user: `user:{userKey}:{role}` (see `CHANNEL_USER_ROLE`), by its public key
|
|
643
|
+
* (`getUserKey(nickname, organizationKey)`) like problems and contests.
|
|
644
|
+
* There are no per-user permissions: `spectators` is everyone who can list the submissions of the user.
|
|
645
|
+
* The private channel of the user is `user:{userId}` (see `CHANNEL_USER`).
|
|
646
|
+
*/
|
|
647
|
+
var UserChannelRole;
|
|
648
|
+
(function (UserChannelRole) {
|
|
649
|
+
UserChannelRole["SPECTATORS"] = "spectators";
|
|
650
|
+
})(UserChannelRole || (UserChannelRole = {}));
|
|
628
651
|
|
|
629
652
|
var DataViewMode;
|
|
630
653
|
(function (DataViewMode) {
|
|
@@ -2232,12 +2255,18 @@ const EMPTY_USER_PERMISSIONS = {
|
|
|
2232
2255
|
const segment = (value) => encodeURIComponent(value);
|
|
2233
2256
|
/** private: events of one session (code runs, requested data) */
|
|
2234
2257
|
const CHANNEL_SESSION = (sessionId) => `session:${segment(sessionId)}`;
|
|
2235
|
-
/** private: events of one user (own submissions while the contest is frozen, notifications) */
|
|
2258
|
+
/** private: events of one user (own submissions while the contest is frozen, notifications, own problem crawls) */
|
|
2236
2259
|
const CHANNEL_USER = (userId) => `user:${segment(userId)}`;
|
|
2260
|
+
/**
|
|
2261
|
+
* per role: public events of one user (judging of its submissions visible to everyone, see `UserChannelRole`).
|
|
2262
|
+
* By its public key (`getUserKey(nickname, organizationKey)`), the one the clients know; the private channel
|
|
2263
|
+
* (`CHANNEL_USER`) is by id. They never collide: the role channel has one more segment.
|
|
2264
|
+
*/
|
|
2265
|
+
const CHANNEL_USER_ROLE = (userKey, role) => `user:${segment(userKey)}:${role}`;
|
|
2237
2266
|
/** public: status of a submission when it is visible to everyone */
|
|
2238
2267
|
const CHANNEL_SUBMISSION = (submitId) => `submission:${segment(submitId)}`;
|
|
2239
|
-
/**
|
|
2240
|
-
const CHANNEL_PROBLEM = (problemKey) => `problem:${segment(problemKey)}`;
|
|
2268
|
+
/** per role: events of a problem for one role (judging of its submissions, see `ProblemChannelRole`) */
|
|
2269
|
+
const CHANNEL_PROBLEM = (problemKey, role) => `problem:${segment(problemKey)}:${role}`;
|
|
2241
2270
|
/** per role: events of a contest for one role (and the roles above it, see `ContestChannelRole`) */
|
|
2242
2271
|
const CHANNEL_CONTEST = (contestKey, role) => `contest:${segment(contestKey)}:${role}`;
|
|
2243
2272
|
/**
|
|
@@ -2248,9 +2277,12 @@ const CHANNEL_CONTEST = (contestKey, role) => `contest:${segment(contestKey)}:${
|
|
|
2248
2277
|
* The identity of a member is its `clientId` (`sessionId|uiId`, verified by Ably), never its data.
|
|
2249
2278
|
*/
|
|
2250
2279
|
const CHANNEL_ORGANIZATION_PRESENCE = (organizationKey) => `presence:organization:${segment(organizationKey)}`;
|
|
2251
|
-
// capability resources (wildcards) of the public channels
|
|
2280
|
+
// capability resources (wildcards) of the public channels.
|
|
2281
|
+
// Ably: a trailing `*` replaces one or more segments, any other `*` exactly one segment. So `user:*:spectators`
|
|
2282
|
+
// matches every `user:{userKey}:spectators` but never the private `user:{userId}`.
|
|
2283
|
+
// There is no public `problem:*`: it would also match the role channels `problem:{problemKey}:{role}`.
|
|
2252
2284
|
const CHANNEL_SUBMISSION_ALL = 'submission:*';
|
|
2253
|
-
const
|
|
2285
|
+
const CHANNEL_USER_SPECTATORS_ALL = `user:*:${UserChannelRole.SPECTATORS}`;
|
|
2254
2286
|
/**
|
|
2255
2287
|
* Returns the contest and role of a `CHANNEL_CONTEST` channel name, or null if it is not one.
|
|
2256
2288
|
*/
|
|
@@ -2261,6 +2293,16 @@ const getContestChannelParams = (channel) => {
|
|
|
2261
2293
|
}
|
|
2262
2294
|
return { contestKey: decodeURIComponent(contestKey), role: role };
|
|
2263
2295
|
};
|
|
2296
|
+
/**
|
|
2297
|
+
* Returns the problem and role of a `CHANNEL_PROBLEM` channel name, or null if it is not one.
|
|
2298
|
+
*/
|
|
2299
|
+
const getProblemChannelParams = (channel) => {
|
|
2300
|
+
const [namespace, problemKey, role, ...rest] = channel.split(':');
|
|
2301
|
+
if (namespace !== 'problem' || !problemKey || rest.length || !Object.values(ProblemChannelRole).includes(role)) {
|
|
2302
|
+
return null;
|
|
2303
|
+
}
|
|
2304
|
+
return { problemKey: decodeURIComponent(problemKey), role: role };
|
|
2305
|
+
};
|
|
2264
2306
|
|
|
2265
2307
|
const NEW_PAGE_SHEET = () => ({
|
|
2266
2308
|
id: v4(),
|
|
@@ -2972,6 +3014,23 @@ const isUnsubscribeUserNotificationWebSocketEventDto = (event) => {
|
|
|
2972
3014
|
return isWebsocketSubscription(event)
|
|
2973
3015
|
&& event?.event === WebSocketSubscriptionEvent.UNSUBSCRIBE_USER_NOTIFICATION;
|
|
2974
3016
|
};
|
|
3017
|
+
const isNonEmptyString = (value) => typeof value === 'string' && !!value;
|
|
3018
|
+
/**
|
|
3019
|
+
* Exactly one of `problemKey` / `contestKey` / `userKey` is set.
|
|
3020
|
+
*/
|
|
3021
|
+
const isSubmissionsJudgingScope = (scope) => {
|
|
3022
|
+
return [scope?.problemKey, scope?.contestKey, scope?.userKey].filter(isNonEmptyString).length === 1;
|
|
3023
|
+
};
|
|
3024
|
+
const isSubscribeSubmissionsJudgingWebSocketEventDto = (event) => {
|
|
3025
|
+
return isWebsocketSubscription(event)
|
|
3026
|
+
&& event?.event === WebSocketSubscriptionEvent.SUBSCRIBE_SUBMISSIONS_JUDGING
|
|
3027
|
+
&& isSubmissionsJudgingScope(event);
|
|
3028
|
+
};
|
|
3029
|
+
const isUnsubscribeSubmissionsJudgingWebSocketEventDto = (event) => {
|
|
3030
|
+
return isWebsocketSubscription(event)
|
|
3031
|
+
&& event?.event === WebSocketSubscriptionEvent.UNSUBSCRIBE_SUBMISSIONS_JUDGING
|
|
3032
|
+
&& isSubmissionsJudgingScope(event);
|
|
3033
|
+
};
|
|
2975
3034
|
// is WebSocketResponseEventDto, CHANNEL_SUBSCRIBE_CLIENT
|
|
2976
3035
|
const isWebSocketResponseEventDto = (event) => {
|
|
2977
3036
|
return Object.values(WebSocketResponseEvent).includes(event?.event)
|
|
@@ -2993,6 +3052,18 @@ const isSubmissionRunStatusMessageWebSocketResponseEventDto = (event) => {
|
|
|
2993
3052
|
&& event?.verdict in ProblemVerdict
|
|
2994
3053
|
&& typeof event?.points === 'number';
|
|
2995
3054
|
};
|
|
3055
|
+
const isSubmissionJudgingWebSocketResponseEventDto = (event) => {
|
|
3056
|
+
return isWebSocketResponseEventDto(event)
|
|
3057
|
+
&& event?.event === WebSocketResponseEvent.SUBMISSION_JUDGING
|
|
3058
|
+
&& isNonEmptyString(event?.submitId)
|
|
3059
|
+
&& isNonEmptyString(event?.problemKey)
|
|
3060
|
+
&& (event?.contestKey === null || isNonEmptyString(event?.contestKey))
|
|
3061
|
+
&& isNonEmptyString(event?.userKey)
|
|
3062
|
+
&& typeof event?.judging === 'boolean'
|
|
3063
|
+
&& event?.status in SubmissionRunStatus
|
|
3064
|
+
&& event?.verdict in ProblemVerdict
|
|
3065
|
+
&& typeof event?.points === 'number';
|
|
3066
|
+
};
|
|
2996
3067
|
const isUserMessageWebSocketResponseEventDto = (event) => {
|
|
2997
3068
|
return isWebSocketResponseEventDto(event)
|
|
2998
3069
|
&& event?.event === WebSocketResponseEvent.USER_MESSAGE
|
|
@@ -3098,6 +3169,11 @@ const getWebSocketResponseEventKeyOfResponse = (response, clientId) => {
|
|
|
3098
3169
|
if (isUserNotificationWebSocketResponseEventDto(response)) {
|
|
3099
3170
|
return getWebSocketResponseEventKey(WebSocketResponseEvent.USER_NOTIFICATION_, clientId, '*');
|
|
3100
3171
|
}
|
|
3172
|
+
if (isSubmissionJudgingWebSocketResponseEventDto(response)) {
|
|
3173
|
+
// one key for every table: the same event is published on several channels (problem / contest / user) and the
|
|
3174
|
+
// client deduplicates it, so it is dispatched once; each table filters by the submissions it lists
|
|
3175
|
+
return getWebSocketResponseEventKey(WebSocketResponseEvent.SUBMISSION_JUDGING, clientId, '*');
|
|
3176
|
+
}
|
|
3101
3177
|
return null;
|
|
3102
3178
|
};
|
|
3103
3179
|
/**
|
|
@@ -3109,12 +3185,23 @@ const getChannelOfWebSocketSubscribeEvent = (event) => {
|
|
|
3109
3185
|
if (isSubscribeSubmissionRunStatusWebSocketEventDto(event)) {
|
|
3110
3186
|
return CHANNEL_SUBMISSION(event.submitId);
|
|
3111
3187
|
}
|
|
3112
|
-
|
|
3113
|
-
return CHANNEL_PROBLEM(event.problemKey);
|
|
3114
|
-
}
|
|
3188
|
+
// SUBSCRIBE_PROBLEM_CRAWLED: the event arrives by the private user channel of who requested the crawl
|
|
3115
3189
|
if (isSubscribeContestChangesWebSocketEventDto(event) || isSubscribeSubmissionsCrawlWebSocketEventDto(event)) {
|
|
3116
3190
|
return CHANNEL_CONTEST(event.contestKey, ContestChannelRole.SPECTATORS);
|
|
3117
3191
|
}
|
|
3192
|
+
if (isSubscribeSubmissionsJudgingWebSocketEventDto(event)) {
|
|
3193
|
+
// the judging start/end is published on the spectators channel when it is visible to everyone; the hidden ones
|
|
3194
|
+
// (frozen / quiet contests) arrive by the role channels of the contest (managers) and the private user channel
|
|
3195
|
+
if ('problemKey' in event && event.problemKey) {
|
|
3196
|
+
return CHANNEL_PROBLEM(event.problemKey, ProblemChannelRole.SPECTATORS);
|
|
3197
|
+
}
|
|
3198
|
+
if ('contestKey' in event && event.contestKey) {
|
|
3199
|
+
return CHANNEL_CONTEST(event.contestKey, ContestChannelRole.SPECTATORS);
|
|
3200
|
+
}
|
|
3201
|
+
if ('userKey' in event && event.userKey) {
|
|
3202
|
+
return CHANNEL_USER_ROLE(event.userKey, UserChannelRole.SPECTATORS);
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3118
3205
|
return null;
|
|
3119
3206
|
};
|
|
3120
3207
|
/**
|
|
@@ -3124,6 +3211,18 @@ const getContestKeyOfWebSocketSubscribeEvent = (event) => {
|
|
|
3124
3211
|
if (isSubscribeContestChangesWebSocketEventDto(event) || isSubscribeSubmissionsCrawlWebSocketEventDto(event)) {
|
|
3125
3212
|
return event.contestKey;
|
|
3126
3213
|
}
|
|
3214
|
+
if (isSubscribeSubmissionsJudgingWebSocketEventDto(event) && 'contestKey' in event && event.contestKey) {
|
|
3215
|
+
return event.contestKey;
|
|
3216
|
+
}
|
|
3217
|
+
return null;
|
|
3218
|
+
};
|
|
3219
|
+
/**
|
|
3220
|
+
* Problem whose role channels the client needs (the API grants them when the token is requested with this problem).
|
|
3221
|
+
*/
|
|
3222
|
+
const getProblemKeyOfWebSocketSubscribeEvent = (event) => {
|
|
3223
|
+
if (isSubscribeSubmissionsJudgingWebSocketEventDto(event) && 'problemKey' in event && event.problemKey) {
|
|
3224
|
+
return event.problemKey;
|
|
3225
|
+
}
|
|
3127
3226
|
return null;
|
|
3128
3227
|
};
|
|
3129
3228
|
|
|
@@ -3318,4 +3417,4 @@ const isQuizTextSheet = (value) => {
|
|
|
3318
3417
|
&& ['text', 'number', 'textarea'].includes(value?.inputType);
|
|
3319
3418
|
};
|
|
3320
3419
|
|
|
3321
|
-
export { ACCEPTED_PROGRAMMING_LANGUAGES, ASPECT_RATIO, AspectRatio, AssignmentClass, AssignmentState, AssignmentType, CHANNEL_CONTEST, CHANNEL_ORGANIZATION_PRESENCE, CHANNEL_PROBLEM,
|
|
3420
|
+
export { ACCEPTED_PROGRAMMING_LANGUAGES, ASPECT_RATIO, AspectRatio, AssignmentClass, AssignmentState, AssignmentType, CHANNEL_CONTEST, CHANNEL_ORGANIZATION_PRESENCE, CHANNEL_PROBLEM, CHANNEL_SESSION, CHANNEL_SUBMISSION, CHANNEL_SUBMISSION_ALL, CHANNEL_USER, CHANNEL_USER_ROLE, CHANNEL_USER_SPECTATORS_ALL, CODE_LANGUAGE, CONTEST_ROLE, CONTEST_STATUS, COOKIE_JUKI_SESSION_ID, COURSE_ROLE, CodeLanguage, CollectionKey, ContestChannelRole, ContestEventAction, ContestProblemBlockedByType, ContestProblemPrerequisiteType, ContestStatus, ContestTimeStatus, CourseRole, CourseStatus, CourseType, DAY_NAMES, DEFAULT_JUKI_PROFILE_IMAGE, DataViewMode, EMPTY_DOCUMENT_MEMBERS, EMPTY_ENTITY_MEMBERS, EMPTY_USER_PERMISSIONS, ENTITY_ACCESS, ENTITY_STATE, ERROR, EmailSubscription, EmailValidationState, EntityAccess, EntityCollection, EntityMembersRank, EntityState, EntityStatus, ErrorCode, FILE_ROLE, FILE_TYPE, FileMemberRole, FileType, FilesJukiPub, GroupType, HEADER_JUKI_FORWARDED_HOST, HEADER_JUKI_INTERNAL_API_KEY, HEADER_JUKI_METADATA, HEADER_JUKI_SECRET_TOKEN, HEADER_JUKI_VISITOR_SESSION_ID, HOURS, HTTPMethod, JUDGE, JUKI_APP_EMAIL_TEMPLATE, JUKI_APP_ORGANIZATION_KEY, JkError, Judge, LANGUAGE, LOWERCASE_LETTERS, Language, LogLevel, LogOperation, MAX_DATE, MEMBER_ROLE, MILLISECONDS, MINUTES, MIN_DATE, MONTH_NAMES, MemberRole, MemberType, MenuViewMode, NEW_PAGE_SHEET, NUMBERS, NotificationType, ONE_DAY, ONE_HOUR, ONE_MINUTE, ONE_SECOND, ONE_WEEK, ORGANIZATION_PLAN, OrganizationLogoType, OrganizationPlan, PAGE, PAGE_SIZE, PAGE_SIZES, PALLETE, PROBLEM_INPUT, PROBLEM_MODE, PROBLEM_MODES, PROBLEM_OUTPUT, PROBLEM_ROLE, PROBLEM_TYPE, PROBLEM_TYPES, PROBLEM_VERDICT, PROBLEM_VERDICTS, ProblemChannelRole, ProblemInput, ProblemOutput, ProblemScoringMode, ProblemType, ProblemVerdict, ProfileSetting, QuizScoringMode, RUNNER_ACCEPTED_PROBLEM_MODES, RUNNER_ACCEPTED_PROBLEM_TYPES, RUNNER_ACCEPTED_PROGRAMMING_LANGUAGES, RUNNER_TYPE, Role, RunnerType, SECONDS, SEC_120, SEC_30, SEC_60, SEC_90, SEPARATOR_TOKEN, SUBMISSION_RUN_STATUS, SUPPORT_EMAIL, SYSTEM_ROLE, ScopeData, Status, SubmissionRunStatus, SystemRole, TEAM_ROLE, THEME, TeamRole, TeamStatus, Theme, TypeTestCase, UPPERCASE_LETTERS, USER_ROLE, USER_STATUS, UserChannelRole, UserRole, UserStatus, UserTeamStatus, WebSocketResponseEvent, WebSocketSubscriptionEvent, WorkingIn, WorksheetType, YEARS, _KEY, _SUB_SUB_TAB, _SUB_TAB, _TAB, _TYPE, areArraysDifferent, changeDay, changeHours, changeMilliseconds, changeMinutes, changeMonth, changeSeconds, changeYear, chunkString, cleanRequest, clearAsyncInterval, consoleError, consoleInfo, consoleWarn, contentResponse, contentsResponse, decreaseDay, decreaseMonth, decreaseYear, endOfDay, endOfHour, endOfMinute, endOfMonth, endOfSecond, endOfWeek, endOfYear, errorsResponse, getAssignmentCommentKey, getAssignmentUserCommentKey, getChannelOfWebSocketSubscribeEvent, getClientId, getContestChannelParams, getContestCommentKey, getContestKeyOfWebSocketSubscribeEvent, getDataLog, getDataOfTestCase, getDays, getDefaultMeta, getDocumentAccess, getHours, getMilliseconds, getMinutes, getMonths, getParamsOfClientId, getParamsOfUserKey, getPlainText, getProblemChannelParams, getProblemCommentKey, getProblemJudgeKey, getProblemKeyOfWebSocketSubscribeEvent, getRandomString, getSeconds, getSummaryWorksheetsInPages, getTotalExercisesOfSummaryWorksheetsInPages, getUserKey, getVerdictFromTestCase, getWebSocketResponseEventKey, getWebSocketResponseEventKeyOfResponse, getWorksheetsInPages, getYears, humanFileSize, increaseDay, increaseMonth, increaseYear, indexToLetters, isAfter, isBasicWorksheet, isBefore, isCodeEditorSheet, isCodeRunStatusMessageWebSocketResponseEventDto, isContestAssignment, isContestChangesWebSocketResponseEventDto, isCourseAssignment, isDayAfter, isDayBefore, isEndlessContest, isEqual, isFutureContest, isGlobalContest, isHoursAfter, isHoursBefore, isJkmdSheet, isMillisecondsAfter, isMillisecondsBefore, isMinutesAfter, isMinutesBefore, isMonthAfter, isMonthBefore, isObjectJson, isPastContest, isProblemCrawledWebSocketResponseEventDto, isQuizOptionsSheet, isQuizProblemSheet, isQuizTextSheet, isSameDay, isSameHour, isSameMillisecond, isSameMinute, isSameMonth, isSameSecond, isSameYear, isSecondsAfter, isSecondsBefore, isSendDataEc2InstancesListWebSocketResponseEventDto, isSendDataEcsTaskDefinitionListWebSocketResponseEventDto, isSendDataEcsTasksListWebSocketResponseEventDto, isSendDataRunCommandWebSocketResponseEventDto, isSendDataSsmSessionsListWebSocketResponseEventDto, isSendDataWebSocketResponseEventDto, isStringJson, isSubmissionJudgingWebSocketResponseEventDto, isSubmissionRunStatusMessageWebSocketResponseEventDto, isSubmissionsCrawlWebSocketResponseEventDto, isSubmissionsJudgingScope, isSubscribeCodeRunStatusWebSocketEventDto, isSubscribeContestChangesWebSocketEventDto, isSubscribeGetDataWebSocketEventDto, isSubscribeProblemCrawledWebSocketEventDto, isSubscribeSubmissionRunStatusWebSocketEventDto, isSubscribeSubmissionsCrawlWebSocketEventDto, isSubscribeSubmissionsJudgingWebSocketEventDto, isSubscribeUserNotificationWebSocketEventDto, isTeamMember, isToday, isUnsubscribeCodeRunStatusWebSocketEventDto, isUnsubscribeContestChangesWebSocketEventDto, isUnsubscribeGetDataWebSocketEventDto, isUnsubscribeProblemCrawledWebSocketEventDto, isUnsubscribeSubmissionRunStatusWebSocketEventDto, isUnsubscribeSubmissionsCrawlWebSocketEventDto, isUnsubscribeSubmissionsJudgingWebSocketEventDto, isUnsubscribeUserNotificationWebSocketEventDto, isUserMember, isUserMessageWebSocketResponseEventDto, isUserNotificationContestClarificationWebSocketResponseEventDto, isUserNotificationSubmissionWebSocketResponseEventDto, isUserNotificationWebSocketResponseEventDto, isValidDate, isWebSocketChannelsMessageDto, isWebSocketResponseEventDto, isWebsocketSubscription, isWithinInterval, isWorksheetAssignment, isYearAfter, isYearBefore, join, lettersToIndex, memorySizeOf, mergeVerdicts, mex, normalizeToLocalStartOfDay, normalizeToLocalStartOfHour, normalizeToLocalStartOfMinutes, normalizeToLocalStartOfSeconds, objectUpdate, objectsUpdate, padEnd, padStart, range, removeExtension, safeJsonParse, setAsyncInterval, split, splitTime, startOfDay, startOfHour, startOfMinute, startOfMonth, startOfSecond, startOfWeek, startOfYear, stringToArrayBuffer, toEntityMembersDto, toJkError };
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import { ContestChannelRole } from '../types';
|
|
1
|
+
import { ContestChannelRole, ProblemChannelRole, UserChannelRole } from '../types';
|
|
2
2
|
/** private: events of one session (code runs, requested data) */
|
|
3
3
|
export declare const CHANNEL_SESSION: (sessionId: string) => string;
|
|
4
|
-
/** private: events of one user (own submissions while the contest is frozen, notifications) */
|
|
4
|
+
/** private: events of one user (own submissions while the contest is frozen, notifications, own problem crawls) */
|
|
5
5
|
export declare const CHANNEL_USER: (userId: string) => string;
|
|
6
|
+
/**
|
|
7
|
+
* per role: public events of one user (judging of its submissions visible to everyone, see `UserChannelRole`).
|
|
8
|
+
* By its public key (`getUserKey(nickname, organizationKey)`), the one the clients know; the private channel
|
|
9
|
+
* (`CHANNEL_USER`) is by id. They never collide: the role channel has one more segment.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CHANNEL_USER_ROLE: (userKey: string, role: UserChannelRole) => string;
|
|
6
12
|
/** public: status of a submission when it is visible to everyone */
|
|
7
13
|
export declare const CHANNEL_SUBMISSION: (submitId: string) => string;
|
|
8
|
-
/**
|
|
9
|
-
export declare const CHANNEL_PROBLEM: (problemKey: string) => string;
|
|
14
|
+
/** per role: events of a problem for one role (judging of its submissions, see `ProblemChannelRole`) */
|
|
15
|
+
export declare const CHANNEL_PROBLEM: (problemKey: string, role: ProblemChannelRole) => string;
|
|
10
16
|
/** per role: events of a contest for one role (and the roles above it, see `ContestChannelRole`) */
|
|
11
17
|
export declare const CHANNEL_CONTEST: (contestKey: string, role: ContestChannelRole) => string;
|
|
12
18
|
/**
|
|
@@ -18,7 +24,7 @@ export declare const CHANNEL_CONTEST: (contestKey: string, role: ContestChannelR
|
|
|
18
24
|
*/
|
|
19
25
|
export declare const CHANNEL_ORGANIZATION_PRESENCE: (organizationKey: string) => string;
|
|
20
26
|
export declare const CHANNEL_SUBMISSION_ALL = "submission:*";
|
|
21
|
-
export declare const
|
|
27
|
+
export declare const CHANNEL_USER_SPECTATORS_ALL = "user:*:spectators";
|
|
22
28
|
/**
|
|
23
29
|
* Returns the contest and role of a `CHANNEL_CONTEST` channel name, or null if it is not one.
|
|
24
30
|
*/
|
|
@@ -26,3 +32,10 @@ export declare const getContestChannelParams: (channel: string) => {
|
|
|
26
32
|
contestKey: string;
|
|
27
33
|
role: ContestChannelRole;
|
|
28
34
|
} | null;
|
|
35
|
+
/**
|
|
36
|
+
* Returns the problem and role of a `CHANNEL_PROBLEM` channel name, or null if it is not one.
|
|
37
|
+
*/
|
|
38
|
+
export declare const getProblemChannelParams: (channel: string) => {
|
|
39
|
+
problemKey: string;
|
|
40
|
+
role: ProblemChannelRole;
|
|
41
|
+
} | null;
|
|
@@ -63,8 +63,25 @@ export interface UnsubscribeUserNotificationWebSocketEventDto extends WebsocketS
|
|
|
63
63
|
event: WebSocketSubscriptionEvent.UNSUBSCRIBE_USER_NOTIFICATION;
|
|
64
64
|
userKey: string;
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Scope of a submissions table: the judging start/end of the submissions of one problem, one contest or one user.
|
|
68
|
+
* Exactly one of the keys is set.
|
|
69
|
+
*/
|
|
70
|
+
export type SubmissionsJudgingScope = {
|
|
71
|
+
problemKey: string;
|
|
72
|
+
} | {
|
|
73
|
+
contestKey: string;
|
|
74
|
+
} | {
|
|
75
|
+
userKey: string;
|
|
76
|
+
};
|
|
77
|
+
export type SubscribeSubmissionsJudgingWebSocketEventDto = WebsocketSubscription & {
|
|
78
|
+
event: WebSocketSubscriptionEvent.SUBSCRIBE_SUBMISSIONS_JUDGING;
|
|
79
|
+
} & SubmissionsJudgingScope;
|
|
80
|
+
export type UnsubscribeSubmissionsJudgingWebSocketEventDto = WebsocketSubscription & {
|
|
81
|
+
event: WebSocketSubscriptionEvent.UNSUBSCRIBE_SUBMISSIONS_JUDGING;
|
|
82
|
+
} & SubmissionsJudgingScope;
|
|
83
|
+
export type WebSocketSubscribeEventDto = SubscribeCodeRunStatusWebSocketEventDto | SubscribeSubmissionRunStatusWebSocketEventDto | SubscribeGetDataWebSocketEventDto | SubscribeProblemCrawledWebSocketEventDto | SubscribeSubmissionsCrawlWebSocketEventDto | SubscribeContestChangesWebSocketEventDto | SubscribeUserNotificationWebsocketEventDto | SubscribeSubmissionsJudgingWebSocketEventDto;
|
|
84
|
+
export type WebSocketUnsubscribeEventDto = UnsubscribeCodeRunStatusWebSocketEventDto | UnsubscribeSubmissionRunStatusWebSocketEventDto | UnsubscribeGetDataWebSocketEventDto | UnsubscribeProblemCrawledWebSocketEventDto | UnsubscribeSubmissionsCrawlWebSocketEventDto | UnsubscribeContestChangesWebSocketEventDto | UnsubscribeUserNotificationWebSocketEventDto | UnsubscribeSubmissionsJudgingWebSocketEventDto;
|
|
68
85
|
export interface WebSocketResponse {
|
|
69
86
|
event: WebSocketResponseEvent;
|
|
70
87
|
key: WebSocketResponseEventKey;
|
|
@@ -86,6 +103,23 @@ export interface SubmissionRunStatusWebSocketResponseEventDto extends WebSocketR
|
|
|
86
103
|
testInfo?: TestInfo;
|
|
87
104
|
shouldValidateContest: boolean;
|
|
88
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Start (`judging: true`, status RECEIVED) and end (`judging: false`, status COMPLETED / FAILED) of the judging of a
|
|
108
|
+
* submission, also on rejudges. Published on the channels of the tables that list it (problem / contest / user), so a
|
|
109
|
+
* table listens to one channel and only attaches `CHANNEL_SUBMISSION` (the case by case progress) of the submissions
|
|
110
|
+
* on screen that are being judged. The case by case events keep arriving as `SubmissionRunStatusWebSocketResponseEventDto`.
|
|
111
|
+
*/
|
|
112
|
+
export interface SubmissionJudgingWebSocketResponseEventDto extends WebSocketResponse {
|
|
113
|
+
event: WebSocketResponseEvent.SUBMISSION_JUDGING;
|
|
114
|
+
submitId: string;
|
|
115
|
+
problemKey: string;
|
|
116
|
+
contestKey: string | null;
|
|
117
|
+
userKey: string;
|
|
118
|
+
judging: boolean;
|
|
119
|
+
status: SubmissionRunStatus;
|
|
120
|
+
verdict: ProblemVerdict;
|
|
121
|
+
points: number;
|
|
122
|
+
}
|
|
89
123
|
export interface UserMessageWebSocketResponseEventDto extends WebSocketResponse {
|
|
90
124
|
event: WebSocketResponseEvent.USER_MESSAGE;
|
|
91
125
|
user: UserOrganizationBasicInfoResponseDto;
|
|
@@ -179,7 +213,7 @@ export interface UserNotificationContestClarificationWebSocketResponseEventDto e
|
|
|
179
213
|
contestKey: string;
|
|
180
214
|
};
|
|
181
215
|
}
|
|
182
|
-
export type WebSocketResponseEventDto = CodeRunStatusWebSocketResponseEventDto | SubmissionRunStatusWebSocketResponseEventDto | UserMessageWebSocketResponseEventDto | SendDataEcsTaskDefinitionListWebSocketResponseEventDto | SendDataEcsTasksListWebSocketResponseEventDto | SendDataEc2InstancesListWebSocketResponseEventDto | SendDataSsmSessionsListWebSocketResponseEventDto | ProblemCrawledWebSocketResponseEventDto | SubmissionsCrawlWebSocketResponseEventDto | ContestChangesWebSocketResponseEventDto | SendDataRunCommandWebSocketResponseEventDto | UserNotificationSubmissionWebSocketResponseEventDto | UserNotificationContestClarificationWebSocketResponseEventDto;
|
|
216
|
+
export type WebSocketResponseEventDto = CodeRunStatusWebSocketResponseEventDto | SubmissionRunStatusWebSocketResponseEventDto | UserMessageWebSocketResponseEventDto | SendDataEcsTaskDefinitionListWebSocketResponseEventDto | SendDataEcsTasksListWebSocketResponseEventDto | SendDataEc2InstancesListWebSocketResponseEventDto | SendDataSsmSessionsListWebSocketResponseEventDto | ProblemCrawledWebSocketResponseEventDto | SubmissionsCrawlWebSocketResponseEventDto | ContestChangesWebSocketResponseEventDto | SendDataRunCommandWebSocketResponseEventDto | UserNotificationSubmissionWebSocketResponseEventDto | UserNotificationContestClarificationWebSocketResponseEventDto | SubmissionJudgingWebSocketResponseEventDto;
|
|
183
217
|
/**
|
|
184
218
|
* Message sent by the backend producers to the websocket SQS queue:
|
|
185
219
|
* the producer (trusted, with access to the DB) decides the Ably channels where the event is published.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CodeRunStatusWebSocketResponseEventDto, ContestChangesWebSocketResponseEventDto, ProblemCrawledWebSocketResponseEventDto, SendDataEc2InstancesListWebSocketResponseEventDto, SendDataEcsTaskDefinitionListWebSocketResponseEventDto, SendDataEcsTasksListWebSocketResponseEventDto, SendDataRunCommandWebSocketResponseEventDto, SendDataSsmSessionsListWebSocketResponseEventDto, SendDataWebSocketResponseEventDto, SubmissionRunStatusWebSocketResponseEventDto, SubmissionsCrawlWebSocketResponseEventDto, SubscribeCodeRunStatusWebSocketEventDto, SubscribeContestChangesWebSocketEventDto, SubscribeGetDataWebSocketEventDto, SubscribeProblemCrawledWebSocketEventDto, SubscribeSubmissionRunStatusWebSocketEventDto, SubscribeSubmissionsCrawlWebSocketEventDto, SubscribeUserNotificationWebsocketEventDto, UnsubscribeCodeRunStatusWebSocketEventDto, UnsubscribeContestChangesWebSocketEventDto, UnsubscribeGetDataWebSocketEventDto, UnsubscribeProblemCrawledWebSocketEventDto, UnsubscribeSubmissionRunStatusWebSocketEventDto, UnsubscribeSubmissionsCrawlWebSocketEventDto, UnsubscribeUserNotificationWebSocketEventDto, UserMessageWebSocketResponseEventDto, UserNotificationContestClarificationWebSocketResponseEventDto, UserNotificationSubmissionWebSocketResponseEventDto, UserNotificationWebSocketResponseEventDto, WebSocketChannelsMessageDto, WebSocketResponseEventDto, WebSocketSubscribeEventDto, WebSocketUnsubscribeEventDto } from '../dto';
|
|
1
|
+
import { CodeRunStatusWebSocketResponseEventDto, ContestChangesWebSocketResponseEventDto, ProblemCrawledWebSocketResponseEventDto, SendDataEc2InstancesListWebSocketResponseEventDto, SendDataEcsTaskDefinitionListWebSocketResponseEventDto, SendDataEcsTasksListWebSocketResponseEventDto, SendDataRunCommandWebSocketResponseEventDto, SendDataSsmSessionsListWebSocketResponseEventDto, SendDataWebSocketResponseEventDto, SubmissionJudgingWebSocketResponseEventDto, SubmissionRunStatusWebSocketResponseEventDto, SubmissionsCrawlWebSocketResponseEventDto, SubmissionsJudgingScope, SubscribeCodeRunStatusWebSocketEventDto, SubscribeContestChangesWebSocketEventDto, SubscribeGetDataWebSocketEventDto, SubscribeProblemCrawledWebSocketEventDto, SubscribeSubmissionRunStatusWebSocketEventDto, SubscribeSubmissionsCrawlWebSocketEventDto, SubscribeSubmissionsJudgingWebSocketEventDto, SubscribeUserNotificationWebsocketEventDto, UnsubscribeCodeRunStatusWebSocketEventDto, UnsubscribeContestChangesWebSocketEventDto, UnsubscribeGetDataWebSocketEventDto, UnsubscribeProblemCrawledWebSocketEventDto, UnsubscribeSubmissionRunStatusWebSocketEventDto, UnsubscribeSubmissionsCrawlWebSocketEventDto, UnsubscribeSubmissionsJudgingWebSocketEventDto, UnsubscribeUserNotificationWebSocketEventDto, UserMessageWebSocketResponseEventDto, UserNotificationContestClarificationWebSocketResponseEventDto, UserNotificationSubmissionWebSocketResponseEventDto, UserNotificationWebSocketResponseEventDto, WebSocketChannelsMessageDto, WebSocketResponseEventDto, WebSocketSubscribeEventDto, WebSocketUnsubscribeEventDto } from '../dto';
|
|
2
2
|
import { ClientId, WebSocketResponseEvent, WebSocketResponseEventKey } from '../types';
|
|
3
3
|
export declare const isWebsocketSubscription: (event: any) => event is WebSocketSubscribeEventDto | WebSocketUnsubscribeEventDto;
|
|
4
4
|
export declare const isSubscribeCodeRunStatusWebSocketEventDto: (event: any) => event is SubscribeCodeRunStatusWebSocketEventDto;
|
|
@@ -15,9 +15,16 @@ export declare const isSubscribeContestChangesWebSocketEventDto: (event: any) =>
|
|
|
15
15
|
export declare const isUnsubscribeContestChangesWebSocketEventDto: (event: any) => event is UnsubscribeContestChangesWebSocketEventDto;
|
|
16
16
|
export declare const isSubscribeUserNotificationWebSocketEventDto: (event: any) => event is SubscribeUserNotificationWebsocketEventDto;
|
|
17
17
|
export declare const isUnsubscribeUserNotificationWebSocketEventDto: (event: any) => event is UnsubscribeUserNotificationWebSocketEventDto;
|
|
18
|
+
/**
|
|
19
|
+
* Exactly one of `problemKey` / `contestKey` / `userKey` is set.
|
|
20
|
+
*/
|
|
21
|
+
export declare const isSubmissionsJudgingScope: (scope: any) => scope is SubmissionsJudgingScope;
|
|
22
|
+
export declare const isSubscribeSubmissionsJudgingWebSocketEventDto: (event: any) => event is SubscribeSubmissionsJudgingWebSocketEventDto;
|
|
23
|
+
export declare const isUnsubscribeSubmissionsJudgingWebSocketEventDto: (event: any) => event is UnsubscribeSubmissionsJudgingWebSocketEventDto;
|
|
18
24
|
export declare const isWebSocketResponseEventDto: (event: any) => event is WebSocketResponseEventDto;
|
|
19
25
|
export declare const isCodeRunStatusMessageWebSocketResponseEventDto: (event: any) => event is CodeRunStatusWebSocketResponseEventDto;
|
|
20
26
|
export declare const isSubmissionRunStatusMessageWebSocketResponseEventDto: (event: any) => event is SubmissionRunStatusWebSocketResponseEventDto;
|
|
27
|
+
export declare const isSubmissionJudgingWebSocketResponseEventDto: (event: any) => event is SubmissionJudgingWebSocketResponseEventDto;
|
|
21
28
|
export declare const isUserMessageWebSocketResponseEventDto: (event: any) => event is UserMessageWebSocketResponseEventDto;
|
|
22
29
|
export declare const isSendDataWebSocketResponseEventDto: (event: any) => event is SendDataWebSocketResponseEventDto;
|
|
23
30
|
export declare const isSendDataEcsTaskDefinitionListWebSocketResponseEventDto: (event: any) => event is SendDataEcsTaskDefinitionListWebSocketResponseEventDto;
|
|
@@ -49,3 +56,7 @@ export declare const getChannelOfWebSocketSubscribeEvent: (event: WebSocketSubsc
|
|
|
49
56
|
* Contest whose role channels the client needs (the API grants them when the token is requested with this contest).
|
|
50
57
|
*/
|
|
51
58
|
export declare const getContestKeyOfWebSocketSubscribeEvent: (event: WebSocketSubscribeEventDto) => string | null;
|
|
59
|
+
/**
|
|
60
|
+
* Problem whose role channels the client needs (the API grants them when the token is requested with this problem).
|
|
61
|
+
*/
|
|
62
|
+
export declare const getProblemKeyOfWebSocketSubscribeEvent: (event: WebSocketSubscribeEventDto) => string | null;
|
|
@@ -17,7 +17,9 @@ export declare enum WebSocketSubscriptionEvent {
|
|
|
17
17
|
SUBSCRIBE_CONTEST_CHANGES = "SUBSCRIBE_CONTEST_CHANGES",
|
|
18
18
|
UNSUBSCRIBE_CONTEST_CHANGES = "UNSUBSCRIBE_CONTEST_CHANGES",
|
|
19
19
|
SUBSCRIBE_USER_NOTIFICATION = "SUBSCRIBE_USER_NOTIFICATION",
|
|
20
|
-
UNSUBSCRIBE_USER_NOTIFICATION = "UNSUBSCRIBE_USER_NOTIFICATION"
|
|
20
|
+
UNSUBSCRIBE_USER_NOTIFICATION = "UNSUBSCRIBE_USER_NOTIFICATION",
|
|
21
|
+
SUBSCRIBE_SUBMISSIONS_JUDGING = "SUBSCRIBE_SUBMISSIONS_JUDGING",
|
|
22
|
+
UNSUBSCRIBE_SUBMISSIONS_JUDGING = "UNSUBSCRIBE_SUBMISSIONS_JUDGING"
|
|
21
23
|
}
|
|
22
24
|
export declare enum WebSocketResponseEvent {
|
|
23
25
|
CODE_RUN_STATUS = "CODE_RUN_STATUS",
|
|
@@ -34,7 +36,8 @@ export declare enum WebSocketResponseEvent {
|
|
|
34
36
|
CONTEST_CHANGES = "CONTEST_CHANGES",
|
|
35
37
|
USER_NOTIFICATION_ = "USER_NOTIFICATION_",
|
|
36
38
|
USER_NOTIFICATION_SUBMISSION = "USER_NOTIFICATION_SUBMISSION",
|
|
37
|
-
USER_NOTIFICATION_CONTEST_CLARIFICATION = "USER_NOTIFICATION_CONTEST_CLARIFICATION"
|
|
39
|
+
USER_NOTIFICATION_CONTEST_CLARIFICATION = "USER_NOTIFICATION_CONTEST_CLARIFICATION",
|
|
40
|
+
SUBMISSION_JUDGING = "SUBMISSION_JUDGING"
|
|
38
41
|
}
|
|
39
42
|
export type ClientId = `${RecordId}|${string}`;
|
|
40
43
|
export type WebSocketResponseEventKey = `${WebSocketResponseEvent}-${ClientId}-${string}`;
|
|
@@ -51,6 +54,24 @@ export declare enum ContestChannelRole {
|
|
|
51
54
|
GUESTS = "guests",
|
|
52
55
|
SPECTATORS = "spectators"
|
|
53
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Roles of a problem, the ones computed by `getProblemAccessPermissions` (backend).
|
|
59
|
+
* Each role has its own Ably channel: `problem:{problemKey}:{role}` (see `CHANNEL_PROBLEM`).
|
|
60
|
+
*/
|
|
61
|
+
export declare enum ProblemChannelRole {
|
|
62
|
+
ADMINISTRATORS = "administrators",
|
|
63
|
+
MANAGERS = "managers",
|
|
64
|
+
SPECTATORS = "spectators"
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Roles of the public channels of a user: `user:{userKey}:{role}` (see `CHANNEL_USER_ROLE`), by its public key
|
|
68
|
+
* (`getUserKey(nickname, organizationKey)`) like problems and contests.
|
|
69
|
+
* There are no per-user permissions: `spectators` is everyone who can list the submissions of the user.
|
|
70
|
+
* The private channel of the user is `user:{userId}` (see `CHANNEL_USER`).
|
|
71
|
+
*/
|
|
72
|
+
export declare enum UserChannelRole {
|
|
73
|
+
SPECTATORS = "spectators"
|
|
74
|
+
}
|
|
54
75
|
export type InfoLogCaseStatus = {
|
|
55
76
|
inputKey: string;
|
|
56
77
|
out: string;
|