in source/packages/services/command-and-control/src/commands/commands.service.ts [102:224]
public async listIds(
tags: Tags,
exclusiveStart?: CommandListPaginationKey,
count = this.MAX_LIST_RESULTS
): Promise<[string[], CommandListPaginationKey]> {
logger.debug(
`commands.service listIds: in: tags:${JSON.stringify(
tags
)}, exclusiveStart:${JSON.stringify(exclusiveStart)}, count:${count}`
);
if (count) {
count = Number(count);
}
// convert tags map to arrays to make referencing them later easier
const tagKeys = Object.keys(tags);
const tagValues = Object.values(tags);
const tagCount = tagKeys.length;
// retrieve the first page of results for each tag
const resultsForTagsFutures: Promise<[string[], CommandListIdsByTagPaginationKey]>[] =
new Array(tagCount);
for (let tagIndex = 0; tagIndex < tagCount; tagIndex++) {
const paginationKey: CommandListIdsByTagPaginationKey = {
commandId: exclusiveStart?.commandId,
tagKey: tagKeys[tagIndex],
tagValue: tagValues[tagIndex],
};
resultsForTagsFutures[tagIndex] = this.commandDao.listIds(
tagKeys[tagIndex],
tagValues[tagIndex],
paginationKey,
count
);
}
const resultsForTags = await Promise.all(resultsForTagsFutures);
const idsForTags = resultsForTags.map(([ids, _paginationKey]) => ids);
// if any of the initial results are empty, then we can exit immediately as no common matches
for (let tagIndex = 0; tagIndex < tagCount; tagIndex++) {
if ((idsForTags[tagIndex]?.length ?? 0) === 0) {
return [undefined, undefined];
}
}
// this inline function will populate new pages of command ids for a specific tag
const getNextPageOfResults = async (tagIndex: number): Promise<boolean> => {
const paginationKey = resultsForTags[tagIndex]?.[1];
if (paginationKey === undefined) {
// no more to process
return false;
}
resultsForTags[tagIndex] = await this.commandDao.listIds(
tagKeys[tagIndex],
tagValues[tagIndex],
paginationKey,
count
);
if ((resultsForTags[tagIndex]?.[0]?.length ?? 0) === 0) {
// no more to process
return false;
} else {
// store the new page of tags, and reset its pointer
idsForTags[tagIndex] = resultsForTags[tagIndex]?.[0];
listPointers[tagIndex] = 0;
return true;
}
};
// process each list of commandIds per tag, saving where the commandId is found across all tags
const matched: string[] = [];
let keepGoing = true;
const listPointers = new Array(tagCount).fill(0);
while (keepGoing && matched.length < count) {
for (let tagIndex = 0; tagIndex < tagCount; tagIndex++) {
let currentTagCommandId = idsForTags[tagIndex][listPointers[tagIndex]];
if (currentTagCommandId === undefined) {
keepGoing = await getNextPageOfResults(tagIndex);
if (!keepGoing) break;
currentTagCommandId = idsForTags[tagIndex][listPointers[tagIndex]];
}
// if we reached the last tag index, it means we found a match across all tags
if (tagIndex === tagCount - 1) {
// add the matched id to the result
matched.push(currentTagCommandId);
// increment all the pointers
listPointers.forEach((_value, index) => listPointers[index]++);
} else {
// check for matching commandIds between this and the next tag being compared
let nextTagCommandId = idsForTags[tagIndex + 1][listPointers[tagIndex + 1]];
if (nextTagCommandId === undefined) {
keepGoing = await getNextPageOfResults(tagIndex + 1);
if (!keepGoing) break;
nextTagCommandId = idsForTags[tagIndex + 1][listPointers[tagIndex + 1]];
}
if (currentTagCommandId === nextTagCommandId) {
// commands match, so move onto checking the next tag pair
continue;
} else if (currentTagCommandId < nextTagCommandId) {
// this tag has a lower command id, therefore increment this tags index
listPointers[tagIndex]++;
break;
} else {
// the next tag has a lower command id, therefore increment the next tags index
listPointers[tagIndex + 1]++;
break;
}
}
}
}
let paginationKey: CommandListPaginationKey;
if (matched.length === count) {
paginationKey = {
commandId: matched[count - 1],
};
}
const result: [string[], CommandListPaginationKey] = [matched, paginationKey];
logger.debug(`commands.service list: exit: result:${JSON.stringify(result)}`);
return result;
}