package com.constellio.app.api.admin.services;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.app.client.entities.MetadataResource;
import com.constellio.app.ui.application.ConstellioUI;
import com.constellio.model.entities.Language;
import com.constellio.model.entities.Taxonomy;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchema;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataSchemaTypes;
import com.constellio.model.entities.schemas.MetadataValueType;
import com.constellio.model.entities.schemas.entries.CalculatedDataEntry;
import com.constellio.model.entities.schemas.entries.DataEntryType;
import com.constellio.model.entities.schemas.validation.RecordMetadataValidator;
import com.constellio.model.entities.schemas.validation.RecordValidator;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.schemas.MetadataSchemasManagerException.OptimisticLocking;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.services.schemas.builders.MetadataBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaBuilderRuntimeException.NoSuchMetadata;
import com.constellio.model.services.schemas.builders.MetadataSchemaTypeBuilder;
import com.constellio.model.services.schemas.builders.MetadataSchemaTypesBuilder;
import com.constellio.model.services.taxonomies.TaxonomiesManager;
@Path("schemas")
@Consumes("application/xml")
@Produces("application/xml")
public class SchemasServicesAPI {
private static Logger LOGGER = LoggerFactory.getLogger(SchemasServicesAPI.class);
@GET
@Path("getSchemaTypes")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getSchemaTypes(@QueryParam("collection") String collection) {
logServiceCall("getSchemaTypes", collection);
List<String> schemaTypes = new ArrayList<>();
for (MetadataSchemaType type : types(collection).getSchemaTypes()) {
schemaTypes.add(type.getCode());
}
return schemaTypes;
}
@GET
@Path("getSchemas")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getSchemas(@QueryParam("collection") String collection, @QueryParam("schemaType") String schemaType) {
logServiceCall("getSchemas", collection, schemaType);
List<String> schemas = new ArrayList<>();
for (MetadataSchema schema : types(collection).getSchemaType(schemaType).getAllSchemas()) {
schemas.add(schema.getCode());
}
return schemas;
}
@GET
@Path("getSchemaMetadataCodes")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getSchemaMetadataCodes(@QueryParam("collection") String collection,
@QueryParam("schemaCode") String schemaCode) {
logServiceCall("getSchemaMetadataCodes", collection, schemaCode);
List<String> metadataCodes = new ArrayList<>();
for (Metadata metadata : types(collection).getSchema(schemaCode).getMetadatas()) {
metadataCodes.add(metadata.getCode());
}
return metadataCodes;
}
@GET
@Path("getSchemaValidators")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getSchemaValidators(@QueryParam("collection") String collection,
@QueryParam("schemaCode") String schemaCode) {
logServiceCall("getSchemaValidators", collection, schemaCode);
List<String> validators = new ArrayList<>();
for (RecordValidator validator : types(collection).getSchema(schemaCode).getValidators()) {
validators.add(validator.getClass().getCanonicalName());
}
return validators;
}
@GET
@Path("getDataStoreFields")
@Produces(MediaType.APPLICATION_JSON)
public MetadataResource getMetadata(@QueryParam("collection") String collection, @QueryParam("code") String code) {
logServiceCall("getDataStoreFields", collection, code);
Metadata metadata = types(collection).getMetadata(code);
return toMetadataResource(metadata);
}
@GET
@Path("getTaxonomies")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getTaxonomies(@QueryParam("collection") String collection) {
logServiceCall("getTaxonomies", collection);
List<String> taxonomyCodes = new ArrayList<>();
for (Taxonomy taxonomy : taxonomyManager().getEnabledTaxonomies(collection)) {
taxonomyCodes.add(taxonomy.getCode());
}
return taxonomyCodes;
}
@GET
@Path("getPrincipalTaxonomy")
@Produces(MediaType.TEXT_PLAIN)
public String getPrincipalTaxonomy(@QueryParam("collection") String collection) {
logServiceCall("getPrincipalTaxonomy", collection);
Taxonomy principalTaxonomy = taxonomyManager().getPrincipalTaxonomy(collection);
return principalTaxonomy == null ? null : principalTaxonomy.getCode();
}
@GET
@Path("getTaxonomySchemaTypes")
@Produces(MediaType.APPLICATION_JSON)
public List<String> getTaxonomySchemaTypes(@QueryParam("collection") String collection,
@QueryParam("taxonomyCode") String taxonomyCode) {
logServiceCall("getTaxonomySchemaTypes", collection, taxonomyCode);
Taxonomy taxonomy = taxonomyManager().getEnabledTaxonomyWithCode(collection, taxonomyCode);
return taxonomy.getSchemaTypes();
}
@POST
@Path("createSchemaType")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public String createSchemaType(@QueryParam("collection") String collection,
@QueryParam("schemaTypeCode") String schemaTypeCode) {
logServiceCall("createSchemaType", collection, schemaTypeCode);
MetadataSchemaTypesBuilder typesBuilder = typesBuilder(collection);
typesBuilder.createNewSchemaType(schemaTypeCode);
save(typesBuilder);
return "OK";
}
@POST
@Path("createCustomSchema")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public String createCustomSchema(@QueryParam("collection") String collection, @QueryParam("schemaCode") String schemaCode) {
logServiceCall("createCustomSchema", collection, schemaCode);
String schemaTypeCode = schemaUtils().getSchemaTypeCode(schemaCode);
String schemaLocalCode = schemaUtils().getSchemaLocalCode(schemaCode);
MetadataSchemaTypesBuilder typesBuilder = typesBuilder(collection);
typesBuilder.getSchemaType(schemaTypeCode).createCustomSchema(schemaLocalCode);
save(typesBuilder);
return "OK";
}
@POST
@Path("addSchemaValidator")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public String addSchemaValidator(@QueryParam("collection") String collection, @QueryParam("schemaCode") String schemaCode,
@QueryParam("schemaValidator") String schemaValidator) {
logServiceCall("getSchemaTypes", collection, schemaCode);
MetadataSchemaTypesBuilder typesBuilder = typesBuilder(collection);
typesBuilder.getSchema(schemaCode).defineValidators().add(schemaValidator);
save(typesBuilder);
return "OK";
}
@POST
@Path("removeSchemaValidator")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public String removeSchemaValidator(@QueryParam("collection") String collection, @QueryParam("schemaCode") String schemaCode,
@QueryParam("schemaValidator") String schemaValidator) {
logServiceCall("removeSchemaValidator", collection, schemaCode);
MetadataSchemaTypesBuilder typesBuilder = typesBuilder(collection);
typesBuilder.getSchema(schemaCode).defineValidators().remove(schemaValidator);
save(typesBuilder);
return "OK";
}
@POST
@Path("addUpdateMetadata")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public String addUpdateMetadata(@QueryParam("collection") String collection, MetadataResource metadataResource) {
logServiceCall("addUpdateMetadata", collection, metadataResource);
MetadataSchemaTypesBuilder typesBuilder = typesBuilder(collection);
String schemaCode = schemaUtils().getSchemaCode(metadataResource.getCode());
MetadataSchemaBuilder schemaBuilder = typesBuilder.getSchema(schemaCode);
String metadataCode = schemaUtils().getLocalCode(metadataResource.getCode(), schemaCode);
try {
MetadataBuilder metadata = schemaBuilder.getMetadata(metadataCode);
configureExitentMetadata(metadata, metadataResource);
} catch (NoSuchMetadata e) {
LOGGER.info("No metadata with code {}, creating one", metadataCode, e);
MetadataBuilder metadata = schemaBuilder.create(metadataCode);
configureNewMetadata(metadata, metadataResource, typesBuilder);
}
save(typesBuilder);
return "OK";
}
private void configureNewMetadata(MetadataBuilder metadata, MetadataResource metadataResource,
MetadataSchemaTypesBuilder typesBuilder) {
configureExitentMetadata(metadata, metadataResource);
metadata.setType(notNull("type", MetadataValueType.valueOf(metadataResource.getType())));
if (metadataResource.getCalculator() != null) {
metadata.defineDataEntry().asCalculated(metadataResource.getCalculator());
}
if (metadataResource.getAllowedReference() != null) {
MetadataSchemaTypeBuilder type = typesBuilder.getSchemaType(metadataResource.getAllowedReference());
metadata.defineReferencesTo(type);
}
if (metadataResource.getChildOfRelationship() != null) {
metadata.setChildOfRelationship(metadataResource.getChildOfRelationship());
}
if (metadataResource.getMultivalue() != null) {
metadata.setMultivalue(metadataResource.getMultivalue());
}
if (metadataResource.getUniqueValue() != null) {
metadata.setUniqueValue(metadataResource.getUniqueValue());
}
if (metadataResource.getSearchable() != null) {
metadata.setSearchable(metadataResource.getSearchable());
}
configureModifiableMetadataAttributes(metadata, metadataResource);
}
private void configureExitentMetadata(MetadataBuilder metadata, MetadataResource metadataResource) {
configureModifiableMetadataAttributes(metadata, metadataResource);
}
private void configureModifiableMetadataAttributes(MetadataBuilder metadata, MetadataResource metadataResource) {
if (metadataResource.getDefaultRequirement() != null) {
metadata.setDefaultRequirement(metadataResource.getDefaultRequirement());
}
if (metadataResource.getEnabled() != null) {
metadata.setEnabled(metadataResource.getEnabled());
}
if (metadataResource.getLabel() != null) {
Language language = Language.withCode(ConstellioUI.getCurrentSessionContext().getCurrentLocale().getLanguage());
metadata.addLabel(language, metadataResource.getLabel());
}
metadata.defineValidators().set(metadataResource.getValidators());
}
@POST
@Path("disableMetadata")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public String disableMetadata(@QueryParam("collection") String collection, @QueryParam("metadataCode") String metadataCode) {
logServiceCall("disableMetadata", collection, metadataCode);
MetadataSchemaTypesBuilder typesBuilder = typesBuilder(collection);
typesBuilder.getMetadata(metadataCode).setEnabled(false);
save(typesBuilder);
return "OK";
}
@POST
@Path("createTaxonomy")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public String createTaxonomy(@QueryParam("collection") String collection, @QueryParam("code") String code,
List<String> schemaTypes) {
logServiceCall("createTaxonomy", collection, code, schemaTypes);
Taxonomy taxonomy = Taxonomy.createPublic(code, code, collection, schemaTypes);
taxonomyManager().addTaxonomy(taxonomy, metadataSchemasManager());
Map<String, String> queryParams = new HashMap<>();
queryParams.put("code", code);
return "OK";
}
@POST
@Path("setAsPrincipalTaxonomy")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public String setAsPrincipalTaxonomy(@QueryParam("collection") String collection, @QueryParam("code") String code) {
logServiceCall("setAsPrincipalTaxonomy", collection, code);
Taxonomy taxonomy = taxonomyManager().getEnabledTaxonomyWithCode(collection, code);
taxonomyManager().setPrincipalTaxonomy(taxonomy, metadataSchemasManager());
return "OK";
}
TaxonomiesManager taxonomyManager() {
return AdminServicesUtils.modelServicesFactory().getTaxonomiesManager();
}
MetadataSchemasManager metadataSchemasManager() {
return AdminServicesUtils.modelServicesFactory().getMetadataSchemasManager();
}
MetadataSchemaTypes types(String collection) {
return metadataSchemasManager().getSchemaTypes(collection);
}
MetadataSchemaTypesBuilder typesBuilder(String collection) {
return metadataSchemasManager().modify(collection);
}
void save(MetadataSchemaTypesBuilder typesBuilder) {
try {
metadataSchemasManager().saveUpdateSchemaTypes(typesBuilder);
} catch (OptimisticLocking optimistickLocking) {
throw new RuntimeException(optimistickLocking);
}
}
SchemaUtils schemaUtils() {
return new SchemaUtils();
}
private MetadataResource toMetadataResource(Metadata metadata) {
MetadataResource resource = new MetadataResource();
resource.setCode(metadata.getCode());
resource.setDataStoreCode(metadata.getDataStoreCode());
Language language = Language.withCode(ConstellioUI.getCurrentSessionContext().getCurrentLocale().getLanguage());
resource.setLabel(metadata.getLabel(language));
resource.setType(metadata.getType().name());
resource.setChildOfRelationship(metadata.isChildOfRelationship());
if (metadata.getAllowedReferences() != null) {
resource.setAllowedReference(metadata.getAllowedReferences().getAllowedSchemaType());
}
resource.setSearchable(metadata.isSearchable());
resource.setEnabled(metadata.isEnabled());
resource.setMultivalue(metadata.isMultivalue());
if (metadata.getDataEntry().getType() == DataEntryType.CALCULATED) {
CalculatedDataEntry entry = (CalculatedDataEntry) metadata.getDataEntry();
resource.setCalculator(entry.getCalculator().getClass().getCanonicalName());
}
resource.setDefaultRequirement(metadata.isDefaultRequirement());
resource.setUniqueValue(metadata.isUniqueValue());
List<String> validators = new ArrayList<>();
for (RecordMetadataValidator validator : metadata.getValidators()) {
validators.add(validator.getClass().getCanonicalName());
}
resource.setValidators(validators);
return resource;
}
private <T> T notNull(String name, T value) {
if (value == null) {
throw new RuntimeException("Value is required or invalid for '" + name + "'");
}
return value;
}
private void logServiceCall(String serviceName, Object... params) {
StringBuilder serviceCall = new StringBuilder();
serviceCall.append(serviceName + " called with {");
boolean first = true;
for (Object object : params) {
if (!first) {
serviceCall.append(", ");
}
serviceCall.append(object);
first = false;
}
serviceCall.append("}");
LOGGER.info(serviceCall.toString());
}
}