Skip to content

Commit

Permalink
Merge pull request #123 from gdcc/allow-older-from
Browse files Browse the repository at this point in the history
Enable from parameter to be older than earliest date
  • Loading branch information
poikilotherm authored Jan 24, 2023
2 parents 3887331 + 06477e4 commit f3cb25e
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class RepositoryConfiguration implements WriterContext {
private final DeletedRecord deleteMethod;

private final boolean enableMetadataAttributes;
private final boolean requireFromAfterEarliest;

RepositoryConfiguration(
List<String> adminEmails,
Expand All @@ -63,7 +64,8 @@ public class RepositoryConfiguration implements WriterContext {
Integer maxListSets,
Integer maxListRecords,
DeletedRecord deleteMethod,
boolean enableMetadataAttributes) {
boolean enableMetadataAttributes,
boolean requireFromAfterEarliest) {
this.adminEmails.addAll(List.copyOf(adminEmails));
this.descriptions.addAll(List.copyOf(descriptions));
this.compressions.addAll(List.copyOf(compressions));
Expand All @@ -77,6 +79,7 @@ public class RepositoryConfiguration implements WriterContext {
this.maxListRecords = maxListRecords;
this.deleteMethod = deleteMethod;
this.enableMetadataAttributes = enableMetadataAttributes;
this.requireFromAfterEarliest = requireFromAfterEarliest;
}

/**
Expand All @@ -97,7 +100,8 @@ public RepositoryConfigurationBuilder asTemplate() {
.withMaxListSets(this.maxListSets)
.withMaxListRecords(this.maxListRecords)
.withDeleteMethod(this.deleteMethod)
.withEnableMetadataAttributes(this.enableMetadataAttributes);
.withEnableMetadataAttributes(this.enableMetadataAttributes)
.withRequireFromAfterEarliest(this.requireFromAfterEarliest);

// Quick and hacky addition as no methods for bulk adding available
builder.descriptions.clear();
Expand Down Expand Up @@ -216,6 +220,10 @@ public boolean isMetadataAttributesEnabled() {
return this.enableMetadataAttributes;
}

public boolean requiresFromAfterEarliest() {
return requireFromAfterEarliest;
}

public static final class RepositoryConfigurationBuilder {

/* All field below package private to access in tests */
Expand All @@ -234,6 +242,7 @@ public static final class RepositoryConfigurationBuilder {
Integer maxListSets = 100;
Integer maxListRecords = 100;
Boolean enableMetadataAttributes = false;
Boolean requireFromAfterEarliest = false;

public RepositoryConfigurationBuilder withGranularity(Granularity granularity) {
this.requireNotNull(granularity, "Granularity must not be null");
Expand Down Expand Up @@ -389,6 +398,17 @@ public RepositoryConfigurationBuilder withEnableMetadataAttributes(boolean enabl
return this;
}

/**
* Configure if any "from" parameter must be required to be a point in time after the
* earliest date of the repo (the oldest item). This is not strictly required by the OAI-PMH
* spec, but was the default behaviour of XOAI 3 and 4. Setting to true restores the
* behaviour.
*/
public RepositoryConfigurationBuilder withRequireFromAfterEarliest(boolean require) {
this.requireFromAfterEarliest = require;
return this;
}

public RepositoryConfiguration build() {
// Basic validation of configuration that is still missing
// 1. At least 1 admin mail present?
Expand Down Expand Up @@ -417,7 +437,8 @@ public RepositoryConfiguration build() {
maxListSets,
maxListRecords,
deleteMethod,
enableMetadataAttributes);
enableMetadataAttributes,
requireFromAfterEarliest);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,11 @@ public static Request buildRequest(
.forEach(rawRequest::withError);

// Verify the from/until arguments make sense
verifyTimeArguments(request, configuration.getEarliestDate(), granularity)
verifyTimeArguments(
request,
configuration.getEarliestDate(),
granularity,
configuration.requiresFromAfterEarliest())
.forEach(rawRequest::withError);

// NOTE: Do not load the resumption token here. The spec says, when no error occurs, we MUST
Expand Down Expand Up @@ -298,7 +302,10 @@ public static List<BadArgumentException> compileRequestArgument(
}

public static List<BadArgumentException> verifyTimeArguments(
final Request request, final Instant earliestDate, final Granularity granularity) {
final Request request,
final Instant earliestDate,
final Granularity granularity,
final boolean requireFromAfterEarliestDate) {
final List<BadArgumentException> errorList = new ArrayList<>();
final Optional<Instant> from = request.getFrom();
final Optional<Instant> until = request.getUntil();
Expand All @@ -324,8 +331,9 @@ public static List<BadArgumentException> verifyTimeArguments(
untilNotAfter = Instant.now().plus(2, ChronoUnit.SECONDS);
}

// Ensure a from argument is after the earliest date supported
if (from.isPresent() && from.get().isBefore(earliestDate))
// Ensure a from argument is after the earliest date supported (when configured in repo
// config)
if (requireFromAfterEarliestDate && from.isPresent() && from.get().isBefore(earliestDate))
errorList.add(
new BadArgumentException(
"'from' may not be before "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public static RepositoryConfigurationBuilder defaults() {
.withDeleteMethod(DeletedRecord.NO)
.withResumptionTokenFormat(
new SimpleResumptionTokenFormat().withGranularity(Granularity.Second))
.withEnableMetadataAttributes(false);
.withEnableMetadataAttributes(false)
.withRequireFromAfterEarliest(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,29 +195,58 @@ Stream<Arguments> errorFreeArguments() {
Granularity day = Granularity.Day;
Granularity len = Granularity.Lenient;
Instant earliest = LocalDate.of(2022, 5, 1).atStartOfDay().toInstant(ZoneOffset.UTC);
Boolean requireFAE = true;

return Stream.of(
Arguments.of(null, null, earliest, sec),
Arguments.of(null, null, earliest, day),
Arguments.of(null, null, earliest, len),
Arguments.of(DateProvider.parse("2022-05-02", day), null, earliest, day),
Arguments.of(null, DateProvider.parse("2022-05-02", day), earliest, day),
Arguments.of(earliest, DateProvider.parse("2022-05-02", day), earliest, day),
Arguments.of(earliest, earliest, earliest, day),
Arguments.of(Instant.now().plus(2, ChronoUnit.SECONDS), null, earliest, day),
Arguments.of(null, Instant.now().plus(1, ChronoUnit.SECONDS), earliest, sec));
Arguments.of(null, null, earliest, sec, requireFAE),
Arguments.of(null, null, earliest, day, requireFAE),
Arguments.of(null, null, earliest, len, requireFAE),
Arguments.of(
DateProvider.parse("2022-05-02", day), null, earliest, day, requireFAE),
Arguments.of(
null, DateProvider.parse("2022-05-02", day), earliest, day, requireFAE),
Arguments.of(
earliest,
DateProvider.parse("2022-05-02", day),
earliest,
day,
requireFAE),
Arguments.of(
DateProvider.parse("2022-04-30", day),
DateProvider.parse("2022-05-02", day),
earliest,
day,
!requireFAE),
Arguments.of(earliest, earliest, earliest, day, requireFAE),
Arguments.of(
Instant.now().plus(2, ChronoUnit.SECONDS),
null,
earliest,
day,
requireFAE),
Arguments.of(
null,
Instant.now().plus(1, ChronoUnit.SECONDS),
earliest,
sec,
requireFAE));
}

@ParameterizedTest
@MethodSource("errorFreeArguments")
void verifyTimeArguments(
Instant from, Instant until, Instant earliestDate, Granularity granularity) {
Instant from,
Instant until,
Instant earliestDate,
Granularity granularity,
Boolean requireFromAfterEarliest) {
// given
Request request = new Request("https://localhost").withFrom(from).withUntil(until);

// when
List<BadArgumentException> errors =
RequestBuilder.verifyTimeArguments(request, earliestDate, granularity);
RequestBuilder.verifyTimeArguments(
request, earliestDate, granularity, requireFromAfterEarliest);

// then
assertTrue(errors.isEmpty());
Expand All @@ -228,50 +257,91 @@ Stream<Arguments> wrongTimes() {
Granularity day = Granularity.Day;
Granularity len = Granularity.Lenient;
Instant earliest = LocalDate.of(2022, 5, 1).atStartOfDay().toInstant(ZoneOffset.UTC);
Boolean requireFAE = true;

return Stream.of(
// from before earliest
Arguments.of(DateProvider.parse("2022-01-01", day), null, earliest, day),
Arguments.of(
DateProvider.parse("2022-01-01", day), null, earliest, day, requireFAE),
Arguments.of(
DateProvider.parse("2022-05-22T10:00:00Z", sec),
null,
DateProvider.parse("2022-05-22T11:00:00Z", sec),
sec),
Arguments.of(DateProvider.parse("2022-01-01", day), null, earliest, len),
sec,
requireFAE),
Arguments.of(
DateProvider.parse("2022-01-01", day), null, earliest, len, requireFAE),

// from after now
Arguments.of(Instant.now().plus(2, ChronoUnit.SECONDS), null, earliest, sec),
Arguments.of(Instant.now().plus(1, ChronoUnit.DAYS), null, earliest, day),
Arguments.of(Instant.now().plus(1, ChronoUnit.DAYS), null, earliest, len),
Arguments.of(
Instant.now().plus(2, ChronoUnit.SECONDS),
null,
earliest,
sec,
requireFAE),
Arguments.of(
Instant.now().plus(1, ChronoUnit.DAYS),
null,
earliest,
day,
requireFAE),
Arguments.of(
Instant.now().plus(1, ChronoUnit.DAYS),
null,
earliest,
len,
requireFAE),

// until after now
Arguments.of(null, Instant.now().plus(5, ChronoUnit.SECONDS), earliest, sec),
Arguments.of(null, Instant.now().plus(1, ChronoUnit.DAYS), earliest, day),
Arguments.of(null, Instant.now().plus(1, ChronoUnit.DAYS), earliest, len),
Arguments.of(
null,
Instant.now().plus(5, ChronoUnit.SECONDS),
earliest,
sec,
requireFAE),
Arguments.of(
null,
Instant.now().plus(1, ChronoUnit.DAYS),
earliest,
day,
requireFAE),
Arguments.of(
null,
Instant.now().plus(1, ChronoUnit.DAYS),
earliest,
len,
requireFAE),

// from after until
Arguments.of(
DateProvider.parse("2022-05-20", day),
DateProvider.parse("2022-05-10", day),
earliest,
day),
day,
requireFAE),
Arguments.of(
DateProvider.parse("2022-05-10T10:00:00Z", sec),
DateProvider.parse("2022-05-10T09:00:00Z", sec),
earliest,
sec));
sec,
requireFAE));
}

@ParameterizedTest
@MethodSource("wrongTimes")
void verifyTimeArgumentsFailing(
Instant from, Instant until, Instant earliestDate, Granularity granularity) {
Instant from,
Instant until,
Instant earliestDate,
Granularity granularity,
Boolean requireFromAfterEarliest) {
// given
Request request = new Request("https://localhost").withFrom(from).withUntil(until);

// when
List<BadArgumentException> errors =
RequestBuilder.verifyTimeArguments(request, earliestDate, granularity);
RequestBuilder.verifyTimeArguments(
request, earliestDate, granularity, requireFromAfterEarliest);

// then
assertFalse(errors.isEmpty());
Expand Down

0 comments on commit f3cb25e

Please sign in to comment.