track-cli 3.1.0 → 4.0.0-rc0

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.
@@ -1,6 +1,6 @@
1
1
  import { VERSION } from "../meta.js";
2
2
  import { HttpStatus } from "../shared/mod.js";
3
- import { APIError, OtherError } from "../shared/errors.js";
3
+ import { APIError } from "../shared/errors.js";
4
4
  // @ts-ignore: has no exported member
5
5
  import { CookieJar, fetch } from "node-fetch-cookies";
6
6
  import { ProxyAgent } from "proxy-agent";
@@ -8,8 +8,8 @@ export const FormType = {
8
8
  URLEncoded: "urlencoded",
9
9
  JSON: "json",
10
10
  };
11
- export class TrackClient {
12
- constructor(scheme, hostname, basic, authData) {
11
+ export class TrackClientBase {
12
+ constructor(baseUrl, basic, cookies, isUnauthorized) {
13
13
  Object.defineProperty(this, "baseUrl", {
14
14
  enumerable: true,
15
15
  configurable: true,
@@ -28,71 +28,34 @@ export class TrackClient {
28
28
  writable: true,
29
29
  value: void 0
30
30
  });
31
- Object.defineProperty(this, "authData", {
32
- enumerable: true,
33
- configurable: true,
34
- writable: true,
35
- value: void 0
36
- });
37
- Object.defineProperty(this, "cookieExpired", {
31
+ Object.defineProperty(this, "agent", {
38
32
  enumerable: true,
39
33
  configurable: true,
40
34
  writable: true,
41
- value: false
35
+ value: new ProxyAgent()
42
36
  });
43
- Object.defineProperty(this, "agent", {
37
+ Object.defineProperty(this, "isUnauthorized", {
44
38
  enumerable: true,
45
39
  configurable: true,
46
40
  writable: true,
47
- value: new ProxyAgent()
41
+ value: void 0
48
42
  });
49
- this.baseUrl = `${scheme}://${hostname}`;
50
- this.headers = {};
51
- this.headers["X-Requested-With"] = "codecheck";
52
- this.headers["User-Agent"] = `track-cli-${VERSION}`;
43
+ this.baseUrl = baseUrl;
44
+ this.headers = {
45
+ "X-Requested-With": "codecheck",
46
+ "User-Agent": `track-cli-${VERSION}`,
47
+ };
53
48
  if (basic) {
54
49
  this.headers["Authorization"] = `Basic ${btoa(`${basic.username}:${basic.password}`)}`;
55
50
  }
56
51
  this.cookies = new CookieJar();
57
- this.authData = authData;
58
- }
59
- getCookies() {
60
- const cookies = {};
61
- for (const cookie of this.cookies.cookiesAll()) {
62
- if (cookie.name && cookie.value) {
63
- cookies[cookie.name] = cookie.value;
64
- }
65
- }
66
- return cookies;
67
- }
68
- authCookieExpired() {
69
- return this.cookieExpired;
70
- }
71
- static fromTrackConfig(config) {
72
- const authData = {
73
- examToken: config.examToken,
74
- orgName: config.orgName,
75
- };
76
- const client = new TrackClient(config.scheme, config.host, config.basicAuth, authData);
77
- if (config.cookies) {
78
- for (const [name, value] of Object.entries(config.cookies)) {
79
- client.cookies.addCookie(`${name}=${value}`, client.baseUrl);
80
- }
81
- }
82
- return client;
83
- }
84
- async authenticate() {
85
- this.cookies = new CookieJar();
86
- if (this.authData) {
87
- const authUrl = `${this.baseUrl}/${this.authData.orgName}/exams/${this.authData.examToken}`;
88
- const res = await fetch(this.cookies, authUrl, {
89
- headers: this.headers,
90
- agent: this.agent,
91
- });
92
- if (!HttpStatus.isSuccess(res)) {
93
- throw new OtherError(`Failed to authenticate: ${res.status}\n${await res.text()}`);
52
+ if (cookies) {
53
+ for (const [name, value] of Object.entries(cookies)) {
54
+ this.cookies.addCookie(`${name}=${value}`, baseUrl);
94
55
  }
95
56
  }
57
+ this.isUnauthorized = isUnauthorized ||
58
+ ((res) => res.status === HttpStatus.Unauthorized);
96
59
  }
97
60
  async _get(path, asText = false) {
98
61
  const url = `${this.baseUrl}${path}`;
@@ -100,12 +63,12 @@ export class TrackClient {
100
63
  method: "GET",
101
64
  headers: this.headers,
102
65
  agent: this.agent,
66
+ redirect: "manual",
103
67
  });
104
- if (response1.status !== HttpStatus.Unauthorized) {
68
+ if (!this.isUnauthorized(response1)) {
105
69
  return await this._result(response1, asText);
106
70
  }
107
71
  await this.authenticate();
108
- this.cookieExpired = true;
109
72
  const response2 = await fetch(this.cookies, url, {
110
73
  method: "GET",
111
74
  headers: this.headers,
@@ -134,12 +97,12 @@ export class TrackClient {
134
97
  headers: headers,
135
98
  body: bodyData,
136
99
  agent: this.agent,
100
+ redirect: "manual",
137
101
  });
138
- if (response1.status !== HttpStatus.Unauthorized) {
102
+ if (!this.isUnauthorized(response1)) {
139
103
  return await this._result(response1);
140
104
  }
141
105
  await this.authenticate();
142
- this.cookieExpired = true;
143
106
  const response2 = await fetch(this.cookies, url, {
144
107
  method: "POST",
145
108
  headers: headers,
@@ -169,12 +132,12 @@ export class TrackClient {
169
132
  headers: headers,
170
133
  body: bodyData,
171
134
  agent: this.agent,
135
+ redirect: "manual",
172
136
  });
173
- if (response1.status !== HttpStatus.Unauthorized) {
137
+ if (!this.isUnauthorized(response1)) {
174
138
  return await this._result(response1);
175
139
  }
176
140
  await this.authenticate();
177
- this.cookieExpired = true;
178
141
  const response2 = await fetch(this.cookies, url, {
179
142
  method: "PUT",
180
143
  headers: headers,
@@ -202,67 +165,13 @@ export class TrackClient {
202
165
  throw await APIError.fromResponse(response);
203
166
  }
204
167
  }
205
- async getApplicantExamNoAuth(token) {
206
- return await this._get(`/api/applicants/exams/${token}/min`);
207
- }
208
- async getExamSession(applicantExamId) {
209
- return await this._get(`/api/applicants/exams/${applicantExamId}`);
210
- }
211
- async getProgrammingLanguages() {
212
- return await this._get(`/api/enum/challenges/programminglanguages`);
213
- }
214
- async findProgrammingLanguage(programmingLanguage) {
215
- return (await this.getProgrammingLanguages())
216
- .find((pl, _) => pl.value === programmingLanguage);
217
- }
218
- async switchChallengeLanguage(applicantExamId, resultId, language) {
219
- return await this._put(`/api/applicants/exams/${applicantExamId}/results/${resultId}/language`, FormType.URLEncoded, {
220
- programmingLanguage: language,
221
- });
222
- }
223
- async startChallenge(applicantExamId, resultId) {
224
- return await this._put(`/api/applicants/exams/${applicantExamId}/results/${resultId}/start`, FormType.URLEncoded, {
225
- takenBy: 3, // 3 = LocalMachine
226
- });
227
- }
228
- async prepareChallenge(applicantExamId, challengeId) {
229
- return await this._post(`/api/applicants/exams/${applicantExamId}/results`, FormType.URLEncoded, {
230
- challengeId: challengeId,
231
- });
232
- }
233
- async timeLeft(applicantExamId, resultId) {
234
- return await this._get(`/api/applicants/exams/${applicantExamId}/results/${resultId}/timeleft`);
235
- }
236
- async getChallengeCodingContext(applicantExamId, resultId) {
237
- return await this._get(`/api/applicants/exams/${applicantExamId}/results/${resultId}/context`);
238
- }
239
- async getPresignedUrls(applicantExamId, resultId, files) {
240
- return await this._post(`/api/applicants/exams/${applicantExamId}/results/${resultId}/presigned`, FormType.JSON, {
241
- files: files,
242
- });
243
- }
244
- async orcaToken(url) {
245
- return await this._get(url);
246
- }
247
- /**
248
- * Temporary code to retrieve the Orca host.
249
- * Will be able to get from CodingContext in the future.
250
- */
251
- async orcaHost() {
252
- const asText = true;
253
- const editorHtml = await this._get("/editor/1.0/coding", asText);
254
- return (/data-orca-host="([^"]+)"/.exec(editorHtml) ||
255
- ["", "track-prod-frontend.orca.run"])[1];
256
- }
257
- async saveFiles(applicantExamId, resultId, saveFilesRequest) {
258
- return await this._put(`/api/applicants/exams/${applicantExamId}/results/${resultId}/save`, FormType.JSON, saveFilesRequest);
259
- }
260
- async updateEditorScore(applicantExamId, resultId, editorScore) {
261
- return await this._put(`/api/applicants/exams/${applicantExamId}/results/${resultId}/editorScore`, FormType.URLEncoded, {
262
- editorScore: editorScore,
263
- });
264
- }
265
- async reset(applicantExamId, resultId) {
266
- return await this._put(`/api/applicants/exams/${applicantExamId}/results/${resultId}/reset`, FormType.URLEncoded, {});
168
+ getCookies() {
169
+ const cookies = {};
170
+ for (const cookie of this.cookies.cookiesAll()) {
171
+ if (cookie.name && cookie.value) {
172
+ cookies[cookie.name] = cookie.value;
173
+ }
174
+ }
175
+ return cookies;
267
176
  }
268
177
  }
@@ -0,0 +1,32 @@
1
+ import { TrackTestConfigPart } from "../shared/config.js";
2
+ import { ChallengeResult, ChallengeSession, CodingContext, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
3
+ import { BasicAuth, TrackClient, TrackClientBase } from "./client.js";
4
+ export declare class TestClient extends TrackClientBase implements TrackClient {
5
+ private orgName;
6
+ private token;
7
+ private challengeId;
8
+ private applicantExamId;
9
+ private resultId;
10
+ constructor(baseUrl: string, orgName: string, token: string, challengeId: number, basic?: BasicAuth, cookies?: Record<string, string>);
11
+ config(): TrackTestConfigPart;
12
+ authenticate(): Promise<void>;
13
+ startChallengeSession(): Promise<ChallengeSession>;
14
+ private getApplicantExamNoAuth;
15
+ private ensureExamInProgress;
16
+ private getExamSession;
17
+ private getChallengeFromExamSession;
18
+ languages(): Promise<ProgrammingLanguageInfo[]>;
19
+ start(): Promise<ChallengeResult>;
20
+ prepare(): Promise<ChallengeResult>;
21
+ updateLanguage(language: ProgrammingLanguage): Promise<void>;
22
+ timeLeft(): Promise<number>;
23
+ context(): Promise<CodingContext>;
24
+ presigned(files: string[]): Promise<{
25
+ [name: string]: string;
26
+ }>;
27
+ orcaHost(): Promise<string>;
28
+ orcaToken(context: CodingContext): Promise<string>;
29
+ saveFiles(files: SaveFilesRequest): Promise<void>;
30
+ updateEditorScore(score: number): Promise<void>;
31
+ reset(): Promise<void>;
32
+ }
@@ -0,0 +1,184 @@
1
+ import { ExamCanceled, ExamExpired, ExamSubmitted, ExamUnread, InvalidChallengeId, OtherError, } from "../shared/errors.js";
2
+ import { HttpStatus } from "../shared/mod.js";
3
+ import { ChallengeStyle, } from "../shared/types.js";
4
+ import { FormType, TrackClientBase, } from "./client.js";
5
+ // @ts-ignore: has no exported member
6
+ import { CookieJar, fetch } from "node-fetch-cookies";
7
+ export class TestClient extends TrackClientBase {
8
+ constructor(baseUrl, orgName, token, challengeId, basic, cookies) {
9
+ super(baseUrl, basic, cookies);
10
+ Object.defineProperty(this, "orgName", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: void 0
15
+ });
16
+ Object.defineProperty(this, "token", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: void 0
21
+ });
22
+ Object.defineProperty(this, "challengeId", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
28
+ Object.defineProperty(this, "applicantExamId", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: 0
33
+ });
34
+ Object.defineProperty(this, "resultId", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: 0
39
+ });
40
+ this.orgName = orgName;
41
+ this.token = token;
42
+ this.challengeId = challengeId;
43
+ }
44
+ config() {
45
+ return {
46
+ challengeId: this.challengeId,
47
+ applicantExamId: this.applicantExamId,
48
+ challengeResultId: this.resultId,
49
+ };
50
+ }
51
+ async authenticate() {
52
+ this.cookies = new CookieJar();
53
+ const authUrl = `${this.baseUrl}/${this.orgName}/exams/${this.token}`;
54
+ const res = await fetch(this.cookies, authUrl, {
55
+ headers: this.headers,
56
+ agent: this.agent,
57
+ });
58
+ if (!HttpStatus.isSuccess(res)) {
59
+ throw new OtherError(`Failed to authenticate: ${res.status}\n${await res.text()}`);
60
+ }
61
+ }
62
+ async startChallengeSession() {
63
+ const applicantExam = await this.getApplicantExamNoAuth();
64
+ const webUrl = `${this.baseUrl}/${this.orgName}/exams/${this.token}`;
65
+ this.ensureExamInProgress(applicantExam.status, webUrl);
66
+ const examSession = await this.getExamSession(applicantExam.id);
67
+ const result = examSession.results.find((r) => r.challengeId === this.challengeId);
68
+ const challenge = this.getChallengeFromExamSession(examSession, result);
69
+ if (!challenge) {
70
+ throw new InvalidChallengeId(this.challengeId);
71
+ }
72
+ this.applicantExamId = applicantExam.id;
73
+ this.resultId = result?.id || 0;
74
+ return challenge;
75
+ }
76
+ async getApplicantExamNoAuth() {
77
+ return await this._get(`/api/applicants/exams/${this.token}/min`);
78
+ }
79
+ ensureExamInProgress(status, webUrlRedirect) {
80
+ switch (status) {
81
+ case ApplicantExamStatus.Submitted:
82
+ case ApplicantExamStatus.Reviewed:
83
+ throw new ExamSubmitted();
84
+ case ApplicantExamStatus.Expired:
85
+ throw new ExamExpired();
86
+ case ApplicantExamStatus.Canceled:
87
+ throw new ExamCanceled();
88
+ case ApplicantExamStatus.Unread:
89
+ throw new ExamUnread(webUrlRedirect);
90
+ case ApplicantExamStatus.InProgress:
91
+ return;
92
+ }
93
+ }
94
+ async getExamSession(applicantExamId) {
95
+ return await this._get(`/api/applicants/exams/${applicantExamId}`);
96
+ }
97
+ getChallengeFromExamSession(examSession, result) {
98
+ return examSession.challengesSets
99
+ .flatMap((s) => s.challenges)
100
+ .map((esc) => toChallengeSession(esc, result))
101
+ .find((c) => c.challengeId === this.challengeId &&
102
+ (c.style === ChallengeStyle.Development ||
103
+ c.style === ChallengeStyle.Algorithm));
104
+ }
105
+ async languages() {
106
+ return (await this._get(`/api/enum/challenges/programminglanguages`))
107
+ .map(toProgrammingLanguageInfo);
108
+ }
109
+ async start() {
110
+ return await this._put(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/start`, FormType.URLEncoded, {
111
+ takenBy: 3, // 3 = LocalMachine
112
+ });
113
+ }
114
+ async prepare() {
115
+ return await this._post(`/api/applicants/exams/${this.applicantExamId}/results`, FormType.URLEncoded, {
116
+ challengeId: this.challengeId,
117
+ });
118
+ }
119
+ async updateLanguage(language) {
120
+ return await this._put(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/language`, FormType.URLEncoded, {
121
+ programmingLanguage: language,
122
+ }); // ChallengeResult
123
+ }
124
+ async timeLeft() {
125
+ return await this._get(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/timeleft`);
126
+ }
127
+ async context() {
128
+ return await this._get(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/context`);
129
+ }
130
+ async presigned(files) {
131
+ return await this._post(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/presigned`, FormType.JSON, {
132
+ files: files,
133
+ });
134
+ }
135
+ async orcaHost() {
136
+ const asText = true;
137
+ const editorHtml = await this._get("/editor/1.0/coding", asText);
138
+ return (/data-orca-host="([^"]+)"/.exec(editorHtml) ||
139
+ ["", "track-prod-frontend.orca.run"])[1];
140
+ }
141
+ async orcaToken(context) {
142
+ return await this._get(context.urlForOrcaToken);
143
+ }
144
+ async saveFiles(files) {
145
+ return await this._put(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/save`, FormType.JSON, files);
146
+ }
147
+ async updateEditorScore(score) {
148
+ return await this._put(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/editorScore`, FormType.URLEncoded, {
149
+ editorScore: score,
150
+ });
151
+ }
152
+ async reset() {
153
+ return await this._put(`/api/applicants/exams/${this.applicantExamId}/results/${this.resultId}/reset`, FormType.URLEncoded, {});
154
+ }
155
+ }
156
+ const ApplicantExamStatus = {
157
+ Unread: 1,
158
+ InProgress: 2,
159
+ Submitted: 3,
160
+ Expired: 4,
161
+ Canceled: 5,
162
+ Reviewed: 6,
163
+ };
164
+ function toChallengeSession(esc, result) {
165
+ return {
166
+ challengeId: esc.id,
167
+ challengeVersionId: esc.challengeVersionId,
168
+ style: esc.style,
169
+ title: esc.title,
170
+ description: esc.description,
171
+ timeLimitMinutes: esc.timeLimitMinutes,
172
+ displayOrder: esc.displayOrder,
173
+ programmingLanguages: esc.programmingLanguages,
174
+ localCodingAllowed: esc.localExamEnabled,
175
+ openTestcases: esc.openTestcases,
176
+ result,
177
+ };
178
+ }
179
+ function toProgrammingLanguageInfo(pl) {
180
+ return {
181
+ value: pl.value,
182
+ name: pl.displayString,
183
+ };
184
+ }
@@ -0,0 +1,29 @@
1
+ import { TrackTrainingConfigPart } from "../shared/config.js";
2
+ import { ChallengeResult, ChallengeSession, CodingContext, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
3
+ import { BasicAuth, TrackClient, TrackClientBase } from "./client.js";
4
+ export declare class TrainingClient extends TrackClientBase implements TrackClient {
5
+ private orgName;
6
+ private token;
7
+ private courseMaterialId;
8
+ private klassId;
9
+ private klassResultId;
10
+ constructor(baseUrl: string, orgName: string, token: string, courseMaterialId: number, klassId: number, klassResultId: number, basic?: BasicAuth, cookies?: Record<string, string>);
11
+ config(): TrackTrainingConfigPart;
12
+ authenticate(): Promise<void>;
13
+ startChallengeSession(): Promise<ChallengeSession>;
14
+ continueChallengeSession(): Promise<ChallengeSession>;
15
+ languages(): Promise<ProgrammingLanguageInfo[]>;
16
+ start(): Promise<ChallengeResult>;
17
+ prepare(): Promise<ChallengeResult>;
18
+ updateLanguage(language: ProgrammingLanguage): Promise<void>;
19
+ timeLeft(): Promise<number>;
20
+ context(): Promise<CodingContext>;
21
+ presigned(files: string[]): Promise<{
22
+ [name: string]: string;
23
+ }>;
24
+ orcaHost(): Promise<string>;
25
+ orcaToken(context: CodingContext): Promise<string>;
26
+ saveFiles(files: SaveFilesRequest): Promise<void>;
27
+ updateEditorScore(score: number): Promise<void>;
28
+ reset(): Promise<void>;
29
+ }
@@ -0,0 +1,149 @@
1
+ import { OtherError } from "../shared/errors.js";
2
+ import { HttpStatus } from "../shared/mod.js";
3
+ import { FormType, TrackClientBase, } from "./client.js";
4
+ // @ts-ignore: has no exported member
5
+ import { CookieJar, fetch } from "node-fetch-cookies";
6
+ export class TrainingClient extends TrackClientBase {
7
+ constructor(baseUrl, orgName, token, courseMaterialId, klassId, klassResultId, basic, cookies) {
8
+ super(baseUrl, basic, cookies, (res) => HttpStatus.isRedirect(res) || res.status === HttpStatus.Unauthorized);
9
+ Object.defineProperty(this, "orgName", {
10
+ enumerable: true,
11
+ configurable: true,
12
+ writable: true,
13
+ value: void 0
14
+ });
15
+ Object.defineProperty(this, "token", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ Object.defineProperty(this, "courseMaterialId", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: void 0
26
+ });
27
+ Object.defineProperty(this, "klassId", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: void 0
32
+ });
33
+ Object.defineProperty(this, "klassResultId", {
34
+ enumerable: true,
35
+ configurable: true,
36
+ writable: true,
37
+ value: void 0
38
+ });
39
+ this.orgName = orgName;
40
+ this.token = token;
41
+ this.courseMaterialId = courseMaterialId;
42
+ this.klassId = klassId;
43
+ this.klassResultId = klassResultId;
44
+ }
45
+ config() {
46
+ return {
47
+ courseMaterialId: this.courseMaterialId,
48
+ klassId: this.klassId,
49
+ klassResultId: this.klassResultId,
50
+ };
51
+ }
52
+ async authenticate() {
53
+ this.cookies = new CookieJar();
54
+ const authUrl = `${this.baseUrl}/${this.orgName}/train/${this.token}/challenges/${this.courseMaterialId}`;
55
+ const res = await fetch(this.cookies, authUrl, {
56
+ headers: this.headers,
57
+ agent: this.agent,
58
+ });
59
+ if (!HttpStatus.isSuccess(res)) {
60
+ throw new OtherError(`Failed to authenticate: ${res.status}\n${await res.text()}`);
61
+ }
62
+ const { klassId } = (await res.json()).result;
63
+ this.klassId = klassId;
64
+ }
65
+ async startChallengeSession() {
66
+ const trainingChallenge = await this._post(`/api/train/${this.klassId}/challenges/${this.courseMaterialId}`, FormType.JSON, {});
67
+ this.klassResultId = trainingChallenge.result.klassResultId;
68
+ return toChallengeSession(trainingChallenge);
69
+ }
70
+ continueChallengeSession() {
71
+ return this.startChallengeSession();
72
+ }
73
+ async languages() {
74
+ return (await this._get(`/cli/languages`))
75
+ .map(toProgrammingLanguageInfo);
76
+ }
77
+ async start() {
78
+ return await this._put(`/api/train/${this.klassId}/challenges/results/${this.klassResultId}/start`, FormType.JSON, {
79
+ takenBy: 3, // 3 = LocalMachine
80
+ });
81
+ }
82
+ prepare() {
83
+ throw new OtherError("Unsupported operation");
84
+ }
85
+ async updateLanguage(language) {
86
+ await this._put(`/api/train/${this.klassId}/challenges/results/${this.klassResultId}/lang`, FormType.JSON, {
87
+ lang: language,
88
+ });
89
+ }
90
+ async timeLeft() {
91
+ return await this._get(`/api/train/${this.klassId}/challenges/results/${this.klassResultId}/timeleft`);
92
+ }
93
+ async context() {
94
+ return await this._get(`/api/train/${this.klassId}/challenges/results/${this.klassResultId}/context`);
95
+ }
96
+ async presigned(files) {
97
+ return await this._post(`/api/train/${this.klassId}/challenges/review/${this.klassResultId}/presigned`, FormType.JSON, {
98
+ files: files,
99
+ });
100
+ }
101
+ async orcaHost() {
102
+ const asText = true;
103
+ const editorHtml = await this._get("/editor/1.0/coding", asText);
104
+ return (/data-orca-host="([^"]+)"/.exec(editorHtml) ||
105
+ ["", "track-prod-frontend.orca.run"])[1];
106
+ }
107
+ async orcaToken(context) {
108
+ return await this._get(context.urlForOrcaToken);
109
+ }
110
+ async saveFiles(files) {
111
+ return await this._put(`/api/train/${this.klassId}/challenges/results/${this.klassResultId}/save`, FormType.JSON, files);
112
+ }
113
+ async updateEditorScore(score) {
114
+ // TODO: implement the API side
115
+ await Promise.resolve();
116
+ console.log(score);
117
+ // return await this._put(
118
+ // `/api/train/${this.klassId}/challenges/results/${this.klassResultId}/editorScore`,
119
+ // FormType.JSON,
120
+ // {
121
+ // editorScore: score,
122
+ // },
123
+ // );
124
+ }
125
+ async reset() {
126
+ return await this._put(`/api/train/${this.klassId}/challenges/results/${this.klassResultId}/reset`, FormType.JSON, {});
127
+ }
128
+ }
129
+ function toChallengeSession(tcs) {
130
+ return {
131
+ challengeId: tcs.challenge.id,
132
+ challengeVersionId: tcs.challenge.challengeVersionId,
133
+ style: tcs.challenge.style,
134
+ title: tcs.challenge.title,
135
+ description: tcs.challenge.description,
136
+ timeLimitMinutes: tcs.challenge.timeLimitMinutes,
137
+ displayOrder: tcs.challenge.displayOrder,
138
+ openTestcases: tcs.challenge.openTestcases,
139
+ programmingLanguages: tcs.challenge.programmingLanguages,
140
+ localCodingAllowed: tcs.challenge.localCodingEnabled,
141
+ result: tcs.result,
142
+ };
143
+ }
144
+ function toProgrammingLanguageInfo(pl) {
145
+ return {
146
+ value: pl.id,
147
+ name: pl.name,
148
+ };
149
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "track": "./esm/src/main.js"
4
4
  },
5
5
  "name": "track-cli",
6
- "version": "3.1.0",
6
+ "version": "4.0.0-rc0",
7
7
  "description": "A CLI for interacting with tracks.run and running code tests on track's servers",
8
8
  "license": "MIT",
9
9
  "repository": {