Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transaction Support #67

Merged
merged 38 commits into from Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a9d558a
Add startTransaction for creating transactions
mikejritter Mar 11, 2022
e0bf3ce
Add transaction header constants
mikejritter Mar 11, 2022
bc9e596
Add method to retrieve transaction uri
mikejritter Mar 11, 2022
a3d2c0e
Add method to add Atomic-Id header for transactions
mikejritter Mar 11, 2022
fb686ac
Use transaction endpoint constant
mikejritter Jun 7, 2022
6a615d8
Override addTransaction for all builders
mikejritter Jun 7, 2022
7b5d998
Initial TransactionBuilder idea
mikejritter Jun 8, 2022
2ba7721
Checkstyle updates
mikejritter Jun 9, 2022
bf4ef02
Create RequestBuilders per transaction endpoint
mikejritter Jun 9, 2022
4ab4583
Move start and TRANSACTION_ENDPOINT to TransactionBuilder
mikejritter Jun 9, 2022
26e68a3
Add uri validation methods
mikejritter Jun 13, 2022
e708c1e
Add simple tests for tx uri validation
mikejritter Jun 13, 2022
cb41eca
Split start valid into separate tests
mikejritter Jul 1, 2022
d5225d0
Use regex from fcrepo for validation help on tx ids
mikejritter Jul 12, 2022
3ab69aa
Create TransactionURI for more strict api definitions
mikejritter Jul 13, 2022
d757231
Update ATOMIC_ID constant
mikejritter Jul 28, 2022
798b055
Try to retrieve the Atomic-ID from a response when getting the transa…
mikejritter Jul 28, 2022
4f1c652
Add TransactionalFcrepoClient for automatically adding Atomic-IDs to …
mikejritter Jul 28, 2022
d1fe986
Add builder option for a TransactionFcrepoClient
mikejritter Jul 28, 2022
441ef5c
Create a container to test the transactional client
mikejritter Jul 28, 2022
bb007a2
Use DateTimeFormatter when checking all Atomic-Expires headers
mikejritter Aug 2, 2022
373e8a7
Add a method for creating transaction clients from a fcrepo client
mikejritter Aug 23, 2022
916e1ff
Extend BodyRequestBuilder to allow for removal of addTransaction
mikejritter Aug 24, 2022
6002a51
Make addTransaction protected to hide it in unused builders
mikejritter Sep 7, 2022
837afe5
Add function to start a tx and create a client
mikejritter Sep 12, 2022
87d40ec
Test for startTransactionalClient
mikejritter Sep 12, 2022
9f49e6e
Drop Optional from getTransactionUri
mikejritter Sep 27, 2022
913a529
Add transaction helpers to client
mikejritter Oct 5, 2022
e8daf3f
Move startTransactionClient to FcrepoClient
mikejritter Oct 28, 2022
e06e813
Update tests to be only through TransactionalFcrepoClient
mikejritter Oct 28, 2022
c68bbd6
Create RequestBuilders for tx endpoints in TransactionalFcrepoClient
mikejritter Oct 28, 2022
b97e05d
Update import for TRANSACTION_ENDPOINT
mikejritter Oct 28, 2022
42ad827
Remove unused classes
mikejritter Oct 28, 2022
6e0230b
Shorten transaction methods
mikejritter Nov 1, 2022
44b64b4
Keep functionality for base or transaction endpoints
mikejritter Nov 1, 2022
a786b92
Add test creating a client using the full transaction endpoint
mikejritter Nov 1, 2022
975192d
Drop need for TransactionURI
mikejritter Nov 1, 2022
1eb0043
Use Get/Post/etc builders for the transaction api
mikejritter Nov 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 30 additions & 4 deletions src/main/java/org/fcrepo/client/FcrepoClient.java
Expand Up @@ -16,6 +16,7 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
Expand Down Expand Up @@ -52,6 +53,8 @@ public class FcrepoClient implements Closeable {

private Boolean throwExceptionOnFailure = true;

public static final String TRANSACTION_ENDPOINT = "fcr:tx";

private static final Logger LOGGER = getLogger(FcrepoClient.class);

/**
Expand Down Expand Up @@ -166,12 +169,35 @@ public HistoricMementoBuilder createMemento(final URI url, final String mementoD
}

/**
* Interact with the Transaction API - start, commit, etc
* Start a transaction and create a new {@link TransactionalFcrepoClient}
*
* @return a transaction request builder object
* @param uri the base rest endpoint or the transaction endpoint
* @return the TransactionalFcrepoClient
* @throws IOException if there's an error with the http request
* @throws IllegalArgumentException if the uri is not the Fedora transaction endpoint
* @throws FcrepoOperationFailedException if there's an error in the fcrepo operation
*/
public TransactionBuilder transaction() {
return new TransactionBuilder(this);
public TransactionalFcrepoClient startTransactionClient(final URI uri)
throws IOException, FcrepoOperationFailedException {
final var target = getTxEndpoint(uri);
try (final var response = post(target).perform()) {
return transactionalClient(response.getTransactionUri());
}
}

private URI getTxEndpoint(final URI uri) {
final var isRoot = Pattern.compile("rest/?$").asPredicate();
final var isTx = Pattern.compile("rest/" + TRANSACTION_ENDPOINT + "/?$").asPredicate();
final var base = uri.toString();
if (isRoot.test(base)) {
LOGGER.debug("Start transaction request matches root, appending {}", TRANSACTION_ENDPOINT);
// preface with ./ so fcr:tx isn't interpreted as a scheme
return uri.resolve("./" + TRANSACTION_ENDPOINT);
} else if (isTx.test(base)) {
return uri;
} else {
throw new IllegalArgumentException("Uri is not the base rest endpoint or the transaction endpoint");
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/fcrepo/client/FcrepoResponse.java
Expand Up @@ -7,8 +7,8 @@

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.fcrepo.client.FcrepoClient.TRANSACTION_ENDPOINT;
import static org.fcrepo.client.FedoraHeaderConstants.ATOMIC_ID;
import static org.fcrepo.client.TransactionBuilder.TRANSACTION_ENDPOINT;
import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_DISPOSITION;
import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_TYPE;
import static org.fcrepo.client.FedoraHeaderConstants.LINK;
Expand Down
154 changes: 0 additions & 154 deletions src/main/java/org/fcrepo/client/TransactionBuilder.java

This file was deleted.

53 changes: 36 additions & 17 deletions src/main/java/org/fcrepo/client/TransactionalFcrepoClient.java
Expand Up @@ -7,8 +7,11 @@

import java.net.URI;

import org.apache.http.client.methods.HttpRequestBase;

/**
* A Transaction aware client which adds the Atomic_ID header to requests
* A Transaction aware client which adds the Atomic_ID header to requests and provides functionality for interacting
* with the Fedora Transaction API
*
* @author mikejritter
*/
Expand Down Expand Up @@ -38,43 +41,59 @@ public FcrepoResponse.TransactionURI getTransactionURI() {
}

/**
* A shorthand for calling {@link TransactionBuilder#commit(FcrepoResponse.TransactionURI)} with the URI of the
* TransactionalFcrepoClient
* Commit a transaction by performing a PUT
*
* @return the commit RequestBuilder
*/
public RequestBuilder transactionCommit() {
return transaction().commit(transactionURI);
public RequestBuilder commit() {
return new RequestBuilder(transactionURI.get(), this) {
@Override
protected HttpRequestBase createRequest() {
return HttpMethods.PUT.createRequest(targetUri);
}
};
}

/**
* A shorthand for calling {@link TransactionBuilder#status(FcrepoResponse.TransactionURI)} with the URI of the
* TransactionalFcrepoClient
* Retrieve the status of a transaction by performing a GET
*
* @return the status RequestBuilder
*/
public RequestBuilder transactionStatus() {
return transaction().status(transactionURI);
public RequestBuilder status() {
return new RequestBuilder(transactionURI.get(), this) {
@Override
protected HttpRequestBase createRequest() {
return HttpMethods.GET.createRequest(targetUri);
}
};
}

/**
* A shorthand for calling {@link TransactionBuilder#keepAlive(FcrepoResponse.TransactionURI)} with the URI of the
* TransactionalFcrepoClient
* Keep a transaction alive by performing a POST
*
* @return the keep alive RequestBuilder
*/
public RequestBuilder transactionKeepAlive() {
return transaction().keepAlive(transactionURI);
public RequestBuilder keepAlive() {
return new RequestBuilder(transactionURI.get(), this) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reducing the set of operations available is probably a good thing. It may not be an issue, but I am realizing this does remove the ability to set headers. My understanding is that authz doesn't require any extra headers in fedora, so the only other use case that comes to mind is extensions like API-X (which seems to have become less of a thing), where headers might be used by the extensions for triggering behaviors. Anyways, I don't know if we need to expose addHeader/addLinkHeader or not, just was realizing the option was being removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to remember why I had gone with returning RequestBuilder in the first place - since that originally came from the TransactionBuilder maybe it was something in there. Either way it seems like it we could have these return PostBuilder/GetBuilder/etc which should expose the additional methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's true, although it would expose a lot of other methods too like addTransaction. But maybe its better to be under-perscriptive rather than over.

@Override
protected HttpRequestBase createRequest() {
return HttpMethods.POST.createRequest(targetUri);
}
};
}

/**
* A shorthand for calling {@link TransactionBuilder#rollback(FcrepoResponse.TransactionURI)} with the URI of the
* TransactionalFcrepoClient
* Rollback a transaction by performing a DELETE
*
* @return the rollback RequestBuilder
*/
public RequestBuilder transactionRollback() {
return transaction().rollback(transactionURI);
public RequestBuilder rollback() {
return new RequestBuilder(transactionURI.get(), this) {
@Override
protected HttpRequestBase createRequest() {
return HttpMethods.DELETE.createRequest(targetUri);
}
};
}

@Override
Expand Down