blob: 15429035231c9c585f33a9272add4dc6887e9793 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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 org.apache.hugegraph.api.schema;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.hugegraph.HugeGraph;
import org.apache.hugegraph.api.API;
import org.apache.hugegraph.api.filter.RedirectFilter;
import org.apache.hugegraph.api.filter.StatusFilter.Status;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.core.GraphManager;
import org.apache.hugegraph.define.Checkable;
import org.apache.hugegraph.schema.Userdata;
import org.apache.hugegraph.schema.VertexLabel;
import org.apache.hugegraph.type.define.GraphMode;
import org.apache.hugegraph.type.define.IdStrategy;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.Log;
import org.slf4j.Logger;
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableMap;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Singleton;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
@Path("graphs/{graph}/schema/vertexlabels")
@Singleton
@Tag(name = "VertexLabelAPI")
public class VertexLabelAPI extends API {
private static final Logger LOG = Log.logger(VertexLabelAPI.class);
@POST
@Timed
@Status(Status.CREATED)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON_WITH_CHARSET)
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_write"})
@RedirectFilter.RedirectMasterRole
public String create(@Context GraphManager manager,
@PathParam("graph") String graph,
JsonVertexLabel jsonVertexLabel) {
LOG.debug("Graph [{}] create vertex label: {}",
graph, jsonVertexLabel);
checkCreatingBody(jsonVertexLabel);
HugeGraph g = graph(manager, graph);
VertexLabel.Builder builder = jsonVertexLabel.convert2Builder(g);
VertexLabel vertexLabel = builder.create();
return manager.serializer(g).writeVertexLabel(vertexLabel);
}
@PUT
@Timed
@Path("{name}")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON_WITH_CHARSET)
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_write"})
@RedirectFilter.RedirectMasterRole
public String update(@Context GraphManager manager,
@PathParam("graph") String graph,
@PathParam("name") String name,
@QueryParam("action") String action,
JsonVertexLabel jsonVertexLabel) {
LOG.debug("Graph [{}] {} vertex label: {}",
graph, action, jsonVertexLabel);
checkUpdatingBody(jsonVertexLabel);
E.checkArgument(name.equals(jsonVertexLabel.name),
"The name in url(%s) and body(%s) are different",
name, jsonVertexLabel.name);
// Parse action parameter
boolean append = checkAndParseAction(action);
HugeGraph g = graph(manager, graph);
VertexLabel.Builder builder = jsonVertexLabel.convert2Builder(g);
VertexLabel vertexLabel = append ?
builder.append() :
builder.eliminate();
return manager.serializer(g).writeVertexLabel(vertexLabel);
}
@GET
@Timed
@Produces(APPLICATION_JSON_WITH_CHARSET)
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_read"})
public String list(@Context GraphManager manager,
@PathParam("graph") String graph,
@QueryParam("names") List<String> names) {
boolean listAll = CollectionUtils.isEmpty(names);
if (listAll) {
LOG.debug("Graph [{}] list vertex labels", graph);
} else {
LOG.debug("Graph [{}] get vertex labels by names {}", graph, names);
}
HugeGraph g = graph(manager, graph);
List<VertexLabel> labels;
if (listAll) {
labels = g.schema().getVertexLabels();
} else {
labels = new ArrayList<>(names.size());
for (String name : names) {
labels.add(g.schema().getVertexLabel(name));
}
}
return manager.serializer(g).writeVertexLabels(labels);
}
@GET
@Timed
@Path("{name}")
@Produces(APPLICATION_JSON_WITH_CHARSET)
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_read"})
public String get(@Context GraphManager manager,
@PathParam("graph") String graph,
@PathParam("name") String name) {
LOG.debug("Graph [{}] get vertex label by name '{}'", graph, name);
HugeGraph g = graph(manager, graph);
VertexLabel vertexLabel = g.schema().getVertexLabel(name);
return manager.serializer(g).writeVertexLabel(vertexLabel);
}
@DELETE
@Timed
@Path("{name}")
@Status(Status.ACCEPTED)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON_WITH_CHARSET)
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_delete"})
@RedirectFilter.RedirectMasterRole
public Map<String, Id> delete(@Context GraphManager manager,
@PathParam("graph") String graph,
@PathParam("name") String name) {
LOG.debug("Graph [{}] remove vertex label by name '{}'", graph, name);
HugeGraph g = graph(manager, graph);
// Throw 404 if not exists
g.schema().getVertexLabel(name);
return ImmutableMap.of("task_id",
g.schema().vertexLabel(name).remove());
}
/**
* JsonVertexLabel is only used to receive create and append requests
*/
@JsonIgnoreProperties(value = {"index_labels", "status"})
private static class JsonVertexLabel implements Checkable {
@JsonProperty("id")
public long id;
@JsonProperty("name")
public String name;
@JsonProperty("id_strategy")
public IdStrategy idStrategy;
@JsonProperty("properties")
public String[] properties;
@JsonProperty("primary_keys")
public String[] primaryKeys;
@JsonProperty("nullable_keys")
public String[] nullableKeys;
@JsonProperty("ttl")
public long ttl;
@JsonProperty("ttl_start_time")
public String ttlStartTime;
@JsonProperty("enable_label_index")
public Boolean enableLabelIndex;
@JsonProperty("user_data")
public Userdata userdata;
@JsonProperty("check_exist")
public Boolean checkExist;
@Override
public void checkCreate(boolean isBatch) {
E.checkArgumentNotNull(this.name,
"The name of vertex label can't be null");
}
private VertexLabel.Builder convert2Builder(HugeGraph g) {
VertexLabel.Builder builder = g.schema().vertexLabel(this.name);
if (this.id != 0) {
E.checkArgument(this.id > 0,
"Only positive number can be assign as " +
"vertex label id");
E.checkArgument(g.mode() == GraphMode.RESTORING,
"Only accept vertex label id when graph in " +
"RESTORING mode, but '%s' is in mode '%s'",
g, g.mode());
builder.id(this.id);
}
if (this.idStrategy != null) {
builder.idStrategy(this.idStrategy);
}
if (this.properties != null) {
builder.properties(this.properties);
}
if (this.primaryKeys != null) {
builder.primaryKeys(this.primaryKeys);
}
if (this.nullableKeys != null) {
builder.nullableKeys(this.nullableKeys);
}
if (this.enableLabelIndex != null) {
builder.enableLabelIndex(this.enableLabelIndex);
}
if (this.userdata != null) {
builder.userdata(this.userdata);
}
if (this.checkExist != null) {
builder.checkExist(this.checkExist);
}
if (this.ttl != 0) {
builder.ttl(this.ttl);
}
if (this.ttlStartTime != null) {
E.checkArgument(this.ttl > 0,
"Only set ttlStartTime when ttl is " +
"positive, but got ttl: %s", this.ttl);
builder.ttlStartTime(this.ttlStartTime);
}
return builder;
}
@Override
public String toString() {
return String.format("JsonVertexLabel{" +
"name=%s, idStrategy=%s, primaryKeys=%s, nullableKeys=%s, " +
"properties=%s, ttl=%s, ttlStartTime=%s}",
this.name, this.idStrategy, Arrays.toString(this.primaryKeys),
Arrays.toString(this.nullableKeys),
Arrays.toString(this.properties), this.ttl, this.ttlStartTime);
}
}
}