heroku-kafka
Version:
heroku plugin to manage heroku kafka
115 lines (114 loc) • 3.74 kB
JavaScript
import { color } from '@heroku/heroku-cli-util';
import { ux } from '@oclif/core';
import debug from './debug.js';
import fetcher from './fetcher.js';
import host from './host.js';
const VERSION = 'v0';
class HerokuKafkaClusters {
app;
context;
env;
heroku;
constructor(heroku, env, context) {
this.heroku = heroku;
this.env = env;
this.context = context;
this.app = context.app;
}
request(params) {
const h = host();
debug(`picked shogun host: ${h}`);
const { path, method, body, ...rest } = params;
const url = `https://${h}${path}`;
return this.heroku.request(url, {
method: method || 'GET',
body,
headers: {
accept: params.accept || 'application/json',
},
...rest,
});
}
async waitStatus(addon) {
const errorResponse = {
message: 'unknown',
'waiting?': true,
'healthy?': false,
};
if (!addon) {
return null;
}
const response = await this.request({
path: `/data/kafka/${VERSION}/clusters/${addon.id}/wait_status`,
}).then((res) => res.body || res)
.catch((error) => {
if (error.statusCode === 410) {
return { 'deprovisioned?': true, ...errorResponse };
}
if (error.statusCode === 404) {
return { 'missing?': true, ...errorResponse };
}
return errorResponse;
});
return response;
}
}
// Looks up cluster by name. Name is optional, but only one cluster
// must exit on the app if it is omitted.
async function withCluster(heroku, app, cluster, fn) {
const fetch = fetcher(heroku);
let addon;
if (cluster) {
addon = await fetch.addon(app, cluster);
}
else {
const addons = await fetch.all(app);
if (addons.length === 1) {
addon = addons[0];
}
else if (addons.length === 0) {
ux.error(`found no kafka add-ons on ${app}`);
}
else {
ux.error(`found more than one kafka add-on on ${app}: ${addons.map(addon => addon.name).join(', ')}`);
}
}
return await fn(addon);
}
async function topicConfig(heroku, addonId, topic) {
const response = await request(heroku, {
path: `/data/kafka/${VERSION}/clusters/${addonId}/topics`,
});
const info = (response?.body || response);
const forTopic = info.topics.find(t => t.name === topic || ((t.prefix || '') + t.name) === topic);
if (!forTopic) {
ux.error(`topic ${topic} not found`);
}
return forTopic;
}
// Fetch kafka info about a provisioned cluster or exit with failure
function fetchProvisionedInfo(heroku, addon) {
const url = `https://${host()}/data/kafka/v0/clusters/${addon.id}`;
return heroku.request(url, { method: 'GET' })
.then((res) => res.body || res)
.catch((error) => {
if (error.statusCode !== 404)
throw error;
ux.error(`${color.addon(addon.name)} is not yet provisioned.\nRun ${color.command('heroku kafka:wait')} to wait until the cluster is provisioned.`);
});
}
function request(heroku, params) {
const h = host();
debug(`picked shogun host: ${h}`);
const { path, method, body, ...rest } = params;
const url = `https://${h}${path}`;
return heroku.request(url, {
method: method || 'GET',
body,
headers: {
accept: params.accept || 'application/json',
},
...rest,
});
}
export { fetchProvisionedInfo, HerokuKafkaClusters, request, topicConfig, withCluster, };