@meshmakers/octo-services 3.4.1170 → 3.4.1200
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.
|
@@ -4676,18 +4676,49 @@ class TusUploadService {
|
|
|
4676
4676
|
httpClient = inject(HttpClient);
|
|
4677
4677
|
configurationService = inject(CONFIGURATION_SERVICE);
|
|
4678
4678
|
authorizeService = inject(AuthorizeService);
|
|
4679
|
+
/**
|
|
4680
|
+
* Delays between retries of a restore-job start that answered 403 (AB#5227). The bot service's
|
|
4681
|
+
* tenant gate authorizes a parent administrator against an EXISTING child tenant, and its
|
|
4682
|
+
* hierarchy answer is cached for up to 60 seconds — so a restore right after (re-)creating the
|
|
4683
|
+
* tenant can be refused although the tenant is there. The ladder spans ~75s, one TTL plus slack.
|
|
4684
|
+
* A genuine permission denial still fails, after the ladder, with the server's own reason.
|
|
4685
|
+
* Overridable so specs don't wait.
|
|
4686
|
+
*/
|
|
4687
|
+
restoreForbiddenRetryDelaysMs = [5_000, 10_000, 15_000, 20_000, 25_000];
|
|
4679
4688
|
async startUpload(options) {
|
|
4680
4689
|
const botServicesUrl = this.configurationService.config?.botServices;
|
|
4681
4690
|
if (!botServicesUrl) {
|
|
4682
4691
|
throw new Error('Bot services URL not configured');
|
|
4683
4692
|
}
|
|
4684
4693
|
const tusFileId = await this.performTusUpload(botServicesUrl, options);
|
|
4685
|
-
const jobResponse = await this.
|
|
4694
|
+
const jobResponse = await this.startRestoreJobWithForbiddenRetry(botServicesUrl, tusFileId, options);
|
|
4686
4695
|
if (!jobResponse?.jobId) {
|
|
4687
4696
|
throw new Error('Failed to start restore job');
|
|
4688
4697
|
}
|
|
4689
4698
|
return { jobId: jobResponse.jobId };
|
|
4690
4699
|
}
|
|
4700
|
+
/**
|
|
4701
|
+
* Starts the restore job, retrying a 403 across the tenant-hierarchy cache TTL (see
|
|
4702
|
+
* {@link restoreForbiddenRetryDelaysMs}). Only the job start is retried — the uploaded artifact
|
|
4703
|
+
* is already staged, so no bytes are re-transferred. Every other error propagates immediately.
|
|
4704
|
+
*/
|
|
4705
|
+
async startRestoreJobWithForbiddenRetry(botServicesUrl, tusFileId, options) {
|
|
4706
|
+
for (let attempt = 0;; attempt++) {
|
|
4707
|
+
try {
|
|
4708
|
+
return await this.startRestoreJob(botServicesUrl, tusFileId, options);
|
|
4709
|
+
}
|
|
4710
|
+
catch (error) {
|
|
4711
|
+
const isForbidden = error instanceof HttpErrorResponse && error.status === 403;
|
|
4712
|
+
if (!isForbidden || attempt >= this.restoreForbiddenRetryDelaysMs.length) {
|
|
4713
|
+
throw error;
|
|
4714
|
+
}
|
|
4715
|
+
const delayMs = this.restoreForbiddenRetryDelaysMs[attempt];
|
|
4716
|
+
options.onStatus?.(`Waiting for tenant authorization to propagate (retry ${attempt + 1} of ` +
|
|
4717
|
+
`${this.restoreForbiddenRetryDelaysMs.length} in ${Math.round(delayMs / 1000)}s)...`);
|
|
4718
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4691
4722
|
performTusUpload(botServicesUrl, options) {
|
|
4692
4723
|
return new Promise((resolve, reject) => {
|
|
4693
4724
|
const metadata = {
|