feat: 🎸 Add fallback if client daemon is down or errors (#2085)

* feat: 🎸 Add fallback if client daemon is down or errors
* test: 💍 Add tests for client daemon being down

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12026
This commit is contained in:
Zhihe Li committed 2024-01-16 10:17:46 -05:00
1 parent 9046c01238
commit 2e36d3c3e2
8 files changed
+195 -119

No files matched your search

@@ -39,6 +39,21 @@ const paginateResults = (array, page, pageSize) => {
return array.slice(start, end);
};
const fetchControllerData = async (context, next) => {
const { data } = context.request;
const { query } = data;
const { recursive, scope_id, page, pageSize } = query;
// If we get an error or client daemon is unavailable, fall back to calling the API
context.request.data.query = { recursive, scope_id };
const results = await next(context.request);
const models = results.content.toArray();
const paginatedResult = paginateResults(models, page, pageSize);
paginatedResult.meta = { totalItems: models.length };
return paginatedResult;
};
/**
* Handler to sit in front of the API request layer
* so we can request from the daemon first
@@ -60,18 +75,17 @@ export default class ClientDaemonHandler {
'isClientDaemonRunning',
);
// eslint-disable-next-line no-unused-vars
let { recursive, scope_id, page, pageSize, ...remainingQuery } = query;
let searchQuery = '';
if (
!Object.keys(supportedTypes).includes(type) ||
!isClientDaemonRunning
) {
return next(context.request);
return fetchControllerData(context, next);
}
// TODO: Remove usages of recursive from callers and scope_id since
// all calls to daemon are recursive, scope_id can be part of search query instead
// eslint-disable-next-line no-unused-vars
let { recursive, scope_id, page, pageSize, ...remainingQuery } = query;
let searchQuery = '';
if (remainingQuery.query) {
const { search, filters } = remainingQuery.query;
@@ -91,12 +105,20 @@ export default class ClientDaemonHandler {
resource: pluralize(type),
};
let clientDaemonResults = {};
try {
clientDaemonResults = await this.ipc.invoke(
'searchClientDaemon',
remainingQuery,
);
} catch (e) {
return fetchControllerData(context, next);
}
// Currently returns with a singular top level field with resource name
// e.g. { targets: [...] } or { sessions: [..] }
// So this just unwraps to the array, or undefined
const [results] = Object.values(
await this.ipc.invoke('searchClientDaemon', remainingQuery),
);
const [results] = Object.values(clientDaemonResults);
const payload = { items: paginateResults(results, page, pageSize) };
const schema = store.modelFor(type);