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,13 +1,15 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
- import { load as loadConfig, save as saveConfig, webUrl, } from "./config.js";
3
- import { BadTarballURL, ChallengeAlreadyFinished, ExamCanceled, ExamExpired, ExamSubmitted, ExamUnread, OtherError, } from "./errors.js";
4
- import { getUniqueDirName, toNativeStyle, untgz } from "./file.js";
5
- import { TrackClient } from "../track/client.js";
6
- import { ApplicantExamStatus, ChallengeResultStatus, ChallengeStyle, FileListType, } from "../track/types.js";
2
+ import { save as saveConfig, } from "./config.js";
3
+ import { BadTarballURL, ChallengeAlreadyFinished, OtherError, } from "./errors.js";
4
+ import { toNativeStyle, untgz } from "./file.js";
5
+ import { ChallengeResultStatus, FileListType, TrackApp, } from "./types.js";
6
+ import { TestClient } from "../track/test.js";
7
+ import { TrainingClient } from "../track/training.js";
7
8
  import * as datetime from "../../deps/deno.land/std@0.195.0/datetime/mod.js";
8
9
  import * as colors from "../../deps/deno.land/std@0.195.0/fmt/colors.js";
9
10
  import { format as duration } from "../../deps/deno.land/std@0.195.0/fmt/duration.js";
10
11
  import * as fs from "../../deps/deno.land/std@0.195.0/fs/mod.js";
12
+ import { exists } from "../../deps/deno.land/std@0.195.0/fs/mod.js";
11
13
  export const _internals = {
12
14
  prompt: dntShim.prompt,
13
15
  scoreRatio: scoreRatio,
@@ -19,6 +21,10 @@ export const HttpStatus = {
19
21
  const status = typeof r === "number" ? r : r.status;
20
22
  return 300 > status && status >= 200;
21
23
  },
24
+ isRedirect(r) {
25
+ const status = typeof r === "number" ? r : r.status;
26
+ return 400 > status && status >= 300;
27
+ },
22
28
  isClientError(r) {
23
29
  const status = typeof r === "number" ? r : r.status;
24
30
  return 500 > status && status >= 400;
@@ -68,30 +74,8 @@ export function printTimeLeft(timeLeftSeconds) {
68
74
  console.log("The exam deadline has passed");
69
75
  }
70
76
  }
71
- export function ensureExamInProgress(applicantExam, webUrlRedirect) {
72
- switch (applicantExam.status) {
73
- case ApplicantExamStatus.Submitted:
74
- case ApplicantExamStatus.Reviewed:
75
- throw new ExamSubmitted();
76
- case ApplicantExamStatus.Expired:
77
- throw new ExamExpired();
78
- case ApplicantExamStatus.Canceled:
79
- throw new ExamCanceled();
80
- case ApplicantExamStatus.Unread:
81
- throw new ExamUnread(webUrlRedirect);
82
- case ApplicantExamStatus.InProgress:
83
- return;
84
- }
85
- }
86
- export function getChallengeFromExamSession(examSession, challengeId) {
87
- return examSession.challengesSets
88
- .flatMap((s) => s.challenges)
89
- .find((c) => c.id === challengeId &&
90
- (c.style === ChallengeStyle.Development ||
91
- c.style === ChallengeStyle.Algorithm));
92
- }
93
- export async function tryStartingChallenge(challenge, api, applicantExam, resultOpt) {
94
- let result = resultOpt;
77
+ export async function tryStartingChallenge(api, challenge) {
78
+ let result = challenge.result;
95
79
  while (true) {
96
80
  if (result) {
97
81
  switch (result.status) {
@@ -109,14 +93,14 @@ export async function tryStartingChallenge(challenge, api, applicantExam, result
109
93
  throw new OtherError("Aborted. Challenge was not started.");
110
94
  }
111
95
  if (challenge.programmingLanguages.length > 0) {
112
- const programmingLanguages = (await api.getProgrammingLanguages())
96
+ const programmingLanguages = (await api.languages())
113
97
  .filter((pl) => challenge.programmingLanguages.includes(pl.value));
114
98
  const desiredLanguage = chooseProgrammingLanguage(programmingLanguages);
115
99
  if (desiredLanguage.value !== result.programmingLanguage) {
116
- await api.switchChallengeLanguage(applicantExam.id, result.id, desiredLanguage.value);
100
+ await api.updateLanguage(desiredLanguage.value);
117
101
  }
118
102
  }
119
- result = await api.startChallenge(applicantExam.id, result.id);
103
+ result = await api.start();
120
104
  break;
121
105
  }
122
106
  case ChallengeResultStatus.Started:
@@ -133,13 +117,13 @@ export async function tryStartingChallenge(challenge, api, applicantExam, result
133
117
  }
134
118
  else {
135
119
  // Challenge hasn't been prepared yet
136
- result = await api.prepareChallenge(applicantExam.id, challenge.id);
120
+ result = await api.prepare();
137
121
  }
138
122
  }
139
123
  }
140
124
  export function chooseProgrammingLanguage(languages) {
141
125
  while (true) {
142
- const desiredLanguageIndex = Prompt.select("Please select a programming language:", languages.map((l) => l.displayString));
126
+ const desiredLanguageIndex = Prompt.select("Please select a programming language:", languages.map((l) => l.name));
143
127
  if (desiredLanguageIndex !== undefined) {
144
128
  return languages[desiredLanguageIndex];
145
129
  }
@@ -244,57 +228,31 @@ export const SnakeCase = {
244
228
  }
245
229
  },
246
230
  };
247
- export async function getCommonChallengeContext() {
248
- const config = await loadConfig();
249
- const api = TrackClient.fromTrackConfig(config);
250
- const applicantExam = await api.getApplicantExamNoAuth(config.examToken);
251
- ensureExamInProgress(applicantExam, webUrl(config));
252
- const codingContext = await api.getChallengeCodingContext(config.applicantExamId, config.challengeResultId);
253
- const examSession = await api.getExamSession(config.applicantExamId);
254
- let result = getResultFromExamSession(examSession, config.challengeId);
255
- if (!result) {
256
- throw new OtherError(`Cchallenge result for challenge id ${config.challengeId} is missing - has the challenge been started yet?`);
257
- }
258
- const challenge = getChallengeFromExamSession(examSession, config.challengeId);
259
- if (!challenge) {
260
- throw new OtherError(`Challenge for challenge id ${config.challengeId} is missing - has the challenge been started yet?`);
231
+ export async function getCommonChallengeContext(config, api) {
232
+ const challenge = await api.startChallengeSession();
233
+ if (!challenge.result) {
234
+ throw new OtherError(`Challenge for challenge id ${challenge.challengeId} is missing - has the challenge been started yet?`);
261
235
  }
236
+ const result = challenge.result;
262
237
  switch (result.status) {
263
238
  case ChallengeResultStatus.Finished:
264
239
  case ChallengeResultStatus.NotModified:
265
240
  throw new ChallengeAlreadyFinished();
266
- case ChallengeResultStatus.CanRestart:
241
+ case ChallengeResultStatus.CanRestart: {
267
242
  console.log("This challenge can be restarted");
268
243
  console.log();
269
- result = await tryStartingChallenge(challenge, api, applicantExam, result);
270
- if (!result) {
244
+ const result2 = await tryStartingChallenge(api, challenge);
245
+ if (!result2) {
271
246
  throw new OtherError("Expected to have a challenge result after preparing and starting");
272
247
  }
273
248
  break;
249
+ }
274
250
  }
275
- const challengePosition = examSession
276
- .challengesSets
277
- .flatMap((s) => s.challenges)
278
- .findIndex((c) => c.id === config.challengeId) +
279
- 1;
280
- if (api.authCookieExpired()) {
281
- config.cookies = api.getCookies();
282
- await saveConfig(config);
283
- }
284
- return {
285
- config,
286
- api,
287
- codingContext,
288
- applicantExam,
289
- challengeResult: result,
290
- challenge,
291
- challengePosition,
292
- };
293
- }
294
- function getResultFromExamSession(examSession, challengeId) {
295
- return examSession
296
- .results
297
- .find((cr) => cr.challengeId === challengeId);
251
+ const codingContext = await api.context();
252
+ // TODO: 保存条件
253
+ config.cookies = api.getCookies();
254
+ await saveConfig(config);
255
+ return [codingContext, challenge];
298
256
  }
299
257
  function scoreRatio(editorScore, openTestcases) {
300
258
  // @ts-expect-error: isFinite(undefined) === false, isFinite(0) === true. See MDN
@@ -305,16 +263,17 @@ function scoreRatio(editorScore, openTestcases) {
305
263
  return "\tN/A - Use 'track run' to save and run your submission";
306
264
  }
307
265
  }
308
- export async function printChallengeInfo(trackContext) {
309
- const { config, api, challengeResult: result } = trackContext;
310
- const timeLeft = await api.timeLeft(config.applicantExamId, config.challengeResultId);
311
- printWorkingFileSet(trackContext.codingContext);
266
+ export async function printChallengeInfo(api, codingContext, challenge) {
267
+ // const { config, api, challengeResult: result } = trackContext;
268
+ const result = challenge.result;
269
+ const timeLeft = await api.timeLeft();
270
+ printWorkingFileSet(codingContext);
312
271
  console.log();
313
272
  const lastScoredAt = result.lastScoredAt
314
273
  ? ` (Ran at ${datetime.format(new Date(result.lastScoredAt), "yyyy-MM-dd HH:mm:ss")})`
315
274
  : "";
316
275
  console.log(`Latest score${lastScoredAt}`);
317
- console.log(colors.green(scoreRatio(result.editorScore, trackContext.challenge.openTestcases)));
276
+ console.log(colors.green(scoreRatio(result.editorScore, challenge.openTestcases)));
318
277
  console.log();
319
278
  printTimeLeft(timeLeft);
320
279
  }
@@ -338,7 +297,7 @@ export function printWorkingFileSet(codingContext) {
338
297
  console.log();
339
298
  }
340
299
  }
341
- export async function downloadChallengeFilesTo(codingContext, dest, api, applicantExamId, challengeResultId, showFileDiff, includeTarball) {
300
+ export async function downloadChallengeFilesTo(codingContext, dest, api, showFileDiff, includeTarball) {
342
301
  if (includeTarball) {
343
302
  console.log("Fetching challenge tarball...");
344
303
  const tarballResp = await dntShim.fetch(codingContext.tarballUrl);
@@ -350,7 +309,7 @@ export async function downloadChallengeFilesTo(codingContext, dest, api, applica
350
309
  }
351
310
  console.log("Downloading editable files");
352
311
  const filesToDownload = listFileNames(codingContext, FileListType.Editable);
353
- const presignedUrls = Object.entries(await api.getPresignedUrls(applicantExamId, challengeResultId, filesToDownload))
312
+ const presignedUrls = Object.entries(await api.presigned(filesToDownload))
354
313
  .map(([file, url]) => ({ file, url }));
355
314
  presignedUrls.sort((a, b) => a.file.localeCompare(b.file));
356
315
  const promises = presignedUrls.map(async ({ file, url }) => {
@@ -370,21 +329,42 @@ export async function downloadChallengeFilesTo(codingContext, dest, api, applica
370
329
  export /**
371
330
  * Archives all files in a challenge directory into a subdirectory of
372
331
  * the challenge directory. Ignores .track/ and other existing archival directories
373
- */ async function archiveExistingChallengeFiles(orgName, challengePosition, options) {
374
- const challengeDir = options?.challengeDir ?? dntShim.Deno.cwd();
375
- const langString = options?.selectedLanguage?.displayString ?? "";
376
- const archivePath = await getUniqueDirName(`${orgName}_challenge_${challengePosition}_old_${langString}`, { root: challengeDir });
377
- console.log(`Your old challenge contents will be archived in ${archivePath}`);
378
- await fs.ensureDir(archivePath);
379
- // Match directories with names like 'ORGNAME_challenge_12_Go_1'
380
- const oldArchivePattern = /.*_challenge_\d+_.+_\d+/;
381
- // Move all files and directories in the challenge dir into the archive path
382
- // Do not copy the track config folder, the archive path, or old archive paths
332
+ */ async function archiveExistingChallengeFiles(orgName) {
333
+ const challengeDir = dntShim.Deno.cwd();
334
+ const base = `${orgName}_challenge_old`;
335
+ let archiveDir;
336
+ let i = 1;
337
+ while (true) {
338
+ archiveDir = `${base}_${i}`;
339
+ const pathExists = await exists(archiveDir);
340
+ if (pathExists) {
341
+ i++;
342
+ continue;
343
+ }
344
+ break;
345
+ }
346
+ console.log(`Your old challenge contents will be archived in ${archiveDir}`);
347
+ await fs.ensureDir(archiveDir);
348
+ const oldArchivePattern = /.*_challenge_old_\d+/;
383
349
  for await (const dirEntry of dntShim.Deno.readDir(challengeDir)) {
384
- if (dirEntry.name === archivePath || dirEntry.name === ".track" ||
350
+ if (dirEntry.name === archiveDir || dirEntry.name === ".track" ||
385
351
  (dirEntry.isDirectory && oldArchivePattern.test(dirEntry.name))) {
386
352
  continue;
387
353
  }
388
- await fs.move(`${challengeDir}/${dirEntry.name}`, `${archivePath}/${dirEntry.name}`);
354
+ await fs.move(`${challengeDir}/${dirEntry.name}`, `${archiveDir}/${dirEntry.name}`);
355
+ }
356
+ }
357
+ export function trackClientFromConfig(config) {
358
+ let api;
359
+ switch (config.app) {
360
+ case TrackApp.Test:
361
+ api = new TestClient(config.baseUrl, config.orgName, config.token, config.challengeId, config.basicAuth, config.cookies);
362
+ break;
363
+ case TrackApp.Training:
364
+ api = new TrainingClient(config.baseUrl, config.orgName, config.token, config.courseMaterialId, config.klassId, config.klassResultId, config.basicAuth, config.cookies);
365
+ break;
366
+ default:
367
+ throw new OtherError(`Unsupported app: ${config.app}`);
389
368
  }
369
+ return api;
390
370
  }
@@ -1,12 +1,142 @@
1
- import { TrackConfig } from "./config.js";
2
- import { TrackClient } from "../track/client.js";
3
- import { ApplicantExamForNoAuth, ChallengeResult, CodingContext, ExamSessionChallenge } from "../track/types.js";
4
- export interface TrackCLIContext {
5
- config: TrackConfig;
6
- api: TrackClient;
7
- codingContext: CodingContext;
8
- applicantExam: ApplicantExamForNoAuth;
9
- challengeResult: ChallengeResult;
10
- challenge: ExamSessionChallenge;
11
- challengePosition: number;
1
+ export declare const TrackApp: {
2
+ readonly Test: 1;
3
+ readonly Training: 2;
4
+ };
5
+ export type TrackApp = typeof TrackApp[keyof typeof TrackApp];
6
+ export declare class CloneUrl {
7
+ app: TrackApp;
8
+ baseUrl: string;
9
+ orgName: string;
10
+ token: string;
11
+ id: number;
12
+ constructor(url: string);
13
+ toString(): string;
12
14
  }
15
+ export interface ProgrammingLanguageInfo {
16
+ value: ProgrammingLanguage;
17
+ name: string;
18
+ }
19
+ export interface ChallengeSession {
20
+ challengeId: number;
21
+ challengeVersionId: number;
22
+ style: ChallengeStyle;
23
+ title: string;
24
+ description?: string;
25
+ timeLimitMinutes?: number;
26
+ displayOrder: number;
27
+ programmingLanguages: ProgrammingLanguage[];
28
+ localCodingAllowed: boolean;
29
+ openTestcases?: number;
30
+ result?: ChallengeResult;
31
+ }
32
+ export interface ChallengeResult {
33
+ challengeId: number;
34
+ challengeVersionId: number;
35
+ dueDate?: string;
36
+ storageId: string;
37
+ status: ChallengeResultStatus;
38
+ openedAt?: string;
39
+ finishedAt?: string;
40
+ totalTestcases?: number;
41
+ successfulTestcases?: number;
42
+ editorScore?: number;
43
+ testOutput?: string;
44
+ lastScoredAt?: string;
45
+ programmingLanguage?: ProgrammingLanguage;
46
+ }
47
+ export interface CodingContext {
48
+ challengeId: number;
49
+ challengeVersionId: number;
50
+ challengeResultId: number;
51
+ title: string;
52
+ urlForPresign: string;
53
+ urlForOrcaToken?: string;
54
+ methodForPresign: string;
55
+ urlForSave?: string;
56
+ tarballUrl: string;
57
+ tarballUrls: string[];
58
+ allowSave: boolean;
59
+ allowEdit?: boolean;
60
+ programmingLanguages: ProgrammingLanguage[];
61
+ selectedLanguage?: ProgrammingLanguage;
62
+ challengeLanguage: SpokenLanguage;
63
+ settings: ChallengeSettings;
64
+ answers: CodingAnswers;
65
+ orcaHost?: string;
66
+ }
67
+ export declare const FileListType: {
68
+ readonly All: 0;
69
+ readonly ReadOnlyChallengeFiles: 1;
70
+ readonly EditableChallengeFiles: 2;
71
+ readonly UserAddedFiles: 3;
72
+ readonly Editable: 4;
73
+ };
74
+ export type FileListType = (typeof FileListType)[keyof typeof FileListType];
75
+ export interface ChallengeSettings {
76
+ files: FileTree;
77
+ hiddenFiles: string[];
78
+ testExcludes: string[];
79
+ images: string[];
80
+ build: string[];
81
+ test: string;
82
+ mainFile?: string;
83
+ allowNewFile: boolean;
84
+ languages: string[];
85
+ sample?: string;
86
+ envConfig?: EnvSettings;
87
+ initialize: string[];
88
+ }
89
+ export type FileTree = {
90
+ [key: string]: FileTree | boolean;
91
+ };
92
+ export interface EnvSettings {
93
+ imageName: string;
94
+ workingDir?: string;
95
+ baseDir?: string;
96
+ username?: string;
97
+ variables?: Record<string, string>;
98
+ cacheDirs: string[];
99
+ }
100
+ export declare const EnvSettings: {
101
+ default: EnvSettings;
102
+ };
103
+ export interface TestCases {
104
+ open: number;
105
+ secret: number;
106
+ }
107
+ export interface CodingAnswers {
108
+ build?: string[];
109
+ appCommand?: string;
110
+ mainFile?: string;
111
+ addedFiles?: string[];
112
+ fileVersions: object;
113
+ envConfig?: EnvSettings;
114
+ }
115
+ export interface SaveFilesRequest {
116
+ versionId: number;
117
+ addedFiles: string[];
118
+ updatedFiles: Record<string, string>;
119
+ }
120
+ export declare const ChallengeStyle: {
121
+ readonly Quiz: 1;
122
+ readonly Development: 2;
123
+ readonly Algorithm: 3;
124
+ readonly Ai: 4;
125
+ };
126
+ export type ChallengeStyle = (typeof ChallengeStyle)[keyof typeof ChallengeStyle];
127
+ export declare const ChallengeResultStatus: {
128
+ readonly Prepared: 0;
129
+ readonly Started: 1;
130
+ readonly InProgress: 2;
131
+ readonly Finished: 3;
132
+ readonly NotModified: 4;
133
+ readonly CanRestart: 5;
134
+ readonly ScoringWaiting: 6;
135
+ };
136
+ export type ChallengeResultStatus = (typeof ChallengeResultStatus)[keyof typeof ChallengeResultStatus];
137
+ export declare const SpokenLanguage: {
138
+ readonly English: "en";
139
+ readonly Japanese: "ja";
140
+ };
141
+ export type SpokenLanguage = (typeof SpokenLanguage)[keyof typeof SpokenLanguage];
142
+ export type ProgrammingLanguage = number;
@@ -1 +1,100 @@
1
- export {};
1
+ export const TrackApp = {
2
+ Test: 1,
3
+ Training: 2,
4
+ };
5
+ const TRACK_TEST_URL_PATTERN = new RegExp("([^/]+)/exams/([^/]+)/challenges/(\\d+)");
6
+ const TRACK_TRAINING_URL_PATTERN = new RegExp("([^/]+)/train/([^/]+)/challenges/(\\d+)");
7
+ export class CloneUrl {
8
+ constructor(url) {
9
+ Object.defineProperty(this, "app", {
10
+ enumerable: true,
11
+ configurable: true,
12
+ writable: true,
13
+ value: void 0
14
+ });
15
+ Object.defineProperty(this, "baseUrl", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ Object.defineProperty(this, "orgName", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: void 0
26
+ });
27
+ Object.defineProperty(this, "token", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: void 0
32
+ });
33
+ Object.defineProperty(this, "id", {
34
+ enumerable: true,
35
+ configurable: true,
36
+ writable: true,
37
+ value: void 0
38
+ });
39
+ const urlObj = new URL(url);
40
+ const scheme = urlObj.protocol.slice(0, -1);
41
+ const host = urlObj.host + (urlObj.port ? ":" + urlObj.port : "");
42
+ this.baseUrl = `${scheme}://${host}`;
43
+ let caps;
44
+ if ((caps = TRACK_TEST_URL_PATTERN.exec(url))) {
45
+ this.app = TrackApp.Test;
46
+ this.orgName = caps[1];
47
+ this.token = caps[2];
48
+ this.id = parseInt(caps[3]);
49
+ }
50
+ else if ((caps = TRACK_TRAINING_URL_PATTERN.exec(url))) {
51
+ this.app = TrackApp.Training;
52
+ this.orgName = caps[1];
53
+ this.token = caps[2];
54
+ this.id = parseInt(caps[3]);
55
+ }
56
+ else {
57
+ throw new Error(`Invalid URL: ${url}`);
58
+ }
59
+ }
60
+ toString() {
61
+ switch (this.app) {
62
+ case TrackApp.Test:
63
+ return `${this.baseUrl}/${this.orgName}/exams/${this.token}/challenges/${this.id}`;
64
+ case TrackApp.Training:
65
+ return `${this.baseUrl}/${this.orgName}/train/${this.token}/challenges/${this.id}`;
66
+ }
67
+ }
68
+ }
69
+ export const FileListType = {
70
+ All: 0,
71
+ ReadOnlyChallengeFiles: 1,
72
+ EditableChallengeFiles: 2,
73
+ UserAddedFiles: 3,
74
+ Editable: 4,
75
+ };
76
+ export const EnvSettings = {
77
+ default: {
78
+ imageName: "givery/codecheck:latest",
79
+ cacheDirs: [],
80
+ },
81
+ };
82
+ export const ChallengeStyle = {
83
+ Quiz: 1,
84
+ Development: 2,
85
+ Algorithm: 3,
86
+ Ai: 4,
87
+ };
88
+ export const ChallengeResultStatus = {
89
+ Prepared: 0,
90
+ Started: 1,
91
+ InProgress: 2,
92
+ Finished: 3,
93
+ NotModified: 4,
94
+ CanRestart: 5,
95
+ ScoringWaiting: 6,
96
+ };
97
+ export const SpokenLanguage = {
98
+ English: "en",
99
+ Japanese: "ja",
100
+ };
@@ -1,13 +1,12 @@
1
- import { TrackConfig } from "../shared/config.js";
2
- import { ApplicantExamForNoAuth, ChallengeResult, CodingContext, ExamSession, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "./types.js";
1
+ import * as dntShim from "../../_dnt.shims.js";
2
+ import { TrackTestConfigPart, TrackTrainingConfigPart } from "../shared/config.js";
3
+ import { ChallengeResult, ChallengeSession, CodingContext, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
4
+ import { CookieJar } from "node-fetch-cookies";
5
+ import { ProxyAgent } from "proxy-agent";
3
6
  export interface BasicAuth {
4
7
  username: string;
5
8
  password?: string;
6
9
  }
7
- export interface AuthData {
8
- examToken: string;
9
- orgName: string;
10
- }
11
10
  export interface ApiResponse<T> {
12
11
  code: number;
13
12
  message?: string[];
@@ -18,43 +17,41 @@ export declare const FormType: {
18
17
  JSON: string;
19
18
  };
20
19
  export type FormType = typeof FormType[keyof typeof FormType];
21
- export declare class TrackClient {
22
- private baseUrl;
23
- private headers;
24
- private cookies;
25
- private authData?;
26
- private cookieExpired;
27
- private agent;
28
- constructor(scheme: string, hostname: string, basic?: BasicAuth, authData?: AuthData);
20
+ export declare abstract class TrackClientBase {
21
+ protected baseUrl: string;
22
+ protected headers: Record<string, string>;
23
+ protected cookies: CookieJar;
24
+ protected agent: ProxyAgent;
25
+ private isUnauthorized;
26
+ abstract authenticate(): Promise<void>;
27
+ constructor(baseUrl: string, basic?: BasicAuth, cookies?: Record<string, string>, isUnauthorized?: (res: dntShim.Response) => boolean);
28
+ protected _get<T>(path: string, asText?: boolean): Promise<T>;
29
+ protected _post<T, F>(path: string, formType: FormType, body: F): Promise<T>;
30
+ protected _put<T, F>(path: string, formType: FormType, body: F): Promise<T>;
31
+ private _result;
29
32
  getCookies(): {
30
33
  [name: string]: string;
31
34
  };
32
- authCookieExpired(): boolean;
33
- static fromTrackConfig(config: TrackConfig): TrackClient;
35
+ }
36
+ export interface TrackClient {
37
+ config(): TrackTestConfigPart | TrackTrainingConfigPart;
34
38
  authenticate(): Promise<void>;
35
- private _get;
36
- private _post;
37
- private _put;
38
- private _result;
39
- getApplicantExamNoAuth(token: string): Promise<ApplicantExamForNoAuth>;
40
- getExamSession(applicantExamId: number): Promise<ExamSession>;
41
- getProgrammingLanguages(): Promise<ProgrammingLanguageInfo[]>;
42
- findProgrammingLanguage(programmingLanguage: ProgrammingLanguage): Promise<ProgrammingLanguageInfo | undefined>;
43
- switchChallengeLanguage(applicantExamId: number, resultId: number, language: ProgrammingLanguage): Promise<ChallengeResult>;
44
- startChallenge(applicantExamId: number, resultId: number): Promise<ChallengeResult>;
45
- prepareChallenge(applicantExamId: number, challengeId: number): Promise<ChallengeResult>;
46
- timeLeft(applicantExamId: number, resultId: number): Promise<number>;
47
- getChallengeCodingContext(applicantExamId: number, resultId: number): Promise<CodingContext>;
48
- getPresignedUrls(applicantExamId: number, resultId: number, files: string[]): Promise<{
39
+ getCookies(): {
40
+ [name: string]: string;
41
+ };
42
+ startChallengeSession(): Promise<ChallengeSession>;
43
+ languages(): Promise<ProgrammingLanguageInfo[]>;
44
+ start(): Promise<ChallengeResult>;
45
+ prepare(): Promise<ChallengeResult>;
46
+ updateLanguage(language: ProgrammingLanguage): Promise<void>;
47
+ timeLeft(): Promise<number>;
48
+ context(): Promise<CodingContext>;
49
+ presigned(files: string[]): Promise<{
49
50
  [name: string]: string;
50
51
  }>;
51
- orcaToken(url: string): Promise<string>;
52
- /**
53
- * Temporary code to retrieve the Orca host.
54
- * Will be able to get from CodingContext in the future.
55
- */
56
52
  orcaHost(): Promise<string>;
57
- saveFiles(applicantExamId: number, resultId: number, saveFilesRequest: SaveFilesRequest): Promise<ChallengeResult>;
58
- updateEditorScore(applicantExamId: number, resultId: number, editorScore: number): Promise<ChallengeResult>;
59
- reset(applicantExamId: number, resultId: number): Promise<ChallengeResult>;
53
+ orcaToken(context: CodingContext): Promise<string>;
54
+ saveFiles(files: SaveFilesRequest): Promise<void>;
55
+ updateEditorScore(score: number): Promise<void>;
56
+ reset(): Promise<void>;
60
57
  }