ml-gradle icon indicating copy to clipboard operation
ml-gradle copied to clipboard

Feature Request: Expose ServerEvalTask results, and allow catching errors

Open grtjn opened this issue 2 years ago • 3 comments

Consider the following tasks:

task checkTemplate1(type: com.marklogic.gradle.task.ServerEvalTask, group: project.name) {
  client = mlAppConfig.newAppServicesDatabaseClient("App-Services")
  xquery = "exists(pki:get-template-by-name('my-template'))"
  doLast {
    if (evalResult == 'false') {
      mlSslTemplate = ""
      mlSslHostname = ""
    }
  }
}
task checkTemplate2(type: com.marklogic.gradle.task.ServerEvalTask, group: project.name) {
  ignoreErrors = true
  client = mlAppConfig.newAppServicesDatabaseClient("App-Services")
  xquery = "if (empty(pki:get-template-by-name('my-template')) then error(xs:QName('ERROR'), 'Template missing') else ()"
  doLast {
    if (evalError) {
      mlSslTemplate = ""
      mlSslHostname = ""
    }
  }
}

Neither approach is feasible with ServerEvalTask, because results are only printed to screen, not captured, and intercepting errors is not possible to my knowledge.

grtjn avatar Aug 31 '22 07:08 grtjn

Workaround is to do the eval call using a ManageClient pointing to App-Services port, and the postForm function, which can easily wrapped in a try catch, and response is captured out of the box. Doing this using ServerEvalTask seems more elegant though.

grtjn avatar Aug 31 '22 07:08 grtjn

Thanks @grtjn for the request. It sounds like your need is for ServerEvalTask to stash its output somewhere so that it can be accessed by some custom code. I'm wondering though if that would be easier than the alternative, which is to not use that task and just roll your own (ServerEvalTask does very little, it's just some syntactic sugar):

task myTask(type: com.marklogic.gradle.task.MarkLogicTask) {
  doLast {
    script = "do whatever you want here"
    result = mlAppConfig.newAppServicesDatabaseClient("App-Services").javascript(script).evalAs(String.class)
    // do whatever you want with the result
  }
}

That is fairly simple, with the main complexity being that you have to know how to 1) construct an appropriate DatabaseClient, and 2) call javascript/xquery and then evalAs. But in your scenario, you already need to construct your own client, and so the only thing ServerEvalTask is doing is calling xquery/evalAs for you - so really, not much of anything.

A downside to ServerEvalTask storing its result in a property - e.g. ext.serverEvalTaskResult - is that this wouldn't work if more than one instance of the task were run in the same call to Gradle.

I think one improvement would be for https://github.com/marklogic-community/ml-gradle/wiki/Writing-your-own-task to make it very clear that the use case for ServerEvalTask is to execute an arbitrary script and print its result, if there is one. And then to provide an example like above to address your use case - i.e. if you want to run an arbitrary script and do something with the result from ML, I think you're better off creating a custom task like the one above because it's pretty simple and reads nicely too.

Thoughts on that?

rjrudin avatar Aug 31 '22 14:08 rjrudin

Ah great, was unaware of this possibility, and never expected this to be so simple! It looks similar to ManageClient.postForm against /v1/eval, but hides some of the unnecessary details. It took me fairly long to even get to the ManageClient solution. Good examples are always welcome!

grtjn avatar Aug 31 '22 16:08 grtjn

Added a note and example to the bottom of https://github.com/marklogic-community/ml-gradle/wiki/Writing-your-own-task for this use case

rjrudin avatar Sep 01 '22 11:09 rjrudin