Conversation
ifndefJOSH
left a comment
There was a problem hiding this comment.
I just have a couple of questions about whether or not there's any potentially sensitive information about jobs in error messages that could leak out?
| val errorMsg = "Exception caught updating the job data map of job %s %s. -- %s".format( | ||
| jobKey.getGroup, | ||
| jobKey.getName, | ||
| e.getLocalizedMessage(), |
There was a problem hiding this comment.
Is this exception passed through to the HTTP response? If so we need to verify nothing sensitive is exposed.
| "updated" -> updated.map(jobKeyJson), | ||
| "failures" -> failures, | ||
| ) | ||
| if (failures.isEmpty) Ok(body) else MultiStatus(body) |
There was a problem hiding this comment.
I'm curious who will be calling this endpoint? Other services or will it be public? If the latter, failures need to be sanitized to ensure no sensitive data leaks out.
There was a problem hiding this comment.
The intention is another service. but piezo doesn't really have a way to distinguish
| GET /data/jobs com.lucidchart.piezo.admin.controllers.Jobs.getJobsDetail | ||
| GET /data/jobs/:group/:name com.lucidchart.piezo.admin.controllers.Jobs.getJobDetail(group: String, name: String) | ||
|
|
||
| POST /data/jobs/job-data-map com.lucidchart.piezo.admin.controllers.Jobs.patchJobDataMaps |
There was a problem hiding this comment.
Curious why this is a POST vs a PATCH request which the controller method implies.
There was a problem hiding this comment.
I don't think it matters too much in this case.
|
Could an in progress job persist its job data after this endpoint updates the |
| * The body looks like | ||
| * {{{ | ||
| * { | ||
| * "jobs": [ |
There was a problem hiding this comment.
Do we anticipate having additional fields here? or would it make sense to just accept the array directly without the "jobs" field?
| * Errors are collected across the whole `jobs` list, so a caller patching hundreds of jobs sees every bad entry at | ||
| * once rather than one per round trip. | ||
| */ | ||
| def parse(json: JsValue): JsResult[List[JobDataMapPatch]] = { |
There was a problem hiding this comment.
nit: It might be a little cleaner if this was something like
private implicit val reads: Reads[JobDataMapPatch] = Reads(parseJob) // or inline parseJob
private val listReads = (JsPath \ "jobs").read[List[JobDataMapPatch]]
def parse(json: JsValue): JsResult[List[JobDataMapPatch]] = json.validate(listReads)|
|
||
| private def parseJob(json: JsValue): JsResult[JobDataMapPatch] = { | ||
| for { | ||
| group <- pathed((json \ "group").validate[String], JsPath \ "group") |
There was a problem hiding this comment.
Instead of using this pathed function you can get equivalent functionality with
group <- json.validate((JsPath \ "group").read[String])or
group <- (JsPath \ "group").read[String].reads(json)| } yield JobDataMapPatch(new JobKey(name, group), entries) | ||
| } | ||
|
|
||
| private def parseEntries(dataMap: JsLookupResult): JsResult[List[DataMap]] = { |
There was a problem hiding this comment.
why accept an array or an object? this could be simpler if we only accept one, and we don't have to worry about backwards compatibility?
| } | ||
| } | ||
|
|
||
| private def parsePairEntry(pair: JsValue): JsResult[DataMap] = { |
There was a problem hiding this comment.
If we want to keep this, we should make it a reads on thedefinition of Datamap
| } | ||
|
|
||
| def patchJobDataMaps: Action[JsValue] = Action(parse.json(maxFormSize)) { request => | ||
| JobDataMapPatch.parse(request.body) match { |
There was a problem hiding this comment.
If we define a Reads for the jobdatamappatch, we can have play handle the parsing (and returning errors for invalid data) for us by using parse.json[List[JobDataMapPatch]] (if we just take an array) or parse.json(JobDataMapPatch.listReads) if we want to keep the wrapper object.
| "updated" -> updated.map(jobKeyJson), | ||
| "failures" -> failures, | ||
| ) | ||
| if (failures.isEmpty) Ok(body) else MultiStatus(body) |
There was a problem hiding this comment.
The intention is another service. but piezo doesn't really have a way to distinguish
| case JsSuccess(patches, _) => | ||
| val (failures, updated) = patches.map(applyJobDataMapPatch).partitionMap(identity) | ||
| val body = Json.obj( | ||
| "count" -> patches.size, |
There was a problem hiding this comment.
this seems unnecessary, shouldn't the caller already know what the count is?
| GET /data/jobs com.lucidchart.piezo.admin.controllers.Jobs.getJobsDetail | ||
| GET /data/jobs/:group/:name com.lucidchart.piezo.admin.controllers.Jobs.getJobDetail(group: String, name: String) | ||
|
|
||
| POST /data/jobs/job-data-map com.lucidchart.piezo.admin.controllers.Jobs.patchJobDataMaps |
There was a problem hiding this comment.
I don't think it matters too much in this case.
| import scala.concurrent.Future | ||
| import scala.jdk.CollectionConverters.* | ||
|
|
||
| class JobDataMapService extends Specification { |
There was a problem hiding this comment.
why is this called a service?
This looks like a test, but it doesn't have Test or Spec in the name
Create a new piezo endpoint that allows us to update job data for multiple jobs at once