Thingsboard custom rule nodes - Defined configuration directive 'myTestNodeConfig' is not available - thingsboard

I created custom RuleNode following https://thingsboard.io/docs/user-guide/contribution/rule-node-development/
When I deploy and run, and when I insert my Custom Rule node it says
Defined configuration directive 'myTestNodeConfig' is not available.
In rule-node-examples project have it defined like this
#RuleNode(
type = ComponentType.FILTER,
name = "MyTestNode",
relationTypes = {"True", "False"},
configClazz = MyTestNodeConfiguration.class,
nodeDescription = "Checks the existence of the selected key in the message payload.",
nodeDetails = "If the selected key exists - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
uiResources = {"static/rulenode/custom-nodes-config.js"},
configDirective = "myTestNodeConfig")
public class MyTestNode implements TbNode {
In rule-node-examples-ui project have it defined like below
import CheckKeyConfigDirective from './check-key-config.directive';
export default angular.module('thingsboard.ruleChain.config.filter', [])
.directive('myTestNodeConfig', CheckKeyConfigDirective)
.name;
Can someone help to figure out whats is missing. Thanks

Related

Why does Uniswap interface use multiple Web3Provider?

I was learning how to develop a front end for dApps by going through the code for Uniswap interface.
Then I found that there are two Web3Providers used in the app like below.
<Web3ReactProvider getLibrary={getLibrary}> // 1st
<Web3ProviderNetwork getLibrary={getLibrary}> // 2nd
<Blocklist>
// some other child nodes
</Blocklist>
<Web3ProviderNetwork>
</Web3ReactProvider>
As for Web3ReactProvider, it uses the component provided by the web3-react package, while Web3ProviderNetwork is created with a key "NETWORK" on the same file those providers are written.
Uniswap has a method useActiveWeb3React which returns web3context values depending on its active variable value.
In other words, default web3Context is returned if active variable is true, otherwise Web3Context with Network is returned. (code below)
export default function useActiveWeb3React() {
const interfaceContext = useWeb3React<Web3Provider>()
const interfaceNetworkContext = useWeb3React<Web3Provider>(
process.env.REACT_APP_IS_WIDGET ? undefined : NetworkContextName
)
if (interfaceContext.active) {
return interfaceContext
}
return interfaceNetworkContext
}
Does anyone know why they switch providers depending on the active variable?
Does it make any difference?

How to customize an existing Grails plugin functionality, modifying behavior of doWithSpring method

I am new to grails and while working with Spring Security LDAP plugin it was identified that it accepts the ldap server password in plain text only. The task in hand is to pass an encrypted password which is decrypted before it is consumed by the plugin during its initialization phase.
I have already searched for all possible blogs and stackoverflow questions but could not find a way to extend the main plugin class to simply override the doWithSpring() method so that i can simply add the required decryption logic for the Ldap server password. Any help here will be appreciated.
I have already seen and tried jasypt plugin but it also does not work well if the password is stored in some external file and not application yml. So I am looking for a solution to extend the Spring security plugin main class, add the required behavior and register the custom class.
EDIT
Adding the snippet from Grails LDAP Security plugin, which I am trying to override. So If i am successfully able to update the value of securityConfig object before the plugin loads, the purpose is solved.
Some snippet from the plugin:
def conf = SpringSecurityUtils.securityConfig
...
...
contextSource(DefaultSpringSecurityContextSource, conf.ldap.context.server) { // 'ldap://localhost:389'
authenticationSource = ref('ldapAuthenticationSource')
authenticationStrategy = ref('authenticationStrategy')
userDn = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**password = conf.ldap.context.managerPassword // 'secret'**
contextFactory = contextFactoryClass
dirObjectFactory = dirObjectFactoryClass
baseEnvironmentProperties = conf.ldap.context.baseEnvironmentProperties // none
cacheEnvironmentProperties = conf.ldap.context.cacheEnvironmentProperties // true
anonymousReadOnly = conf.ldap.context.anonymousReadOnly // false
referral = conf.ldap.context.referral // null
}
ldapAuthenticationSource(SimpleAuthenticationSource) {
principal = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**credentials = conf.ldap.context.managerPassword // 'secret'**
}
You don't need to override the doWithSpring() method in the existing plugin. You can provide your own plugin which loads after the one you want to affect and have your doWithSpring() add whatever you want to the context. If you add beans with the same name as the ones added by the other plugin, yours will replace the ones provided by the other plugin as long as you configure your plugin to load after the other one. Similarly, you could do the same think in resources.groovy of the app if you don't want to write a plugin for this.
You have other options too. You could write a bean post processor or bean definition post processor that affects the beans created by the other plugin. Depending on the particulars, that might be a better idea.
EDIT:
After seeing your comment below I created a simple example that shows how you might use a definition post processor. See the project at https://github.com/jeffbrown/postprocessordemo.
The interesting bits:
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomeBean.groovy
package demo
class SomeBean {
String someValue
}
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomePostProcessor.groovy
package demo
import org.springframework.beans.BeansException
import org.springframework.beans.MutablePropertyValues
import org.springframework.beans.PropertyValue
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
class SomePostProcessor implements BeanDefinitionRegistryPostProcessor{
#Override
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition definition = registry.getBeanDefinition('someBean')
MutablePropertyValues values = definition.getPropertyValues()
PropertyValue value = values.getPropertyValue('someValue')
def originalValue = value.getValue()
// this is where you could do your decrypting...
values.addPropertyValue('someValue', "MODIFIED: ${originalValue}".toString())
}
#Override
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/conf/spring/resources.groovy
beans = {
someBean(demo.SomeBean) {
someValue = 'Some Value'
}
somePostProcessor demo.SomePostProcessor
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/init/postprocessordemo/BootStrap.groovy
package postprocessordemo
import demo.SomeBean
class BootStrap {
SomeBean someBean
def init = { servletContext ->
log.info "The Value: ${someBean.someValue}"
}
def destroy = {
}
}
At application startup you will see log output that looks something like this...
2017-10-23 19:04:54.356 INFO --- [ main] postprocessordemo.BootStrap : The Value: MODIFIED: Some Value
The "MODIFIED" there is evidence that the bean definition post processor modified the property value in the bean. In my example I am simply prepending some text to the string. In your implementation you could decrypt a password or do whatever you want to do there.
I hope that helps.
After trying Jasypt plugin and BeanPostProcessor solutions unsuccessfully for my use case, I found below solution to work perfectly.
To describe again the problem statement here,
a) we had to keep the passwords in an encrypted format inside properties files
b) and given we were packaging as a war file so the properties must not be kept inside the war to allow automated deployment scripts update the encrypted passwords depending on the environment
Jasypt plugin was a perfect solution for the use case a), but it was not able to cover the b) scenario
Moreover, the Grails LDAP Security plugin was getting loaded quite early hence Bean Post processors were also not helping out here.
Solution:
Created a new class by implementing the interface SpringApplicationRunListener. Extended its methods and parsed the properties file using YamlPropertySourceLoader
Sample code:
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load(
"application.yml", new ClassPathResource("application.yml"),"default");
return applicationYamlPropertySource;
Once the properties were loaded inside the MapPropertySource object, parsed them for the encrypted values and applied the decryption logic.
This whole implementation was executed before any plugins were initialized during Grails bootup process solving the purpose.
Hope it will help others.

Dealing with dynamic options for SelectList in Jira issue creation

In my Jira project I have a custom field called "CASE-Environment". Its a Select List(Multiple values)
I would like this custom field to have values dynamically populated. In my actual project, I am making use of RESTFul calls wherein I get the actual values from an internal RESTFul service. But for demo purposes I am showing the values here as hard coded via an initializer.
I am making use of the Script Runner Jira plugin which lets me define behaviors, associate it with fields. I have the following in the initializer section of the behavior.
import com.onresolve.jira.groovy.user.FieldBehaviours
import com.onresolve.jira.groovy.user.FormField
Map fieldOptions = [:]
fieldOptions.put("-1","None")
fieldOptions.put("100","Dev");
fieldOptions.put("101","Staging");
fieldOptions.put("102","QA");
fieldOptions.put("103","Production");
FormField env = getFieldByName("CASE-Environment");
env.setFieldOptions(fieldOptions)
I am successfully able to see this values on the UI when I am trying to create an issue.
But when I am trying to submit the issue, I hit a Jira validation error message which reads as below
Invalid value '102' passed for customfield 'CASE-Environment'
I am guessing that Jira somehow is not recognizing the dynamically added option values for my SelectList [ multiple values ]
I even tried building a mapping for this field wherein my behavior would get triggered everytime the custom field CASE-Environment under went changes.
The mapping code is shown as below :
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.customfields.option.Options
import com.atlassian.jira.issue.customfields.option.Option
import com.onresolve.jira.groovy.user.FieldBehaviours
import com.onresolve.jira.groovy.user.FormField
def adddOption = {
Issue issue,
CustomField customField,
String value ->
Options l = ComponentAccessor.getOptionsManager().getOptions(customField.getRelevantConfig(issue))
int nextSequence = l.isEmpty() ? 1 : l.getRootOptions().size() + 1;
Option newOption = ComponentAccessor.getOptionsManager().createOption(customField.getRelevantConfig(issue), null, (Long)nextSequence, value);
return newOption;
}
FormField environment = getFieldById(fieldChanged)
Issue issue = getUnderlyingIssue()
MutableIssue mutableIssue = ComponentAccessor.getIssueManager().getIssueObject(issue.id);
CustomField field = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("CASE-Environment")
List<Option> allOptions = new LinkedList<>();
List<String> chosenValues = (List<String>) mutableIssue.getCustomFieldValue(field);
for (String eachValue : chosenValues) {
allOptions.add(adddOption(issue, field, eachValue));
}
mutableIssue.setCustomFieldValue(field, allOptions);
But even after this I still can't get past the error message. Can someone please help me with getting this sorted out ?
For what its worth, I am using Jira : JIRA v6.3.5
So after toying around for quite sometime on this, I finally managed to crack this on my own.
The below mentioned initializer section should help get this sorted out easily.
import com.atlassian.jira.component.ComponentAccessor
def optionsManager = ComponentAccessor.getOptionsManager()
def fieldManager = ComponentAccessor.getCustomFieldManager()
def envFld = fieldManager.getCustomFieldObjectByName("CASE-Environment")
def fieldConfig = envFld.getRelevantConfig(getIssueContext())
def newSeqId = 0
//For the sake of e.g., I am using a initialized array. In real life one can
// construct this by querying a DB or hitting a RestFul service etc.,
def envs = ["QA", "Staging", "Dev", "Production"]
def issueService = ComponentAccessor.getIssueService()
def issueInputParameters = issueService.newIssueInputParameters()
for (String env : envs) {
def option = optionsManager.createOption(fieldConfig, null, newSeqId++, env)
issueInputParameters.addCustomFieldValue(envFld.idAsLong, option.optionId.toString())
}
Thanks #Krishan
just one comment, new JIRA 7/ScriptRunner have problem with static type check. Replace the definition of "newSeqID"
def newSeqId = 0
with
Long newSeqId = 0

F# Sql Type Provider: How to access

I am using a TypeProvider to access some data like this:
type internal SqlConnection =
SqlEntityConnection<ConnectionString =
#"XXXXXXXXXX">
type FooRepository () =
member x.GetFoo (Id) =
let context = SqlConnection.GetDataContext()
query {for foo in context.Foos do
where (foo.Id = Id)
select foo}
The problem is I am getting this:
The type 'FooRepository' is less accessible than the value, member or type 'member System.Linq.IQueryable' it is used in
I see the same problem asked on SO here.
Since I want to expose this type and I don't like the solutions on SO, I tried doing this:
SqlDataConnection<ConnectionString =
#"XXXXXXX">
The problem is that the database is on Azure Sql Server so I am getting this:
The type provider 'Microsoft.FSharp.Data.TypeProviders.DesignTime.DataProviders' reported an error: Error reading schema. Warning : SQM1012: Unable to extract table 'dbo.Foos from SqlServer.
Does anyone have an idea about how to get around this? One of the best things about using TPs is that I don't need to explicitly defines the types - but it looks like I am going to have to? Ugh
Are you sure the error you are getting is the one you posted here? My impression is that
the type which is less accessible should be the one from the generated database schema
(because you mark that as internal).
I tried reproducing your error by writing a simple library that uses the Northwind database:
open Microsoft.FSharp.Data.TypeProviders
type internal Nwind = SqlDataConnection<"Data Source=.\sqlexpress;Initial Catalog=Northwind;Integrated Security=True">
type NwindLibrary() =
let nw = Nwind.GetDataContext()
member this.Products = nw.Products
And I get the following message:
The type 'Products' is less accessible than the value, member or type 'member Class1.Products : System.Data.Linq.Table' it is used in C:\temp\Library1\Library1\Library1.fs 9 17 Library1
This is what I was expecting - because the Nwind type (generated by a provider) is marked as internal. However, the NwindLibrary type is public and the property Products (that I defined) returns a value of type IQueryable<Table<Nwind.ServiceTypes.Product>>. The compiler error is not particularly useful here (because it says that the type returns just Table), but the problem is that Nwind.ServiceTypes.Product is internal and so you cannot return it in a public property of a public type.
If you make the Nwind type public, then it should work fine. If you do not want to make the generated types public, then you'll have to wrap the returned values in your custom types. For example, even if you leave the type internal, the following works fine (returning a tuple instead of generated type):
member this.Products =
query { for p in mys.Products do
select (p.ProductID, p.ProductName) }

Autofac error: The instance registration can support SingleInstance() sharing only

When I try to register my class with autofac I get the following error: "The instance registration 'GetAllDivisionsCommand' can support SingleInstance() sharing only".
I don't understand why I'm getting this error, but assume it's something to do with the class having static member variables used for caching as that's the only thing that's different about this class. I haven't had any trouble registering any other classes as either SingleInstance or InstancePerDependency.
Essentially, the class is used to retrieve a rarely changing list of divisions from the database, and caches the result. Each time the command is run, it first checks for changes on the database and then re-runs the query if changes are detected; if not, it returns the cached list.
So I am trying to register GetAllDivisionsCommand with Autofac as an IGetAllDivisionsCommand. IGetAllDivisionsCommand itself implements IInjectableCommand, which is just a marker interface, and ICachedListCommand. The concrete command class inherits from the abstract base class CachedListCommand which is where the static member variables live.
Does anyone know what would cause this error message? SingleInstance won't work for me as I can't keep reusing the same session.
Code:
Type commandType = typeof(IInjectedCommand);
Type aCommandType = typeof(GetAllDivisions);
var commands =
from t in aCommandType.Assembly.GetExportedTypes()
where t.Namespace == aCommandType.Namespace
&& t.IsClass
&& !t.IsAbstract
&& (commandType.IsAssignableFrom(t))
let iface = t.GetInterfaces().FirstOrDefault(x => "I" + t.Name == x.Name)
select new { Command = t, Interface = iface };
foreach (var cmd in commands)
{
builder.RegisterInstance(cmd.Command).As(cmd.Interface).InstancePerLifetimeScope();
}
RegisterInstace as the name implies is for registering instances not types.
What you need is RegisterType:
foreach (var cmd in commands)
{
builder.RegisterType(cmd.Command).As(cmd.Interface).InstancePerLifetimeScope();
}
And by the way with the Autofac scanning feature your registration code is roughly equivalent:
builder
.RegisterAssemblyTypes(aCommandType.Assembly)
.AssignableTo<IInjectedCommand>()
.InNamespace(aCommandType.Namespace)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
In my case, I did want RegisterInstance because I actually have an instance in hand that I wanted to register.
I had
builder.RegisterInstance(myInstance).InstancePerDependency();
The documentation for InstancePerDependency reads:
Configure the component so that every dependent component or call to
Resolve() gets a new, unique instance (default.)
On closer inspection, it makes sense that registering an instance with "instance per dependency" is not possible, since it is not possible for Autofac to give back a new instance each time Resolve is called if there is in fact 1 instance registered.
So, in my case, the solution was this.
builder.RegisterInstance(myInstance).SingleInstance();
The Autofac exception could possibly have been worded more clearly to explain this problem.

Resources