Context: I'm implementing a Jenkins Pipeline. For this, in order to define the pipeline's parameters I implemented the DSL file.
In the DSL file, I have a parameter of ActiveChoiceParam type, called ORDERS. This parameter will allow me to choose one or more orders numbers at the same time.
Problem: What I want to do is to set the values that gets rendered for ORDERS parameter from a custom library. Basically I have a directory my_libraries with a file, orders.groovy. In this file, there is an order class, with a references list property that contains my values.
The code in the DSL file is as follows:
def order = new my_libraries.order()
pipelineJob("111_name_of_pipeline") {
description("pipeline_description")
keepDependencies(false)
parameters {
stringParam("BRANCH", "master", "The branch")
activeChoiceParam("ORDERS") {
description("orders references")
choiceType('CHECKBOX')
groovyScript{
script("return order.references")
fallbackScript("return ['error']")
}
}
}
}
Also, is good to mention that my custom library works well. For example, if I choose to use a ChoiceParam as below, it works, but of course, is not the behaviour I want. I want to select multiple choices.
choiceParam("ORDERS", order.references, "Orders references list but single-choice")
How can I make order.references available in the script part of groovyScript?
I've tried using global instance of order class, instantiate the class directly in the groovyScript, but no positive result.
Related
In FileNet, using FEM or ACCE, it is possible to use JScript in order to set attributes' values to an object. The official IBM guide provides this example (for Strings):
importClass(Packages.com.filenet.api.property.Properties);
importClass(Packages.com.filenet.api.constants.RefreshMode);
function OnCustomProcess (CEObject)
{
CEObject.refresh();
CEObject.getProperties().putValue("DocumentTitle", "Test1");
CEObject.save(RefreshMode.REFRESH);
}
But is it possible to do the same thing for more complex objects? I'm referring, in particular, to StringList type. There are no examples on the web, and defining a JS-like array doesn't work.
It is definitely possible to do this for more complex objects. Most of it is just following the path you would follow using Java, but changing the name of the variable types to var. Therefore the code for setting the value of a multivalue string property is as follows:
importClass(Packages.com.filenet.api.constants.RefreshMode);
importClass(Packages.com.filenet.api.core.Factory);
function OnCustomProcess (CEObject)
{
CEObject.refresh();
var list = Factory.StringList.createList();
list.add("Value 1");
list.add("Value 2");
CEObject.getProperties().putObjectValue("TestMultiValueProperty1", list);
CEObject.save(RefreshMode.REFRESH);
}
I often use the putObjectValue() method instead of the putValue() method because JavaScript sometimes has problems determining which type safe version of the putValue() it should use.
For a lot of examples you could go to the Global Configuration > Data Design > Add-ons section in the domain tab of the ACCE. The pre- and post-import scripts of the different Add-ons contain a lot of relevant JavaScript code.
I have a SpringBootTest that reads in properties from application.properties. The setup code uses the #Value annotation to set the values accordingly. One of these properties is an array of names.
I am trying to write a data driven test using Spock. The where statement is using these names that are initialized in the setup:
expect:
retrievedName == value
where:
value << getNames()
This always fails with org.spockframework.runtime.SpockExecutionException: Data provider is null.
It appears that the getNames() call is invoked before the properties are initialized in the setup code. If I do not use the where statement (data driven testing), all works fine. Is there a workaround for this?
You cannot use data initialized in the setup section as a source for data driven tests. As per the docs:
Although it is declared last, the where block is evaluated before the feature method containing it runs.
You can try and use setupSpec() methods and #Shared fields as a workaround.
See here for an example.
I am using https://github.com/openstack/puppet-keystone to set up an OpenStack management/controller node. I need to add the 'glance' user to keystone. I want to try and do as much as I can in my hiera data so my manifest will be simple.
Here is my manifest:
class kilo2_keystone {
include controller_ceph
include keystone
include keystone::config
include keystone::user
# keystone_user { 'glance':
# ensure => present,
# }
}
The commented out section works, but I want to be able to do include keystone::user and supply the parameters in my hiera data like so:
keystone::user:
"%{hiera('glance_admin_user')}":
ensure: present
But when I run puppet agent -t on my node I get this error:
Could not find class ::keystone::user
The commented-out code declares a resource of type keystone_user, not a class. Presumably its type, keystone_user, is provided by the puppet-keystone module. The include() family of functions are for declaring classes, not resources, so they are inapplicable to keystone_user.
There is more than one way you could proceed. If you don't anticipate wanting to anything more complicated than declaring one or more keystone_users present, then I'd recommend giving your class a parameter for the user name(s), to which you can assign a value via Hiera:
class kilo2_keystone($usernames = []) {
include controller_ceph
include keystone
include keystone::config
keystone_user { $usernames:
ensure => present,
}
}
On the other hand, if you want to be able to declare multiple users, each with its own set of attributes, then the create_resources() function is probably the path of least resistance. You still want to parameterize your class so that it gets the data from Hiera via automated data binding, but now you want the data to be structured differently, as described in the create_resources() docs: as a hash mapping resource titles (usernames, in your case) to inner hashes of resource parameters to corresponding values.
For example, your class might look like this:
class kilo2_keystone($userdata = {}) {
include controller_ceph
include keystone
include keystone::config
create_resources('keystone_user', $userdata)
}
The corresponding data for this class might look like this:
kilo2_keystone::userdata:
glance:
ensure: present
enabled: true
another_user:
ensure: absent
Note also that you are placing your kilo2_keystone class in the top scope. You really ought to put it in a module and assign it to that module's namespace. The latter would look like this:
class mymodule::kilo2_keystone($userdata = {}) {
# ...
}
I need to build a grammer containing a cross reference, which may be invalid, i.e. points to a nonexisting target. A file containing such a reference should not yield an error, but only a warning. The generator would handle this as as a special case.
How can I do this with XText?
It's not possible to create valid cross references to non-existing targets in EMF.
I would suggest to go with EAttributes instead of EReferences:
Change the feature=[EClass|ID] by feature=ID in {YourDSL} grammar.
Provide a scope calculation utility like it's done in *scope_EClass_feature(context, reference)* method in the {YourDSL}ScopeProvider class. As this scoping methods simply use the eType of the given reference the reimplementation should be straightforward.
Use this scope calculation utility in {YourDSL}ProposalProvider to propose values for the introduced EAttribute.
Optionally you can use this utility in a validation rule to add a warning/info to this EAttribute if it's not "valid".
Finally use the utility in your generator to create output based on valid target eObjects.
I also ran into this problem when creating a DSL to provide declerations of variables for a none-declerative language for a transition pahse. This method works but ask yourself if you realy want to have those nasty may-references.
You can drop the auto generated error in you UI module only. To do so, provide an ILinkingDiagnosticMessageProvider and override the function getUnresolvedProxyMessage:
class DSLLinkingDiagnosticMessageProvider extends LinkingDiagnosticMessageProvider {
override getUnresolvedProxyMessage(ILinkingDiagnosticContext context) {
if(context.context instanceof YourReference) {
// return null so the your error is left out
null
} else {
// use super implementation for others
super.getUnresolvedProxyMessage(context)
}
}
}
All linker-errors for YourReference will be missed. But be aware that there will be a dummy referenced object with all fealds null. Exspecialy the name ist lost and you can not set it due to a CyclicLinkingException. But you may create a new method that sets the name directly.
Note that the dummy object will have the type you entered in your gramma. If its abstract you can easily check witch reference is not linked.
After spending a year working with the Microsoft.Xrm.Sdk namespace, I just discovered yesterday the Entity.FormattedValues property contains the text value for Entity specific (ie Local) Option Set texts.
The reason I didn't discover it before, is there is no early bound method of getting the value. i.e. entity.new_myOptionSet is of type OptionSetValue which only contains the int value. You have to call entity.FormattedValues["new_myoptionset"] to get the string text value of the OptionSetValue.
Therefore, I'd like to get the crmsrvcutil to auto-generate a text property for local option sets. i.e. Along with Entity.new_myOptionSet being generated as it currently does, Entity.new_myOptionSetText would be generated as well.
I've looked into the Microsoft.Crm.Services.Utility.ICodeGenerationService, but that looks like it is mostly for specifying what CodeGenerationType something should be...
Is there a way supported way using CrmServiceUtil to add these properties, or am I better off writing a custom app that I can run that can generate these properties as a partial class to the auto-generated ones?
Edit - Example of the code that I would like to be generated
Currently, whenever I need to access the text value of a OptionSetValue, I use this code:
var textValue = OptionSetCache.GetText(service, entity, e => e.New_MyOptionSet);
The option set cache will use the entity.LogicalName, and the property expression to determine the name of the option set that I'm asking for. It will then query the SDK using the RetrieveAttriubteRequest, to get a list of the option set int and text values, which it then caches so it doesn't have to hit CRM again. It then looks up the int value of the New_MyOptionSet of the entity and cross references it with the cached list, to get the text value of the OptionSet.
Instead of doing all of that, I can just do this (assuming that the entity has been retrieved from the server, and not just populated client side):
var textValue = entity.FormattedValues["new_myoptionset"];
but the "new_myoptionset" is no longer early bound. I would like the early bound entity classes that gets generated to also generate an extra "Text" property for OptionSetValue properties that calls the above line, so my entity would have this added to it:
public string New_MyOptionSetText {
return this.GetFormattedAttributeValue("new_myoptionset"); // this is a protected method on the Entity class itself...
}
Could you utilize the CrmServiceUtil extension that will generate enums for your OptionSets and then add your new_myOptionSetText property to a partial class that compares the int value to the enums and returns the enum string
Again, I think specifically for this case, getting CrmSvcUtil.exe to generate the code you want is a great idea, but more generally, you can access the property name via reflection using an approach similar to the accepted answer # workarounds for nameof() operator in C#: typesafe databinding.
var textValue = entity.FormattedValues["new_myoptionset"];
// becomes
var textValue = entity.FormattedValues
[
// renamed the class from Nameof to NameOf
NameOf(Xrm.MyEntity).Property(x => x.new_MyOptionSet).ToLower()
];
The latest version of the CRM Early Bound Generator includes a Fields struct that that contains the field names. This allows accessing the FormattedValues to be as simple as this:
var textValue = entity.FormattedValues[MyEntity.Fields.new_MyOptionSet];
You could create a new property via an interface for the CrmSvcUtil, but that's a lot of work for a fairly simple call, and I don't think it justifies creating additional properties.