SonarQube OpenEdge custom rule to verify &IF preprocessor with Proparse - sonarqube

I am trying to create a custom plugin, based on the Riverside OpenEdge plugin and its version of Proparse to create a rule that valids a &IF preprocessor.
This rule needs to verify if the application is using a deprecated value of a &GLOBAL-DEFINE like this:
/* "A" is the deprecated value of "opts" so I want to create a new ISSUE here */
&IF "{&opts}" = "A" &THEN
MESSAGE "DEPRECATED CODE".
&ENDIF
&IF "{&opts}" > "A" &THEN
MESSAGE "OK CODE".
&ENDIF
For this rule I extended I tried to do something like this:
if (unit.getMacroGraph().macroEventList.stream().noneMatch(macro -> macro instanceof NamedMacroRef
&& ((NamedMacroRef) macro).getMacroDef().getName().equalsIgnoreCase("opts"))) {
return;
}
TokenSource stream = unit.lex();
ProToken tok = (ProToken) stream.nextToken();
while (tok.getNodeType() != ABLNodeType.EOF_ANTLR4) {
if (tok.getNodeType() == ABLNodeType.AMPIF) {
// Verify node.
System.out.println(tok);
}
tok = (ProToken) stream.nextToken();
}
But I don't know if its the best way to verify (I did based on the code from other sources) and it's not working because the next node comes as an empty "QSSTRING". I am very new in the Proparse world, any help is appreciated.

First, you have to know that Proparse doesn't give access to every detail of the preprocessor. That said, the method unit.getMacroGraph() will give you access to the visible part of the preprocessor, so that's a good starting point.
If you're looking for usage of given preprocessor variable, you can search for NamedMacroRef instances pointing to the right MacroDef object (with NamedMacroRef#getMacroDef()#getName()), and the right value.
In a old-style for-each loop:
for (MacroRef ref : unit.getMacroSourceArray()) {
if ((ref instanceof NamedMacroRef)) {
if ("opts".equalsIgnoreCase(((NamedMacroRef) ref).getMacroDef().getName())
&& "A".equalsIgnoreCase(((NamedMacroRef) ref).getMacroDef().getValue())) {
System.out.println("OPTS variable usage with value 'A' at file " + ref.getFileIndex() + ":" + ref.getLine());
}
}
}
On this file:
&global-define opts a
&IF "{&opts}" = "A" &THEN
MESSAGE "DEPRECATED CODE".
&ENDIF
&undefine opts
&global-define opts b
&IF "{&opts}" > "A" &THEN
MESSAGE "OK CODE".
&ENDIF
This gives:
OPTS variable usage with value 'A' at file 0:2
So you don't have access to the expression engine, but I think that the current API is enough for what you want to do.
You can then report the issue with SonarQube with OpenEdgeProparseCheck#reportIssue()

Related

Can asynchronous module definitions be used with abstract syntax trees on v8 engine to read third party dependencies? [duplicate]

I understand eval string-to-function is impossible to use on the browsers' application programming interfaces, but there must be another strategy to use third party dependencies without node.js on v8 engine, given Cloudflare does it in-house, unless they disable the exclusive method by necessity or otherwise on their edge servers for Workers. I imagine I could gather the AST of the commonjs module, as I was able to by rollup watch, but what might the actual steps be, by tooling? I mention AMD for it seems to rely on string-to-function (to-which I've notice Mozilla MDN says nothing much about it).
I have been exploring the require.js repositories, and they either use eval or AST
function DEFNODE(type, props, methods, base) {
if (arguments.length < 4) base = AST_Node;
if (!props) props = [];
else props = props.split(/\s+/);
var self_props = props;
if (base && base.PROPS) props = props.concat(base.PROPS);
var code = "return function AST_" + type + "(props){ if (props) { ";
for (var i = props.length; --i >= 0; ) {
code += "this." + props[i] + " = props." + props[i] + ";";
}
var proto = base && new base();
if ((proto && proto.initialize) || (methods && methods.initialize))
code += "this.initialize();";
code += "}}";
//constructor
var cnstor = new Function(code)();
if (proto) {
cnstor.prototype = proto;
cnstor.BASE = base;
}
if (base) base.SUBCLASSES.push(cnstor);
cnstor.prototype.CTOR = cnstor;
cnstor.PROPS = props || null;
cnstor.SELF_PROPS = self_props;
cnstor.SUBCLASSES = [];
if (type) {
cnstor.prototype.TYPE = cnstor.TYPE = type;
}
if (methods)
for (i in methods)
if (HOP(methods, i)) {
if (/^\$/.test(i)) {
cnstor[i.substr(1)] = methods[i];
} else {
cnstor.prototype[i] = methods[i];
}
}
//a function that returns an object with [name]:method
cnstor.DEFMETHOD = function (name, method) {
this.prototype[name] = method;
};
if (typeof exports !== "undefined") exports[`AST_${type}`] = cnstor;
return cnstor;
}
var AST_Token = DEFNODE(
"Token",
"type value line col pos endline endcol endpos nlb comments_before file raw",
{},
null
);
https://codesandbox.io/s/infallible-darwin-8jcl2k?file=/src/mastercard-backbank/uglify/index.js
https://www.youtube.com/watch?v=EF7UW9HxOe4
Is it possible to make a C++ addon just to add a default object for
node.js named exports or am I Y’ing up the wrong X
'.so' shared library for C++ dlopen/LoadLibrary (or #include?)
“I have to say that I'm amazed that there is code out there that loads one native addon from another native addon! Is it done by acquiring and then calling an instance of the require() function, or perhaps by using uv_dlopen() directly?”
N-API: An api for embedding Node in applications
"[there is no ]napi_env[ just yet]."
node-api: allow retrieval of add-on file name - Missing module in Init
Andreas Rossberg - is AST parsing, or initialize node.js abstraction for native c++, enough?
v8::String::NewFromUtf8(isolate, "Index from C++!");
Rising Stack - Node Source
"a macro implicit" parameter - bridge object between
C++ and JavaScript runtimes
extract a function's parameters and set the return value.
#include <nan.h>
int build () {
NAN_METHOD(Index) {
info.GetReturnValue().Set(
Nan::New("Index from C++!").ToLocalChecked()
);
}
}
// Module initialization logic
NAN_MODULE_INIT(Initialize) {
/*Export the `Index` function
(equivalent to `export function Index (...)` in JS)*/
NAN_EXPORT(target, Index);
}
New module "App" Initialize function from NAN_MODULE_INIT (an atomic?-macro)
"__napi_something doesn't exist."
"node-addon-API module for C++ code (N-API's C code's headers)"
NODE_MODULE(App, Initialize);
Sep 17, 2013, 4:42:17 AM to v8-u...#googlegroups.com "This comes up
frequently, but the answer remains the same: scrap the idea. ;)
Neither the V8 parser nor its AST are designed for external
interfacing. In particular (1) V8's AST does not necessarily reflect
JavaScript syntax 1-to-1, (2) we change it all the time, and (3) it
depends on various V8 internals. And since all these points are
important for V8, don't expect the situation to change.
/Andreas"
V8 c++: How to import module via code to script context (5/28/22, edit)
"The export keyword may only be used in a module interface unit.
The keyword is attached to a declaration of an entity, and causes that
declaration (and sometimes the definition) to become visible to module
importers[ - except for] the export keyword in the module-declaration, which is just a re-use of the keyword (and does not actually “export” ...entities)."
SyntheticModule::virtual
ScriptCompiler::CompileModule() - "Corresponds to the ParseModule abstract operation in the ECMAScript specification."
Local<Function> foo_func = ...;//external
Local<Module> module = Module::CreateSyntheticModule(
isolate, name,
{String::NewFromUtf8(isolate, "foo")},
[](Local<Context> context, Local<Module> module) {
module->SetSyntheticModuleExport(
String::NewFromUtf8(isolate, "foo"), foo_func
);
});
Context-Aware addons from node.js' commonjs modules
export module index;
export class Index {
public:
const char* app() {
return "done!";
}
};
import index;
import <iostream>;
int main() {
std::cout << Index().app() << '\n';
}
node-addon-api (new)
native abstractions (old)
"Thanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.12 to 4.0, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect NODE_MODULE_VERSION and get yourself into a macro-tangle[ macro = extern atomics?]."
Scope Isolate (v8::Isolate), variable Local (v8::Local)
typed_array_to_native.cc
"require is part of the Asynchronous Module Definition AMD API[, without "string-to-function" eval/new Function()],"
node.js makes objects, for it is written in C++.
"According to the algorithm, before finding
./node_modules/_/index.js, it tried looking for express in the
core Node.js modules. This didn’t exist, so it looked in node_modules,
and found a directory called _. (If there was a
./node_modules/_.js, it would load that directly.) It then
loaded ./node_modules/_/package.json, and looked for an exports
field, but this didn’t exist. It also looked for a main field, but
this didn’t exist either. It then fell back to index.js, which it
found. ...require() looks for node_modules in all of the parent directories of the caller."
But java?
I won't accept this answer until it works, but this looks promising:
https://developer.oracle.com/databases/nashorn-javascript-part1.html
If not to run a jar file or something, in the Worker:
https://github.com/nodyn/jvm-npm
require and build equivalent in maven, first, use "dist/index.js".
Specifically: [ScriptEngineManager][21]
https://stackoverflow.com/a/15787930/11711280
Actually: js.commonjs-require experimental
https://docs.oracle.com/en/graalvm/enterprise/21/docs/reference-manual/js/Modules/
Alternatively/favorably: commonjs builder in C (v8 and node.js)
https://www.reddit.com/r/java/comments/u7elf4/what_are_your_thoughts_on_java_isolates_on_graalvm/
Here I will explore v8/node.js src .h and .cc for this purpose
https://codesandbox.io/s/infallible-darwin-8jcl2k?file=/src/c.cpp
I'm curious why there is near machine-level C operability in Workers if not to use std::ifstream, and/or build-locally, without node.js require.

How can i Print own Error Message on Java Jar CMD

how can I Print adittional information to Command line Console?
Output now is:
C:\Users\admin\Desktop\java>java -jar pdf.jar
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at readDataIn.main(readDataIn.java:31)
Code:
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
try {
String arg = args[0];
fileNameSource = "import/" + arg + ".xml";
fileNameTarget = "export/" + arg + ".pdf";
} catch (Exception e) {
// TODO: handle exception
**System.out.println("Personal-Number is missing");**
e.printStackTrace();
}
How can i give the information out, that the Personal Number ist Missing?
First of all, as a general rule you should check for possible exceptions before they actually occur if that is possible, which in your case it definitely is.
So instead of catching the ArrayIndexOutOfBounds insert an if statement that checks the length of the args array before accessing it.
if(args.length == 0){
// no argument has been provided
// handle error here
}
In terms of how to handle the error, there are many options available and depending of what you want to do either could be a good fit.
IllegalArgumentException
It is a common idiom in Java that whenever a function receives an invalid/ illegal argument to throw an IllegalArgumentException.
if (args.length == 0){
throw new IllegalArgumentException("Personal number is missing");
}
This will print the message that you have provided and the stack trace. However if your application should be a Command Line Interface (CLI) you should not use this kind of error handling.
Print message & exit program
if (args.length == 0){
// notice: "err" instead of "out": print to stderr instead of stdout
System.err.println("Personal number is missing");
// exit program with non-zero exit code as exit code == 0 means everything is fine
System.exit(1);
}
For more information on stdout and stderr see this StackOverflow question.
This is what many CLI applications and e.g. java itself does. When you type java fdsdfsdfs or some similar nonsense as an argument Java will give you an error message and exit with some non-zero return code ("1" in this case).
It is also common that CLI applications print an error message and following some usage information on how to correctly use the application or provide a help command so a user can get more information. This happens for example if you just enter java without any parameters.
So it is really up to you what you want to do.
If you are thinking of implementing a full featured CLI application with more (complex) commands with multiple options etc. you should consider using a CLI library like JCommander or Apache Commons CLI as parsing command line arguments can quickly get ugly. All these common things are already handled there.
Logging
In case your application is some script that will be executed in a non-interactive way logging the error to a file and exiting with a non-zero exit code might also be an option.
PS
Your code looks to me like it should not compile at all as you are not declaring a type for your variables fileNameSource and fileNameTarget.
Use String or var here (assuming you're running > Java 11).
String fileNameSource = "import/" + arg + ".xml";
var fileNameTarget = "export/" + arg + ".pdf";
You might also need to consider that your program name is part of the args array, so you might have more than 0 values in the array and therefore might need to adjust the if statements above.
You may be interested in picocli, which is a modern CLI library for Java and other JVM languages.
Picocli does some basic validation automatically, and results in very compact code that produces user-friendly applications. For example:
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
#Command(name = "myapp", mixinStandardHelpOptions = true, version = "1.0",
description = "This command does something useful.")
class MyApp implements Runnable {
#Parameters(description = "File name (without extension) of the file to import and export.")
private String personalNumber;
#Override
public void run() {
String fileNameSource = "import/" + personalNumber + ".xml";
String fileNameTarget = "export/" + personalNumber + ".pdf";
// remaining business logic
}
public static void main(String[] args) {
System.exit(new CommandLine(new MyApp()).execute(args));
}
}
If I run this class without any parameters, the following message is printed to the standard error stream, and the process finished with exit code 2. (Exit codes are customizable.)
Missing required parameter: '<personalNumber>'
Usage: myapp [-hV] <personalNumber>
This command does something useful.
<personalNumber> File name (without extension) of the file to import
and export.
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
The usage help message is created automatically from the descriptions of the command, and the descriptions of its options and positional parameters, but can be further customized.
Note how the mixinStandardHelpOptions = true annotation adds --help and --version options to the command. These options are handled by the library without requiring any further logic in the application.
Picocli comes with an annotation processor that makes it very easy to turn your application into a native image with GraalVM. Native images have faster startup time and lower runtime memory overhead compared to a Java VM.

How to match EOF condition using antlr 4

I am new to ANTLR and I am currently writing a lexer for cool language in ANTLR 4.
For more about cool language please refer http://theory.stanford.edu/~aiken/software/cool/cool-manual.pdf.
One rule of cool language that I was trying to implement was detecting EOF inside Comments (may be nested) or String Constants and reporting as an error.
This is the rule that I wrote :
ERROR : '(*' (COMMENT|~['(*'|'*)'])*? (~['*)']) EOF {reportError("EOF in comment");}
|'"' (~[\n"])* EOF {reportError("EOF in string");};
fragment COMMENT : '(*' (COMMENT|~['(*'|'*)'])*? '*)'
Here the fragment COMMENT is a recursive rule that I used.
The function reportError used above reports error which is given below:
public void reportError(String errorString){
setText(errorString);
setType(ERROR);
}
But when I run it on the test file given below:
"Test String
It gives the following output :
line 1:0 token recognition error at: '"Test String\n'
#name "helloworld.cl"
Clearly the String with EOF in it was not recognised and ERROR was not detected.
Can someone help me in pointing out where I am going wrong as EOF (and hence, the error rule) is somehow not getting detected by the lexer.
If something is not clear please do mention it.
'"' (~[\n"])* EOF
Here the ~[\n"]* part will stop at the first \n or " or at the end of the file.
If it stops at a ", the rule does not match because the EOF does not match and that's what we want because the string literal is properly terminated.
If it stops at the end of file, then the subsequent EOF will match and you'll get an ERROR token. So that's also what you want.
But if it stops at a \n, the EOF will not match and you won't get an error token even though you'd want one in this case. And since your input ends with a \n, that's exactly the scenario you're running into here. So in addition to EOF, you should also allow for erroneous string literals to end in \n:
'"' (~[\n"])* ('\n' | EOF)
You don't need a dedicated ERROR rule. You can handle that specific situation with an unfinished string directly in your error listener. Your comment rule shouldn't be a fragment however, as it has to recognize a lexeme on its own that must be handled (fragment rules are rather rules to be used in other lexer rules only).
When the lexer reaches a string but cannot finish it due to the end of the input, you can get the offending input from the current lexer state in your error listener. You can then check that to see what exactly wasn't finished, like I do here for 3 quoted text types in MySQL:
void LexerErrorListener::syntaxError(Recognizer *recognizer, Token *, size_t line,
size_t charPositionInLine, const std::string &, std::exception_ptr ep) {
// The passed in string is the ANTLR generated error message which we want to improve here.
// The token reference is always null in a lexer error.
std::string message;
try {
std::rethrow_exception(ep);
} catch (LexerNoViableAltException &) {
Lexer *lexer = dynamic_cast<Lexer *>(recognizer);
CharStream *input = lexer->getInputStream();
std::string text = lexer->getErrorDisplay(input->getText(misc::Interval(lexer->tokenStartCharIndex, input->index())));
if (text.empty())
text = " "; // Should never happen.
switch (text[0]) {
case '/':
message = "Unfinished multiline comment";
break;
case '"':
message = "Unfinished double quoted string literal";
break;
case '\'':
message = "Unfinished single quoted string literal";
break;
case '`':
message = "Unfinished back tick quoted string literal";
break;
default:
// Hex or bin string?
if (text.size() > 1 && text[1] == '\'' && (text[0] == 'x' || text[0] == 'b')) {
message = std::string("Unfinished ") + (text[0] == 'x' ? "hex" : "binary") + " string literal";
break;
}
// Something else the lexer couldn't make sense of (likely there is no rule that accepts this input).
message = "\"" + text + "\" is no valid input at all";
break;
}
owner->addError(message, 0, lexer->tokenStartCharIndex, line, charPositionInLine,
input->index() - lexer->tokenStartCharIndex);
}
}
This code was taken from the parser module in MySQL Workbench.

Enabling Closed-Display Mode w/o Meeting Apple's Requirements

EDIT:
I have heavily edited this question after making some significant new discoveries and the question not having any answers yet.
Historically/AFAIK, keeping your Mac awake while in closed-display mode and not meeting Apple's requirements, has only been possible with a kernel extension (kext), or a command run as root. Recently however, I have discovered that there must be another way. I could really use some help figuring out how to get this working for use in a (100% free, no IAP) sandboxed Mac App Store (MAS) compatible app.
I have confirmed that some other MAS apps are able to do this, and it looks like they might be writing YES to a key named clamshellSleepDisabled. Or perhaps there's some other trickery involved that causes the key value to be set to YES? I found the function in IOPMrootDomain.cpp:
void IOPMrootDomain::setDisableClamShellSleep( bool val )
{
if (gIOPMWorkLoop->inGate() == false) {
gIOPMWorkLoop->runAction(
OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
(OSObject *)this,
(void *)val);
return;
}
else {
DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
if ( clamshellSleepDisabled != val )
{
clamshellSleepDisabled = val;
// If clamshellSleepDisabled is reset to 0, reevaluate if
// system need to go to sleep due to clamshell state
if ( !clamshellSleepDisabled && clamshellClosed)
handlePowerNotification(kLocalEvalClamshellCommand);
}
}
}
I'd like to give this a try and see if that's all it takes, but I don't really have any idea about how to go about calling this function. It's certainly not a part of the IOPMrootDomain documentation, and I can't seem to find any helpful example code for functions that are in the IOPMrootDomain documentation, such as setAggressiveness or setPMAssertionLevel. Here's some evidence of what's going on behind the scenes according to Console:
I've had a tiny bit of experience working with IOMProotDomain via adapting some of ControlPlane's source for another project, but I'm at a loss for how to get started on this. Any help would be greatly appreciated. Thank you!
EDIT:
With #pmdj's contribution/answer, this has been solved!
Full example project:
https://github.com/x74353/CDMManager
This ended up being surprisingly simple/straightforward:
1. Import header:
#import <IOKit/pwr_mgt/IOPMLib.h>
2. Add this function in your implementation file:
IOReturn RootDomain_SetDisableClamShellSleep (io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint32_t input_count = 1;
uint64_t input[input_count];
input[0] = (uint64_t) { disable ? 1 : 0 };
return IOConnectCallScalarMethod(root_domain_connection, kPMSetClamshellSleepState, input, input_count, NULL, &num_outputs);
}
3. Use the following to call the above function from somewhere else in your implementation:
io_connect_t connection = IO_OBJECT_NULL;
io_service_t pmRootDomain = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
IOServiceOpen (pmRootDomain, current_task(), 0, &connection);
// 'enable' is a bool you should assign a YES or NO value to prior to making this call
RootDomain_SetDisableClamShellSleep(connection, enable);
IOServiceClose(connection);
I have no personal experience with the PM root domain, but I do have extensive experience with IOKit, so here goes:
You want IOPMrootDomain::setDisableClamShellSleep() to be called.
A code search for sites calling setDisableClamShellSleep() quickly reveals a location in RootDomainUserClient::externalMethod(), in the file iokit/Kernel/RootDomainUserClient.cpp. This is certainly promising, as externalMethod() is what gets called in response to user space programs calling the IOConnectCall*() family of functions.
Let's dig in:
IOReturn RootDomainUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
IOReturn ret = kIOReturnBadArgument;
switch (selector)
{
…
…
…
case kPMSetClamshellSleepState:
fOwner->setDisableClamShellSleep(arguments->scalarInput[0] ? true : false);
ret = kIOReturnSuccess;
break;
…
So, to invoke setDisableClamShellSleep() you'll need to:
Open a user client connection to IOPMrootDomain. This looks straightforward, because:
Upon inspection, IOPMrootDomain has an IOUserClientClass property of RootDomainUserClient, so IOServiceOpen() from user space will by default create an RootDomainUserClient instance.
IOPMrootDomain does not override the newUserClient member function, so there are no access controls there.
RootDomainUserClient::initWithTask() does not appear to place any restrictions (e.g. root user, code signing) on the connecting user space process.
So it should simply be a case of running this code in your program:
io_connect_t connection = IO_OBJECT_NULL;
IOReturn ret = IOServiceOpen(
root_domain_service,
current_task(),
0, // user client type, ignored
&connection);
Call the appropriate external method.
From the code excerpt earlier on, we know that the selector must be kPMSetClamshellSleepState.
arguments->scalarInput[0] being zero will call setDisableClamShellSleep(false), while a nonzero value will call setDisableClamShellSleep(true).
This amounts to:
IOReturn RootDomain_SetDisableClamShellSleep(io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint64_t inputs[] = { disable ? 1 : 0 };
return IOConnectCallScalarMethod(
root_domain_connection, kPMSetClamshellSleepState,
&inputs, 1, // 1 = length of array 'inputs'
NULL, &num_outputs);
}
When you're done with your io_connect_t handle, don't forget to IOServiceClose() it.
This should let you toggle clamshell sleep on or off. Note that there does not appear to be any provision for automatically resetting the value to its original state, so if your program crashes or exits without cleaning up after itself, whatever state was last set will remain. This might not be great from a user experience perspective, so perhaps try to defend against it somehow, for example in a crash handler.

sendReminderToParticipants and Localisable.strings not working

I'm trying to make GKTurnBasedMatch's sendReminderToParticipants work correctly in conjunction with Localizable.strings so as to implement 'poke' functionality whereby you poke another player to have their turn.
Here is what my Localizable.strings file looks like:
"POKE" = "%# has poked you!";
It's set to UTF-16 encoding as per below:
And here is my function call:
let messageKey = "POKE"
if opponent?.player?.alias != nil
{
match.sendReminderToParticipants([opponent!], localizableMessageKey: messageKey, arguments: [opponent!.player!.alias!]) { (error) in
if error != nil {
NSLog("Problem poking player: \(error!.description)")
}
}
NSLog("You have poked the opponent for match \(match.matchID). resetting poke date")
APPDELEGATE.pokeMgr.update(match.matchID!)
}
Localizable.strings is included in the "Copy Bundle Resources" of my build phases for my build target
I deleted the app from my device and rebuilt it cleanly.
Issue: It doesn't work! The push notification that is received simply is the default one saying "Your Turn". I must have missed something. Can someone please advise if there is something else I need to have done to make this work?

Resources