UNPKG

@confluentinc/schemaregistry

Version:
400 lines (399 loc) 15.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MockClient = void 0; const schemaregistry_client_1 = require("./schemaregistry-client"); const json_stringify_deterministic_1 = __importDefault(require("json-stringify-deterministic")); const uuid_1 = require("uuid"); const rest_error_1 = require("./rest-error"); const serde_1 = require("./serde/serde"); class Counter { constructor() { this.count = 0; } currentValue() { return this.count; } increment() { this.count++; return this.count; } } const noSubject = ""; class MockClient { constructor(config) { this.clientConfig = config; this.infoToSchemaCache = new Map(); this.idToSchemaCache = new Map(); this.guidToSchemaCache = new Map(); this.schemaToVersionCache = new Map(); this.configCache = new Map(); this.counter = new Counter(); } config() { return this.clientConfig; } async register(subject, schema, normalize = false) { const metadata = await this.registerFullResponse(subject, schema, normalize); if (!metadata) { throw new rest_error_1.RestError("Failed to register schema", 422, 42200); } return metadata.id; } async registerFullResponse(subject, schema, normalize = false) { const cacheKey = (0, json_stringify_deterministic_1.default)({ subject, schema: (0, schemaregistry_client_1.minimize)(schema) }); const cacheEntry = this.infoToSchemaCache.get(cacheKey); if (cacheEntry && !cacheEntry.softDeleted) { return cacheEntry.metadata; } const schemaId = await this.getIDFromRegistry(subject, schema); if (schemaId.id === -1) { throw new rest_error_1.RestError("Failed to retrieve schema ID from registry", 422, 42200); } const metadata = { id: schemaId.id, guid: schemaId.guid, ...schema }; this.infoToSchemaCache.set(cacheKey, { metadata, softDeleted: false }); return metadata; } async getIDFromRegistry(subject, schema) { let id = -1; for (const [key, value] of this.idToSchemaCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && this.schemasEqual(value.info, schema)) { id = parsedKey.id; break; } } let guid = ""; for (const [key, value] of this.guidToSchemaCache.entries()) { if (this.schemasEqual(value.info, schema)) { guid = key; break; } } await this.generateVersion(subject, schema); if (id < 0) { id = this.counter.increment(); const idCacheKey = (0, json_stringify_deterministic_1.default)({ subject, id }); this.idToSchemaCache.set(idCacheKey, { info: schema, softDeleted: false }); guid = (0, uuid_1.v4)(); this.guidToSchemaCache.set(guid, { info: schema, softDeleted: false }); } return new serde_1.SchemaId("", id, guid); } async generateVersion(subject, schema) { const versions = await this.allVersions(subject); let newVersion; if (versions.length === 0) { newVersion = 1; } else { newVersion = versions[versions.length - 1] + 1; } const cacheKey = (0, json_stringify_deterministic_1.default)({ subject, schema: (0, schemaregistry_client_1.minimize)(schema) }); this.schemaToVersionCache.set(cacheKey, { version: newVersion, softDeleted: false }); } async getBySubjectAndId(subject, id, format) { const cacheKey = (0, json_stringify_deterministic_1.default)({ subject, id }); const cacheEntry = this.idToSchemaCache.get(cacheKey); if (!cacheEntry || cacheEntry.softDeleted) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } return cacheEntry.info; } async getByGuid(guid, format) { const cacheEntry = this.guidToSchemaCache.get(guid); if (!cacheEntry || cacheEntry.softDeleted) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } return cacheEntry.info; } async getId(subject, schema) { const metadata = await this.getIdFullResponse(subject, schema); return metadata.id; } async getIdFullResponse(subject, schema) { const cacheKey = (0, json_stringify_deterministic_1.default)({ subject, schema: (0, schemaregistry_client_1.minimize)(schema) }); const cacheEntry = this.infoToSchemaCache.get(cacheKey); if (!cacheEntry || cacheEntry.softDeleted) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } return cacheEntry.metadata; } async getLatestSchemaMetadata(subject, format) { const version = await this.latestVersion(subject); if (version === -1) { throw new rest_error_1.RestError("No versions found for subject", 404, 40400); } return this.getSchemaMetadata(subject, version); } async getSchemaMetadata(subject, version, deleted = false, format) { let json; for (const [key, value] of this.schemaToVersionCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && value.version === version) { json = parsedKey; break; } } if (!json) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } let id = -1; for (const [key, value] of this.idToSchemaCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && value.info.schema === json.schema.schema) { id = parsedKey.id; break; } } if (id === -1) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } let guid = ""; for (const [key, value] of this.guidToSchemaCache.entries()) { if (value.info.schema === json.schema.schema) { guid = key; break; } } if (guid === "") { throw new rest_error_1.RestError("Schema not found", 404, 40400); } return { id, guid, version, subject, ...json.schema, }; } async getLatestWithMetadata(subject, metadata, deleted = false, format) { let metadataStr = ''; for (const key in metadata) { const encodedKey = encodeURIComponent(key); const encodedValue = encodeURIComponent(metadata[key]); metadataStr += `&key=${encodedKey}&value=${encodedValue}`; } let results = []; for (const [key, value] of this.schemaToVersionCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && (!value.softDeleted || deleted)) { if (parsedKey.schema.metadata && this.isSubset(metadata, parsedKey.schema.metadata.properties)) { results.push({ version: value.version, subject, ...parsedKey.schema }); } } } if (results.length === 0) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } let latest = results[0]; results.forEach((result) => { if (result.version > latest.version) { latest = result; } }); let id = -1; for (const [key, value] of this.idToSchemaCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && value.info.schema === latest.schema) { id = parsedKey.id; break; } } if (id === -1) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } latest.id = id; return latest; } isSubset(containee, container) { for (const key in containee) { if (containee[key] !== container[key]) { return false; } } return true; } async getAllVersions(subject) { const results = await this.allVersions(subject); if (results.length === 0) { throw new rest_error_1.RestError("No versions found for subject", 404, 40400); } return results; } async allVersions(subject) { const versions = []; for (const [key, value] of this.schemaToVersionCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && !value.softDeleted) { versions.push(value.version); } } return versions; } async latestVersion(subject) { const versions = await this.allVersions(subject); if (versions.length === 0) { return -1; } return versions[versions.length - 1]; } async deleteVersion(cacheKey, version, permanent) { if (permanent) { this.schemaToVersionCache.delete(cacheKey); } else { this.schemaToVersionCache.set(cacheKey, { version, softDeleted: true }); } } async deleteInfo(cacheKey, info, permanent) { if (permanent) { this.idToSchemaCache.delete(cacheKey); } else { this.idToSchemaCache.set(cacheKey, { info, softDeleted: true }); } } async deleteMetadata(cacheKey, metadata, permanent) { if (permanent) { this.infoToSchemaCache.delete(cacheKey); } else { this.infoToSchemaCache.set(cacheKey, { metadata, softDeleted: true }); } } async getVersion(subject, schema, normalize = false, deleted = false) { const cacheKey = (0, json_stringify_deterministic_1.default)({ subject, schema: (0, schemaregistry_client_1.minimize)(schema) }); const cacheEntry = this.schemaToVersionCache.get(cacheKey); if (!cacheEntry || cacheEntry.softDeleted) { throw new rest_error_1.RestError("Schema not found", 404, 40400); } return cacheEntry.version; } async getAllSubjects() { const subjects = []; for (const [key, value] of this.schemaToVersionCache.entries()) { const parsedKey = JSON.parse(key); if (!value.softDeleted && !subjects.includes(parsedKey.subject)) { subjects.push(parsedKey.subject); } } return subjects.sort(); } async deleteSubject(subject, permanent = false) { const deletedVersions = []; for (const [key, value] of this.infoToSchemaCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && (permanent || !value.softDeleted)) { await this.deleteMetadata(key, value.metadata, permanent); } } for (const [key, value] of this.schemaToVersionCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && (permanent || !value.softDeleted)) { await this.deleteVersion(key, value.version, permanent); deletedVersions.push(value.version); } } this.configCache.delete(subject); if (permanent) { for (const [key, value] of this.idToSchemaCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && (!value.softDeleted)) { await this.deleteInfo(key, value.info, permanent); } } } return deletedVersions; } async deleteSubjectVersion(subject, version, permanent = false) { for (const [key, value] of this.schemaToVersionCache.entries()) { const parsedKey = JSON.parse(key); if (parsedKey.subject === subject && value.version === version) { await this.deleteVersion(key, version, permanent); const cacheKeySchema = (0, json_stringify_deterministic_1.default)({ subject, schema: (0, schemaregistry_client_1.minimize)(parsedKey.schema) }); const cacheEntry = this.infoToSchemaCache.get(cacheKeySchema); if (cacheEntry) { await this.deleteMetadata(cacheKeySchema, cacheEntry.metadata, permanent); } if (permanent && cacheEntry) { const cacheKeyInfo = (0, json_stringify_deterministic_1.default)({ subject, id: cacheEntry.metadata.id }); const cacheSchemaEntry = this.idToSchemaCache.get(cacheKeyInfo); if (cacheSchemaEntry) { await this.deleteInfo(cacheKeyInfo, cacheSchemaEntry.info, permanent); } } } } return version; } async testSubjectCompatibility(subject, schema) { throw new Error("Unsupported operation"); } async testCompatibility(subject, version, schema) { throw new Error("Unsupported operation"); } async getCompatibility(subject) { const cacheEntry = this.configCache.get(subject); if (!cacheEntry) { throw new rest_error_1.RestError("Subject not found", 404, 40400); } return cacheEntry.compatibilityLevel; } async updateCompatibility(subject, compatibility) { this.configCache.set(subject, { compatibilityLevel: compatibility }); return compatibility; } async getDefaultCompatibility() { const cacheEntry = this.configCache.get(noSubject); if (!cacheEntry) { throw new rest_error_1.RestError("Default compatibility not found", 404, 40400); } return cacheEntry.compatibilityLevel; } async updateDefaultCompatibility(compatibility) { this.configCache.set(noSubject, { compatibilityLevel: compatibility }); return compatibility; } async getConfig(subject) { const cacheEntry = this.configCache.get(subject); if (!cacheEntry) { throw new rest_error_1.RestError("Subject not found", 404, 40400); } return cacheEntry; } async updateConfig(subject, config) { this.configCache.set(subject, config); return config; } async getDefaultConfig() { const cacheEntry = this.configCache.get(noSubject); if (!cacheEntry) { throw new rest_error_1.RestError("Default config not found", 404, 40400); } return cacheEntry; } async updateDefaultConfig(config) { this.configCache.set(noSubject, config); return config; } clearLatestCaches() { return; } clearCaches() { return; } async close() { return; } schemasEqual(schema1, schema2) { return (0, json_stringify_deterministic_1.default)(schema1) === (0, json_stringify_deterministic_1.default)(schema2); } } exports.MockClient = MockClient;