catalogue-util-library/src/main/java/org/gcube/datacatalogue/ckanutillibrary/jackan/CheckedCkanClient.java

286 lines
10 KiB
Java

/*
* Copyright 2015 Trento Rise.
*
* 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 org.gcube.datacatalogue.ckanutillibrary.jackan;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.gcube.datacatalogue.ckanutillibrary.server.utils.GenericUtils.checkNotEmpty;
import static org.gcube.datacatalogue.ckanutillibrary.server.utils.GenericUtils.isNotEmpty;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.CkanDataset;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.CkanDatasetBase;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.CkanGroup;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.CkanLicense;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.CkanOrganization;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.CkanResource;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.CkanResourceBase;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.exceptions.CkanNotFoundException;
import org.gcube.datacatalogue.ckanutillibrary.shared.jackan.model.exceptions.CkanValidationException;
/**
* This client performs additional checks when writing to CKAN to ensure written
* content is correct. For this reason it might do additional calls and results
* of validation might be different from default Ckan ones. But if Ckan actually
* performed all the checks it should do there wouldn't be any need of this
* class as well..
*
* <p>
* Note: For {@code create} operations, this client fails if item is already
* existing on the server. This behaviour is different from ckan default
* {@code upsert} behaviour (update an existing) to prevent misuse.
* </p>
*
* @author David Leoni
* @since 0.4.1
*/
public class CheckedCkanClient extends CkanClient {
protected CheckedCkanClient() {
super();
}
protected CheckedCkanClient(String url) {
super(url);
}
public CheckedCkanClient(String catalogUrl, @Nullable String ckanToken) {
super(catalogUrl, ckanToken);
}
/**
* Returns a builder instance. The builder is not threadsafe and you can use
* one builder instance to build only one client instance.
*/
public static CkanClient.Builder builder() {
return CkanClient.newBuilder(new CheckedCkanClient());
}
private void checkUrl(String url, String prependedErrorMessage) {
try {
new URL(url).toURI();
} catch (MalformedURLException | URISyntaxException ex) {
throw new CkanValidationException(String.valueOf(prependedErrorMessage) + " -- Ill-formed url:" + url, this,
ex);
}
}
private void checkUuid(String uuid, String prependedErrorMessage) {
try {
UUID.fromString(uuid);
} catch (Exception ex) {
throw new CkanValidationException(String.valueOf(prependedErrorMessage) + " -- Ill-formed uuid:" + uuid,
this, ex);
}
}
/**
* {@inheritDoc}
*
* <p>
* NOTE: In CheckedCkanClient {@code create} operations fail if item already
* exists. This is different from Ckan default behaviour, which updates
* items if they already exist.
* </p>
*/
@Override
public synchronized CkanOrganization createOrganization(CkanOrganization org) {
if (org.getId() != null) {
checkUuid(org.getId(),
"Jackan validation failed! Tried to create organization with invalid id:" + org.getId());
try {
getOrganization(org.getId());
throw new CkanValidationException(
"Jackan validation failed! Tried to create organization with existing id! " + org.getId(),
this);
} catch (CkanNotFoundException ex) {
}
}
return super.createOrganization(org);
}
/**
* {@inheritDoc}
*
* <p>
* NOTE: In CheckedCkanClient {@code create} operations fail if item already
* exists. This is different from Ckan default behaviour, which updates
* items if they already exist.
* </p>
*/
@Override
public synchronized CkanResource createResource(CkanResourceBase resource) {
if (resource.getId() != null) {
checkUuid(resource.getId(),
"Jackan validation failed! Tried to create resource with invalid id:" + resource.getId());
try {
getResource(resource.getId());
throw new CkanValidationException(
"Jackan validation failed! Tried to create resource with existing id! " + resource.getId(),
this);
} catch (CkanNotFoundException ex) {
}
}
// Only check for URL if no file is given
// If a file is given, the URL field is statically set to "upload"
if (resource.getUpload() == null)
checkUrl(resource.getUrl(),
"Jackan validation error! Tried to create resource " + resource.getId() + " with wrong url!");
return super.createResource(resource);
}
@Override
public synchronized CkanResource updateResource(CkanResourceBase resource) {
checkUrl(resource.getUrl(),
"Jackan validation error! Tried to update resource " + resource.getId() + " with wrong url!");
return super.updateResource(resource);
}
@Override
public synchronized CkanResource patchUpdateResource(CkanResourceBase resource) {
checkUrl(resource.getUrl(),
"Jackan validation error! Tried to patch update resource " + resource.getId() + " with wrong url!");
return super.patchUpdateResource(resource);
}
/**
* {@inheritDoc}
*
* <p>
* NOTE: In CheckedCkanClient {@code create} operations fail if item already
* exists. This is different from Ckan default behaviour, which updates
* items if they already exist.
* </p>
*/
@Override
public synchronized CkanGroup createGroup(CkanGroup group) {
if (group.getId() != null) {
checkUuid(group.getId(),
"Jackan validation failed! Tried to create group with invalid id:" + group.getId());
try {
getGroup(group.getId());
throw new CkanValidationException(
"Jackan validation failed! Tried to create group with existing id! " + group.getId(), this);
} catch (CkanNotFoundException ex) {
}
}
return super.createGroup(group);
}
private void checkGroupsExist(Iterable<CkanGroup> groups, String prependedErrorMessage) {
if (groups != null) {
for (CkanGroup group : groups) {
checkNotNull(group, String.valueOf(prependedErrorMessage) + " -- Found null group! ");
checkNotEmpty(group.idOrName(),
String.valueOf(prependedErrorMessage) + " -- Found group with both id and name invalid!");
try {
getGroup(group.idOrName());
} catch (CkanNotFoundException ex) {
throw new CkanValidationException(
prependedErrorMessage + " -- Tried to refer to non existing group " + group.idOrName(),
this, ex);
}
}
}
}
private void checkLicenseExist(@Nullable String licenseId, String prependedErrorMessage) {
if (isNotEmpty(licenseId)) {
List<CkanLicense> licenseList = getLicenseList();
boolean found = false;
for (CkanLicense lic : licenseList) {
if (licenseId.equals(lic.getId())) {
found = true;
break;
}
}
if (!found) {
throw new CkanValidationException(String.valueOf(prependedErrorMessage) + " -- licenseId '" + licenseId
+ "' doesn't belong to allowed licenses: " + licenseList.toString(), this);
}
}
}
/**
* {@inheritDoc}
*
* <p>
* NOTE: In CheckedCkanClient {@code create} operations fail if item already
* exists. This is different from Ckan default behaviour, which updates
* items if they already exist.
* </p>
*/
@Override
public synchronized CkanDataset createDataset(CkanDatasetBase dataset) {
checkGroupsExist(dataset.getGroups(), "Jackan validation error when creating dataset " + dataset.getName());
checkLicenseExist(dataset.getLicenseId(), "Jackan validation error when creating dataset " + dataset.getName());
return super.createDataset(dataset);
}
@Override
public synchronized CkanDataset updateDataset(CkanDatasetBase dataset) {
checkGroupsExist(dataset.getGroups(), "Jackan validation error when updating dataset " + dataset.getName());
checkLicenseExist(dataset.getLicenseId(), "Jackan validation error updating dataset " + dataset.getName());
return super.updateDataset(dataset);
}
@Override
public synchronized CkanDataset patchUpdateDataset(CkanDatasetBase dataset) {
checkGroupsExist(dataset.getGroups(),
"Jackan validation error when patch updating dataset " + dataset.getName());
checkLicenseExist(dataset.getLicenseId(),
"Jackan validation error when patch updating dataset " + dataset.getName());
return super.patchUpdateDataset(dataset);
}
}