Skip to content

Commit

Permalink
Adobe-Consulting-Services#2977 Redirect Manager : Add Effective From …
Browse files Browse the repository at this point in the history
…field
  • Loading branch information
YegorKozlov committed Nov 4, 2022
1 parent bf45428 commit d9dddaf
Show file tree
Hide file tree
Showing 13 changed files with 816 additions and 212 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
Expand Down Expand Up @@ -153,6 +152,11 @@ public class RedirectFilter extends AnnotatedStandardMBean
@AttributeDefinition(name = "Preserve Query String", description = "Preserve query string in redirects", type = AttributeType.BOOLEAN)
boolean preserveQueryString() default true;

@AttributeDefinition(name = "Preserve Extension", description = "Whether to preserve extensions" +
"When this flag is checked (default), redirect filter will preserve the extension from the request, " +
"e.g. append .html to the Location header. ", type = AttributeType.BOOLEAN)
boolean preserveExtension() default true;

@AttributeDefinition(name = "Evaluate Selectors", description = "Take into account selectors when evaluating redirects. " +
"When this flag is unchecked (default), selectors are ignored and don't participate in rule matching", type = AttributeType.BOOLEAN)
boolean evaluateSelectors() default false;
Expand Down Expand Up @@ -391,9 +395,10 @@ boolean handleRedirect(SlingHttpServletRequest slingRequest, SlingHttpServletRes
if (match != null) {

RedirectRule redirectRule = match.getRule();
Calendar untilDateTime = redirectRule.getUntilDate();
if (untilDateTime != null && untilDateTime.before(Calendar.getInstance())) {
log.debug("redirect rule matched, but expired: {}", untilDateTime);

if (redirectRule.isExpired() || !redirectRule.isActive()) {
log.debug("redirect rule matched, but didn't meet on/off time criteria: untilDate: {}, effectiveFrom: {}",
redirectRule.getUntilDate(), redirectRule.getEffectiveFrom());
} else {
RequestPathInfo pathInfo = slingRequest.getRequestPathInfo();
String resourcePath = pathInfo.getResourcePath();
Expand Down Expand Up @@ -432,7 +437,7 @@ String evaluate(RedirectMatch match, SlingHttpServletRequest slingRequest){

if (StringUtils.startsWith(location, "/") && !StringUtils.startsWith(location, "//")) {
String ext = pathInfo.getExtension();
if (ext != null && !location.endsWith(ext)) {
if (ext != null && config.preserveExtension() && !location.endsWith(ext)) {
location += "." + ext;
}
if (mapUrls()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class RedirectRule {
public static final String TARGET_PROPERTY_NAME = "target";
public static final String STATUS_CODE_PROPERTY_NAME = "statusCode";
public static final String UNTIL_DATE_PROPERTY_NAME = "untilDate";
public static final String EFFECTIVE_FROM_PROPERTY_NAME = "effectiveFrom";
public static final String NOTE_PROPERTY_NAME = "note";
public static final String CONTEXT_PREFIX_IGNORED_PROPERTY_NAME = "contextPrefixIgnored";
public static final String CREATED_PROPERTY_NAME = "jcr:created";
Expand All @@ -71,6 +72,9 @@ public class RedirectRule {
@ValueMapValue
private Calendar untilDate;

@ValueMapValue
private Calendar effectiveFrom;

@ValueMapValue
private String note;

Expand Down Expand Up @@ -154,18 +158,14 @@ public Calendar getModified() {
return modified;
}

/**
* This is ugly, but needed to format untilDate in HTL in the redirect-row component.
* <p>
* AEM as a Cloud Service supports formatting java.time.Instant (SLING-10651), but
* classic AEMs 6.4 and 6.5 only support formatting java.util.Date and java.util.Calendar
*
* @return java.util.Calendar representation of untilDate
*/
public Calendar getUntilDate() {
return untilDate;
}

public Calendar getEffectiveFrom() {
return effectiveFrom;
}

public String[] getTagIds() {
return tagIds;
}
Expand All @@ -190,9 +190,9 @@ public List<Tag> getTags() {

@Override
public String toString() {
return String.format("RedirectRule{source='%s', target='%s', statusCode=%s, untilDate=%s, note=%s, "
return String.format("RedirectRule{source='%s', target='%s', statusCode=%s, untilDate=%s, effectiveFrom=%s, note=%s, "
+ "contextPrefixIgnored=%s, tags=%s, created=%s, createdBy=%s, modified=%s, modifiedBy=%s}",
source, target, statusCode, untilDate, note, contextPrefixIgnored,
source, target, statusCode, untilDate, effectiveFrom, note, contextPrefixIgnored,
Arrays.toString(tagIds), created, createdBy, modified, modifiedBy);
}

Expand Down Expand Up @@ -236,4 +236,18 @@ static Pattern toRegex(String src) {
}
return ptrn;
}

/**
* @return whether the rule has expired, i.e. the 'untileDate' property is before the current time
*/
public boolean isExpired(){
return untilDate != null && untilDate.before(Calendar.getInstance());
}

/**
* @return whether the rule is active, i.e. the 'effectiveFrom' property is empty or after the current time
*/
public boolean isActive() {
return effectiveFrom == null || Calendar.getInstance().after(effectiveFrom);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,15 @@ static XSSFWorkbook export(Collection<RedirectRule> rules) {
headerRow.createCell(0).setCellValue("Source Url");
headerRow.createCell(1).setCellValue("Target Url");
headerRow.createCell(2).setCellValue("Status Code");
headerRow.createCell(3).setCellValue("Until Date");
headerRow.createCell(3).setCellValue("Off Time");
headerRow.createCell(4).setCellValue("Notes");
headerRow.createCell(5).setCellValue("Ignore Context Prefix");
headerRow.createCell(6).setCellValue("Tags");
headerRow.createCell(7).setCellValue("Created");
headerRow.createCell(8).setCellValue("Created By");
headerRow.createCell(9).setCellValue("Modified");
headerRow.createCell(10).setCellValue("Modified By");
headerRow.createCell(11).setCellValue("On Time");
for (Cell cell : headerRow) {
cell.setCellStyle(headerStyle);
}
Expand Down Expand Up @@ -153,7 +154,14 @@ static XSSFWorkbook export(Collection<RedirectRule> rules) {
Cell cell10 = row.createCell(10);
cell10.setCellValue(rule.getModifiedBy());
cell10.setCellStyle(lockedCellStyle);
}

Calendar effectiveFrom = rule.getEffectiveFrom();
if (effectiveFrom != null) {
Cell cell = row.createCell(11);
cell.setCellValue(effectiveFrom);
cell.setCellStyle(dateStyle);
}
}
sheet.setAutoFilter(new CellRangeAddress(0, rownum - 1, 0, 10));
sheet.setColumnWidth(0, 256 * 50);
sheet.setColumnWidth(1, 256 * 50);
Expand All @@ -166,6 +174,7 @@ static XSSFWorkbook export(Collection<RedirectRule> rules) {
sheet.setColumnWidth(8, 256 * 30);
sheet.setColumnWidth(9, 256 * 12);
sheet.setColumnWidth(10, 256 * 30);
sheet.setColumnWidth(11, 256 * 12);

return wb;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ private Map<String, Object> readRedirect(Row row){
untilDate.setTime(c3.getDateCellValue());
props.put(RedirectRule.UNTIL_DATE_PROPERTY_NAME, untilDate);
}
Cell c11 = row.getCell(11);
if (DateUtil.isCellDateFormatted(c11)) {
Calendar effectiveFrom = Calendar.getInstance();
effectiveFrom.setTime(c11.getDateCellValue());
props.put(RedirectRule.EFFECTIVE_FROM_PROPERTY_NAME, effectiveFrom);
}
Cell c4 = row.getCell(4);
String note = c4 == null ? null : c4.getStringCellValue();
props.put(RedirectRule.NOTE_PROPERTY_NAME, note);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public RedirectResourceBuilder setUntilDate(Calendar calendar) {
return this;
}

public RedirectResourceBuilder setEffectiveFrom(Calendar calendar) {
props.put(EFFECTIVE_FROM_PROPERTY_NAME, calendar);
return this;
}

public RedirectResourceBuilder setCreatedBy(String createdBy) {
props.put(CREATED_BY_PROPERTY_NAME, createdBy);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class RedirectFilterTest {

private RedirectFilter filter;
private FilterChain filterChain;
private RedirectFilter.Configuration configuration;
private String redirectStoragePath = RedirectResourceBuilder.DEFAULT_CONF_PATH;

private String[] contentRoots = new String[]{
Expand All @@ -88,13 +89,14 @@ public void setUp() throws Exception {
.thenReturn(context.resourceResolver());
filter.resourceResolverFactory = resourceResolverFactory;

RedirectFilter.Configuration configuration = mock(RedirectFilter.Configuration.class);
configuration = mock(RedirectFilter.Configuration.class);
when(configuration.enabled()).thenReturn(true);
when(configuration.preserveQueryString()).thenReturn(true);
when(configuration.paths()).thenReturn(contentRoots);
when(configuration.additionalHeaders()).thenReturn(new String[]{"Cache-Control: no-cache", "Invalid"});
when(configuration.bucketName()).thenReturn("settings");
when(configuration.configName()).thenReturn("redirects");
when(configuration.preserveExtension()).thenReturn(true);
filter.activate(configuration, context.bundleContext());

filterChain = mock(FilterChain.class);
Expand Down Expand Up @@ -321,6 +323,40 @@ public void testPreserveQueryString() throws Exception {
.doFilter(any(SlingHttpServletRequest.class), any(SlingHttpServletResponse.class));
}

@Test
public void testPreserveExtension() throws Exception {
when(configuration.preserveExtension()).thenReturn(true); // append .html extension to the Location header
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/we-retail/en/events/test")
.setTarget("/content/we-retail/en")
.setStatusCode(302).build()
);
MockSlingHttpServletResponse response = navigate("/content/we-retail/en/events/test.html");

verify(filterChain, never())
.doFilter(any(SlingHttpServletRequest.class), any(SlingHttpServletResponse.class));

assertEquals("/content/we-retail/en.html", response.getHeader("Location"));
}

@Test
public void testNotPreserveExtension() throws Exception {
when(configuration.preserveExtension()).thenReturn(false); // no .html extension in the Location header
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/we-retail/en/events/test")
.setTarget("/content/we-retail/en")
.setStatusCode(302).build()
);
MockSlingHttpServletResponse response = navigate("/content/we-retail/en/events/test.html");

verify(filterChain, never())
.doFilter(any(SlingHttpServletRequest.class), any(SlingHttpServletResponse.class));

assertEquals("/content/we-retail/en", response.getHeader("Location"));
}

@Test
public void testMatchSingleAsset() throws Exception {
withRules(
Expand Down Expand Up @@ -629,33 +665,83 @@ public void testInvalidRules() throws Exception {
}

@Test
public void testUntilDateRedirectExpired() throws Exception {
ZonedDateTime dateInPast = ZonedDateTime.now().minusDays(1);
public void testUntilDateExpired() throws Exception {
ZonedDateTime offTime = ZonedDateTime.now().minusDays(1);
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/we-retail/en/contact-us")
.setTarget("/content/we-retail/en/contact-them")
.setStatusCode(302)
.setUntilDate(GregorianCalendar.from(dateInPast))
.setUntilDate(GregorianCalendar.from(offTime))
.build()
);
doReturn(false).when(filter).mapUrls();
assertEquals(null, navigate("/content/we-retail/en/contact-us").getHeader("Location"));
}

@Test
public void testUntilDateInFuture() throws Exception {
ZonedDateTime dateInFuture = ZonedDateTime.now().plusDays(1);
ZonedDateTime offTime = ZonedDateTime.now().plusDays(1);
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/geometrixx/en/contact-us")
.setTarget("/content/geometrixx/en/contact-them")
.setStatusCode(302)
.setUntilDate(GregorianCalendar.from(dateInFuture)).build());
doReturn(false).when(filter).mapUrls();
.setUntilDate(GregorianCalendar.from(offTime)).build());
assertEquals("/content/geometrixx/en/contact-them", navigate("/content/geometrixx/en/contact-us").getHeader("Location"));
}

@Test
public void testEffectiveDateInPast() throws Exception {
ZonedDateTime onTime = ZonedDateTime.now().minusDays(1);
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/geometrixx/en/contact-us")
.setTarget("/content/geometrixx/en/contact-them")
.setStatusCode(302)
.setEffectiveFrom(GregorianCalendar.from(onTime)).build());
assertEquals("/content/geometrixx/en/contact-them", navigate("/content/geometrixx/en/contact-us").getHeader("Location"));
}

@Test
public void testEffectiveDateInFuture() throws Exception {
ZonedDateTime onTime = ZonedDateTime.now().plusDays(1);
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/geometrixx/en/contact-us")
.setTarget("/content/geometrixx/en/contact-them")
.setStatusCode(302)
.setEffectiveFrom(GregorianCalendar.from(onTime)).build());
assertEquals(null, navigate("/content/geometrixx/en/contact-us").getHeader("Location"));
}

@Test
public void testEffectiveDateLessThanUntilDate() throws Exception {
ZonedDateTime onTime = ZonedDateTime.now().minusDays(1);
ZonedDateTime offTime = ZonedDateTime.now().plusDays(1);
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/geometrixx/en/contact-us")
.setTarget("/content/geometrixx/en/contact-them")
.setStatusCode(302)
.setUntilDate(GregorianCalendar.from(offTime))
.setEffectiveFrom(GregorianCalendar.from(onTime)).build());
assertEquals("/content/geometrixx/en/contact-them", navigate("/content/geometrixx/en/contact-us").getHeader("Location"));
}

@Test
public void testEffectiveDateGreaterThanUntilDate() throws Exception {
ZonedDateTime onTime = ZonedDateTime.now().plusDays(1);
ZonedDateTime offTime = ZonedDateTime.now().minusDays(1);
withRules(
new RedirectResourceBuilder(context)
.setSource("/content/geometrixx/en/contact-us")
.setTarget("/content/geometrixx/en/contact-them")
.setStatusCode(302)
.setUntilDate(GregorianCalendar.from(offTime))
.setEffectiveFrom(GregorianCalendar.from(onTime)).build());
assertEquals(null, navigate("/content/geometrixx/en/contact-us").getHeader("Location"));
}

private void withRules(Resource... rules) {
withRules(redirectStoragePath, rules);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public void setUp() throws PersistenceException {
.setTarget("/content/two")
.setStatusCode(302)
.setUntilDate(new Calendar.Builder().setDate(2022, 9, 9).build())
.setEffectiveFrom(new Calendar.Builder().setDate(2025, 2, 2).build())
.setNotes("note-1")
.setContextPrefixIgnored(true)
.setTagIds(new String[]{"redirects:tag1"})
Expand Down Expand Up @@ -113,12 +114,14 @@ public void assertSpreadsheet(XSSFWorkbook wb) {
assertEquals("note-1", row1.getCell(4).getStringCellValue());
assertEquals("/content/two", row1.getCell(1).getStringCellValue());
assertEquals(302, (int) row1.getCell(2).getNumericCellValue());
assertDateEquals("09 October 2022", new Calendar.Builder().setInstant(row1.getCell(3).getDateCellValue()).build());
assertTrue(row1.getCell(5).getBooleanCellValue());
assertEquals("redirects:tag1", row1.getCell(6).getStringCellValue());
assertDateEquals("16 February 1974", new Calendar.Builder().setInstant(row1.getCell(7).getDateCellValue()).build());
assertEquals("john.doe", row1.getCell(8).getStringCellValue());
assertDateEquals("22 November 1976", new Calendar.Builder().setInstant(row1.getCell(9).getDateCellValue()).build());
assertEquals("jane.doe", row1.getCell(10).getStringCellValue());
assertDateEquals("02 March 2025", new Calendar.Builder().setInstant(row1.getCell(11).getDateCellValue()).build());

XSSFRow row2 = sheet.getRow(2);
assertEquals("/content/three", row2.getCell(0).getStringCellValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ public void testImport() throws ServletException, IOException {
row1.getCell(3).setCellStyle(dateStyle);
row1.createCell(4).setCellValue("note-abc");
row1.createCell(6).setCellValue("redirects:tag1\nredirects:tag2");
row1.createCell(11).setCellValue(new Calendar.Builder().setDate(2025, 02, 02).build());
row1.getCell(11).setCellStyle(dateStyle);

Row row2 = sheet.createRow(2);
row2.createCell(0).setCellValue("/content/2");
Expand Down Expand Up @@ -157,6 +159,7 @@ public void testImport() throws ServletException, IOException {
assertDateEquals("16 February 1974", rule3.getUntilDate());
assertEquals("note-abc", rule3.getNote());
assertArrayEquals(new String[]{"redirects:tag1", "redirects:tag2"}, rule3.getTagIds());
assertDateEquals("02 March 2025", rule3.getEffectiveFrom());

RedirectRule rule4 = rules.get("/content/2").adaptTo(RedirectRule.class);
assertEquals("/en/we-retail", rule4.getTarget());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
var note = tr.find('.note').data('value');
var statusCode = tr.find('.statusCode').data('value');
var untilDate = tr.find('.untilDate').data('value');
var effectiveFrom = tr.find('.effectiveFrom').data('value');
var contextPrefixIgnored = tr.find('.contextPrefixIgnored').data('value');
var tags = tr.find('.tags').data('value');

Expand All @@ -200,6 +201,7 @@
var select = $('#status-code-select-box').get(0);
select.value =statusCode;
form.find('coral-datepicker[name="./untilDate"]').val(untilDate);
form.find('coral-datepicker[name="./effectiveFrom"]').val(effectiveFrom);
form.find('input[name="./note"]').val(note);
var cpi = form.find('input[name="./contextPrefixIgnored"]');
cpi.val(contextPrefixIgnored);
Expand Down
Loading

0 comments on commit d9dddaf

Please sign in to comment.