Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,16 @@ public static boolean isMavenLayoutChoiceOffered() {
/// generated there.
static final String BASTION_SINCE = "7.0.280";

/// The first release whose ORM maps `java.time.Instant` entity fields, which
/// every other full-stack template's server stores its timestamps in. Against
/// an older release its backend does not compile, so it is neither offered nor
/// generated there.
static final String JAVA_TIME_ENTITIES_SINCE = "7.0.280";

/// The first release a template can be generated against.
static String templateSince(Template template) {
return template == Template.BASTION ? BASTION_SINCE
: template == Template.REEL ? REEL_SINCE : FULL_STACK_SINCE;
: template == Template.REEL ? REEL_SINCE : JAVA_TIME_ENTITIES_SINCE;
}

/// The brand colour of the full-stack template as it ships: the `--brand` its
Expand Down
Binary file modified scripts/initializr/common/src/main/resources/parley-modules.zip
Binary file not shown.
Binary file modified scripts/initializr/common/src/main/resources/savor-modules.zip
Binary file not shown.
Binary file not shown.
Binary file modified scripts/initializr/common/src/main/resources/wayline-modules.zip
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -507,19 +507,22 @@ private void validateFullStackTemplate() throws Exception {
true, false, ProjectOptions.PreviewLanguage.ENGLISH, ProjectOptions.JavaVersion.JAVA_8, null,
ProjectOptions.BuildTool.MAVEN, ProjectOptions.ProjectType.APP_WITH_BACKEND);
assertRefused(GeneratorModel.createForPluginVersion(IDE.INTELLIJ, Template.WAYLINE, "NoJava8",
"com.acme.nojava8", java8, FULL_STACK_VERSION), "Java 17", "A full-stack template is Java 17");
"com.acme.nojava8", java8, WAYLINE_VERSION), "Java 17", "A full-stack template is Java 17");
// Bastion's app calls framework API newer than the other templates need.
assertRefused(GeneratorModel.createForPluginVersion(IDE.INTELLIJ, Template.BASTION, "TooOld",
"com.acme.tooold", mavenOptions(ProjectOptions.ProjectType.APP_WITH_BACKEND, false),
FULL_STACK_VERSION), GeneratorModel.BASTION_SINCE, "Bastion must refuse a release before its own");
assertRefused(GeneratorModel.createForPluginVersion(IDE.INTELLIJ, Template.WAYLINE, "OldTime",
"com.acme.oldtime", base, FULL_STACK_VERSION), GeneratorModel.JAVA_TIME_ENTITIES_SINCE,
"Wayline's server stores java.time entity fields, which a release before its gate cannot map");
ProjectOptions forced = java8.withBuild(ProjectOptions.BuildTool.GRADLE, ProjectOptions.ProjectType.APP)
.forFullStack();
assertTrue(!forced.isGradle() && forced.projectType == ProjectOptions.ProjectType.APP_WITH_BACKEND
&& forced.javaVersion == ProjectOptions.JavaVersion.JAVA_17,
"forFullStack should settle the build, the project type and the Java level");

String themePath = "common/src/main/css/theme.css";
String stock = getText(mavenEntries(FULL_STACK_VERSION, Template.WAYLINE, IDE.INTELLIJ, "StockApp",
String stock = getText(mavenEntries(WAYLINE_VERSION, Template.WAYLINE, IDE.INTELLIJ, "StockApp",
"com.acme.stock", base), themePath);
String brand = Integer.toHexString(GeneratorModel.FULL_STACK_BRAND);
assertContains(stock, "--brand: #" + brand + ";",
Expand All @@ -529,7 +532,7 @@ private void validateFullStackTemplate() throws Exception {
"The stock scheme must leave the stylesheet exactly as the template has it");

byte[] icon = new byte[] {(byte) 0x89, 'P', 'N', 'G', 1, 2, 3};
Map<String, byte[]> styled = mavenEntries(FULL_STACK_VERSION, Template.WAYLINE, IDE.INTELLIJ, "StyledApp",
Map<String, byte[]> styled = mavenEntries(WAYLINE_VERSION, Template.WAYLINE, IDE.INTELLIJ, "StyledApp",
"com.acme.styled", base.withScheme(0x0f766e, false).withIcon(icon));
String css = getText(styled, themePath);
assertContains(css, "--brand: #0f766e;", "The chosen brand colour should be the theme's");
Expand All @@ -545,7 +548,7 @@ private void validateFullStackTemplate() throws Exception {
"Choosing a scheme should rewrite declarations and add no rules");
assertTrue(java.util.Arrays.equals(icon, styled.get("common/icon.png")),
"The chosen icon should replace the stock one");
assertFalse(java.util.Arrays.equals(icon, mavenEntries(FULL_STACK_VERSION, Template.WAYLINE, IDE.INTELLIJ,
assertFalse(java.util.Arrays.equals(icon, mavenEntries(WAYLINE_VERSION, Template.WAYLINE, IDE.INTELLIJ,
"StockApp", "com.acme.stock", base).get("common/icon.png")),
"Without a chosen icon the project keeps the stock one");

Expand Down Expand Up @@ -576,6 +579,8 @@ private void assertRefused(GeneratorModel model, String expectedReason, String m
private static final String LAYOUTS_VERSION = "7.0.275";
/// The version the full-stack combinations are generated for.
private static final String FULL_STACK_VERSION = GeneratorModel.FULL_STACK_SINCE;
/// The version Wayline is generated for: its own gate, past the contract module's.
private static final String WAYLINE_VERSION = GeneratorModel.templateSince(Template.WAYLINE);

/// The streaming template needs a newer core than the other complete apps:
/// below its own gate it is refused, and the page does not list it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
package com.codenameone.examples.parley;


import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -39,6 +39,17 @@ public static long number(Object value) {
return value instanceof Number ? ((Number) value).longValue() : 0L;
}

/// A timestamp column as epoch milliseconds. Anything but an `Instant`
/// means the query selected something other than a timestamp, which is
/// a bug to see rather than a zero to chart.
public static long millis(Object value) {
if (value instanceof Instant) {
return ((Instant) value).toEpochMilli();
}
throw new IllegalStateException("Expected a java.time.Instant column but got "
+ (value == null ? "null" : value.getClass().getName()));
}

/// A column as text; empty for null.
public static String text(Object value) {
return value == null ? "" : String.valueOf(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.codenameone.examples.parley.repository.ReportRepository;

import java.io.IOException;
import java.time.Instant;
import java.util.List;

/// What closing an account does to the rows other parts of the server keep
Expand Down Expand Up @@ -118,7 +119,7 @@ public void removeEverythingOf(String username) throws IOException {
pushDevices.deleteByUsername(username);
// The other side of a live call holds a line too, and nothing else
// would ever free it once the call record is gone.
List<CallRecord> live = calls.liveOf(username);
List<CallRecord> live = calls.liveOf(username, Instant.EPOCH);
for (int iter = 0; iter < live.size(); iter++) {
lines.deleteByCallId(live.get(iter).getId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.codenameone.examples.parley.repository.ProfileRepository;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -156,7 +157,7 @@ void create(String username, String password, String displayName, String phone,
profile.setDisplayName(displayName);
profile.setPhone(phone);
profile.setPhoneVerified(phoneVerified);
profile.setCreatedAt(openedAt);
profile.setCreatedAt(Instant.ofEpochMilli(openedAt));
profile.setLanguage("");
profiles.insert(profile);
}
Expand Down Expand Up @@ -393,7 +394,7 @@ private void retire(String username) throws IOException {
if (!closed.existsById(hash)) {
ClosedAccount row = new ClosedAccount();
row.setUsernameHash(hash);
row.setClosedAt(System.currentTimeMillis());
row.setClosedAt(Instant.now());
closed.insert(row);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.codenameone.examples.parley.repository.ModerationEventRepository;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -58,8 +59,8 @@ public void record(String username, String by, String action, String reason)
// came with it -- would otherwise be listed in either order. The later
// one is stamped after the one before it, so the list is the order in
// which things were done.
Long newest = events.newest(username);
long after = newest == null ? 0L : newest.longValue() + 1L;
Instant newest = events.newest(username);
long after = newest == null ? 0L : newest.toEpochMilli() + 1L;
record(username, by, action, reason, Math.max(System.currentTimeMillis(), after));
}

Expand All @@ -72,7 +73,7 @@ public void record(String username, String by, String action, String reason, lon
ModerationEvent event = new ModerationEvent();
event.setId(Ids.next());
event.setUsername(username);
event.setCreatedAt(at);
event.setCreatedAt(Instant.ofEpochMilli(at));
event.setActor(by);
event.setAction(action);
event.setReason(why.length() > 255 ? why.substring(0, 255) : why);
Expand All @@ -86,7 +87,7 @@ public List<ModerationEventDto> events(String username) throws IOException {
List<ModerationEventDto> out = new ArrayList<ModerationEventDto>();
for (int iter = 0; iter < rows.size(); iter++) {
ModerationEvent row = rows.get(iter);
out.add(new ModerationEventDto(row.getCreatedAt(), row.getActor(), row.getAction(),
out.add(new ModerationEventDto(row.getCreatedAt().toEpochMilli(), row.getActor(), row.getAction(),
row.getReason()));
}
return out;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.codenameone.examples.parley.sms.SmsSender;

import java.io.IOException;
import java.time.Instant;

/// Proves an account's phone number by texting it a code.
///
Expand Down Expand Up @@ -72,7 +73,7 @@ public PhoneChallengeDto start(String username) throws IOException {
UserDto user = accounts.describe(username);
String code = newCode();
String codeHash = hash(username, code);
store(username, codeHash, System.currentTimeMillis());
store(username, codeHash, Instant.now());
try {
sms.send(user.phone(), "Your Parley code is " + code);
} catch (IOException failed) {
Expand All @@ -98,32 +99,32 @@ public PhoneChallengeDto start(String username) throws IOException {
/// one is kept, so at most two codes are live, and only one whose guesses
/// were not used up.
@Transactional
void store(String username, String codeHash, long now) throws IOException {
void store(String username, String codeHash, Instant now) throws IOException {
// Before the code is read: two requests for one account take turns,
// so the second finds the code the first just stored and is told to
// wait. Otherwise both pass the check below and both send a text.
accounts.hold(username);
PhoneCode pending = codes.findById(username);
if (pending != null && now - pending.getSentAt() < RESEND_SECONDS * 1000L) {
if (pending != null && pending.getSentAt().plusSeconds(RESEND_SECONDS).isAfter(now)) {
throw new ResponseStatusException(429, "Wait a moment before asking for another code");
}
boolean first = pending == null;
if (first) {
pending = new PhoneCode();
pending.setUsername(username);
} else if (pending.getExpiresAt() >= now && pending.getAttempts() < MAX_ATTEMPTS) {
} else if (!pending.getExpiresAt().isBefore(now) && pending.getAttempts() < MAX_ATTEMPTS) {
// With the guesses it has used: asking for another code must not
// buy the old one a fresh set.
pending.setPreviousHash(pending.getCodeHash());
pending.setPreviousExpiresAt(pending.getExpiresAt());
pending.setPreviousAttempts(pending.getAttempts());
} else {
pending.setPreviousHash("");
pending.setPreviousExpiresAt(0L);
pending.setPreviousExpiresAt(Instant.EPOCH);
pending.setPreviousAttempts(0);
}
pending.setCodeHash(codeHash);
pending.setExpiresAt(now + CODE_SECONDS * 1000L);
pending.setExpiresAt(now.plusSeconds(CODE_SECONDS));
pending.setAttempts(0);
pending.setSentAt(now);
if (first) {
Expand All @@ -148,17 +149,17 @@ void discard(String username, String codeHash) throws IOException {
return;
}
if (pending.getPreviousHash().length() == 0
|| pending.getPreviousExpiresAt() < System.currentTimeMillis()) {
|| pending.getPreviousExpiresAt().isBefore(Instant.now())) {
codes.deleteById(username);
return;
}
pending.setCodeHash(pending.getPreviousHash());
pending.setExpiresAt(pending.getPreviousExpiresAt());
pending.setAttempts(pending.getPreviousAttempts());
pending.setPreviousHash("");
pending.setPreviousExpiresAt(0L);
pending.setPreviousExpiresAt(Instant.EPOCH);
pending.setPreviousAttempts(0);
pending.setSentAt(0L);
pending.setSentAt(Instant.EPOCH);
}

/// Checks the code the user typed. Answers 400 for a wrong or expired one
Expand Down Expand Up @@ -186,7 +187,7 @@ boolean guess(String username, String code) throws IOException {
// row the other has.
accounts.hold(username);
PhoneCode pending = codes.findById(username);
if (pending == null || pending.getExpiresAt() < System.currentTimeMillis()) {
if (pending == null || pending.getExpiresAt().isBefore(Instant.now())) {
throw new ResponseStatusException(400, "That code has expired. Ask for a new one");
}
if (pending.getAttempts() >= MAX_ATTEMPTS) {
Expand Down Expand Up @@ -222,7 +223,7 @@ boolean guess(String username, String code) throws IOException {
boolean previous = Crypto.equalsConstantTime(given,
pending.getPreviousHash().getBytes("UTF-8"))
&& pending.getPreviousHash().length() > 0
&& pending.getPreviousExpiresAt() >= System.currentTimeMillis()
&& !pending.getPreviousExpiresAt().isBefore(Instant.now())
&& counted != null && counted.getPreviousAttempts() <= MAX_ATTEMPTS;
if (!current && !previous) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.codenameone.examples.parley.repository.ReportRepository;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -93,8 +94,8 @@ public AdminStatsDto dashboard() throws IOException {
(int) conversations.count(),
(int) (conversations.countByKind(Conversations.GROUP)
+ conversations.countByKind(Conversations.CHANNEL)),
(int) messages.countBySentAtGreaterThanEqual(today),
(int) calls.countByStartedAtGreaterThanEqual(today),
(int) messages.countBySentAtGreaterThanEqual(Instant.ofEpochMilli(today)),
(int) calls.countByStartedAtGreaterThanEqual(Instant.ofEpochMilli(today)),
(int) reports.countByStatus(OPEN), (int) bridges.count());
}

Expand All @@ -113,23 +114,23 @@ public StatsSeriesDto series(int days) throws IOException {
active.add(new HashSet<>());
}
int[] byHour = new int[24];
for (Object[] row : messages.since(since)) {
long at = Rows.number(row[0]);
for (Object[] row : messages.since(Instant.ofEpochMilli(since))) {
long at = Rows.millis(row[0]);
byHour[(int) (at % Days.MILLIS / 3600000L)]++;
int index = index(at, since, span);
if (index >= 0) {
dayMessages[index]++;
active.get(index).add(Rows.text(row[1]));
}
}
for (Long at : calls.startedSince(since)) {
int index = index(Rows.number(at), since, span);
for (Instant at : calls.startedSince(Instant.ofEpochMilli(since))) {
int index = index(Rows.millis(at), since, span);
if (index >= 0) {
dayCalls[index]++;
}
}
for (Long at : profiles.signUps(since)) {
int index = index(Rows.number(at), since, span);
for (Instant at : profiles.signUps(Instant.ofEpochMilli(since))) {
int index = index(Rows.millis(at), since, span);
if (index >= 0) {
dayUsers[index]++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.codenameone.examples.parley.repository.MembershipRepository;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -134,7 +135,7 @@ public BridgeLinkDto link(String username, BridgeRequestDto request) throws IOEx
link.setExternalChat(chat);
link.setLabel(Text.optional(request.label(), 80, "the link's name"));
link.setCreatedBy(username);
link.setCreatedAt(System.currentTimeMillis());
link.setCreatedAt(Instant.now());
links.save(link);
conversations.announce(row.getId(), "This conversation is now bridged to "
+ name(bridge.network()) + ". Messages here are no longer end-to-end encrypted.");
Expand Down Expand Up @@ -174,8 +175,8 @@ public void incoming(String network, String externalChat, String senderName, Str
return;
}
long now = System.currentTimeMillis();
events.deleteByReceivedAtLessThan(now - EVENT_MEMORY_MILLIS);
events.insert(new BridgeEvent(key, now));
events.deleteByReceivedAtLessThan(Instant.ofEpochMilli(now - EVENT_MEMORY_MILLIS));
events.insert(new BridgeEvent(key, Instant.ofEpochMilli(now)));
}
BridgeLink link = links.findByNetworkAndExternalChat(network, externalChat);
if (link == null) {
Expand Down Expand Up @@ -232,6 +233,6 @@ static String name(String network) {

static BridgeLinkDto toDto(BridgeLink row) {
return new BridgeLinkDto(row.getId(), row.getConversationId(), row.getNetwork(),
row.getExternalChat(), row.getLabel(), row.getCreatedAt());
row.getExternalChat(), row.getLabel(), row.getCreatedAt().toEpochMilli());
}
}
Loading
Loading