Jenkins: Using validateButton on a class without Descriptor - java

I have a hudson.model.ManagementLink (which creates a page which can be accessed via Manage Jenkins.
I have some form fields which I'd like to validate using the f:validateButton. If this were to be done on say the config.jelly page of a Publisher (which has a descriptor) a simple doXXX() method would work in the descriptor.
If I add a similar doXXX() method directly to hudson.model.ManagementLink it is in fact accessible directly via the URL myPage/myMethod however the f:validateButton just returns a 404 and obviously I need to send the form values.
One potential solution I've come accross is withCustomDescriptorByName and have the logic in a descriptor I have elsewhere, but I can't get that to work.

To answer the actual question above (i.e. no descriptor), you can supply any fields needed for validation to the with attribute of f:validateButton (comma separated) then set the method attribute to something like the following...
method="${it.fullURL}triggerOfficial"
... then in the java (it) there's a method...
public String getFullURL(){
return Stapler.getCurrentRequest().getOriginalRequestURI().substring(1);
}
...then also the method to perform the validation itself...
public FormValidation doTriggerOfficial() {
return FormValidation.ok("hello");
}
However, if you want to perform normal field validation (without a validateButton) on a class type which doesn't normally have a descriptor...
1) Add implements Describable<YourClassName> to your class signature
2) Add something like this...
#SuppressWarnings("unchecked")
#Override
public Descriptor<ConfigLink> getDescriptor() {
Jenkins jenkins = Jenkins.getInstance();
if (jenkins == null) {
throw new IllegalStateException("Jenkins has not been started");
}
return jenkins.getDescriptorOrDie(getClass());
}
3) Plus an inner class (with the normal doCheckXXX methods)
#Extension
public static final class DescriptorImpl extends Descriptor<YourClassName> {...}
4) Then finally to link it in the jelly add attribute descriptor="${it.descriptor}" to the f:form tag containing your form elements you want to have auto-validated (this will invoke the getDescriptor detailed in step 2)

Related

Use a generic type to pass a specific class

I'm very new to programming language. My question might not even make sense. My environment is using java and trying to implement both ios and android apps in the same automation testing framework.
So, the idea is that any test script should be able to run on both the apps. Ex: one signin test script should be run for both ios and android.
I've decided to use interface and class implementation approach. The problem I'm facing is with test data. My company doesn't want to use excel. They want to use json for test data.
Here's my problem, look at the following line of code:
ValidBuy goodBuy = JsonFileReader.loadDaTa(TestBase.DATA_PATH, "good-buy.json", ValidBuy.class);
As you can see I have a class "ValidBuy" that has all the getters for a particular json file. I have another class "JsonFileReader" which takes the json filePath, fileName, and a class as an input and returns the data for that class name that I passed in. For this example I've passed ValidBuy.class
So, when I run a positive test, I'm passing "goodBuy" variable which is of type "ValidBuy". The problem starts here.
The test case is now specified with the data from goodBuy because it's type is "ValidBuy" and I'm passing goodBuy as a parameter.
Look at one of my extracted methods:
private void enterBuyInfo(ValidBuy goodBuy) {
itemPage = nativeApp.getItemPage(goodBuy);
itemPage.setItemName(goodBuy.getItemName());
itemPage.setItemSize(goodBuy.getItemSize());
itemPage.setItemDigitSSN(goodBuy.getSsn());
itemPage.clickContinue();
}
You can see those getters I'm using are coming from ValidBuy class.
If I run this test with the data for a badBuy:
InvalidBuy badBuy = JsonFileReader.loadDaTa(TestBase.DATA_PATH, "bad-buy.json", InvalidBuy.class);
It fails because now I have to change "ValidBuy" class with "InvalidBuy" class. Since, changing the parameter in the extracted method in every run is not possible, how can I make it more generic?
I want something like this:
TestData data = JsonFileReader.loadDaTa(RESOURCES_PATH, "good-client.json", InvalidBuy.class);
Here, TestData is generic. It could either be a class or interface (I don't know if that's possible) and the return type will be specified by whichever class I pass into the loadData() method. In this case InvalidBuy.class
The extracted method should look like this:
private void enterBuyInfo(TestData data) {
itemPage = nativeApp.getItemPage(data);
itemPage.setItemName(data.getItemName());
itemPage.setItemSize(data.getItemSize());
itemPage.setItemDigitSSN(data.getSsn());
itemPage.clickContinue();
}
If I can do this, I can use those extracted methods to create more tests.
I know I wrote a lot. I've only tried to make it as clear as possible. If it doesn't make any sense, just disregard it.
Any suggestions, ideas, code samples will be highly appreciated.
Firstly let me see if I understand your question. I think you are saying that loadData may return a value of type ValidBuy or InvalidBuy and you want to pass into it the class that you want returned. You then want to know how to use an interface that might represent either of these classes in your test methods so you can test various return values (both valid and invalid). You use the term "generic" in your question but I'm guessing you don't mean to use it in the specific way it's used in Java.
If I've understood your question correctly, then here's an answer:
Passing the class you wish to have returned into a method is an unusual usage and almost certainly not ideal. Better OOD would be to extract the common methods for all objects returned from loadData into an interface.
So:
interface Buy {
String getItemName();
boolean isValid();
}
class ValidBuy implements Buy {
#Override
public boolean isValid() {
return true;
}
...
}
class InvalidBuy implements Buy {
#Override
public boolean isValid() {
return false;
}
...
}
class JsonFileReader {
Buy loadData(Path path) {
...
}
}
Then your tests can look like:
#Test
void testValidBuy() {
assertTrue(reader.loadData(validPath).isvalid());
}
#Test
void testInvalidBuy() {
assertFalse(reader.loadData(invalidPath).isValid());
}
I realise I've simplified it a bit but hopefully you get the idea.

Is there a generic way of passing FXML controller references as parameters to a method?

I'm writing an app that will have many data entry windows, each of which has a label for system messages.
I have a GenUtil class for common methods, one of which sets the system message in the controller that called the method.
Setting a system message works if I pass the controller reference to the method ie.
Create a reference to the data entry window controller when the FXML is loaded:
deWindowController = loader.getController();
In the data entry window controller:
genUtil.setSystemMessage(this);
In GenUtil:
public void setSystemMessage(FXMLDEWindowController deWindowController) {
deWindowController.lblSysMsg.setText("setting the message");
}
However, the setSystemMessage method will be called from many FXML controllers I can't figure out how to "genericise" this process ie.
1) What goes in the method's parameter:
public void setSystemMessage(**<WHAT_GOES_HERE?>** controllerRef) {
2) Assuming the system message label IDs are all lblSysMsg, can I use controllerRef in the same way as before to set a message label?
I could include references to all of the controllers in the GenUtil class and in each of the controllers, pass a string containing the data entry window name when I call the setSystemMessage method. That way I could manually work out which controller to use. However, I'm trying to avoid that.
Can anyone help please?
I'm using JavaSE8 and NetBeans8.2.
You should not provide direct access to fields. This would allow a user of the class to do with the field including setting it to null or modifying properties other than the text property.
Declare a setSystemMessage method in a common supertype of the controller. If all of the controllers contain the same field, a abstract class would be a good choice to avoid repetition but you could also use a interface.
Use this supertype as the type of the controllerRef parameter:
public void setSystemMessage(SuperType controllerRef) {
controllerRef.setSystemMessage("setting the message");
}
public abstract class SuperType {
#FXML
private Label lblSysMsg;
public void setSystemMessage(String message) {
lblSysMsg.setText(message);
}
}

Cucumber Java - How to use returned parameters from a step in a new step?

I'm using cucumber with java in order to test my app ,
I would like to know if it is possible to take an object returned from first scenario step and use it in other steps.
Here is an example for the desirable feature file and Java code :
Scenario: create and check an object
Given I create an object
When I am using this object(#from step one)
Then I check the object is ok
#Given("^I create an object$")
public myObj I_create_an_object() throws Throwable {
myObj newObj = new myObj();
return newObj;
}
#When("^I am using this object$")
public void I_am_using_this_object(myObj obj) throws Throwable {
doSomething(obj);
}
#Then("^I check the object is ok$")
public void I_check_the_object_is_ok() throws Throwable {
check(obj);
}
I rather not to use variables in the class
(Because then all method variables will be in class level)
but i'm not sure it's possible.
Is it possible to use a return value in a method as an input in the next step?
There is no direct support for using the return values from step methods in other steps. As you said, you can achieve sharing of state via instance variables, which works fine for smaller examples. Once you get more steps and want to reorganize them into separate classes you might run into problems.
An alternative would be to encapsulate the state into its own class which manages it using ThreadLocals, you would have to make sure to initialize or reset this state, maybe using hooks.
If you are using a dependency injection framework like spring you could use the provided scenario scope.
#Component
#Scope("cucumber-glue")
public class SharedContext { ... }
This context object could then be injected into multiple classes containing the steps.

How to use Picocontainer Web?

I'm trying to use Picocontainer Web (picocontainer-web-core-2.5.1.jar).
I have configured everything and I checked out that everything works just fine, until trying to retrieve anything from container... :p
I though I should use static method getRequestComponentForThread(Class type) from PicoServletContainerFilter class, which looks like this:
public static Object getRequestComponentForThread(Class type) {
MutablePicoContainer requestContainer = ServletFilter.currentRequestContainer.get();
MutablePicoContainer container = new DefaultPicoContainer(requestContainer);
container.addComponent(type);
return container.getComponent(type);
}
But as you can see, in that method, new instance of DefaultPicoContainer is created and type which I'm trying to retrieve is being registered.
if type is a Class - new instance is created and returned, instead of cached one from parent container...
if type is a Interface - runtime exception ("'ExampleInterface' is not instantiable") is being thrown, at 3rd line (addComponent).
And my question is: How to use this library? I was pretty sure that I understand it, but implementation of this one method blows my mind...
Actually you should not use getComponent unless there's a special case.
App/Session/Request containers are created for you when you add pico context listener to the web.xml.
Just configure components for each scope and picocontainer will inject stuff automatically and instantiate components when needed. Also use Startable lifecycle interface.
I figured out one acceptable solution - writing own version of org.picocontainer.web.PicoServletContainerFilter.ServletFilter - and adding one method:
public class MyComponentContainer extends PicoServletContainerFilter {
/*
code from original class PicoServletContainerFilter.ServletFilter
[...]
*/
public static <T> T getComponent(Class<T> clazz) {
return (T) currentRequestContainer.get().getComponent(clazz);
}
}
I'm not sure if it's the best to do, but it work's fine for me. However, if you know better solution I'd be grateful for information :)

Spring #ModelAttribute and translating request parameter binding names

I'm working on converting a legacy project to Spring (trying to adjust little as possible for now) and I'm running into a small issue with mapping/translating legacy parameters to a model attribute object. I may be completely wrong in thinking about this problem but it appears to me that to translate a parameter to a specific model attribute setter is to pass in the request parameter through a method for creating a model attribute and manually call the correct setter:
#ModelAttribute("form")
public MyForm createMyForm(#RequestParameter("legacy-param") legacy) {
MyForm myForm = new MyForm();
myForm.setNewParam(legacy);
return myForm;
}
I don't necessarily want to change the request parameter name yet since some javascript and JSPs are depending on it being named that way but is there any way to do something like this? Or is there a different way to map/translate request parameters to model attributes?
public class MyForm {
#ParameterName("legacy-param")
private String newParam;
public void setNewParam(String value) { ... }
public String getNewParam() { ... }
}
#Controller
public class MyController {
#RequestMapping("/a/url")
public String myMethod(#ModelAttribute("form") MyForm myForm, BindingResult result) { ... }
}
The way you've written that model attribute method is indeed odd. I'm not entirely clear what you're actually trying to do.Assuming there are many parameters, you're going to end up with an awful lot of instances of MyForm in your ModelMap. A more 'normal' way to create model attribute would be like this:
#ModelAttribute("legacyParamNotCamel")
public MyForm createMyForm(#RequestParameter("legacy-param-not-camel") String legacy) {
return legacy;
}
Then in the JSP you can refer to it directly in expression language. e.g.,
<c:out value="${legacyParamNotCamel}"/>
If you want to put them onto a form backing object, you need to do it all in a single method that creates the object, not make new copies of it in each method. (assuming your form has more than a single parameter associated with it.)
--
It seems like what you're really trying to do though is translate the parameter names in the request before the web data binder gets ahold of it, so that you can bind oddly named parameters onto a java bean? For that you'll need to use an interceptor that translates the names before the binding process begins, or make your own subclass of the databinder than can take a property name translation map.
You placed the #ModelAttribute at the Method Level but the intention seems to be more of a formBackingObject hence we should be dealing at the Method Parameter Level
There's a difference.
I put up an explanation here on my blog along examples at Spring 3 MVC: Using #ModelAttribute in Your JSPs at http://krams915.blogspot.com/2010/12/spring-3-mvc-using-modelattribute-in.html

Categories