Groovy GString template engine use default engine - jenkins

I have a Jenkins shared library.
I'm loading a groovy resource file which looks like this:
return [${Constants.nodeIP}#${Constants.username}]
The Constants class:
public class Constants{
public static String nodeIP = "10.0.0.1"
public static String username = "barel"
}
My purpose is to load the resource file into String and also subtitute the $ signs with the actual value.
import Constants
def template = libraryResource resource: 'myTemplate.txt'
def finalResult = """
more text goes here
$template
and here
"""
println(finalResult)
Expected output is:
more text goes here
return [10.0.0.1#barel]
and here
But I'm getting:
more text goes here
return [${Constants.nodeIP}#${Constants.username}]
and here
The main point is that i need to pass the template through the template engine in order to substitute the values but for that I need to create an implicit mapping object and provide it to the template:
def engine=new groovy.text.SimpleTemplateEngine()
def templateEngine = engine.createTemplate(template)
def binding = [ "nodeIP" : "10.0.0.1", "username" : "barel"]
templateEngine.make(binding)
I don't want to re-declare all fields when I'm using the template.
Is there any way to get a reference to the defaultive/built-in TemplateEngine which already has access to all project fields and properties?

Related

Jenkins+Groovy: Libraries

Situation is simple - IN A GLOBAL LIBRARY (OUTSIDE THE SANDBOX):
in src - a.b.c.Utils.groovy
in vars - Defaults.groovy
How do I call Defaults.groovy from within Utils.groovy?
In src:
#!groovy
package a.b.c
public class Utils implements Serializable {
def script
public def run() {
println(Defaults.text)
//groovy.lang.MissingPropertyException: No such property: Defaults for class: a.b.c.Utils
}
}
in vars:
#!groovy
public class Defaults {
public static def text = "hello world"
}
in Jenkinsfile:
#Library("ItLoads")
utils = new a.b.c.Utils(script:this)
...
utils.run()
so I tried to load the library explicitly
#!groovy
package a.b.c
public class Utils implements Serializable {
def script
public def run() {
println(script.library("ItLoads").Defaults.text)
//Only using first definition of library ItLoads
//java.lang.IllegalAccessException: Defaults was defined in file:///apps/opt/.../vars/Ansible.groovy which was not inside file:///apps/opt/.../src/
}
}
So, Defaults is defined somewhere, but I have no idea how to get to it...
If I try to use Defaults in the Jenkinsfile, it works.
HELP
Looking at your code I can see that you're not using the script object to access Defaults:
Instead of Defaults.text you should use something like script.Defaults.text.
Adding some links as a reference for future readers:
https://jenkins.io/doc/book/pipeline/shared-libraries/
How to access pipeline DSL in groovy classes(and not in Jenkinsfile)?

How to query jira tickets information to fill in the chart macro required data in Confluence?

I created a confluence template in which I want to insert a chart(pie) showing the status of tickets related to a specific project. I want the chart macro could retrieve the number of different tickets by their type in JIRA automatically so that each time when the user create a page based on this template, they don't need to fill in the chart data manually.
I know that in JIRA Report macro one can retrieve this kind of information easily. But how can I access this data in the report result in the chart macro? Or do I have to implement another own custom macro? If so, do I have to write some Java or Javascript code, or just using the macro template language is enough?
I am a newbie to confluence. Any ideas would be helpful.
Problem solved. The Confluence Page template is written in Storage Format and rendered by in Confluence internally before returned to client. There is a way to declare variables in the template and then feed them data by adding entries in the template context in Java or Javascript.
For instance, the JIRA chart macro is inserted in the template simple-template.xml below :
<ac:structured-macro ac:name="jira" ac:schema-version="1">
<ac:parameter ac:name="server">Your Company JIRA</ac:parameter>
<ac:parameter ac:name="jqlQuery"><at:var at:name="vJql" /></ac:parameter<name />
<ac:parameter ac:name="count">true</ac:parameter>
<ac:parameter ac:name="serverId"><at:var at:name="vServerId" /></ac:parameter>
</ac:structured-macro>
Two vars vJql and vServerId are declared using syntax <at:var at:name="varName"/>. These vars are accessible in the template context provided by a class that extends class com.atlassian.confluence.plugins.createcontent.api.contextproviders.AbstractBlueprintContextProvider. To bind the context provider with the template, one need to config the template declaration in atlassian-plugin.xml by adding the element context-provider :
<content-template key="simple-template"
template-title-key="delivery.blueprint.template.title" i18n-name-key="new.template.blueprint.name">
<resource name="template" type="download" location="/templates/simple-template.xml" />
<context-provider class="com.company.atlassian.plugins.confluence.SimpleTemplateContextProvider" />
</content-template>
Within the class, feed the vars by returning a context containing entries for the vars :
private final String VAR_PROJECT_KEY = "jira-project";
private final String VAR_VERSION = "jira-fix-version";
private final String VAR_JQL = "vJql";
private final String VAR_SERVER_ID = "vServerId";
#Override
protected BlueprintContext updateBlueprintContext(BlueprintContext context) {
try {
String projectKey = (String) context.get(VAR_PROJECT_KEY);
String version = (String) context.get(VAR_VERSION);
String jql = "project = \'" + projectKey + "\' AND fixVersion = " + version;
String serverId = ResourceBundle.getBundle("simple-template-example").getString("jira.serverid");
context.put(VAR_JQL, jql);
context.put(VAR_SERVER_ID, serverId);
return context;
} catch (Exception e) {
e.printStackTrace();
return context;
}
}
Done.

Grails 3: Custom taglib not recognized

Using Grails 3.0.9
I tried setting up a custom tag, but I can't get it to work. I created the following groovy file in grails-app/taglib: BeanFormGenerator.groovy
class BeanFormGenerator {
def renderList = { attrs, body ->
// reads the 'values' attribute from the attributes list
def list = attrs.values
// iterates and renders list values
list.each {
// uses the implicit 'out' variable to append content to the response
out << "<span class=\"element\"> ${it} </span>"
}
}
}
And I have this call in a gsp file:
<g:renderList values="[1, 2, 3]">check check</g:renderList>
I get the error:
Tag [renderList] does not exist. No tag library found for namespace: g
I tried creating a custom namespace inside of BeanFormGenerator:
static namespace = "beanform"
But this just caused to be treated as markup. What am I doing wrong? The documentation makes it seem like this should be all there is to it. I'm running this inside of IntelliJ community edition if that makes a difference.
Quite simply, to create a tag library create a Groovy class that ends
with the convention TagLib and place it within the grails-app/taglib
directory
grails.github.io/grails-doc/3.0.9/guide/single.html#taglibs

Executing scripts in a grails Job

I am working on two parts of a program. One that creates small groovy scripts and another that executes them in a Job. Each script is used to convert information from a map to a Domain object. The job will then save the domain object for future use.
Here is a small example.
Domain
class Report {
Date date
Country country
}
Map
Map<String, String> map = new HashMap<String, String>();
map.put("date", "2015-04-21 11:31:11");
map.put("country", "United States");
Date Script
String script = "x.date = Date.parse('y-M-d h:m:s', y['date'])"
The script is currently executed using Eval.
Eval(report, map, script)
There are other more complicated scripts that need to look up information and make decisions based on values.
The date script works fine but when the country script executes I get an error.
Country Script
String script = "import app.Country\nx.country = Country.findByName(y['country'])"
Error
Script1.groovy: 1: unable to resolve class app.Country
It seems like the Country class is not getting loaded in the call to Eval. How can I do an import in Eval? Should I use Eval or GroovyShell?
You need to instantiate a GroovyShell passing as argument the grailsApplication class loader, see the example below:
Bootstrap.groovy
Map bindingValues = new HashMap()
bindingValues.sessionFactory = sessionFactory
bindingValues.propertyInstanceMap = propertyInstanceMap
bindingValues.dataSource = dataSource
bindingValues.ctx = grailsApplication.mainContext
GroovyShell gs = new GroovyShell(grailsApplication.classLoader, new Binding(bindingValues))
gs.evaluate new File("docs/CargaAvaliacoes.groovy")
CargaAvaliacoes.groovy
import avaliacao.Colaborador
import modelo.ModeloAvaliacao
import programa.Programa
def programa = Programa.get(1)
def modelo = ModeloAvaliacao.get(1)
def avaliadores = ["02270789332":"1020016","11388449681":"1010002","02231772331":"1010004","04247774332":"1020002"]
def avaliacaoService = ctx.getBean("avaliacaoService")
avaliadores.each {
def avaliador = Colaborador.findByCpf(it.key)
def avaliados = Colaborador.findAllBySetorAndCpfNotEqual(it.value,it.key)
avaliados.each {
avaliacaoService.cadastrar(programa, modelo, avaliador, it)
}
}
** You need to specify the grailsApplication.classLoader because this class loader is used to load the Grails domain classes.

Setting Grails domain id in Bootstrap.groovy

Is it possible to explicitly set the id of a domain object in Grails' Bootstrap.groovy (or anywhere, for that matter)?
I've tried the following:
new Foo(id: 1234, name: "My Foo").save()
and:
def foo = new Foo()
foo.id = 1234
foo.name = "My Foo"
foo.save()
But in both cases, when I print out the results of Foo.list() at runtime, I see that my object has been given an id of 1, or whatever the next id in the sequence is.
Edit:
This is in Grails 1.0.3, and when I'm running my application in 'dev' with the built-in HSQL database.
Edit:
chanwit has provided one good solution below. However, I was actually looking for a way to set the id without changing my domain's id generation method. This is primarily for testing: I'd like to be able to set certain things to known id values either in my test bootstrap or setUp(), but still be able to use auto_increment or a sequence in production.
Yes, with manually GORM mapping:
class Foo {
String name
static mapping = {
id generator:'assigned'
}
}
and your second snippet (not the first one) will do the job (Id won't be assigned when passing it through constructor).
What I ended up using as a workaround was to not try and retrieve objects by their id. So for the example given in the question, I changed my domain object:
class Foo {
short code /* new field */
String name
static constraints = {
code(unique: true)
name()
}
}
I then used an enum to hold all of the possible values for code (which are static), and would retrieve Foo objects by doing a Foo.findByCode() with the appropriate enum value (instead of using Foo.get() with the id like I wanted to do previously).
It's not the most elegant solution, but it worked for me.
As an alternative, assuming that you're importing data or migrating data from an existing app, for test purposes you could use local maps within the Bootstrap file. Think of it like an import.sql with benefits ;-)
Using this approach:
you wouldn't need to change your domain constraints just for
testing,
you'll have a tested migration path from existing data, and
you'll have a good data slice (or full slice) for future integration tests
Cheers!
def init = { servletContext ->
addFoos()
addBars()
}
def foosByImportId = [:]
private addFoos(){
def pattern = ~/.*\{FooID=(.*), FooCode=(.*), FooName=(.*)}/
new File("import/Foos.txt").eachLine {
def matcher = pattern.matcher(it)
if (!matcher.matches()){
return;
}
String fooId = StringUtils.trimToNull(matcher.group(1))
String fooCode = StringUtils.trimToNull(matcher.group(2))
String fooName = StringUtils.trimToNull(matcher.group(3))
def foo = Foo.findByFooName(fooName) ?: new Foo(fooCode:fooCode,fooName:fooName).save(faileOnError:true)
foosByImportId.putAt(Long.valueOf(fooId), foo) // ids could differ
}
}
private addBars(){
...
String fooId = StringUtils.trimToNull(matcher.group(5))
def foo = foosByImportId[Long.valueOf(fooId)]
...
}

Resources