io.streamnative.pulsar.handlers.kop.schemaregistry.resources.SchemaResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pulsar-kafka-schema-registry Show documentation
Show all versions of pulsar-kafka-schema-registry Show documentation
Kafka Compatible Schema Registry
/**
* Copyright (c) 2019 - 2024 StreamNative, Inc.. All Rights Reserved.
*/
/**
* 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.
*/
package io.streamnative.pulsar.handlers.kop.schemaregistry.resources;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.netty.handler.codec.http.FullHttpRequest;
import io.streamnative.pulsar.handlers.kop.schemaregistry.HttpJsonRequestProcessor;
import io.streamnative.pulsar.handlers.kop.schemaregistry.SchemaRegistryHandler;
import io.streamnative.pulsar.handlers.kop.schemaregistry.exceptions.Errors;
import io.streamnative.pulsar.handlers.kop.schemaregistry.model.Schema;
import io.streamnative.pulsar.handlers.kop.schemaregistry.model.SchemaStorage;
import io.streamnative.pulsar.handlers.kop.schemaregistry.model.SchemaStorageAccessor;
import io.streamnative.pulsar.handlers.kop.schemaregistry.model.rest.SchemaReference;
import io.streamnative.pulsar.handlers.kop.schemaregistry.utils.SubjectUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
public class SchemaResource extends AbstractResource {
// type boolean, default false
public static final String QUERY_PARAM_DELETED = "deleted";
public SchemaResource(SchemaStorageAccessor schemaStorageAccessor,
String defaultNamespace) {
super(schemaStorageAccessor, defaultNamespace);
}
/**
* Register all processors.
* @param schemaRegistryHandler
*/
public void register(SchemaRegistryHandler schemaRegistryHandler) {
schemaRegistryHandler.addProcessor(new GetSchemaById());
schemaRegistryHandler.addProcessor(new GetSchemaStringById());
schemaRegistryHandler.addProcessor(new GetSchemaTypes());
schemaRegistryHandler.addProcessor(new GetVersions());
schemaRegistryHandler.addProcessor(new GetSubjects());
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public static final class GetSchemaResponse {
private String schema;
private String schemaType;
private List references;
//todo: metadata, ruleSet, maxId
public String getSchemaType() {
return "AVRO".equals(schemaType) ? null : schemaType;
}
}
// /schemas/types
public static class GetSchemaTypes extends HttpJsonRequestProcessor> {
public GetSchemaTypes() {
super(Void.class, "/schemas/types", GET);
}
@Override
protected CompletableFuture> processRequest(Void payload, List patternGroups,
FullHttpRequest request,
Map> queryParams,
String currentTenant) {
return CompletableFuture.completedFuture(Schema.getAllTypes());
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static final class SubjectVersionPair {
private String subject;
private int version;
}
// GET /schemas/ids/{int: id}
public class GetSchemaById extends HttpJsonRequestProcessor {
private static final String QUERY_PARAM_SUBJECT = "subject";
public GetSchemaById() {
super(Void.class, "/schemas/ids/" + INT_PATTERN, GET);
}
@Override
protected CompletableFuture processRequest(Void payload,
List patternGroups,
FullHttpRequest request,
Map> queryParams,
String currentTenant)
throws Exception {
String querySubject = getStringQueryParam(QUERY_PARAM_SUBJECT, null, queryParams);
final String subject = querySubject == null ? null : SubjectUtils.normalize(querySubject, defaultNamespace);
int id = getInt(0, patternGroups);
SchemaStorage schemaStorage = getSchemaStorage(currentTenant);
CompletableFuture> schemasFuture = schemaStorage.findSchemaById(id, false);
return schemasFuture.thenApply(list -> {
if (CollectionUtils.isEmpty(list)) {
throw Errors.schemaNotFoundException(id);
}
if (subject == null) {
return new GetSchemaResponse(list.get(0).getSchema(), list.get(0).getType(),
list.get(0).getReferences());
}
for (Schema schema : list) {
if (schema.getSubject().equals(subject)) {
return new GetSchemaResponse(schema.getSchema(), schema.getType(),
schema.getReferences());
}
}
throw Errors.schemaNotFoundException(id);
});
}
}
// GET /schemas/ids/{int: id}/schema Get one schema string
public class GetSchemaStringById extends HttpJsonRequestProcessor {
public GetSchemaStringById() {
super(Void.class, "/schemas/ids/" + INT_PATTERN + "/schema", GET);
}
@Override
protected CompletableFuture processRequest(Void payload, List patternGroups,
FullHttpRequest request,
Map> queryParams,
String currentTenant)
throws Exception {
int id = getInt(0, patternGroups);
SchemaStorage schemaStorage = getSchemaStorage(currentTenant);
CompletableFuture> schemasFuture = schemaStorage.findSchemaById(id, false);
return schemasFuture.thenApply(list -> {
if (CollectionUtils.isEmpty(list)) {
return null;
}
return list.get(0).getSchema();
});
}
}
// GET /schemas/ids/{int: id}/versions
public class GetVersions extends HttpJsonRequestProcessor> {
public GetVersions() {
super(Void.class, "/schemas/ids/" + INT_PATTERN + "/versions", GET);
}
@Override
protected CompletableFuture> processRequest(Void payload,
List patternGroups,
FullHttpRequest request,
Map> queryParams,
String currentTenant)
throws Exception {
SchemaStorage schemaStorage = getSchemaStorage(currentTenant);
boolean lookupDeleted = getBooleanQueryParam(QUERY_PARAM_DELETED, "false", queryParams);
int id = getInt(0, patternGroups);
CompletableFuture> schemasFuture = schemaStorage.findSchemaById(id, lookupDeleted);
return schemasFuture.thenApply(list -> {
if (CollectionUtils.isEmpty(list)) {
return null;
}
return list.stream()
.map(s -> new SubjectVersionPair(s.getSubject(), s.getVersion()))
.collect(Collectors.toList());
});
}
}
// GET /schemas/ids/{int: id}/subjects
public class GetSubjects extends HttpJsonRequestProcessor> {
public GetSubjects() {
super(Void.class, "/schemas/ids/" + INT_PATTERN + "/subjects", GET);
}
@Override
protected CompletableFuture> processRequest(
Void payload, List patternGroups, FullHttpRequest request,
Map> queryParams,
String currentTenant) throws Exception {
boolean lookupDeleted = getBooleanQueryParam(QUERY_PARAM_DELETED, "false", queryParams);
int id = getInt(0, patternGroups);
SchemaStorage schemaStorage = getSchemaStorage(currentTenant);
return schemaStorage.findSchemaById(id, lookupDeleted).thenApply(schemas -> {
if (CollectionUtils.isEmpty(schemas)) {
throw Errors.schemaNotFoundException();
}
return schemas.stream().map(Schema::getSubject).collect(Collectors.toSet());
});
}
}
}