Files
threshold-ui/ui/admin/app/services/store.js
T

82 lines
2.2 KiB
JavaScript

/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from 'api/services/store';
import { service } from '@ember/service';
import { TrackedObject } from 'tracked-built-ins';
import { dropTask, timeout } from 'ember-concurrency';
export default class extends Store {
@service flashMessages;
@service intl;
@service router;
/**
* Overrides store.query to detect a rate-limit warning and surface a
* flash notification to the user. The partial result is still returned
* normally so the UI renders whatever data was fetched.
* @override
*/
async query(type, query, options) {
const result = await super.query(type, query, options);
if (result?.meta?.rateLimitWarning) {
const { retryAfter } = result.meta.rateLimitWarning;
this._countdownTask.perform(retryAfter);
}
return result;
}
/**
* Creates a sticky flash notification and counts down each second until the
* rate-limit window expires and drops any new ones that come in.
*/
_countdownTask = dropTask(async (retryAfter) => {
// Use a TrackedObject for `button` so that mutations to `button.disabled`
// are tracked and the template re-renders when the countdown finishes.
const button = new TrackedObject({
label: this.intl.t('actions.retry'),
disabled: true,
action: (flash) => {
flash.destroyMessage();
this.router.refresh();
},
});
const flash = this.flashMessages
.warning(
this.intl.t('errors.429.description-partial', { seconds: retryAfter }),
{
color: 'warning',
title: this.intl.t('errors.429.title'),
sticky: true,
dismiss: (flash) => {
this._countdownTask.cancelAll();
flash.destroyMessage();
},
button,
},
)
.getFlashObject();
let remaining = retryAfter;
while (remaining > 0) {
await timeout(1000);
remaining -= 1;
flash.message =
remaining > 0
? this.intl.t('errors.429.description-partial', {
seconds: remaining,
})
: this.intl.t('errors.429.description-ready');
}
button.disabled = false;
});
}