@biorate/schema-registry
Version:
Schema registry connector
240 lines (209 loc) • 7.19 kB
text/typescript
import { AxiosPrometheus, AxiosInstance } from '@biorate/axios-prometheus';
import { Type } from 'avsc';
import { ICompatibilities, ISchemaRegistryConfig } from './interfaces';
import { SchemaRegistryAvroSchemaParseError } from './errors';
export const create = (config: ISchemaRegistryConfig) => {
const cache = new Map<number, Type>();
class SchemaRegistryApi extends AxiosPrometheus {
public baseURL = config.baseURL;
public headers = config.headers;
public url: string;
public method: string;
private '#client': AxiosInstance;
}
class Ping extends SchemaRegistryApi {
public url = '/';
public method = 'get';
public static fetch() {
return this._fetch<Record<string, unknown>>({});
}
}
class GetSchemasById extends SchemaRegistryApi {
public url = '/schemas/ids/:id';
public method = 'get';
public static fetch(id: number) {
return this._fetch<{ schema: string }>({ path: { id } });
}
}
class GetSchemasTypes extends SchemaRegistryApi {
public url = '/schemas/types';
public method = 'get';
public static fetch() {
return this._fetch<string[]>({});
}
}
class GetSchemasVersionsById extends SchemaRegistryApi {
public url = '/schemas/ids/:id/versions';
public method = 'get';
public static fetch(id: number) {
return this._fetch<{ subject: string; version: number }[]>({ path: { id } });
}
}
class GetSubjects extends SchemaRegistryApi {
public url = '/subjects';
public method = 'get';
public static fetch() {
return this._fetch<string[]>({});
}
}
class GetSubjectsVersions extends SchemaRegistryApi {
public url = '/subjects/:subject/versions';
public method = 'get';
public static fetch(subject: string) {
return this._fetch<number[]>({ path: { subject } });
}
}
class DeleteSubjects extends SchemaRegistryApi {
public url = '/subjects/:subject';
public method = 'delete';
public static fetch(data: { subject: string; permanent?: boolean }) {
return this._fetch<number[]>({
path: { subject: data.subject },
params: { permanent: !!data.permanent },
});
}
}
class GetSubjectsByVersion extends SchemaRegistryApi {
public url = '/subjects/:subject/versions/:version';
public method = 'get';
public static fetch(data: { subject: string; version: number | string }) {
return this._fetch<{
subject: string;
id: number;
version: number;
schemaType: string;
schema: string;
}>({ path: data });
}
}
class GetSchemaBySubjectsAndVersion extends SchemaRegistryApi {
public url = '/subjects/:subject/versions/:version/schema';
public method = 'get';
public static fetch(data: { subject: string; version: number | string }) {
return this._fetch<unknown>({ path: data });
}
}
class PostSubjectsVersions extends SchemaRegistryApi {
public url = '/subjects/:subject/versions';
public method = 'post';
public static fetch(data: {
subject: string;
schema: string | Record<string, any>;
schemaType?: string;
reference?: string;
normalize?: boolean;
}) {
return this._fetch<{ id: number }>({
path: { subject: data.subject },
params: { normalize: !!data.normalize },
data: {
schema: toStringData(data.schema),
schemaType: data.schemaType,
reference: data.reference,
},
});
}
}
class PostSubjects extends SchemaRegistryApi {
public url = '/subjects/:subject';
public method = 'post';
public static fetch(data: {
subject: string;
schema: string | Record<string, any>;
schemaType?: string;
reference?: string;
normalize?: boolean;
}) {
return this._fetch<{
subject: string;
id: number;
version: number;
schema: string;
}>({
path: { subject: data.subject },
params: { normalize: !!data.normalize },
data: {
schema: toStringData(data.schema),
schemaType: data.schemaType,
reference: data.reference,
},
});
}
}
class PutConfig extends SchemaRegistryApi {
public url = '/config/:subject';
public method = 'put';
public static fetch(data: { subject: string; compatibility: ICompatibilities }) {
return this._fetch<{
compatibility: ICompatibilities;
}>({
path: { subject: data.subject },
data: { compatibility: data.compatibility },
});
}
}
async function encode(
subject: string,
data: Record<string, any>,
version: string | number = 'latest',
) {
const errors: string[] = [];
const response = await GetSubjectsByVersion.fetch({ subject, version });
const header = Buffer.alloc(5);
const schema = Type.forSchema(JSON.parse(response.data.schema));
schema.isValid(data, {
errorHook: (path: string[], value: unknown) => {
errors.push(`${path.join('.')}: ${value} (${typeof value})`);
},
});
if (errors.length) throw new SchemaRegistryAvroSchemaParseError(errors);
header.writeInt32BE(response.data.id, 1);
return Buffer.concat([header, schema.toBuffer(data)]);
}
async function decode(buffer: Buffer) {
const id = buffer.readInt32BE(1);
let data: Type | undefined = cache.get(id);
if (!data) {
const response = await GetSchemasById.fetch(id);
data = Type.forSchema(JSON.parse(response.data.schema));
cache.set(id, data);
}
const schema = Type.forSchema(data);
return schema.fromBuffer(buffer.slice(5));
}
function toStringData(data: string | Record<string, any>) {
return typeof data === 'string' ? data : JSON.stringify(data);
}
return {
ping: <typeof Ping.fetch>Ping.fetch.bind(Ping),
getSchemasById: <typeof GetSchemasById.fetch>(
GetSchemasById.fetch.bind(GetSchemasById)
),
getSchemasTypes: <typeof GetSchemasTypes.fetch>(
GetSchemasTypes.fetch.bind(GetSchemasTypes)
),
getSchemasVersionsById: <typeof GetSchemasVersionsById.fetch>(
GetSchemasVersionsById.fetch.bind(GetSchemasVersionsById)
),
getSubjects: <typeof GetSubjects.fetch>GetSubjects.fetch.bind(GetSubjects),
getSubjectsVersions: <typeof GetSubjectsVersions.fetch>(
GetSubjectsVersions.fetch.bind(GetSubjectsVersions)
),
deleteSubjects: <typeof DeleteSubjects.fetch>(
DeleteSubjects.fetch.bind(DeleteSubjects)
),
getSubjectsByVersion: <typeof GetSubjectsByVersion.fetch>(
GetSubjectsByVersion.fetch.bind(GetSubjectsByVersion)
),
getSchemaBySubjectsAndVersion: <typeof GetSchemaBySubjectsAndVersion.fetch>(
GetSchemaBySubjectsAndVersion.fetch.bind(GetSchemaBySubjectsAndVersion)
),
postSubjects: <typeof PostSubjects.fetch>PostSubjects.fetch.bind(PostSubjects),
postSubjectsVersions: <typeof PostSubjectsVersions.fetch>(
PostSubjectsVersions.fetch.bind(PostSubjectsVersions)
),
putConfig: <typeof PutConfig.fetch>PutConfig.fetch.bind(PutConfig),
encode,
decode,
};
};