Skip to content

Commit

Permalink
Merge branch 'dev' into #319-master-data-management
Browse files Browse the repository at this point in the history
  • Loading branch information
marcos-lg committed Nov 30, 2021
2 parents 7785e18 + 6bcce6d commit b7cff5f
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public interface NetworkMapper extends BaseNetworkEntityMapper<Network> {

int countDatasetsInNetwork(@Param("networkKey") UUID networkKey);

boolean constituentExists(
@Param("networkKey") UUID networkKey, @Param("datasetKey") UUID datasetKey);

void addDatasetConstituent(
@Param("networkKey") UUID networkKey, @Param("datasetKey") UUID datasetKey);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,13 @@
network_key = #{targetEntityKey,jdbcType=OTHER} AND comment_key = #{commentKey,jdbcType=INTEGER}
</delete>

<select id="constituentExists" resultType="java.lang.Boolean">
SELECT EXISTS (
SELECT *
FROM dataset_network
WHERE dataset_key = #{datasetKey,jdbcType=OTHER} AND network_key = #{networkKey,jdbcType=OTHER}
)
</select>

<!-- DATASET CONSTITUENTS -->
<insert id="addDatasetConstituent">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class LegacyAuthorizationFilter extends OncePerRequestFilter {
private static final String SERVICE_MAPPING = "/service";
private static final String IPT_MAPPING = "/ipt";
private static final String RESOURCE_BOTH_SIDE_SLASH_MAPPING = "/resource/";
private static final String NETWORK_MAPPING = "/network";

private final LegacyAuthorizationService legacyAuthorizationService;

Expand All @@ -75,7 +76,7 @@ protected void doFilterInternal(
filterPostPutDeleteRequests(request, path);
}
}
// otherwise just do nothing (request unchanged)
// otherwise, just do nothing (request unchanged)
filterChain.doFilter(request, response);
}

Expand Down Expand Up @@ -106,6 +107,9 @@ else if (path.contains(RESOURCE_MAPPING)) {
// register dataset?
if (path.endsWith(RESOURCE_MAPPING)) {
authorizeOrganizationChange(httpRequest);
} else if (path.contains(NETWORK_MAPPING)) {
UUID datasetKey = retrieveKeyFromMiddleRequestPath(httpRequest);
authorizeOrganizationDatasetChange(httpRequest, datasetKey);
}
// update dataset, delete dataset?
else if (path.contains(RESOURCE_BOTH_SIDE_SLASH_MAPPING)) {
Expand Down Expand Up @@ -228,6 +232,30 @@ private UUID retrieveKeyFromRequestPath(HttpServletRequest request) {
}
}

/**
* Retrieve key from request path, where the key is in the middle segment, e.g.
* /registry/resource/{key}/network/{networkKey}
*
* @param request request
* @return dataset key
* @throws WebApplicationException if incoming string key isn't a valid UUID
*/
private UUID retrieveKeyFromMiddleRequestPath(HttpServletRequest request) {
String path = request.getRequestURI();
String key = path.substring(path.lastIndexOf(RESOURCE_BOTH_SIDE_SLASH_MAPPING) + RESOURCE_BOTH_SIDE_SLASH_MAPPING.length());

if (key.contains(SLASH)) {
key = key.substring(0, key.indexOf(SLASH));
}

try {
return UUID.fromString(key);
} catch (IllegalArgumentException e) {
throw new WebApplicationException(
"Key is not present it the request", HttpStatus.BAD_REQUEST);
}
}

/**
* Retrieve dataset key from form or query parameters.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

import static org.gbif.registry.security.UserRoles.ADMIN_ROLE;
import static org.gbif.registry.security.UserRoles.EDITOR_ROLE;
import static org.gbif.registry.security.UserRoles.IPT_ROLE;

@Validated
@Primary
Expand Down Expand Up @@ -123,7 +124,7 @@ public PagingResponse<Dataset> listConstituents(
}

/**
* Validates if a the requested dataset exists.
* Validates if the requested dataset exists.
*/
private void existDatasetCheck(UUID datasetKey) {
if (datasetMapper.get(datasetKey) == null) {
Expand All @@ -133,7 +134,7 @@ private void existDatasetCheck(UUID datasetKey) {
}

/**
* Validates if a the requested network exists.
* Validates if the requested network exists.
*/
private void existNetworkCheck(UUID networkKey) {
if (networkMapper.get(networkKey) == null) {
Expand All @@ -143,22 +144,28 @@ private void existNetworkCheck(UUID networkKey) {
}

@PostMapping("{key}/constituents/{datasetKey}")
@Secured({ADMIN_ROLE, EDITOR_ROLE})
@Secured({ADMIN_ROLE, EDITOR_ROLE, IPT_ROLE})
@Override
public void addConstituent(@PathVariable("key") UUID networkKey, @PathVariable UUID datasetKey) {
if (networkMapper.constituentExists(networkKey, datasetKey)) {
throw new WebApplicationException(
"Dataset " + datasetKey + " is already connected to the network " + networkKey, HttpStatus.BAD_REQUEST);
}
existDatasetCheck(datasetKey);
existNetworkCheck(networkKey);
networkMapper.addDatasetConstituent(networkKey, datasetKey);
eventManager.post(ChangedComponentEvent.newInstance(datasetKey, Network.class, Dataset.class));
}

@DeleteMapping("{key}/constituents/{datasetKey}")
@Secured({ADMIN_ROLE, EDITOR_ROLE})
@Secured({ADMIN_ROLE, EDITOR_ROLE, IPT_ROLE})
@Override
public void removeConstituent(
@PathVariable("key") UUID networkKey, @PathVariable UUID datasetKey) {
existDatasetCheck(datasetKey);
existNetworkCheck(networkKey);
if (!networkMapper.constituentExists(networkKey, datasetKey)) {
throw new WebApplicationException(
"Dataset " + datasetKey + " is not connected to the network " + networkKey, HttpStatus.BAD_REQUEST);
}
networkMapper.deleteDatasetConstituent(networkKey, datasetKey);
eventManager.post(ChangedComponentEvent.newInstance(datasetKey, Network.class, Dataset.class));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ public IptResource(
/**
* Register IPT installation, handling incoming request with path /ipt/register. The primary
* contact and hosting organization key are mandatory. Only after both the installation and
* primary contact have been persisted is a ResponseEntity with HttpStatus.CREATED returned.
* primary contact have been persisted is a response with {@link HttpStatus#CREATED} returned.
*
* @param installation IptInstallation with HTTP form parameters
* @return ResponseEntity with HttpStatus.CREATED if successful
* @param installation {@link LegacyInstallation} with HTTP form parameters
* @return {@link ResponseEntity} with {@link HttpStatus#CREATED} if successful
*/
@PostMapping(
value = "register",
Expand Down Expand Up @@ -137,11 +137,11 @@ public ResponseEntity<IptEntityResponse> registerIpt(
/**
* Update IPT installation, handling incoming request with path /ipt/update/{key}. The primary
* contact and hosting organization key are mandatory. Only after both the installation and
* primary contact have been updated is a ResponseEntity with HttpStatus.CREATED returned.
* primary contact have been updated is a response with {@link HttpStatus#CREATED} returned.
*
* @param installationKey installation key (UUID) coming in as path param
* @param installation IptInstallation with HTTP form parameters
* @return ResponseEntity with HttpStatus.NO_CONTENT if successful
* @param installation {@link LegacyInstallation} with HTTP form parameters
* @return {@link ResponseEntity} with {@link HttpStatus#NO_CONTENT} if successful
*/
@PostMapping(value = "update/{key}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<Void> updateIpt(
Expand Down Expand Up @@ -217,14 +217,14 @@ public ResponseEntity<Void> updateIpt(
/**
* Register IPT dataset, handling incoming request with path /ipt/resource. The primary contact
* and publishing organization key are mandatory. Only after both the dataset and primary contact
* have been persisted is a ResponseEntity with HttpStatus.CREATED returned. </br> Before being
* have been persisted is a response with {@link HttpStatus#CREATED} returned. </br> Before being
* persisted, the dataset is the UNSPECIFIED license. This will be replaced (if possible) by the
* publisher assigned license when the dataset gets crawled the first time. Since IPT 2.2, the IPT
* EML metadata document always includes a machine readable license. See discussion at
* EML metadata document always includes a machine-readable license. See discussion at
* https://github.com/gbif/registry/issues/71
*
* @param dataset LegacyDataset with HTTP form parameters
* @return ResponseEntity with HttpStatus.CREATED if successful
* @return {@link ResponseEntity} with {@link HttpStatus#CREATED} if successful
*/
@PostMapping(
value = "resource",
Expand Down Expand Up @@ -301,14 +301,14 @@ public ResponseEntity<IptEntityResponse> registerDataset(
* Update IPT Dataset, handling incoming request with path /ipt/resource/{key}. The publishing
* organization key is mandatory (supplied in the credentials not the parameters). The contacts
* are preserved from the existing dataset, careful not to duplicate contacts. Only after both the
* dataset and primary contact have been updated is a Response with Status.OK returned. </br> This
* dataset and primary contact have been updated is a response with {@link HttpStatus#OK} returned. </br> This
* update does not change the IPT Dataset license. The license gets updated every time the dataset
* is crawled using the publisher assigned license found in the EML metadata document. Since IPT
* 2.2, the IPT EML metadata document always includes a machine readable license.
*
* @param datasetKey dataset key (UUID) coming in as path param
* @param dataset LegacyDataset with HTTP form parameters
* @return ResponseEntity with HttpStatus.CREATED (201) if successful
* @param dataset {@link LegacyDataset} with HTTP form parameters
* @return {@link ResponseEntity} with {@link HttpStatus#CREATED} (201) if successful
*/
@PostMapping(value = "resource/{key}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<Void> updateDataset(
Expand Down Expand Up @@ -403,10 +403,10 @@ else if (dataset.getInstallationKey() != existing.getInstallationKey()) {

/**
* Delete IPT Dataset, handling incoming request with path /ipt/resource/{key}. Only credentials
* are mandatory. If deletion is successful, returns Response with Status.OK.
* are mandatory. If deletion is successful, returns response with {@link HttpStatus#OK}.
*
* @param datasetKey dataset key (UUID) coming in as path param
* @return ResponseEntity with HttpStatus.OK if successful
* @return {@link ResponseEntity} with {@link HttpStatus#OK} if successful
*/
@SuppressWarnings("rawtypes")
@DeleteMapping("resource/{key}")
Expand Down Expand Up @@ -439,7 +439,7 @@ public ResponseEntity deleteDataset(@PathVariable("key") UUID datasetKey) {
* this must be the installation that serves the dataset. Conversely, if the organization has more
* or less than 1 installation, no inference can be made, and null is returned instead.
*
* @param dataset LegacyDataset with HTTP form parameters
* @param dataset {@link LegacyDataset} with HTTP form parameters
* @return inferred installation key, or null if none inferred
*/
private UUID inferInstallationKey(LegacyDataset dataset) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.gbif.api.model.registry.Dataset;
import org.gbif.api.service.registry.DatasetService;
import org.gbif.api.service.registry.InstallationService;
import org.gbif.api.service.registry.NetworkService;
import org.gbif.api.service.registry.OrganizationService;
import org.gbif.registry.domain.ws.ErrorResponse;
import org.gbif.registry.domain.ws.IptEntityResponse;
Expand Down Expand Up @@ -68,25 +69,28 @@ public class LegacyDatasetResource {
private final DatasetService datasetService;
private final InstallationService installationService;
private final IptResource iptResource;
private final NetworkService networkService;

public LegacyDatasetResource(
OrganizationService organizationService,
DatasetService datasetService,
IptResource iptResource,
InstallationService installationService) {
InstallationService installationService,
NetworkService networkService) {
this.organizationService = organizationService;
this.datasetService = datasetService;
this.iptResource = iptResource;
this.installationService = installationService;
this.networkService = networkService;
}

/**
* Register GBRDS dataset, handling incoming request with path /resource. The primary contact,
* publishing organization key, and resource name are mandatory. Only after both the dataset and
* primary contact have been persisted is a ResponseEntity with HttpStatus.CREATED (201) returned.
* primary contact have been persisted is a response with {@link HttpStatus#CREATED} (201) returned.
*
* @param dataset IptDataset with HTTP form parameters
* @return ResponseEntity
* @param dataset {@link LegacyDataset} with HTTP form parameters
* @return {@link ResponseEntity}
* @see IptResource#registerDataset(LegacyDataset, Authentication)
*/
@PostMapping(
Expand All @@ -104,12 +108,12 @@ public ResponseEntity<IptEntityResponse> registerDataset(
* organization key is mandatory (supplied in the credentials not the parameters). The primary
* contact is not required, but if any of the primary contact parameters were included in the
* request, it is required. This is the difference between this method and registerDataset. Only
* after both the dataset and optional primary contact have been updated is a ResponseEntity with
* HttpStatus.OK (201) returned.
* after both the dataset and optional primary contact have been updated is a response with
* {@link HttpStatus#OK} (201) returned.
*
* @param datasetKey dataset key (UUID) coming in as path param
* @param dataset IptDataset with HTTP form parameters
* @return ResponseEntity with HttpStatus.CREATED (201) if successful
* @param dataset {@link LegacyDataset} with HTTP form parameters
* @return {@link ResponseEntity} with {@link HttpStatus#CREATED} (201) if successful
*/
@PostMapping(
value = "resource/{key}",
Expand Down Expand Up @@ -210,11 +214,11 @@ else if (dataset.getInstallationKey() != existing.getInstallationKey()) {
/**
* Retrieve all Datasets owned by an organization, handling incoming request with path /resource.
* The publishing organization query parameter is mandatory. Only after both the organizationKey
* is verified to correspond to an existing organization, is a Response including the list of
* is verified to correspond to an existing organization, is a response including the list of
* Datasets returned.
*
* @param organizationKey organization key (UUID) coming in as query param
* @return ResponseEntity with list of Datasets or empty list with error message if none found
* @return {@link ResponseEntity} with list of Datasets or empty list with error message if none found
*/
@GetMapping(
value = {"resource", "resource{extension:\\.[a-z]+}"},
Expand Down Expand Up @@ -272,7 +276,7 @@ public ResponseEntity<?> datasetsForOrganization(
* Read GBRDS Dataset, handling incoming request with path /resource/{key}.
*
* @param datasetKey dataset key (UUID) coming in as path param
* @return ResponseEntity with HttpStatus.OK (200) if dataset exists
* @return {@link ResponseEntity} with {@link HttpStatus#OK} (200) if dataset exists
*/
@GetMapping(
value = {"resource/{key:[a-zA-Z0-9-]+}", "resource/{key:[a-zA-Z0-9-]+}{extension:\\.[a-z]+}"},
Expand Down Expand Up @@ -311,10 +315,10 @@ public ResponseEntity<?> readDataset(

/**
* Delete GBRDS Dataset, handling incoming request with path /resource/{key}. Only credentials are
* mandatory. If deletion is successful, returns ResponseEntity with HttpStatus.OK.
* mandatory. If deletion is successful, returns response with {@link HttpStatus#OK}.
*
* @param datasetKey dataset key (UUID) coming in as path param
* @return ResponseEntity with HttpStatus.OK if successful
* @return {@link ResponseEntity} with {@link HttpStatus#OK} if successful
* @see IptResource#deleteDataset(java.util.UUID)
*/
@SuppressWarnings("rawtypes")
Expand All @@ -323,4 +327,22 @@ public ResponseEntity deleteDataset(@PathVariable("key") UUID datasetKey) {
// reuse existing method
return iptResource.deleteDataset(datasetKey);
}

@PostMapping(
value = "resource/{key}/network/{networkKey}")
public ResponseEntity<Void> addDatasetToNetwork(
@PathVariable("networkKey") UUID networkKey,
@PathVariable("key") UUID key) {
networkService.addConstituent(networkKey, key);
return ResponseEntity.noContent().cacheControl(CacheControl.noCache()).build();
}

@DeleteMapping(
value = "resource/{key}/network/{networkKey}")
public ResponseEntity<Void> removeDatasetFromNetwork(
@PathVariable("networkKey") UUID networkKey,
@PathVariable("key") UUID key) {
networkService.removeConstituent(networkKey, key);
return ResponseEntity.noContent().cacheControl(CacheControl.noCache()).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ public LegacyEndpointResource(DatasetService datasetService) {
/**
* Register Dataset Endpoint, handling incoming request with path /resource/service. The access
* point URL, type, and dataset key are mandatory. Only after both the endpoint has been persisted
* is a ResponseEntity with HttpStatus.CREATED returned.
* is a response with {@link HttpStatus#CREATED} returned.
*
* @param endpoint LegacyEndpoint with HTTP form parameters
* @return ResponseEntity with HttpStatus.CREATED if successful
* @return {@link ResponseEntity} with {@link HttpStatus#CREATED} if successful
*/
@PostMapping(
value = "service",
Expand Down Expand Up @@ -106,10 +106,10 @@ public ResponseEntity registerEndpoint(
/**
* Delete all Endpoints for a Dataset, handling incoming request with path /resource/service and
* query parameter resourceKey. Only credentials are mandatory. If deletion is successful, returns
* ResponseEntity with HttpStatus.OK.
* response with {@link HttpStatus#OK}.
*
* @param datasetKey dataset key (UUID) coming in as query param
* @return ResponseEntity with HttpStatus.OK if successful
* @return {@link ResponseEntity} with {@link HttpStatus#OK} if successful
*/
@DeleteMapping(value = "service", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity deleteAllDatasetEndpoints(
Expand Down Expand Up @@ -146,7 +146,7 @@ public ResponseEntity deleteAllDatasetEndpoints(
* incoming request with path /service.json and query parameter op=types
*
* @param datasetKey dataset key (UUID) coming in as query param
* @return ResponseEntity with list of Endpoints or empty list with error message if none found
* @return {@link ResponseEntity} with list of Endpoints or empty list with error message if none found
*/
@GetMapping(
value = {"service", "service{extension:\\.[a-z]+}"},
Expand Down
Loading

0 comments on commit b7cff5f

Please sign in to comment.