Skip to content
This repository was archived by the owner on Nov 11, 2020. It is now read-only.

Commit 428b679

Browse files
authored
Merge pull request #98 from conveyal/dev
4.0.1 Release
2 parents 4df8f18 + 6015233 commit 428b679

17 files changed

+132
-78
lines changed

Diff for: pom.xml

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66

77
<groupId>com.conveyal</groupId>
88
<artifactId>analyst</artifactId>
9-
<version>4.0.0</version>
10-
9+
<version>4.0.1</version>
1110
<build>
1211
<plugins>
1312
<plugin>

Diff for: src/main/java/com/conveyal/taui/AnalysisServer.java

+18-17
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,17 @@ public static void main (String... args) {
8181
// Default is JSON, will be overridden by the few controllers that do not return JSON
8282
res.type("application/json");
8383

84-
// Log each API request
85-
LOG.info("{} {}", req.requestMethod(), req.pathInfo());
86-
87-
if (!AnalysisServerConfig.offline) {
84+
if (AnalysisServerConfig.auth0ClientId != null && AnalysisServerConfig.auth0Secret != null) {
8885
handleAuthentication(req, res);
8986
} else {
87+
LOG.warn("No Auth0 credentials were supplied, setting accessGroup and email to placeholder defaults");
9088
// hardwire group name if we're working offline
9189
req.attribute("accessGroup", "OFFLINE");
9290
req.attribute("email", "[email protected]");
9391
}
92+
93+
// Log each API request
94+
LOG.info("{} {} by {} of {}", req.requestMethod(), req.pathInfo(), req.attribute("email"), req.attribute("accessGroup"));
9495
});
9596

9697
// Register all our HTTP request handlers with the Spark HTTP framework.
@@ -123,23 +124,23 @@ public static void main (String... args) {
123124

124125
exception(AnalysisServerException.class, (e, request, response) -> {
125126
AnalysisServerException ase = ((AnalysisServerException) e);
126-
AnalysisServer.respondToException(response, ase, ase.type.name(), ase.message, ase.httpCode);
127+
AnalysisServer.respondToException(ase, request, response, ase.type.name(), ase.message, ase.httpCode);
127128
});
128129

129130
exception(IOException.class, (e, request, response) -> {
130-
AnalysisServer.respondToException(response, e, "BAD_REQUEST", e.getMessage(), 400);
131+
AnalysisServer.respondToException(e, request, response, "BAD_REQUEST", e.getMessage(), 400);
131132
});
132133

133134
exception(FileUploadException.class, (e, request, response) -> {
134-
AnalysisServer.respondToException(response, e, "BAD_REQUEST", e.getMessage(), 400);
135+
AnalysisServer.respondToException(e, request, response, "BAD_REQUEST", e.getMessage(), 400);
135136
});
136137

137138
exception(NullPointerException.class, (e, request, response) -> {
138-
AnalysisServer.respondToException(response, e, "UNKNOWN", e.getMessage(), 400);
139+
AnalysisServer.respondToException(e, request, response, "UNKNOWN", e.getMessage(), 400);
139140
});
140141

141142
exception(RuntimeException.class, (e, request, response) -> {
142-
AnalysisServer.respondToException(response, e, "RUNTIME", e.getMessage(), 400);
143+
AnalysisServer.respondToException(e, request, response, "RUNTIME", e.getMessage(), 400);
143144
});
144145

145146
LOG.info("Conveyal Analysis server is ready.");
@@ -150,14 +151,14 @@ public static void handleAuthentication (Request req, Response res) {
150151

151152
// authorization required
152153
if (auth == null || auth.isEmpty()) {
153-
AnalysisServerException.Unauthorized("You must be logged in.");
154+
throw AnalysisServerException.Unauthorized("You must be logged in.");
154155
}
155156

156157
// make sure it's properly formed
157158
String[] authComponents = auth.split(" ");
158159

159160
if (authComponents.length != 2 || !"bearer".equals(authComponents[0].toLowerCase())) {
160-
AnalysisServerException.Unknown("Authorization header is malformed: " + auth);
161+
throw AnalysisServerException.Unknown("Authorization header is malformed: " + auth);
161162
}
162163

163164
// validate the JWT
@@ -167,33 +168,33 @@ public static void handleAuthentication (Request req, Response res) {
167168
try {
168169
jwt = verifier.verify(authComponents[1]);
169170
} catch (Exception e) {
170-
AnalysisServerException.Forbidden("Login failed to verify with our authorization provider. " + e.getMessage());
171+
throw AnalysisServerException.Forbidden("Login failed to verify with our authorization provider. " + e.getMessage());
171172
}
172173

173174
if (!jwt.containsKey("analyst")) {
174-
AnalysisServerException.Forbidden("Access denied. User does not have access to Analysis.");
175+
throw AnalysisServerException.Forbidden("Access denied. User does not have access to Analysis.");
175176
}
176177

177178
String group = null;
178179
try {
179180
group = (String) ((Map<String, Object>) jwt.get("analyst")).get("group");
180181
} catch (Exception e) {
181-
AnalysisServerException.Forbidden("Access denied. User is not associated with any group. " + e.getMessage());
182+
throw AnalysisServerException.Forbidden("Access denied. User is not associated with any group. " + e.getMessage());
182183
}
183184

184185
if (group == null) {
185-
AnalysisServerException.Forbidden("Access denied. User is not associated with any group.");
186+
throw AnalysisServerException.Forbidden("Access denied. User is not associated with any group.");
186187
}
187188

188189
// attributes to be used on models
189190
req.attribute("accessGroup", group);
190191
req.attribute("email", jwt.get("email"));
191192
}
192193

193-
public static void respondToException(Response response, Exception e, String type, String message, int code) {
194+
public static void respondToException(Exception e, Request request, Response response, String type, String message, int code) {
194195
String stack = ExceptionUtils.getStackTrace(e);
195196

196-
LOG.error("Server exception thrown, type: {}, message: {}", type, message);
197+
LOG.error("{} {} -> {} {} by {} of {}", type, message, request.requestMethod(), request.pathInfo(), request.attribute("email"), request.attribute("accessGroup"));
197198
LOG.error(stack);
198199

199200
JSONObject body = new JSONObject();

Diff for: src/main/java/com/conveyal/taui/analysis/RegionalAnalysisManager.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.conveyal.r5.analyst.cluster.RegionalTask;
1313
import com.conveyal.r5.analyst.scenario.Scenario;
1414
import com.conveyal.taui.AnalysisServerConfig;
15+
import com.conveyal.taui.AnalysisServerException;
1516
import com.conveyal.taui.models.RegionalAnalysis;
1617
import com.conveyal.taui.persistence.TiledAccessGrid;
1718
import com.conveyal.taui.util.HttpUtil;
@@ -116,7 +117,7 @@ public static void enqueue (RegionalAnalysis regionalAnalysis) {
116117
}
117118
} catch (IOException e) {
118119
LOG.error("error enqueueing requests", e);
119-
throw new RuntimeException("error enqueueing requests", e);
120+
throw AnalysisServerException.Unknown(e);
120121
}
121122

122123
consumer.registerJob(templateTask,

Diff for: src/main/java/com/conveyal/taui/controllers/OpportunityDatasetsController.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ private static Object downloadOpportunityDataset (Request req, Response res) thr
283283
if (!s3.doesObjectExist(BUCKET, String.format("%s.%s", gridPath, format))) {
284284
// if this grid is not on S3 in the requested format, try to get the .grid format
285285
if (!s3.doesObjectExist(BUCKET, String.format("%s.grid", gridPath))) {
286-
throw new IllegalArgumentException("This grid does not exist.");
286+
throw AnalysisServerException.NotFound("This grid does not exist.");
287287
} else {
288288
// get the grid and convert it to the requested format
289289
S3Object s3Grid = s3.getObject(BUCKET, String.format("%s.grid", gridPath));
@@ -327,7 +327,7 @@ private static List<Region.OpportunityDataset> writeOpportunityDatasetToS3(Map<S
327327
status.status = Status.ERROR;
328328
status.message = e.getMessage();
329329
status.completed();
330-
throw new RuntimeException(e);
330+
throw AnalysisServerException.Unknown(e);
331331
}
332332

333333
Region.OpportunityDataset opportunityDataset = new Region.OpportunityDataset();

Diff for: src/main/java/com/conveyal/taui/controllers/RegionalAnalysisController.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.conveyal.r5.analyst.SelectingGridReducer;
88
import com.conveyal.r5.analyst.cluster.RegionalTask;
99
import com.conveyal.taui.AnalysisServerConfig;
10+
import com.conveyal.taui.AnalysisServerException;
1011
import com.conveyal.taui.analysis.RegionalAnalysisManager;
1112
import com.conveyal.taui.grids.GridExporter;
1213
import com.conveyal.taui.models.AnalysisRequest;
@@ -95,7 +96,7 @@ public static Object getPercentile (Request req, Response res) throws IOExceptio
9596
// Andrew Owen style average instantaneous accessibility
9697
// The samples stored in the access grid are samples of instantaneous accessibility at different minutes
9798
// and Monte Carlo draws, average them together
98-
throw new IllegalArgumentException("Old-style instantaneous-accessibility regional analyses are no longer supported");
99+
throw AnalysisServerException.BadRequest("Old-style instantaneous-accessibility regional analyses are no longer supported");
99100
} else {
100101
// This is accessibility given x percentile travel time, the first sample is the point estimate
101102
// computed using all monte carlo draws, and subsequent samples are bootstrap replications. Return the

Diff for: src/main/java/com/conveyal/taui/grids/GridExporter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public static void writeToS3(Grid grid, AmazonS3 s3, String bucket, String key,
9292
try {
9393
if ("grid".equals(format)) {
9494
grid.write(new GZIPOutputStream(pos));
95-
} else if ("png".equals("format")) {
95+
} else if ("png".equals(format)) {
9696
grid.writePng(pos);
9797
} else if ("tiff".equals(format)) {
9898
grid.writeGeotiff(pos);

Diff for: src/main/java/com/conveyal/taui/models/AddTripPattern.java

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public AddTrips toR5 () {
4747
at.comment = name;
4848

4949
at.bidirectional = bidirectional;
50+
5051
List<ModificationStop> stops = ModificationStop.getStopsFromSegments(segments);
5152
at.frequencies = timetables.stream().map(tt -> tt.toR5(stops)).collect(Collectors.toList());
5253
at.stops = ModificationStop.toSpec(stops);

Diff for: src/main/java/com/conveyal/taui/models/AnalysisRequest.java

+21-13
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public class AnalysisRequest {
2828
// All analyses parameters
2929
public String accessModes;
3030
public float bikeSpeed;
31-
public float carSpeed;
3231
public LocalDate date;
3332
public String directModes;
3433
public String egressModes;
@@ -43,6 +42,7 @@ public class AnalysisRequest {
4342

4443
// Parameters that aren't currently configurable in the UI
4544
public int bikeTrafficStress = 4;
45+
public float carSpeed = 20;
4646
public int maxWalkTime = 20;
4747
public int maxBikeTime = 20;
4848
public int maxCarTime = 45;
@@ -83,14 +83,16 @@ public AnalysisTask populateTask (AnalysisTask task, Project project) {
8383
modifications = modificationsForProject(project.accessGroup, projectId, variantIndex);
8484
}
8585

86-
// No idea how long this operation takes or if it is actually necessary
86+
// The CRC is appended to the scenario ID to identify a unique revision of the scenario (still denoted here
87+
// as variant) allowing the worker to cache and reuse networks built by applying that exact revision of the
88+
// scenario to a base network.
8789
CRC32 crc = new CRC32();
88-
crc.update(modifications.stream().map(Modification::toString).collect(Collectors.joining("-")).getBytes());
89-
crc.update(JsonUtilities.objectToJsonBytes(this));
90+
crc.update(JsonUtilities.objectToJsonBytes(modifications));
91+
long crcValue = crc.getValue();
9092

9193
task.scenario = new Scenario();
9294
// TODO figure out why we use both
93-
task.jobId = String.format("%s-%s-%s", projectId, variantIndex, crc.getValue());
95+
task.jobId = String.format("%s-%s-%s", projectId, variantIndex, crcValue);
9496
task.scenario.id = task.scenarioId = task.jobId;
9597
task.scenario.modifications = modifications;
9698

@@ -146,15 +148,21 @@ public AnalysisTask populateTask (AnalysisTask task, Project project) {
146148
task.maxTripDurationMinutes = maxTripDurationMinutes;
147149
}
148150

149-
task.accessModes = EnumSet.copyOf(Arrays.stream(accessModes.split(","))
150-
.map(LegMode::valueOf).collect(Collectors.toList()));
151-
task.directModes = EnumSet.copyOf(Arrays.stream(directModes.split(","))
152-
.map(LegMode::valueOf).collect(Collectors.toList()));
153-
task.egressModes = EnumSet.copyOf(Arrays.stream(egressModes.split(","))
154-
.map(LegMode::valueOf).collect(Collectors.toList()));
155-
task.transitModes = EnumSet.copyOf(Arrays.stream(transitModes.split(","))
156-
.map(TransitModes::valueOf).collect(Collectors.toList()));
151+
task.accessModes = getEnumSetFromString(accessModes);
152+
task.directModes = getEnumSetFromString(directModes);
153+
task.egressModes = getEnumSetFromString(egressModes);
154+
task.transitModes = transitModes != null && !"".equals(transitModes)
155+
? EnumSet.copyOf(Arrays.stream(transitModes.split(",")).map(TransitModes::valueOf).collect(Collectors.toList()))
156+
: EnumSet.noneOf(TransitModes.class);
157157

158158
return task;
159159
}
160+
161+
private EnumSet<LegMode> getEnumSetFromString (String s) {
162+
if (s != null && !"".equals(s)) {
163+
return EnumSet.copyOf(Arrays.stream(s.split(",")).map(LegMode::valueOf).collect(Collectors.toList()));
164+
} else {
165+
return EnumSet.noneOf(LegMode.class);
166+
}
167+
}
160168
}

Diff for: src/main/java/com/conveyal/taui/models/Bookmark.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package com.conveyal.taui.models;
22

3-
import com.conveyal.r5.profile.ProfileRequest;
4-
53
/**
64
* A bookmark represents "frozen" settings for single point results.
75
*/
86
public class Bookmark extends Model {
9-
public ProfileRequest profileRequest;
7+
public AnalysisRequest profileRequest;
108

119
public int isochroneCutoff;
1210

Diff for: src/main/java/com/conveyal/taui/models/Bundle.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.conveyal.gtfs.GTFSFeed;
77
import com.conveyal.r5.analyst.cluster.BundleManifest;
88
import com.conveyal.taui.AnalysisServerConfig;
9+
import com.conveyal.taui.AnalysisServerException;
910
import com.conveyal.taui.util.JsonUtil;
1011

1112
import java.io.File;
@@ -62,7 +63,7 @@ public Bundle clone () {
6263
try {
6364
return (Bundle) super.clone();
6465
} catch (CloneNotSupportedException e) {
65-
throw new RuntimeException(e);
66+
throw AnalysisServerException.Unknown(e);
6667
}
6768
}
6869

@@ -93,7 +94,7 @@ public FeedSummary clone () {
9394
try {
9495
return (FeedSummary) super.clone();
9596
} catch (CloneNotSupportedException e) {
96-
throw new RuntimeException(e);
97+
throw AnalysisServerException.Unknown(e);
9798
}
9899
}
99100
}

0 commit comments

Comments
 (0)