Skip to content

Commit 24eb671

Browse files
committed
Add timeout
1 parent f89c593 commit 24eb671

File tree

2 files changed

+117
-2
lines changed

2 files changed

+117
-2
lines changed

components/public-api/typescript-common/src/public-api-converter.spec.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,64 @@ describe("PublicAPIConverter", () => {
177177
});
178178
});
179179

180+
describe("toDuration", () => {
181+
it("should convert with 0", () => {
182+
expect(converter.toDuration("").seconds).to.equal(BigInt(0));
183+
expect(converter.toDuration(" ").seconds).to.equal(BigInt(0));
184+
expect(converter.toDuration("0").seconds).to.equal(BigInt(0));
185+
expect(converter.toDuration("0ms").seconds).to.equal(BigInt(0));
186+
expect(converter.toDuration("0s").seconds).to.equal(BigInt(0));
187+
expect(converter.toDuration("0m").seconds).to.equal(BigInt(0));
188+
expect(converter.toDuration("0h").seconds).to.equal(BigInt(0));
189+
});
190+
it("should convert with hours", () => {
191+
expect(converter.toDuration("1h").seconds).to.equal(BigInt(3600));
192+
expect(converter.toDuration("24h").seconds).to.equal(BigInt(3600 * 24));
193+
expect(converter.toDuration("25h").seconds).to.equal(BigInt(3600 * 25));
194+
});
195+
it("should convert with minutes", () => {
196+
expect(converter.toDuration("1m").seconds).to.equal(BigInt(60));
197+
expect(converter.toDuration("120m").seconds).to.equal(BigInt(120 * 60));
198+
});
199+
it("should convert with seconds", () => {
200+
expect(converter.toDuration("1s").seconds).to.equal(BigInt(1));
201+
expect(converter.toDuration("120s").seconds).to.equal(BigInt(120));
202+
});
203+
it("should convert with mixed", () => {
204+
expect(converter.toDuration("1h20m").seconds).to.equal(BigInt(3600 + 20 * 60));
205+
expect(converter.toDuration("1h0m0s").seconds).to.equal(BigInt(3600));
206+
expect(converter.toDuration("20m1h").seconds).to.equal(BigInt(3600 + 20 * 60));
207+
expect(converter.toDuration("20m25h1s").seconds).to.equal(BigInt(25 * 3600 + 20 * 60 + 1));
208+
});
209+
});
210+
211+
describe("toDurationString", () => {
212+
it("should convert with 0", () => {
213+
expect(converter.toDurationString(new Duration())).to.equal("0");
214+
expect(converter.toDurationString(new Duration({ seconds: BigInt(0) }))).to.equal("0");
215+
expect(converter.toDurationString(new Duration({ nanos: 0 }))).to.equal("0");
216+
});
217+
it("should convert with hours", () => {
218+
expect(converter.toDurationString(new Duration({ seconds: BigInt(3600) }))).to.equal("1h");
219+
expect(converter.toDurationString(new Duration({ seconds: BigInt(3600 * 24) }))).to.equal("24h");
220+
expect(converter.toDurationString(new Duration({ seconds: BigInt(3600 * 25) }))).to.equal("25h");
221+
});
222+
it("should convert with minutes", () => {
223+
expect(converter.toDurationString(new Duration({ seconds: BigInt(60) }))).to.equal("1m");
224+
expect(converter.toDurationString(new Duration({ seconds: BigInt(2 * 60 * 60) }))).to.equal("2h");
225+
});
226+
it("should convert with seconds", () => {
227+
expect(converter.toDurationString(new Duration({ seconds: BigInt(1) }))).to.equal("1s");
228+
expect(converter.toDurationString(new Duration({ seconds: BigInt(120) }))).to.equal("2m");
229+
});
230+
it("should convert with mixed", () => {
231+
expect(converter.toDurationString(new Duration({ seconds: BigInt(3600 + 20 * 60) }))).to.equal("1h20m");
232+
expect(converter.toDurationString(new Duration({ seconds: BigInt(25 * 3600 + 20 * 60 + 1) }))).to.equal(
233+
"25h20m1s",
234+
);
235+
});
236+
});
237+
180238
describe("errors", () => {
181239
it("USER_BLOCKED", () => {
182240
const connectError = converter.toError(new ApplicationError(ErrorCodes.USER_BLOCKED, "user blocked"));

components/public-api/typescript-common/src/public-api-converter.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { Timestamp, toPlainMessage } from "@bufbuild/protobuf";
7+
import { Timestamp, toPlainMessage, PartialMessage } from "@bufbuild/protobuf";
8+
import { Duration } from "@bufbuild/protobuf";
89
import { Code, ConnectError } from "@connectrpc/connect";
910
import {
1011
FailedPreconditionDetails,
@@ -43,6 +44,7 @@ import {
4344
GitInitializer_GitConfig,
4445
PrebuildInitializer,
4546
SnapshotInitializer,
47+
UpdateWorkspaceRequest_UpdateTimeout,
4648
Workspace,
4749
WorkspaceGitStatus,
4850
WorkspaceInitializer,
@@ -210,7 +212,11 @@ export class PublicAPIConverter {
210212
spec.editor = this.toEditor(arg.configuration.ideConfig);
211213
spec.ports = this.toPorts(arg.status.exposedPorts);
212214
if (arg.status.timeout) {
213-
// TODO: timeout
215+
spec.timeout = new UpdateWorkspaceRequest_UpdateTimeout({
216+
disconnected: this.toDuration(arg.status.timeout),
217+
// TODO: inactivity
218+
// TODO: maximum_lifetime
219+
});
214220
}
215221
if (arg.workspaceClass) {
216222
spec.class = arg.workspaceClass;
@@ -1026,4 +1032,55 @@ export class PublicAPIConverter {
10261032
result.lastUsedTime = Timestamp.fromDate(new Date(sshKey.lastUsedTime || sshKey.creationTime));
10271033
return result;
10281034
}
1035+
1036+
/**
1037+
* Converts a duration to a string like "1h2m3s4ms"
1038+
*
1039+
* `Duration.nanos` is ignored
1040+
* @returns a string like "1h2m3s", valid time units are `s`, `m`, `h`
1041+
*/
1042+
toDurationString(duration: PartialMessage<Duration>): string {
1043+
const seconds = duration.seconds || 0;
1044+
if (seconds === 0) {
1045+
return "0";
1046+
}
1047+
const totalMilliseconds = Number(seconds) * 1000;
1048+
1049+
const hours = Math.floor(totalMilliseconds / 3600000);
1050+
const remainingMillisecondsAfterHours = totalMilliseconds % 3600000;
1051+
const minutes = Math.floor(remainingMillisecondsAfterHours / 60000);
1052+
const remainingMillisecondsAfterMinutes = remainingMillisecondsAfterHours % 60000;
1053+
const secondsResult = Math.floor(remainingMillisecondsAfterMinutes / 1000);
1054+
1055+
return `${hours > 0 ? hours + "h" : ""}${minutes > 0 ? minutes + "m" : ""}${
1056+
secondsResult > 0 ? secondsResult + "s" : ""
1057+
}`;
1058+
}
1059+
1060+
/**
1061+
* Converts a duration string like "1h2m3s" to a Duration
1062+
*
1063+
* @param durationString "1h2m3s" valid time units are `s`, `m`, `h`
1064+
*/
1065+
toDuration(durationString: string): Duration {
1066+
const units = new Map([
1067+
["h", 3600],
1068+
["m", 60],
1069+
["s", 1],
1070+
]);
1071+
const regex = /(\d+(?:\.\d+)?)([hmsµµs]+)/g;
1072+
let totalSeconds = 0;
1073+
let match: RegExpExecArray | null;
1074+
1075+
while ((match = regex.exec(durationString)) !== null) {
1076+
const value = parseFloat(match[1]);
1077+
const unit = match[2];
1078+
totalSeconds += value * (units.get(unit) || 0);
1079+
}
1080+
1081+
return new Duration({
1082+
seconds: BigInt(Math.floor(totalSeconds)),
1083+
nanos: (totalSeconds - Math.floor(totalSeconds)) * 1e9,
1084+
});
1085+
}
10291086
}

0 commit comments

Comments
 (0)