Skip to content

Commit

Permalink
feat: JSON clients can add/edit/remove Operations Location
Browse files Browse the repository at this point in the history
  • Loading branch information
rhwood committed May 9, 2019
1 parent 7ab4fdb commit da04002
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 54 deletions.
2 changes: 1 addition & 1 deletion java/src/jmri/jmrit/operations/locations/Location.java
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ public boolean hasSchedules() {
*
* @param reader jmri.Reporter object.
*/
protected void setReporter(Reporter r) {
public void setReporter(Reporter r) {
Reporter old = _reader;
_reader = r;
if (old != r) {
Expand Down
4 changes: 3 additions & 1 deletion java/src/jmri/server/json/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ NotAServerType=No messages from servers of type "{0}" are allowed.
#HTTP 404 Not Found
ErrorNotFound=Object type {0} named {1} not found.
#HTTP 409 Conflict
ErrorDeleteConflict=Unable to delete object type {0} named {1} since is in use by other objects.
ErrorDeleteConflict=Unable to delete object type {0} with name "{1}" since is in use by other objects.
ErrorPutNameConflict=Unable to create object type {0} with name "{1}" since already exists.
ErrorPutUserNameConflict=Unable to create object type {0} with user name "{1}" since already exists.
#HTTP 500 Internal Error
ErrorInternal = Internal {0} handling error. See JMRI logs for information.
#HTTP 501 Not Implemented
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@
import static jmri.server.json.operations.JsonOperations.TRAINS;
import static jmri.server.json.operations.JsonOperations.WEIGHT;
import static jmri.server.json.operations.JsonOperations.*;
import static jmri.server.json.reporter.JsonReporter.REPORTER;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -45,6 +47,8 @@
import com.fasterxml.jackson.databind.node.ObjectNode;

import jmri.InstanceManager;
import jmri.Reporter;
import jmri.ReporterManager;
import jmri.jmrit.operations.locations.Location;
import jmri.jmrit.operations.locations.LocationManager;
import jmri.jmrit.operations.locations.Track;
Expand Down Expand Up @@ -112,7 +116,7 @@ public JsonNode doPost(String type, String name, JsonNode data, Locale locale, i
this.setTrain(name, data, locale, id);
break;
case CAR:
return message(CAR, postCar(name, data, locale, id), id);
return message(type, postCar(name, data, locale, id), id);
case CAR_TYPE:
if (!data.path(RENAME).isMissingNode() && data.path(RENAME).isTextual()) {
newName = data.path(RENAME).asText();
Expand All @@ -126,8 +130,9 @@ public JsonNode doPost(String type, String name, JsonNode data, Locale locale, i
}
return getKernel(carManager().getKernelByName(name), locale, id).put(RENAME, name);
case ENGINE:
return message(ENGINE, postEngine(name, data, locale, id), id);
return message(type, postEngine(name, data, locale, id), id);
case LOCATION:
return message(type, postLocation(name, data, locale, id), id);
case TRAINS:
// do nothing
break;
Expand All @@ -139,7 +144,8 @@ public JsonNode doPost(String type, String name, JsonNode data, Locale locale, i
}

@Override
public JsonNode doPut(String type, String name, JsonNode data, Locale locale, int id) throws JsonException {
// Nullable overrides super class'es non-null requirement for name
public JsonNode doPut(String type, @Nullable String name, JsonNode data, Locale locale, int id) throws JsonException {
switch (type) {
case CAR:
if (data.path(ROAD).isMissingNode()) {
Expand All @@ -157,9 +163,12 @@ public JsonNode doPut(String type, String name, JsonNode data, Locale locale, in
Bundle.getMessage(locale, "ErrorPutRollingStockConflict", type, road, number), id); // NOI18N
}
Car car = carManager().newRS(road, number);
postCar(car, data, locale, id);
return message(CAR, utilities.getCar(car, locale), id);
return message(CAR, postCar(car, data, locale, id), id);
case CAR_TYPE:
if (name == null) {
throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
Bundle.getMessage(locale, "ErrorMissingPropertyPut", NAME, type), id); // NOI18N
}
InstanceManager.getDefault(CarTypes.class).addName(name);
return getCarType(name, locale, id);
case ENGINE:
Expand All @@ -178,12 +187,35 @@ public JsonNode doPut(String type, String name, JsonNode data, Locale locale, in
Bundle.getMessage(locale, "ErrorPutRollingStockConflict", type, road, number), id); // NOI18N
}
Engine engine = engineManager().newRS(road, number);
postEngine(engine, data, locale, id);
return message(ENGINE, utilities.getEngine(engine, locale), id);
return message(ENGINE, postEngine(engine, data, locale, id), id);
case KERNEL:
if (name == null) {
throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
Bundle.getMessage(locale, "ErrorMissingPropertyPut", NAME, type), id); // NOI18N
}
Kernel kernel = carManager().newKernel(name);
return getKernel(kernel, locale, id);
case LOCATION:
if (data.path(USERNAME).isMissingNode()) {
throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
Bundle.getMessage(locale, "ErrorMissingPropertyPut", USERNAME, type), id); // NOI18N
}
String userName = data.path(USERNAME).asText();
if (name != null && locationManager().getLocationById(name) != null) {
throw new JsonException(HttpServletResponse.SC_CONFLICT,
Bundle.getMessage(locale, "ErrorPutNameConflict", type, name), id); // NOI18N
}
if (locationManager().getLocationByName(userName) != null) {
throw new JsonException(HttpServletResponse.SC_CONFLICT,
Bundle.getMessage(locale, "ErrorPutUserNameConflict", type, userName), id); // NOI18N
}
Location location = locationManager().newLocation(userName);
return message(LOCATION, postLocation(location, data, locale, id), id);
default:
if (name == null) {
throw new JsonException(HttpServletResponse.SC_BAD_REQUEST,
Bundle.getMessage(locale, "ErrorMissingPropertyPut", NAME, type), id); // NOI18N
}
return super.doPut(type, name, data, locale, id);
}
}
Expand Down Expand Up @@ -220,6 +252,7 @@ public void doDelete(String type, String name, JsonNode data, Locale locale, int
String token = data.path(FORCE_DELETE).asText();
switch (type) {
case CAR:
// TODO: do not remove an in use car
deleteCar(name, data, locale, id);
break;
case CAR_TYPE:
Expand All @@ -243,6 +276,7 @@ public void doDelete(String type, String name, JsonNode data, Locale locale, int
InstanceManager.getDefault(CarTypes.class).deleteName(name);
break;
case ENGINE:
// TODO: do not remove an in use engine
deleteEngine(name, data, locale, id);
break;
case KERNEL:
Expand All @@ -256,6 +290,10 @@ public void doDelete(String type, String name, JsonNode data, Locale locale, int
}
carManager().deleteKernel(name);
break;
case LOCATION:
// TODO: do not remove an in use location
deleteLocation(name, data, locale, id);
break;
default:
super.doDelete(type, name, data, locale, id);
}
Expand Down Expand Up @@ -378,6 +416,26 @@ public void setTrain(String name, JsonNode data, Locale locale, int id) throws J
}
}

public ObjectNode postLocation(String name, JsonNode data, Locale locale, int id) throws JsonException {
return postLocation(getLocationByName(name, locale, id), data, locale, id);
}

public ObjectNode postLocation(Location location, JsonNode data, Locale locale, int id) throws JsonException {
// set things that throw exceptions first
if (!data.path(REPORTER).isMissingNode()) {
String name = data.path(REPORTER).asText();
Reporter reporter = InstanceManager.getDefault(ReporterManager.class).getBeanBySystemName(name);
if (reporter != null) {
location.setReporter(reporter);
} else {
throw new JsonException(HttpServletResponse.SC_NOT_FOUND, Bundle.getMessage(locale, "ErrorNotFound", REPORTER, name), id);
}
}
location.setName(data.path(USERNAME).asText(location.getName()));
location.setComment(data.path(COMMENT).asText(location.getComment()));
return utilities.getLocation(location, locale);
}

/**
* Set the properties in the data parameter for the given car.
* <p>
Expand Down Expand Up @@ -517,6 +575,11 @@ public void deleteEngine(@Nonnull String name, @Nonnull JsonNode data, @Nonnull
engineManager().deregister(getEngineByName(name, locale, id));
}

public void deleteLocation(@Nonnull String name, @Nonnull JsonNode data, @Nonnull Locale locale, int id)
throws JsonException {
locationManager().deregister(getLocationByName(name, locale, id));
}

protected Car getCarByName(@Nonnull String name, @Nonnull Locale locale, int id) throws JsonException {
Car car = carManager().getById(name);
if (car == null) {
Expand All @@ -535,6 +598,15 @@ protected Engine getEngineByName(@Nonnull String name, @Nonnull Locale locale, i
return engine;
}

protected Location getLocationByName(@Nonnull String name, @Nonnull Locale locale, int id) throws JsonException {
Location location = locationManager().getLocationById(name);
if (location == null) {
throw new JsonException(HttpServletResponse.SC_NOT_FOUND,
Bundle.getMessage(locale, "ErrorNotFound", LOCATION, name), id);
}
return location;
}

protected CarManager carManager() {
return InstanceManager.getDefault(CarManager.class);
}
Expand Down
9 changes: 6 additions & 3 deletions java/src/jmri/server/json/operations/JsonUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static jmri.server.json.operations.JsonOperations.LOCATION;
import static jmri.server.json.operations.JsonOperations.OUT_OF_SERVICE;
import static jmri.server.json.operations.JsonOperations.TRACK;
import static jmri.server.json.reporter.JsonReporter.REPORTER;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
Expand All @@ -31,6 +32,7 @@
import javax.servlet.http.HttpServletResponse;

import jmri.InstanceManager;
import jmri.Reporter;
import jmri.jmrit.operations.locations.Location;
import jmri.jmrit.operations.locations.LocationManager;
import jmri.jmrit.operations.locations.Track;
Expand Down Expand Up @@ -191,6 +193,9 @@ public ObjectNode getLocation(@Nonnull Location location, Locale locale) {
data.put(NAME, location.getId());
data.put(LENGTH, location.getLength());
data.put(COMMENT, location.getComment());
Reporter reporter = location.getReporter();
data.put(REPORTER, reporter != null ? reporter.getSystemName() : null);
// note type defaults to all in-use rolling stock types
ArrayNode types = data.putArray(TYPE);
for (String type : location.getTypeNames()) {
types.add(type);
Expand All @@ -217,9 +222,7 @@ public ObjectNode getLocation(String name, Locale locale, int id) throws JsonExc
}

private ObjectNode getLocation(Location location, RouteLocation routeLocation, Locale locale) {
ObjectNode node = mapper.createObjectNode();
node.put(USERNAME, location.getName());
node.put(NAME, location.getId());
ObjectNode node = getLocation(location, locale);
if (routeLocation != null) {
node.put(ROUTE, routeLocation.getId());
} else {
Expand Down
34 changes: 30 additions & 4 deletions java/src/jmri/server/json/operations/location-client.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "jmri-json-engine-client-message",
"title": "jmri-json-location-client-message",
"type": "object",
"description": "Schema data object in message from client to JMRI for type \"engine\"",
"description": "Schema data object in message from client to JMRI for type \"location\"",
"properties": {
"name": {
"type": "string",
"description": "Operations id of the engine being requested"
"description": "Operations id of the location being requested; required except when PUTing a location"
},
"userName": {
"type": "string",
"description": "Operations name of the location being requested; required if PUTing a location"
},
"comment": {
"type": "string",
"description": "Freeform comment concerning location"
},
"reporter": {
"type": [
"string",
"null"
],
"description": "The system name of the reporter for this location"
}
},
"additionalProperties": false,
"required": ["name"]
"anyOf": [
{
"required": [
"name"
]
},
{
"required": [
"userName"
]
}
]
}
52 changes: 14 additions & 38 deletions java/src/jmri/server/json/operations/location-server.json
Original file line number Diff line number Diff line change
@@ -1,58 +1,34 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "jmri-json-engine-server-message",
"title": "jmri-json-location-server-message",
"type": "object",
"description": "Data portion of message from JMRI to client for type \"engine\"",
"description": "Data portion of message from JMRI to client for type \"location\"",
"properties": {
"name": {
"type": "string",
"description": "Operations id for engine"
"description": "Operations id for location"
},
"number": {
"userName": {
"type": "string",
"description": "The engine number"
},
"road": {
"type": "string",
"description": "The engine owning railroad"
"description": "Operations name for location"
},
"type": {
"type": "string",
"description": "The engine type"
"type": "array",
"description": "The types of rolling stock accepted at location"
},
"length": {
"type": "int",
"description": "Current engine length for available siding calculations"
},
"color": {
"type": "string",
"description": "The color of the engine"
},
"owner": {
"type": "string",
"description": "The engine owner"
"type": "integer",
"description": "Current location length for available siding calculations"
},
"comment": {
"type": "string",
"description": "Freeform comment concerning engine"
"description": "Freeform comment concerning location"
},
"location": {
"type": "string",
"description": "The current location of the engine"
},
"destination": {
"type": "string",
"description": "the current destination of the engine"
},
"model": {
"type": "string",
"description": "The model of engine"
},
"consist": {
"type": "string",
"description": "The name of the consist the engine is in"
"reporter": {
"type": ["string", "null"],
"description": "The system name of the reporter for this location"
}
},
"additionalProperties": false,
"required": ["id", "number", "road", "type", "length", "owner", "comment", "model", "consist"]
"required": ["name", "userName", "comment", "reporter", "type"]
}
Loading

0 comments on commit da04002

Please sign in to comment.