Split to kafka backend plugin

This commit is contained in:
Nir Gazit
2021-01-09 21:18:41 +02:00
parent 343026aade
commit c139285492
21 changed files with 18886 additions and 158 deletions
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
};
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface Config {
kafka?: {
clientId: string;
brokers: string[];
};
}
File diff suppressed because it is too large Load Diff
+48
View File
@@ -0,0 +1,48 @@
{
"name": "@backstage/plugin-kafka-backend",
"version": "0.1.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/kafka-backend"
},
"keywords": [
"backstage",
"kafka"
],
"configSchema": "config.d.ts",
"scripts": {
"start": "backstage-cli backend:dev",
"build": "backstage-cli backend:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"prepack": "backstage-cli prepack",
"postpack": "backstage-cli postpack",
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.4.1",
"@backstage/catalog-model": "^0.6.0",
"@backstage/config": "^0.1.2",
"kafkajs": "^1.16.0-beta.6",
"winston": "^3.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.4.2",
"supertest": "^4.0.2"
},
"files": [
"dist",
"schema.d.ts"
]
}
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import '@backstage/backend-common';
describe('test', () => {
it('unbreaks the test runner', () => {
expect(true).toBeTruthy();
});
});
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export { KafkaApi } from './KafkaApi';
export * from './service/router';
export * from './types/types';
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import yn from 'yn';
import { getRootLogger } from '@backstage/backend-common';
import { startStandaloneServer } from './service/standaloneServer';
const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7000;
const enableCors = yn(process.env.PLUGIN_CORS, { default: false });
const logger = getRootLogger();
startStandaloneServer({ port, enableCors, logger }).catch(err => {
logger.error(err);
process.exit(1);
});
process.on('SIGINT', () => {
logger.info('CTRL+C pressed; exiting.');
process.exit(0);
});
@@ -0,0 +1,82 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Kafka, SeekEntry } from 'kafkajs';
import { Logger } from 'winston';
export type PartitionOffset = {
id: number;
offset: string;
};
export type TopicOffset = {
topic: string;
partitions: PartitionOffset[];
};
export class KafkaApi {
private readonly kafka: Kafka;
private readonly logger: Logger;
constructor(clientId: string, brokers: string[], logger: Logger) {
logger.info(
`creating kafka client with clientId=${clientId} and brokers=${brokers}`,
);
this.kafka = new Kafka({ clientId, brokers });
this.logger = logger;
}
async fetchTopicOffsets(topic: string): Promise<Array<PartitionOffset>> {
this.logger.info(`fetching topic offsets for ${topic}`);
const admin = this.kafka.admin();
await admin.connect();
try {
return KafkaApi.toPartitionOffsets(await admin.fetchTopicOffsets(topic));
} finally {
await admin.disconnect();
}
}
async fetchGroupOffsets(groupId: string): Promise<Array<TopicOffset>> {
this.logger.info(`fetching consumer group offsets for ${groupId}`);
const admin = this.kafka.admin();
await admin.connect();
try {
let groupOffsets = await admin.fetchOffsets({ groupId });
return groupOffsets.map(topicOffset => ({
topic: topicOffset.topic,
partitions: KafkaApi.toPartitionOffsets(topicOffset.partitions),
}));
} finally {
await admin.disconnect();
}
}
private static toPartitionOffsets(
result: Array<SeekEntry>,
): Array<PartitionOffset> {
return result.map(seekEntry => ({
id: seekEntry.partition,
offset: seekEntry.offset,
}));
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { Config } from '@backstage/config';
import { KafkaApi } from './KafkaApi';
export interface RouterOptions {
logger: Logger;
config: Config;
}
export const makeRouter = (
logger: Logger,
kafkaApi: KafkaApi,
): express.Router => {
const router = Router();
router.use(express.json());
router.get('/topic/:topicId/offsets', async (req, res) => {
const topicId = req.params.topicId;
try {
const response = await kafkaApi.fetchTopicOffsets(topicId);
res.send(response);
} catch (e) {
logger.error(`action=fetchTopicOffsets topicId=${topicId}, error=${e}`);
res.status(500).send({ error: e.message });
}
});
router.get('/consumer/:consumerId/offsets', async (req, res) => {
const consumerId = req.params.consumerId;
try {
const response = await kafkaApi.fetchGroupOffsets(consumerId);
res.send(response);
} catch (e) {
logger.error(
`action=fetchGroupOffsets consumerId=${consumerId}, error=${e}`,
);
res.status(500).send({ error: e.message });
}
});
return router;
};
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const logger = options.logger;
logger.info('Initializing Kafka backend');
const clientId = options.config.getString('kafka.clientId');
const brokers = options.config.getStringArray('kafka.brokers');
const kafkaApi = new KafkaApi(clientId, brokers, logger);
return makeRouter(logger, kafkaApi);
}
@@ -0,0 +1,54 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
errorHandler,
notFoundHandler,
requestLoggingHandler,
} from '@backstage/backend-common';
import compression from 'compression';
import cors from 'cors';
import express from 'express';
import helmet from 'helmet';
import { Logger } from 'winston';
import { createRouter } from './router';
import { ConfigReader } from '@backstage/config';
export interface ApplicationOptions {
enableCors: boolean;
logger: Logger;
}
export async function createStandaloneApplication(
options: ApplicationOptions,
): Promise<express.Application> {
const { enableCors, logger } = options;
const config = new ConfigReader({});
const app = express();
app.use(helmet());
if (enableCors) {
app.use(cors());
}
app.use(compression());
app.use(express.json());
app.use(requestLoggingHandler());
app.use('/', await createRouter({ logger, config }));
app.use(notFoundHandler());
app.use(errorHandler());
return app;
}
@@ -0,0 +1,50 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Server } from 'http';
import { Logger } from 'winston';
import { createStandaloneApplication } from './standaloneApplication';
export interface ServerOptions {
port: number;
enableCors: boolean;
logger: Logger;
}
export async function startStandaloneServer(
options: ServerOptions,
): Promise<Server> {
const logger = options.logger.child({ service: 'kafka-backend' });
logger.debug('Creating application...');
const app = await createStandaloneApplication({
enableCors: options.enableCors,
logger,
});
logger.debug('Starting application server...');
return await new Promise((resolve, reject) => {
const server = app.listen(options.port, (err?: Error) => {
if (err) {
reject(err);
return;
}
logger.info(`Listening on port ${options.port}`);
resolve(server);
});
});
}
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {};
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TopicOffset, PartitionOffset } from '../service/KafkaApi';
export type TopicOffsetsResponse = Array<PartitionOffset>;
export type ConsumerGroupOffsetsResponse = Array<TopicOffset>;
+1 -1
View File
@@ -25,7 +25,6 @@
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "4.0.0-alpha.45",
"kafkajs": "^1.15.0",
"lodash": "^4.17.20",
"react": "^16.13.1",
"react-dom": "^16.13.1",
@@ -35,6 +34,7 @@
"@backstage/cli": "^0.4.2",
"@backstage/dev-utils": "^0.1.6",
"@backstage/test-utils": "^0.1.5",
"@backstage/plugin-kafka-backend": "^0.1.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
-112
View File
@@ -1,112 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConfigApi, DiscoveryApi, createApiRef } from '@backstage/core';
import { Kafka, SeekEntry } from 'kafkajs';
export const kafkaApiRef = createApiRef<KafkaApi>({
id: 'plugin.kafka.service',
description: 'Used by the Kafka plugin to connect to the Kafka cluster',
});
export type TopicOffsets = {
topic: string;
partitions: {
id: number;
offset: string;
}[];
};
export class KafkaApi {
static fromConfig(configApi: ConfigApi, discoveryApi: DiscoveryApi) {
const clientId =
configApi.getOptionalString('kafka.clientId') ?? 'kafka-backstage';
const brokers = configApi.getOptionalStringArray('kafka.brokers') ?? [
'/kafka/broker',
];
return new KafkaApi(discoveryApi, clientId, brokers);
}
constructor(
private readonly discoveryApi: DiscoveryApi,
private readonly clientId: string,
private readonly brokers: string[],
) {}
async fetchTopicsOffsets(topics: string[]): Promise<Array<TopicOffsets>> {
const admin = await this.getAdmin();
await admin.connect();
try {
return await Promise.all(
topics.map(async topic =>
admin
.fetchTopicOffsets(topic)
.then(result => KafkaApi.toTopicOffsets(topic, result)),
),
);
} finally {
await admin.disconnect();
}
}
async fetchGroupOffsets(groupId: string): Promise<Array<TopicOffsets>> {
const admin = await this.getAdmin();
await admin.connect();
try {
let groupOffsets = [
{
topic: 'shula',
partitions: await admin.fetchOffsets({ groupId, topic: 'shula' }),
},
{
topic: 'test',
partitions: await admin.fetchOffsets({ groupId, topic: 'test' }),
},
];
return groupOffsets.map(topicOffset =>
KafkaApi.toTopicOffsets(topicOffset.topic, topicOffset.partitions),
);
} finally {
await admin.disconnect();
}
}
private async getAdmin() {
const kafka = await this.getKafka();
return kafka.admin();
}
private async getKafka() {
const proxyUrl = await this.discoveryApi.getBaseUrl('proxy');
return new Kafka({
clientId: this.clientId,
brokers: this.brokers.map(brokerUrl => proxyUrl + brokerUrl),
});
}
private static toTopicOffsets(topic: string, result: Array<SeekEntry>) {
const partitions = result.map(seekEntry => ({
id: seekEntry.partition,
offset: seekEntry.offset,
}));
return { topic, partitions };
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { DiscoveryApi } from '@backstage/core';
import { KafkaApi } from './types';
import {
ConsumerGroupOffsetsResponse,
TopicOffsetsResponse,
} from '@backstage/plugin-kafka-backend';
export class KafkaBackendClient implements KafkaApi {
private readonly discoveryApi: DiscoveryApi;
constructor(options: { discoveryApi: DiscoveryApi }) {
this.discoveryApi = options.discoveryApi;
}
private async getRequired(path: string): Promise<any> {
const url = `${await this.discoveryApi.getBaseUrl('kafka')}${path}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const payload = await response.text();
const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
throw new Error(message);
}
return await response.json();
}
async getTopicOffsets(topic: string): Promise<TopicOffsetsResponse> {
return await this.getRequired(`/topic/${topic}/offsets`);
}
async getConsumerGroupOffsets(
consumerGroup: string,
): Promise<ConsumerGroupOffsetsResponse> {
return await this.getRequired(`/consumer/${consumerGroup}/offsets`);
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createApiRef } from '@backstage/core';
import {
ConsumerGroupOffsetsResponse,
TopicOffsetsResponse,
} from '@backstage/plugin-kafka-backend';
export const kafkaApiRef = createApiRef<KafkaApi>({
id: 'plugin.kafka.service',
description:
'Used by the Kafka plugin to make requests to accompanying backend',
});
export interface KafkaApi {
getTopicOffsets(topic: string): Promise<TopicOffsetsResponse>;
getConsumerGroupOffsets(consumerGroup: string): Promise<ConsumerGroupOffsetsResponse>;
}
@@ -16,7 +16,7 @@
import { errorApiRef, useApi } from '@backstage/core';
import { useAsyncRetry } from 'react-use';
import { kafkaApiRef } from '../../api/KafkaApi';
import { kafkaApiRef } from '../../api/types';
import _ from 'lodash';
export function useConsumerGroupOffsets(groupId: string) {
@@ -25,32 +25,32 @@ export function useConsumerGroupOffsets(groupId: string) {
const { loading, value: topics, retry } = useAsyncRetry(async () => {
try {
const groupOnlyOffsets = await api.fetchGroupOffsets(groupId);
const topicOffsets = _.keyBy(
await api.fetchTopicsOffsets(
groupOnlyOffsets.map(value => value.topic),
),
offsets => offsets.topic,
);
const groupOffsets = await api.getConsumerGroupOffsets(groupId);
const groupWithTopicOffsets = await Promise.all(
groupOffsets.map(async ({ topic, partitions }) => {
debugger;
const topicOffsets = _.keyBy(
await api.getTopicOffsets(topic),
partition => partition.id,
);
return groupOnlyOffsets.flatMap(value => {
let topicPartitionOffsets = _.keyBy(
topicOffsets[value.topic].partitions,
partition => partition.id,
);
return value.partitions.map(partition => ({
topic: value.topic,
partitionId: partition.id,
groupOffset: partition.offset,
topicOffset: topicPartitionOffsets[partition.id].offset,
}));
});
return partitions.map(partition => ({
topic: topic,
partitionId: partition.id,
groupOffset: partition.offset,
topicOffset: topicOffsets[partition.id].offset,
}));
}),
);
return groupWithTopicOffsets.flat();
} catch (e) {
errorApi.post(e);
throw e;
}
}, [api, errorApi, groupId]);
debugger;
return [
{
loading,
-1
View File
@@ -16,4 +16,3 @@
export { plugin } from './plugin';
export { KAFKA_CONSUMER_GROUP_ANNOTATION } from './constants';
export { Router, isPluginApplicableToEntity } from './Router';
export * from './api';
+3 -3
View File
@@ -20,7 +20,8 @@ import {
createRouteRef,
discoveryApiRef,
} from '@backstage/core';
import { KafkaApi, kafkaApiRef } from './api/KafkaApi';
import { KafkaBackendClient } from './api/KafkaBackendClient';
import { kafkaApiRef } from './api/types';
export const rootCatalogKafkaRouteRef = createRouteRef({
path: '*',
@@ -33,8 +34,7 @@ export const plugin = createPlugin({
createApiFactory({
api: kafkaApiRef,
deps: { discoveryApi: discoveryApiRef, configApi: configApiRef },
factory: ({ configApi, discoveryApi }) =>
KafkaApi.fromConfig(configApi, discoveryApi),
factory: ({ discoveryApi }) => new KafkaBackendClient({ discoveryApi }),
}),
],
});
+85 -20
View File
@@ -1644,11 +1644,9 @@
to-fast-properties "^2.0.0"
"@backstage/catalog-model@^0.2.0":
version "0.2.0"
resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.2.0.tgz#e3fe2a4ddeb6a9b6ec480c80cb2b9c39cb245576"
integrity sha512-Y1ocdRpBlxK/VrJQjHlQd0bgADECd1B2NRjwd8ss46ibT5hwLvMOfD80+Fa7oPLu0ktJrH4lq0pNIIJIml48zA==
version "0.6.0"
dependencies:
"@backstage/config" "^0.1.1"
"@backstage/config" "^0.1.2"
"@types/json-schema" "^7.0.5"
"@types/yup" "^0.29.8"
json-schema "^0.2.5"
@@ -1657,11 +1655,9 @@
yup "^0.29.3"
"@backstage/catalog-model@^0.3.0":
version "0.3.1"
resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.3.1.tgz#45d08e2f333c9c566b2bf2629fd707fe989bb404"
integrity sha512-9XhV7c4rmVW+Yzj2PiwTQ7DsegWGB3C4ELsDRExuEVZONdqNcC02cyJtrt3fT5F31ZS3tHkB9bMUymFOBLqUSA==
version "0.6.0"
dependencies:
"@backstage/config" "^0.1.1"
"@backstage/config" "^0.1.2"
"@types/json-schema" "^7.0.5"
"@types/yup" "^0.29.8"
json-schema "^0.2.5"
@@ -1670,17 +1666,16 @@
yup "^0.29.3"
"@backstage/core@^0.3.0":
version "0.3.2"
resolved "https://registry.npmjs.org/@backstage/core/-/core-0.3.2.tgz#a8209126d5076cf4a8b9bd632fe4e5e2edb62916"
integrity sha512-i5d+Wh8js4qEWoAsPY5L7HVSWpumr1OhfF2dUCGYdyW6AMqVJPca6+n6zp1Rg2CO+J9norp44XAVVCbyhtUpig==
version "0.4.1"
dependencies:
"@backstage/config" "^0.1.1"
"@backstage/core-api" "^0.2.1"
"@backstage/theme" "^0.2.1"
"@backstage/config" "^0.1.2"
"@backstage/core-api" "^0.2.6"
"@backstage/theme" "^0.2.2"
"@material-ui/core" "^4.11.0"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
"@types/dagre" "^0.7.44"
"@types/prop-types" "^15.7.3"
"@types/react" "^16.9"
"@types/react-sparklines" "^1.7.0"
classnames "^2.2.6"
@@ -1958,6 +1953,15 @@
debug "^3.1.0"
lodash.once "^4.1.1"
"@dabh/diagnostics@^2.0.2":
version "2.0.2"
resolved "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==
dependencies:
colorspace "1.1.x"
enabled "2.0.x"
kuler "^2.0.0"
"@date-io/core@1.x", "@date-io/core@^1.3.13":
version "1.3.13"
resolved "https://registry.npmjs.org/@date-io/core/-/core-1.3.13.tgz#90c71da493f20204b7a972929cc5c482d078b3fa"
@@ -7679,7 +7683,7 @@ async@^2.6.1, async@^2.6.2:
dependencies:
lodash "^4.17.14"
async@^3.2.0:
async@^3.1.0, async@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
@@ -11432,6 +11436,11 @@ enabled@1.0.x:
dependencies:
env-variable "0.0.x"
enabled@2.0.x:
version "2.0.0"
resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2"
integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@@ -12468,6 +12477,11 @@ fecha@^2.3.3:
resolved "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd"
integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==
fecha@^4.2.0:
version "4.2.0"
resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41"
integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==
fetch-readablestream@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795"
@@ -12705,6 +12719,11 @@ fn-name@~3.0.0:
resolved "https://registry.npmjs.org/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c"
integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA==
fn.name@1.x.x:
version "1.1.0"
resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
@@ -16198,10 +16217,10 @@ jwt-decode@*, jwt-decode@^3.1.0:
resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
kafkajs@^1.15.0:
version "1.15.0"
resolved "https://registry.npmjs.org/kafkajs/-/kafkajs-1.15.0.tgz#a5ada0d933edca2149177393562be6fb0875ec3a"
integrity sha512-yjPyEnQCkPxAuQLIJnY5dI+xnmmgXmhuOQ1GVxClG5KTOV/rJcW1qA3UfvyEJKTp/RTSqQnUR3HJsKFvHyTpNg==
kafkajs@^1.16.0-beta.6:
version "1.16.0-beta.6"
resolved "https://registry.npmjs.org/kafkajs/-/kafkajs-1.16.0-beta.6.tgz#650f4d16abd60516aa0c375be613368391216df1"
integrity sha512-R4DT3s7oCAoxdq3tN8w2WkUxLXcZgLQSjQVSmEZpEmT6hVdZ5AUZWqyLYr1IRbQowsJK4Z7eWJAk68J0JiRlUw==
keyv@^3.0.0:
version "3.1.0"
@@ -16291,6 +16310,11 @@ kuler@1.0.x:
dependencies:
colornames "^1.1.1"
kuler@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
latest-version@5.1.0, latest-version@^5.0.0:
version "5.1.0"
resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
@@ -16856,6 +16880,17 @@ logform@^2.1.1:
ms "^2.1.1"
triple-beam "^1.3.0"
logform@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2"
integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==
dependencies:
colors "^1.2.1"
fast-safe-stringify "^2.0.4"
fecha "^4.2.0"
ms "^2.1.1"
triple-beam "^1.3.0"
loglevel@^1.6.7, loglevel@^1.6.8:
version "1.6.8"
resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171"
@@ -18540,6 +18575,13 @@ one-time@0.0.4:
resolved "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e"
integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=
one-time@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45"
integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
dependencies:
fn.name "1.x.x"
onetime@^1.0.0:
version "1.1.0"
resolved "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
@@ -21188,7 +21230,7 @@ read@1, read@~1.0.1:
dependencies:
mute-stream "~0.0.4"
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -25211,6 +25253,14 @@ winston-transport@^4.3.0:
readable-stream "^2.3.6"
triple-beam "^1.2.0"
winston-transport@^4.4.0:
version "4.4.0"
resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59"
integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==
dependencies:
readable-stream "^2.3.7"
triple-beam "^1.2.0"
winston@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz#63061377976c73584028be2490a1846055f77f07"
@@ -25226,6 +25276,21 @@ winston@^3.2.1:
triple-beam "^1.3.0"
winston-transport "^4.3.0"
winston@^3.3.3:
version "3.3.3"
resolved "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170"
integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==
dependencies:
"@dabh/diagnostics" "^2.0.2"
async "^3.1.0"
is-stream "^2.0.0"
logform "^2.2.0"
one-time "^1.0.0"
readable-stream "^3.4.0"
stack-trace "0.0.x"
triple-beam "^1.3.0"
winston-transport "^4.4.0"
word-wrap@^1.2.3, word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"