Skip to content

Commit 82ed02c

Browse files
Merge pull request #251 from wttech/next-schedule-info
Next schedule info
2 parents 270229c + 65a06fa commit 82ed02c

File tree

11 files changed

+162
-38
lines changed

11 files changed

+162
-38
lines changed

core/src/main/java/dev/vml/es/acm/core/acl/Acl.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,12 @@ private void deleteAuthorizable(AclAuthorizable authorizable, String id) {
204204
if (authorizable == null) {
205205
context.getLogger().info("Skipped deleting authorizable '{}' (already deleted or never existed)", id);
206206
return;
207-
}
207+
}
208208
if (context.getAuthorizableManager().getAuthorizable(id) == null) {
209209
context.getLogger().info("Skipped deleting authorizable '{}' (already deleted)", id);
210210
return;
211211
}
212-
212+
213213
purge(authorizable);
214214
context.getAuthorizableManager().deleteAuthorizable(authorizable.get());
215215
}
@@ -259,7 +259,10 @@ public void addToGroup(GroupOptions options) {
259259
String authorizableId = context.determineId(options.getAuthorizable(), options.getAuthorizableId());
260260
String groupId = context.determineId(options.getGroup(), options.getGroupId());
261261
context.getLogger()
262-
.info("Skipped adding authorizable '{}' to group '{}' (authorizable not found)", authorizableId, groupId);
262+
.info(
263+
"Skipped adding authorizable '{}' to group '{}' (authorizable not found)",
264+
authorizableId,
265+
groupId);
263266
return;
264267
}
265268
authorizable.addToGroup(options);
@@ -300,7 +303,10 @@ public void removeFromGroup(GroupOptions options) {
300303
String authorizableId = context.determineId(options.getAuthorizable(), options.getAuthorizableId());
301304
String groupId = context.determineId(options.getGroup(), options.getGroupId());
302305
context.getLogger()
303-
.info("Skipped removing authorizable '{}' from group '{}' (authorizable not found)", authorizableId, groupId);
306+
.info(
307+
"Skipped removing authorizable '{}' from group '{}' (authorizable not found)",
308+
authorizableId,
309+
groupId);
304310
return;
305311
}
306312
authorizable.removeFromGroup(options);
@@ -340,7 +346,9 @@ public void removeFromAllGroups(AuthorizableOptions options) {
340346
if (authorizable == null) {
341347
String authorizableId = context.determineId(options.getAuthorizable(), options.getAuthorizableId());
342348
context.getLogger()
343-
.info("Skipped removing authorizable '{}' from all groups (authorizable not found)", authorizableId);
349+
.info(
350+
"Skipped removing authorizable '{}' from all groups (authorizable not found)",
351+
authorizableId);
344352
return;
345353
}
346354
authorizable.removeFromAllGroups();
@@ -402,7 +410,8 @@ public void removeMember(MemberOptions options) {
402410
if (group == null) {
403411
String memberId = context.determineId(options.getMember(), options.getMemberId());
404412
String groupId = context.determineId(options.getGroup(), options.getGroupId());
405-
context.getLogger().info("Skipped removing member '{}' from group '{}' (group not found)", memberId, groupId);
413+
context.getLogger()
414+
.info("Skipped removing member '{}' from group '{}' (group not found)", memberId, groupId);
406415
return;
407416
}
408417
group.removeMember(options);

core/src/main/java/dev/vml/es/acm/core/acl/authorizable/AclAuthorizable.java

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ public void removeProperty(Closure<RemovePropertyOptions> closure) {
6363
public void addToGroup(GroupOptions options) {
6464
AclGroup group = context.determineGroup(options.getGroup(), options.getGroupId());
6565
String groupId = context.determineId(options.getGroup(), options.getGroupId());
66-
66+
6767
if (group == null) {
6868
context.getLogger().info("Skipped adding authorizable '{}' to group '{}' (group not found)", id, groupId);
6969
return;
7070
}
71-
71+
7272
boolean changed = context.getAuthorizableManager().addMember(group.get(), authorizable);
7373
if (changed) {
7474
context.getLogger().info("Added authorizable '{}' to group '{}'", id, groupId);
@@ -92,12 +92,13 @@ public void addToGroup(AclGroup group) {
9292
public void removeFromGroup(GroupOptions options) {
9393
AclGroup group = context.determineGroup(options.getGroup(), options.getGroupId());
9494
String groupId = context.determineId(options.getGroup(), options.getGroupId());
95-
95+
9696
if (group == null) {
97-
context.getLogger().info("Skipped removing authorizable '{}' from group '{}' (group not found)", id, groupId);
97+
context.getLogger()
98+
.info("Skipped removing authorizable '{}' from group '{}' (group not found)", id, groupId);
9899
return;
99100
}
100-
101+
101102
boolean changed = context.getAuthorizableManager().removeMember(group.get(), authorizable);
102103
if (changed) {
103104
context.getLogger().info("Removed authorizable '{}' from group '{}'", id, groupId);
@@ -132,7 +133,8 @@ public void removeFromAllGroups() {
132133
if (anyChanged) {
133134
context.getLogger().info("Removed authorizable '{}' from all groups", id);
134135
} else {
135-
context.getLogger().info("Skipped removing authorizable '{}' from all groups (not a member of any group)", id);
136+
context.getLogger()
137+
.info("Skipped removing authorizable '{}' from all groups (not a member of any group)", id);
136138
}
137139
} catch (RepositoryException e) {
138140
throw new AclException(String.format("Cannot remove authorizable '%s' from all groups!", id), e);
@@ -145,18 +147,27 @@ public void clear(ClearOptions options) {
145147

146148
public void clear(String path, boolean strict) {
147149
if (context.isCompositeNodeStore() && isAppsOrLibsPath(path)) {
148-
context.getLogger().info("Skipped clearing permissions for authorizable '{}' at path '{}' (composite node store)", id, path);
150+
context.getLogger()
151+
.info(
152+
"Skipped clearing permissions for authorizable '{}' at path '{}' (composite node store)",
153+
id,
154+
path);
149155
return;
150156
}
151157
if (context.getResourceResolver().getResource(path) == null) {
152-
context.getLogger().info("Skipped clearing permissions for authorizable '{}' at path '{}' (path not found)", id, path);
158+
context.getLogger()
159+
.info("Skipped clearing permissions for authorizable '{}' at path '{}' (path not found)", id, path);
153160
return;
154161
}
155162
boolean changed = context.getPermissionsManager().clear(authorizable, path, strict);
156163
if (changed) {
157164
context.getLogger().info("Cleared permissions for authorizable '{}' at path '{}'", id, path);
158165
} else {
159-
context.getLogger().info("Skipped clearing permissions for authorizable '{}' at path '{}' (no permissions to clear)", id, path);
166+
context.getLogger()
167+
.info(
168+
"Skipped clearing permissions for authorizable '{}' at path '{}' (no permissions to clear)",
169+
id,
170+
path);
160171
}
161172
}
162173

@@ -173,25 +184,41 @@ private void apply(PermissionsOptions options, boolean allow) {
173184
List<String> permissions = options.determineAllPermissions();
174185
Map<String, Object> restrictions = options.determineAllRestrictions();
175186
PermissionsOptions.Mode mode = options.getMode();
176-
187+
177188
if (context.isCompositeNodeStore() && isAppsOrLibsPath(path)) {
178189
String actionDescription = allow ? "allow permissions" : "deny permissions";
179-
context.getLogger().info("Skipped setting {} for authorizable '{}' at path '{}' (composite node store)", actionDescription, id, path);
190+
context.getLogger()
191+
.info(
192+
"Skipped setting {} for authorizable '{}' at path '{}' (composite node store)",
193+
actionDescription,
194+
id,
195+
path);
180196
return;
181197
}
182-
198+
183199
if (context.getResourceResolver().getResource(path) == null) {
184200
if (mode == PermissionsOptions.Mode.FAIL) {
185-
throw new AclException(String.format("Cannot apply permissions for authorizable '%s' at path '%s'! (path not found)", id, path));
201+
throw new AclException(String.format(
202+
"Cannot apply permissions for authorizable '%s' at path '%s'! (path not found)", id, path));
186203
}
187204
String actionDescription = allow ? "allow permissions" : "deny permissions";
188-
context.getLogger().info("Skipped setting {} for authorizable '{}' at path '{}' (path not found)", actionDescription, id, path);
205+
context.getLogger()
206+
.info(
207+
"Skipped setting {} for authorizable '{}' at path '{}' (path not found)",
208+
actionDescription,
209+
id,
210+
path);
189211
return;
190212
}
191-
213+
192214
if (context.getPermissionsManager().check(authorizable, path, permissions, restrictions, allow)) {
193215
String actionDescription = allow ? "allow permissions" : "deny permissions";
194-
context.getLogger().info("Skipped setting {} for authorizable '{}' at path '{}' (already set)", actionDescription, id, path);
216+
context.getLogger()
217+
.info(
218+
"Skipped setting {} for authorizable '{}' at path '{}' (already set)",
219+
actionDescription,
220+
id,
221+
path);
195222
} else {
196223
context.getPermissionsManager().apply(authorizable, path, permissions, restrictions, allow);
197224
String actionDescription = allow ? "allow permissions" : "deny permissions";
@@ -230,7 +257,8 @@ public void removeProperty(String relPath) {
230257
if (changed) {
231258
context.getLogger().info("Removed property '{}' for authorizable '{}'", relPath, id);
232259
} else {
233-
context.getLogger().info("Skipped removing property '{}' for authorizable '{}' (property not set)", relPath, id);
260+
context.getLogger()
261+
.info("Skipped removing property '{}' for authorizable '{}' (property not set)", relPath, id);
234262
}
235263
}
236264

core/src/main/java/dev/vml/es/acm/core/acl/authorizable/AclGroup.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ public void removeMember(Closure<MemberOptions> closure) {
3636
public void addMember(MemberOptions options) {
3737
AclAuthorizable member = context.determineAuthorizable(options.getMember(), options.getMemberId());
3838
String memberId = context.determineId(options.getMember(), options.getMemberId());
39-
39+
4040
if (member == null) {
4141
context.getLogger().info("Skipped adding member '{}' to group '{}' (member not found)", memberId, getId());
4242
return;
4343
}
44-
44+
4545
boolean changed = context.getAuthorizableManager().addMember(group, member.get());
4646
if (changed) {
4747
context.getLogger().info("Added member '{}' to group '{}'", memberId, getId());
@@ -65,12 +65,13 @@ public void addMember(AclAuthorizable member) {
6565
public void removeMember(MemberOptions options) {
6666
AclAuthorizable member = context.determineAuthorizable(options.getMember(), options.getMemberId());
6767
String memberId = context.determineId(options.getMember(), options.getMemberId());
68-
68+
6969
if (member == null) {
70-
context.getLogger().info("Skipped removing member '{}' from group '{}' (member not found)", memberId, getId());
70+
context.getLogger()
71+
.info("Skipped removing member '{}' from group '{}' (member not found)", memberId, getId());
7172
return;
7273
}
73-
74+
7475
boolean changed = context.getAuthorizableManager().removeMember(group, member.get());
7576
if (changed) {
7677
context.getLogger().info("Removed member '{}' from group '{}'", memberId, getId());
@@ -102,7 +103,8 @@ public void removeAllMembers() {
102103
if (anyChanged) {
103104
context.getLogger().info("Removed all members from group '{}'", getId());
104105
} else {
105-
context.getLogger().info("Skipped removing all members from group '{}' (no members to remove)", getId());
106+
context.getLogger()
107+
.info("Skipped removing all members from group '{}' (no members to remove)", getId());
106108
}
107109
} catch (RepositoryException e) {
108110
throw new AclException(String.format("Cannot remove all members from group '%s'!", getId()), e);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dev.vml.es.acm.core.script;
2+
3+
import java.io.Serializable;
4+
import java.util.Date;
5+
6+
public class ScriptSchedule implements Serializable {
7+
8+
private final String path;
9+
10+
private final Date nextExecution;
11+
12+
public ScriptSchedule(String path, Date nextExecution) {
13+
this.path = path;
14+
this.nextExecution = nextExecution;
15+
}
16+
17+
public String getPath() {
18+
return path;
19+
}
20+
21+
public Date getNextExecution() {
22+
return nextExecution;
23+
}
24+
}

core/src/main/java/dev/vml/es/acm/core/script/ScriptScheduler.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
import org.slf4j.LoggerFactory;
4444

4545
@Component(
46-
service = {ResourceChangeListener.class, EventListener.class, JobConsumer.class},
46+
service = {ScriptScheduler.class, ResourceChangeListener.class, EventListener.class, JobConsumer.class},
4747
immediate = true,
4848
property = {
4949
ResourceChangeListener.PATHS + "=glob:" + ScriptRepository.ROOT + "/automatic/**/*.groovy",
@@ -458,4 +458,22 @@ private boolean awaitInstanceHealthy(String operation, long retryMaxCount, long
458458
}
459459
return true;
460460
}
461+
462+
public Optional<ScriptSchedule> findScriptSchedule(String path) {
463+
Map<String, Object> filterProps = new HashMap<>();
464+
filterProps.put(ScriptScheduler.JOB_PROP_TYPE, ScriptScheduler.JobType.CRON.name());
465+
filterProps.put(ScriptScheduler.JOB_PROP_SCRIPT_PATH, path);
466+
467+
@SuppressWarnings("unchecked")
468+
Collection<ScheduledJobInfo> jobInfos = jobManager.getScheduledJobs(ScriptScheduler.JOB_TOPIC, -1, filterProps);
469+
470+
if (jobInfos.isEmpty()) {
471+
return Optional.empty();
472+
}
473+
474+
ScheduledJobInfo jobInfo = jobInfos.iterator().next();
475+
Date nextExecution = jobInfo.getNextScheduledExecution();
476+
477+
return Optional.of(new ScriptSchedule(path, nextExecution));
478+
}
461479
}

core/src/main/java/dev/vml/es/acm/core/servlet/ScriptServlet.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import dev.vml.es.acm.core.replication.Activator;
1010
import dev.vml.es.acm.core.script.Script;
1111
import dev.vml.es.acm.core.script.ScriptRepository;
12+
import dev.vml.es.acm.core.script.ScriptSchedule;
13+
import dev.vml.es.acm.core.script.ScriptScheduler;
1214
import dev.vml.es.acm.core.script.ScriptStats;
1315
import dev.vml.es.acm.core.script.ScriptType;
1416
import dev.vml.es.acm.core.servlet.input.ScriptInput;
@@ -70,6 +72,9 @@ public static Optional<Action> of(String name) {
7072
@Reference
7173
private transient SpaSettings spaSettings;
7274

75+
@Reference
76+
private transient ScriptScheduler scriptScheduler;
77+
7378
@Override
7479
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
7580
long statsLimit =
@@ -101,8 +106,14 @@ protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse r
101106
.map(Script::getPath)
102107
.map(path -> ScriptStats.forCompletedByPath(request.getResourceResolver(), path, statsLimit))
103108
.collect(Collectors.toList());
109+
List<ScriptSchedule> schedules = scripts.stream()
110+
.map(Script::getPath)
111+
.map(path -> scriptScheduler.findScriptSchedule(path))
112+
.filter(Optional::isPresent)
113+
.map(Optional::get)
114+
.collect(Collectors.toList());
104115

105-
ScriptListOutput output = new ScriptListOutput(scripts, stats);
116+
ScriptListOutput output = new ScriptListOutput(scripts, stats, schedules);
106117
respondJson(response, ok("Scripts listed successfully", output));
107118
} catch (Exception e) {
108119
LOG.error("Scripts cannot be read!", e);

core/src/main/java/dev/vml/es/acm/core/servlet/output/ScriptListOutput.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.vml.es.acm.core.servlet.output;
22

33
import dev.vml.es.acm.core.script.Script;
4+
import dev.vml.es.acm.core.script.ScriptSchedule;
45
import dev.vml.es.acm.core.script.ScriptStats;
56
import java.io.Serializable;
67
import java.util.List;
@@ -11,9 +12,12 @@ public class ScriptListOutput implements Serializable {
1112

1213
private final List<ScriptStats> stats;
1314

14-
public ScriptListOutput(List<Script> scripts, List<ScriptStats> stats) {
15+
private final List<ScriptSchedule> schedules;
16+
17+
public ScriptListOutput(List<Script> scripts, List<ScriptStats> stats, List<ScriptSchedule> schedules) {
1518
this.list = scripts;
1619
this.stats = stats;
20+
this.schedules = schedules;
1721
}
1822

1923
public List<Script> getList() {
@@ -23,4 +27,8 @@ public List<Script> getList() {
2327
public List<ScriptStats> getStats() {
2428
return stats;
2529
}
30+
31+
public List<ScriptSchedule> getSchedules() {
32+
return schedules;
33+
}
2634
}

ui.frontend/src/components/DateExplained.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ const DateExplained: React.FC<DateExplainedProps> = ({ value }) => {
1414
}
1515

1616
if (formatter.isTimezoneDifference()) {
17+
const date = value instanceof Date ? value : new Date(value);
18+
const isFuture = date.getTime() > Date.now();
19+
const relativeText = isFuture ? 'This will be' : 'This was';
20+
1721
return (
1822
<>
1923
<Text>{formatter.dateAtInstance(value)}</Text>
@@ -26,7 +30,7 @@ const DateExplained: React.FC<DateExplainedProps> = ({ value }) => {
2630
<p>
2731
In your local timezone ({formatter.userTimezone()}), the date and time are {formatter.dateAtUser(value)}.
2832
</p>
29-
<p>This was {formatter.dateRelative(value)}.</p>
33+
<p>{relativeText} {formatter.dateRelative(value)}.</p>
3034
</Text>
3135
</Content>
3236
</ContextualHelp>

0 commit comments

Comments
 (0)