Efficient way to find out which command was called? - parsing

I have a bot written in Go that is constantly parsing messages and has commands that users of the bot can call, all commands are prefixed with bot#
Let's say I have several commands and some of them are: bot#thing, bot#another, bot#fu, bot#special
To find out which command was called, I first check if the message is prefixed with "bot#", if it is, I split the message string by " " (as some commands can have an argument after) and then apply a switch to find out what command is it and call its appropriate function
if strings.HasPrefix(message, "bot#") {
args := strings.Split(message, " ")
switch args[0] {
case "bot#thing":
//do something...
case "bot#another":
//do something...
case "bot#fu":
//do something...
case "bot#special":
//do something...
//and more cases...
}
}
My problem is, as more commands are added I think this can get quite inefficient, specially since I check for the prefix on every message. Is there a more efficient approach to parsing the messages to find out which ones are a command and which command is it?

Use a map:
var commands:=map[string]func([]string) {
"bot#command1": doCommand1,
"bot#command2": doCommand2,
}
func processMsg(msg string) {
if cmd, ok:=commands[msg]; ok {
args:= // parse arguments
cmd(args)
}
}

Related

Jenkinsfile Switch case with contains is throwing CpsCallableInvocation error

I am trying to write a jenkins file with a switch case, where I am trying to use string operations in case. In a regular groovy it is working fine but in JenkinsFile it is throwing errors. What am I missing?
Code
switch (env.UPSTREAM)
{
case { it.contains("SDK") }:
//do something
break
case { it.contains("sample") }:
//do something
break
}
Error
hudson.remoting.ProxyException: CpsCallableInvocation{methodName=call, call=com.cloudbees.groovy.cps.impl.CpsClosureDef#47e13b68, receiver=org.jenkinsci.plugins.workflow.cps.CpsClosure2#3d8cf0c6, arguments=[asjkndakjsd_sample]}
Jenkins uses Groovy CPS to run pipeline scripts, you can read about it here. It's kind of confusing but basically you can't use some of the more complicated groovy functions/commands.
A way to get around this is to use the #NonCPS annotation. You have to create a function outside of your pipeline with that #NonCPS annotation. But in your case you could probably just use simple if statements instead of the switch. For example just create a new variable def upStream = env.UPSTREAM then check each case:
if (upStream.contains("SDK"))
{
// do something
}
else if (upStream.contains("sample"))
{
// do something
}

Jenkins and its WHEN statement

this is very tough to me to understand how Jenkins works. In general when you read documentation and define pipeline, things go smooth. I understand pipelines, stages, steps, scripts. What I don't understand is declaration vs runtime. Especially when it comes to WHEN declaration and evaluating expression. For example:
What is the form of expression? Should it return something like: return true; or maybe it should be statement like: true
When it gets executed? If I access params.MY_PARAMETER_FROM_INPUT, do WHEN has access to its value picked by user?
Is it possible to switch execution between runtime vs pipeline declaration time?
Can I ask for stage (input with message box) only if given condition within WHEN is meet and if not, then don't ask for it but run stage anyway?
When you use IF from script and when WHEN from stage. Can WHEN be defined else where? Within steps, scripts, pipeline?
For example in a stage I've put when { expression { params.ENV == 'prod' } } input { message "Really?" ok "Yeah!" } but the expression was ignored and the question was always asked (current understanding is that it should skip stage/abort whole pipeline when ENV input param is different than "prod" value)
Any thoughts?
OK. Using existing semantic of Jenkins I have manage to achieve what I wanted with following snippet:
stage('Confirm if production related') {
when {
beforeInput true
expression { params.ENV == 'production'; }
}
input {
message "Should I deploy to PRODUCTION?"
ok "Yes, do it!"
}
steps {
script { _ }
}
}
Not bad but not good either.

Reactor. List of Monos, retry on fail

I have list List<Mono<String>>. Each Mono represents API call where I wait on I/O for result. The problem is that some times some calls return nothing (empty String), and I need repeat them again on that case.
Now it looks like this:
val firstAskForItemsRetrieved = firstAskForItems.map {
it["statistic"] = (it["statistic"] as Mono<Map<Any, Any>>).block()
it
}
I'm waiting for all Monos to finish, then in case of empty body I repeat request
val secondAskForItem = firstAskForItemsRetrieved
.map {
if ((it["statistic"] as Map<Any, Any>).isEmpty()) {
// repeat request
it["statistic"] = getUserItem(userName) // return Mono
} else
it["statistic"] = Mono.just(it["statistic"])
it
}
And then block on each item again
val secondAskForItemsRetrieved = secondAskForItems.map {
it["statistic"] = (it["statistic"] as Mono<Map<Any, Any>>).block()
it
}
I see that looks ugly
Are any other ways to retry call in Mono if it fails, without doing it manually?
Is it block on each item a right way to get them all?
How to make the code better?
Thank you.
There are 2 operators I believe can help your:
For the "wait for all Mono" use case, have a look at the static methods when and zip.
when just cares about completion, so even if the monos are empty it will just signal an onComplete whenever all of the monos have finished. You don't get the data however.
zip cares about the values and expects all Monos to be valued. When all Monos are valued, it combines their values according to the passed Function. Otherwise it just completes empty.
To retry the empty Monos, have a look at repeatWhenEmpty. It resubscribes to an empty Mono, so if that Mono is "cold" it would restart the source (eg. make another HTTP request).

Dart - How do you write a test against a function that calls exit()?

I want to test a function that calls exit.
Basicly, I have a console application, that asks the user if he is sure that he wants a directory to be overwritten. When the users answers "No", the directory won't be overwritten, and the program should exit.
promptToDeleteRepo() {
bool okToDelete = ...
if(okToDelete) {
deleteRepo();
} else {
exit(0);
}
}
So I want to test that if the user answers "No", that the program really exits. But if I test this, my test runner exits.
In python I seem to be able to do something like:
with pytest.raises(SystemExit):
promptToDeleteRepo();
Is there something like this in Dart?
You can inject a custom exit function during the tests.
import 'dart:io' as io;
typedef ExitFn(int code);
ExitFn exit = io.exit;
promptToDeleteRepo() {
bool okToDelete = ...
if(okToDelete) {
deleteRepo();
} else {
exit(0);
}
}
and in your test :
int exitCodeUsed;
mylib.exit = (int code) {exitCodeUsed = code};
mylib.promptToDeleteRepo();
A better solution whould have to use zones but there doesn't seem to be possible to handle exit. It could be worth to file an issue.
One option that comes to my mind is to run the code you want to test in a new process Process.run() or Process.start() and check the exit code at the end. You can use stdin/stdout to communicate with the process (send keyboard input, read output)

How do I read a multiline value using the Ant 'input' task?

Anyone know how I can enter a multiline value in an Ant script? I'm prompting the user for a Subversion commit comment using the input task, and I'd like to be able to support multiple lines of text.
I'm running the standalone version of Ant at the Windows command prompt.
I thought I might be able to do a search and replace for \n, but I can't see any easy way to do a replace from property value to property value in Ant. It looks like I'd have to write a file, replace in the file, and then load the file into another property. I don't want it that badly.
I'm not 100% positive about this, but I took a look at the Ant source code, and it just does a readLine():
From /org/apache/tools/ant/input/DefaultInputHandler.java:
/**
* Prompts and requests input. May loop until a valid input has
* been entered.
* #param request the request to handle
* #throws BuildException if not possible to read from console
*/
public void handleInput(InputRequest request) throws BuildException {
String prompt = getPrompt(request);
BufferedReader r = null;
try {
r = new BufferedReader(new InputStreamReader(getInputStream()));
do {
System.err.println(prompt);
System.err.flush();
try {
String input = r.readLine();
request.setInput(input);
} catch (IOException e) {
throw new BuildException("Failed to read input from"
+ " Console.", e);
}
} while (!request.isInputValid());
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
throw new BuildException("Failed to close input.", e);
}
}
}
}
Here is what I would do if I were you:
If you are using Ant 1.7, then try implementing your own InputHandler, as described in the documentation. The Apache License permits you to basically copy-and-paste the above code as a starting point.
If you are using Ant 1.6 or earlier, then just create your own MultiLineInput task. You can extend the existing Input class and just read multiple lines.
In either case, you would need to decide how the user indicates "I'm done." You could use a blank line or a period or something.
Good luck!
P.S. When I did a Google search for "ant multi-line input", this page was the first hit :-). Pretty impressive for a question that was asked less than an hour ago.

Resources