Skip to content

Commit

Permalink
Merge pull request #1330 from libris/feature/lxl-4209-allow-outgoing-…
Browse files Browse the repository at this point in the history
…links-to-deleted

Feature/lxl 4209 allow outgoing links to deleted
  • Loading branch information
kwahlin authored Nov 17, 2023
2 parents 17c471b + 3da67ed commit 1b8caa7
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 19 deletions.
16 changes: 10 additions & 6 deletions rest/src/main/groovy/whelk/rest/api/Crud.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -823,13 +823,17 @@ class Crud extends HttpServlet {
throw new OtherStatusException("You do not have sufficient privileges to perform this operation.", HttpServletResponse.SC_FORBIDDEN)
} else if (doc && doc.deleted) {
throw new OtherStatusException("Document has been deleted.", HttpServletResponse.SC_GONE)
} else if(!whelk.storage.followDependers(doc.getShortId(), JsonLd.ALLOW_LINK_TO_DELETED).isEmpty()) {
throw new OtherStatusException("This record may not be deleted, because it is referenced by other records.", HttpServletResponse.SC_FORBIDDEN)
} else {
log.debug("Removing resource at ${doc.getShortId()}")
String activeSigel = request.getHeader(XL_ACTIVE_SIGEL_HEADER)
whelk.remove(doc.getShortId(), "xl", activeSigel)
response.setStatus(HttpServletResponse.SC_NO_CONTENT)
def referencedBy = whelk.storage.followDependers(doc.getShortId(), JsonLd.ALLOW_LINK_TO_DELETED)
if (!referencedBy.isEmpty()) {
def referencedByStr = referencedBy.collect { shortId, path -> "$shortId at $path" }.join(', ')
throw new OtherStatusException("This record may not be deleted, because it is referenced by other records: " + referencedByStr, HttpServletResponse.SC_FORBIDDEN)
} else {
log.debug("Removing resource at ${doc.getShortId()}")
String activeSigel = request.getHeader(XL_ACTIVE_SIGEL_HEADER)
whelk.remove(doc.getShortId(), "xl", activeSigel)
response.setStatus(HttpServletResponse.SC_NO_CONTENT)
}
}
}

Expand Down
1 change: 1 addition & 0 deletions whelk-core/src/main/groovy/whelk/Document.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class Document {
static final List thingIndirectTypedIDsPath = ["@graph", 1, "indirectlyIdentifiedBy"]
static final List thingCarrierTypesPath = ["@graph", 1, "carrierType"]
static final List thingInSchemePath = ["@graph",1,"inScheme","@id"]
static final List recordPath = ["@graph", 0]
static final List recordIdPath = ["@graph", 0, "@id"]
static final List workIdPath = ["@graph", 1, "instanceOf", "@id"]
static final List thingMetaPath = ["@graph", 1, "meta", "@id"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2023,7 +2023,10 @@ class PostgreSQLComponent {
}
}

boolean iriIsLinkable(String iri) {
boolean iriIsLinkable(String iri, String path) {
if (path in JsonLd.ALLOW_LINK_TO_DELETED) {
return true
}
withDbConnection {
PreparedStatement preparedStatement = null
ResultSet rs = null
Expand Down Expand Up @@ -2653,8 +2656,13 @@ class PostgreSQLComponent {

void remove(String identifier, String changedIn, String changedBy, boolean force=false) {
if (versioning) {
if(!force && !followDependers(identifier, JsonLd.ALLOW_LINK_TO_DELETED).isEmpty())
throw new RuntimeException("Deleting depended upon records is not allowed.")
if (!force) {
def referencedBy = followDependers(identifier, JsonLd.ALLOW_LINK_TO_DELETED)
if (!referencedBy.isEmpty()) {
def referencedByStr = referencedBy.collect { shortId, path -> "$shortId at $path" }.join(', ')
throw new RuntimeException("Deleting depended upon records is not allowed. Referenced by: $referencedByStr")
}
}

log.debug("Marking document with ID ${identifier} as deleted.")
try {
Expand Down
31 changes: 21 additions & 10 deletions whelk-core/src/main/groovy/whelk/filter/LinkFinder.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,19 @@ class LinkFinder {
items[0][JsonLd.THING_KEY]['@id'] = items[1]['@id']
}

private void replaceSameAsLinksWithPrimaries(Map data) {
private void replaceSameAsLinksWithPrimaries(Map data, List path = []) {
// If this is a link (an object containing _only_ an id)

String id = data.get("@id")
if (id != null && data.keySet().size() == 1) {
String primaryId = lookupPrimaryId(id)
// Path to same form as in lddb__dependencies.relation
String normalizedPath = (path.take(2) == Document.recordPath
? [JsonLd.RECORD_KEY] + path.drop(2)
: (path.take(2) == Document.thingPath ? path.drop(2) : path)
)
.findAll { it instanceof String }
.join('.')
String primaryId = lookupPrimaryId(id, normalizedPath)
if (primaryId != null)
data.put("@id", primaryId)
}
Expand All @@ -134,30 +142,33 @@ class LinkFinder {
Object value = data.get(key)

if (value instanceof List)
replaceSameAsLinksWithPrimaries( (List) value )
replaceSameAsLinksWithPrimaries( (List) value, path + keyString )
if (value instanceof Map)
replaceSameAsLinksWithPrimaries( (Map) value )
replaceSameAsLinksWithPrimaries( (Map) value, path + keyString )
}
}

private void replaceSameAsLinksWithPrimaries(List data) {
private void replaceSameAsLinksWithPrimaries(List data, List path) {
int idx = 0
for (Object element : data){
if (element instanceof List)
replaceSameAsLinksWithPrimaries( (List) element )
replaceSameAsLinksWithPrimaries( (List) element, path + idx )
else if (element instanceof Map)
replaceSameAsLinksWithPrimaries( (Map) element )
replaceSameAsLinksWithPrimaries( (Map) element, path + idx )
idx += 1
}
}

private String lookupPrimaryId(String id) {
private String lookupPrimaryId(String id, String path) {
String mainIri = postgres.getMainId(id)

if (mainIri == null)
return null

if (postgres.iriIsLinkable(mainIri))
if (postgres.iriIsLinkable(mainIri, path))
return mainIri
throw new LinkValidationException("Not allowed to link to the deleted resource: " + mainIri)

throw new LinkValidationException("Forbidden link to deleted resource $mainIri found at $path")
}

/**
Expand Down

0 comments on commit 1b8caa7

Please sign in to comment.