All files / src/commands/kv kv.command.js

100% Statements 99/99
100% Branches 22/22
100% Functions 3/3
100% Lines 99/99

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 6x 6x 6x 6x 6x 1x 1x 1x 1x 4x 4x 6x 193x 4x 4x 4x 4x 4x 3x 3x 4x 4x 4x 4x 4x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 193x 10x 10x 10x 8x 8x 1x 1x 7x 7x 1x 1x 1x 1x 1x 6x 6x 6x 6x 4x 4x 4x 4x 4x 3x 3x 3x 1x 1x 1x 10x 10x 2x 2x 10x 193x 193x  
import { getAllEnvVars } from '@clevercloud/client/esm/api/v2/addon.js';
import Redis from 'ioredis';
import { z } from 'zod';
import { defineArgument } from '../../lib/define-argument.js';
import { defineCommand } from '../../lib/define-command.js';
import { styleText } from '../../lib/style-text.js';
import { Logger } from '../../logger.js';
import { findAddonsByNameOrId } from '../../models/ids-resolver.js';
import { sendToApi } from '../../models/send-to-api.js';
import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
 
const URL_ENV_KEY = 'REDIS_URL';
 
const MAX_RETRIES_PER_REQUEST = 1;
 
async function getAddonUrl(ownerId, addonId) {
  const envVars = await getAllEnvVars({ id: ownerId, addonId }).then(sendToApi);
  const redisUrl = envVars.find((env) => env.name === URL_ENV_KEY)?.value;
 
  if (!redisUrl) {
    throw new Error(
      `Environment variable ${styleText('red', URL_ENV_KEY)} not found, is it a Materia KV or Redis® add-on?`,
    );
  }
 
  return redisUrl;
}
 
async function sendCommand(url, command) {
  Logger.debug(`Sending command '${command.join(' ')}' to ${url}`);
  const client = new Redis(url, { maxRetriesPerRequest: MAX_RETRIES_PER_REQUEST });
  try {
    const result = await client.call(...command);
    Logger.debug(`Command result: ${result}`);
    return result;
  } finally {
    await client.disconnect();
    Logger.debug('Disconnected from server');
  }
}
 
export const kvCommand = defineCommand({
  description: 'Send a raw command to a Materia KV or Redis® add-on',
  since: '3.11.0',
  isExperimental: true,
  featureFlag: 'kv',
  options: {
    org: orgaIdOrNameOption,
    format: humanJsonOutputFormatOption,
  },
  args: [
    defineArgument({
      schema: z.string(),
      description: 'Add-on/Real ID (or name, if unambiguous) of a Materia KV or Redis® add-on',
      placeholder: 'kv-id|addon-id|addon-name',
    }),
    defineArgument({
      schema: z.string(),
      description: 'The raw command to send to the Materia KV or Redis® add-on',
      placeholder: 'command',
    }),
  ],
  async handler(options, addonIdOrRealIdOrName, ...restArgs) {
    const { org, format } = options;
 
    const addons = await findAddonsByNameOrId(addonIdOrRealIdOrName, org);
 
    if (addons.length === 0) {
      throw new Error(`Add-on ${addonIdOrRealIdOrName} not found`);
    }
 
    if (addons.length > 1) {
      const formattedAddons = addons
        .map(({ addonId, ownerId }) => `\n${styleText('grey', `- ${addonId} (${ownerId})`)}`)
        .join('');
      throw new Error(`Several add-ons found for '${addonIdOrRealIdOrName}', use ID instead:${formattedAddons}`);
    }
 
    const { addonId, ownerId } = addons[0];
 
    const url = await getAddonUrl(ownerId, addonId);
 
    Logger.debug(`Extracted command: ${restArgs.join(' ')}`);
    const command = restArgs;
 
    const result = await sendCommand(url, command);
 
    switch (format) {
      case 'json': {
        Logger.printJson(result);
        break;
      }
      case 'human':
      default: {
        Logger.println(result);
      }
    }
  },
});