query run from task server cannot find documents? - task

I have a XQuery defined that I can run from the Query Console(QC).
I have a task setup on the task server.
The query seems unable to find the documents searched for, so this must be a permissions problem is my guess.
The task query:
xquery version "1.0-ml";
import module namespace object = "http://marklogic.com/solutions/obi/object" at
"/ext/obi/lib/object-query-lib.xqy",
"/ext/obi/lib/object-service-lib.xqy",
"/ext/obi/lib/object-lib.xqy";
declare namespace obj="http://marklogic.com/solutions/obi/object";
declare namespace alt="http://example.com/sccs/alert";
declare namespace scc="http://example.com/sccs";
declare namespace source="http://marklogic.com/solutions/obi/source";
(: Start task :)
let $_ := xdmp:log("******* START CHECK UPDATE ALERTS TASK *********")
(: Find al active alerts :)
let $alerts := cts:search(
collection("object"),
cts:and-query((
cts:element-range-query(xs:QName("obj:type"), "=", "alert"),
cts:element-range-query(xs:QName("alt:status"), "=", "Active")
(:
cts:element-range-query(xs:QName("alt:device-id"), "=", $device-id)
:)
))
)/obj:object
let $update-object-content := <alert xmlns="http://example.com/sccs/alert">
<obj:property>
<status xmlns="http://example.com/sccs/alert">Inactive</status>
</obj:property>
</alert>
let $now := fn:current-dateTime()
(: minimum duration before alerts turns inactive :)
let $duration-min := xs:dayTimeDuration('PT25M0S') (: defines 30 minutes :)
(: loop over all active alerts :)
let $_ :=
for $a in $alerts
let $a-id := $a//obj:id/text()
let $s-id := $a//source:id/text()[1]
let $timestamp := xdmp:parse-dateTime('[Y0001]-[M01]-[D01]T[h01]:[m01]:[s01]',$a//scc:timestamp/text())
(: if Alert is older then $delta then set status to Inactive :)
let $delta := $now - $timestamp
let $upd := if ($delta > $duration-min) then
(
let $_ := xdmp:log(fn:concat("Update Alert ID : ",$a-id," to INACTIVE."))
let $detail-id := obj:add-details($a-id, $update-object-content, $s-id,())
return $detail-id
)
else ()
return $upd
let $_ := xdmp:log(fn:concat("DEBUG NUM ALERTS:",fn:count($alerts)))
return ()
Task defenition :
<scheduled-task>
<task-path>/tasks/update-alerts-to-inactive.xqy</task-path>
<task-root>/</task-root>
<task-type>minutely</task-type>
<task-period>5</task-period>
<task-database name="${content-db}"/>
<task-modules name="${app-modules-db}"/>
<task-user name="${app-name}-user"/>
</scheduled-task>
The task checks every 5 minutes if alert objects need to be set to "Inactive".
From the log Error.txt I can see the tasks runs but cannot find the docs.
2015-10-26 00:45:00.395 Info: TaskServer: ******* START CHECK UPDATE ALERTS TASK *********
2015-10-26 00:45:00.395 Info: TaskServer: DEBUG NUM ALERTS:0
I run all code in codepoint root collation but cannot find anything wrt the taskserver in this context.
The documentation on permissions needed for the user to perform a task are very cryptical:
In the Task User and Task Host fields, specify the user with
permission to invoke the task and the host computer on which the task
is to be invoked. If no host is specified, then the task runs on all
hosts. The user specified in the Task User field must have the
privileges required to execute the functions used in the module. See
Appendix B: Pre-defined Execute Privileges for the full list of
execute privileges.
Question: Why cant the app-user find the documents when on the task server?
UPDATE
Tried to get permissions on the OBI document but fail.
This is the code (as suggested by Dave)
import module namespace sec="http://marklogic.com/xdmp/security" at
"/MarkLogic/security.xqy";
declare namespace obj="http://marklogic.com/solutions/obi/object";
let $o-id := cts:search(
collection("object"),
cts:and-query((
cts:element-range-query(xs:QName("obj:type"), "=", "alert")
))
)/obj:object/obj:id/text()
let $uri := object:master-uri($o-id)
let $res := for $perm in xdmp:document-get-permissions($uri)
let $role-name := sec:get-role-names($perm/sec:role-id)
return $role-name || ": " || $perm/sec:capability/fn:string()
let $string-uri := '/marklogic.solutions.obi/object/cec48c59-a648-4da5-a758-2b6bb4065279.xml'
return xdmp:document-get-permissions($string-uri)
I am running this as admin on the QC, it returns empty sequence...

The simplest way to check the permissions part of this is to run your code in Query Console as ${app-name}-user}. Either connect to Query Console as that user, or wrap the code in an xdmp:eval() and specify the user in the options. I'd expect you'd get the same result.
This does not appear to be a problem with execute permissions, since it appears that the task did run.
In Query Console, run this code:
import module namespace sec="http://marklogic.com/xdmp/security" at
"/MarkLogic/security.xqy";
for $perm in xdmp:document-get-permissions($uri)
let $role-name := sec:get-role-names($perm/sec:role-id)
return $role-name || ": " || $perm/sec:capability/fn:string()
... substituting the URI of one of your target documents for $uri. That will tell you what roles have what permissions on that document. Now you can check whether ${app-name}-user has (or inherits) that role.

Related

How can i send a notification to alert that the task has create the table?

I've a stored procedure which contains SQL Statements to create a table.
I have put this stored procedure inside a task snowflake.
I would like to send a message/mail/notifications no matter to announce that the table is created ?
That is my code :
CREATE OR REPLACE PROCEDURE test_stored_procedure()
RETURNS VARCHAR
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
BEGIN
CREATE OR REPLACE TABLE test AS
SELECT xxx
FROM xxx;
RETURN 'TABLE test created';
END;
$$;
CREATE OR REPLACE TASK procedure_task_test
WAREHOUSE = XXXXX
SCHEDULE = 'USING CRON 30 5 * * * Europe/Paris'
USER_TASK_TIMEOUT_MS = 60000
COMMENT = 'TABLE test is updated'
AS
CALL test_stored_procedure();
How can i send a message like 'table test is created' for example ?
no matter how it is done
Thanks a lot for your help
If you really want to send a notification, you need to use External Function with e.g. AWS SES and Lambda:
https://docs.snowflake.com/en/sql-reference/external-functions-introduction.html
https://medium.com/hashmapinc/sending-email-notifications-from-snowflake-using-external-functions-4b985c182292
For easier solution you could try to log in a table and query that.
At the end of the stored procedure, add a call to send an email (new Snowflake functionality).
Right now you have:
BEGIN
CREATE OR REPLACE TABLE test AS
SELECT xxx
FROM xxx;
RETURN 'TABLE test created';
END;
Modify it to:
BEGIN
CREATE OR REPLACE TABLE test AS
SELECT xxx
FROM xxx;
call system$send_email(
'my_email_int',
'max#example.com',
'TABLE test created',
'TABLE test created body'
);
RETURN 'TABLE test created';
END;

Inserting name into database, getting korean signs as output

Trying to insert simple xml file with one row in IIB with simple message flow into Oracle XE DB. Message flow works fine and inserts data into database, but data written in db is different from starting data. For example, as I'm trying to insert my name "Dino" I'd get Korean/Japanese/Chinese signs in return.
I've tried changing XML formats thinking there might be problem, but I suppose it has to do with encoding.
Input:
Output in DB:
This is how my compute node looks like:
CREATE COMPUTE MODULE SimpleDB_mf_Compute
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
CALL CopyMessageHeaders();
-- CALL CopyEntireMessage();
INSERT INTO Database.dkralj.emp VALUES(InputRoot.XMLNSC.emp.name);
SET OutputRoot.XMLNSC.DBINSERT.STATUS='SUCCESS';
RETURN TRUE;
END;
CREATE PROCEDURE CopyMessageHeaders() BEGIN
DECLARE I INTEGER 1;
DECLARE J INTEGER;
SET J = CARDINALITY(InputRoot.*[]);
WHILE I < J DO
SET OutputRoot.*[I] = InputRoot.*[I];
SET I = I + 1;
END WHILE;
END;
CREATE PROCEDURE CopyEntireMessage() BEGIN
SET OutputRoot = InputRoot;
END;
END MODULE;
Looking at the IBM documentation for the INSERT statement in ESQL it might be worth trying.
INSERT INTO Database.dkralj(NAME) VALUES(InputRoot.XMLNSC.emp.name);
If weird things are still happening then I'd try a string constant to avoid any issues with character coding in the input message.
INSERT INTO Database.dkralj(NAME) VALUES('TheEmpValue');
Before this statement in your code
SET OutputRoot.XMLNSC.DBINSERT.STATUS='SUCCESS';
You should check for success or otherwise by using the inbuilt SQLSTATE, SQLCODE, SQLERRORTEXT to check the result of your call.
IF NOT ((SQLCODE = 0) OR (SQLSTATE = '01000' AND SQLNATIVEERROR = 8153)) THEN
-- Do something about the error.
-- The check of SQLSTATE and SQLNATIVEERROR covers warnings
-- The 8153 is for Microsoft SQL Server other databases may use a different value
END IF;
Also check the codepages aka CodedCharSetId of the source system data, the message in IIB and the default codepage of the database.
Use mqsicvp MYBROKER -n ODBC_DB_NAME to get other details about the connection you need to use -n to get the details.
Use something like DBeaver to add some data. Have a look at the datatype specified for the field.
As per your comment below and my response here is an example of a PASSTHRU statement. Note the use of the ? to avoid SQL Injection.
PASSTHRU('SELECT RTRIM(A.EMPLID) AS EMPLID,
RTRIM(A.ADDRESS_TYPE) AS ADDRESS_TYPE,
RTRIM(A.ADDR_TYPE_DESCR) AS ADDR_TYPE_DESCR,
CAST(RTRIM(A.EFFDT) AS DATE) AS EFFDT,
RTRIM(A.EFF_STATUS) AS EFF_STATUS,
RTRIM(A.ADDRESS1) AS ADDRESS1,
RTRIM(A.ADDRESS2) AS ADDRESS2,
RTRIM(A.ADDRESS3) AS ADDRESS3,
RTRIM(A.ADDRESS4) AS ADDRESS4,
RTRIM(A.CITY) AS CITY,
RTRIM(A.STATE) AS STATE,
RTRIM(A.POSTAL) AS POSTAL
FROM ADDRESS_VW AS A
WHERE UPPER(A.EMPLID) = ?') VALUES(AggrRef.EmployeeID)

Reading Program Memory In AHK

So I am working on a program to read values from a game (Mitos.is: The Game).
It is similiar to Agar.io
You have a size (mass), and I want to get the mass amount, it is a program, not an online game like Agar.io.
I have found this Auto Hotkey script:
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
ReadMemory(MADDRESS=0,PROGRAM="",BYTES=4)
{
Static OLDPROC, ProcessHandle
VarSetCapacity(MVALUE, BYTES,0)
If PROGRAM != %OLDPROC%
{
WinGet, pid, pid, % OLDPROC := PROGRAM
ProcessHandle := ( ProcessHandle ? 0*(closed:=DllCall("CloseHandle"
,"UInt",ProcessHandle)) : 0 )+(pid ? DllCall("OpenProcess"
,"Int",16,"Int",0,"UInt",pid) : 0)
}
If (ProcessHandle) && DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Str",MVALUE,"UInt",BYTES,"UInt *",0)
{ Loop % BYTES
Result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)
Return Result
}
return !ProcessHandle ? "Handle Closed:" closed : "Fail"
}
mass := ReadMemory(Address here, "Mitos.is: The Game")
MsgBox, %mass%
It works seamlessly but there is one slight problem, in Cheat Engine I took the liberty of finding the base address as shown below:
So I took the address circled here:
And inserted that into the program where it says "Address Here", correct me if this is not the right address, but when I restart the game and run my script it says "Fail", but in Cheat Engine the address is still valid. Help?
Check the address if it changes after restarting the game, or do not restart game just run the script without restart and you haven't defined bytes so try following,
ReadMemory(MADDRESS=0, PROGRAM="", BYTES=4 )
mass := ReadMemory("0x123456", "Mitos.is: The Game", "4")
"PROGRAM" should be correct window title use spy to get correct window title, address has to be in hex value i.e. "0x15B29DD0", I don't know how your cheat engine reads program memory address.

Using TASK Results in code section

Based in the users selection on the TASK Wizard page, I need to be able to use the answer to create 5-different variables/variant to use in the FILES & ICONS sections.
Examples;
1.- Results would indicate what directory to place files into.
2.- Results would also indicate what the TEXT in the Perameters would be.
Each example above would be a different variable/variant
These variants would essentially replace #define(s) variable that I am currently using.
My application is a multi-state application with each state having different support file contents, I wish to be able to use the TASK option instead of having a separate exe file for each.
Your question is too broad to cover, so I'll try to show you just a principle of getting [Files] entry DestDir parameter from script code, which is what you can apply also for [Icons] parameters. The key is to use the {code:...} constant in which you can specify a getter function declared in the [Code] section of your script. The following example shows, how to install file into 4 different directories based on selected tasks:
#define PathNone "None"
#define PathBoth "Both"
#define PathFirst "First"
#define PathSecond "Second"
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Tasks]
Name: TaskFirst; Description: "First task"
Name: TaskSecond; Description: "Second task"
[Files]
Source: "MyApp.exe"; DestDir: "{code:GetMyAppDir}"
[Code]
function GetMyAppDir(Param: string): string;
begin
// check if both tasks are selected; if yes, then assign a subfolder path defined
// by the PathBoth preprocessor variable to the Result
if IsTaskSelected('TaskFirst') and IsTaskSelected('TaskSecond') then
Result := '{#PathBoth}'
else
// both tasks are not selected, so let's check if the first one is; if yes, then
// assign the PathFirst preprocessor variable to the Result
if IsTaskSelected('TaskFirst') then
Result := '{#PathFirst}'
else
// first task nor both are selected, so let's check if the second one is; if so,
// assign the PathSecond preprocessor variable to the Result
if IsTaskSelected('TaskSecond') then
Result := '{#PathSecond}'
else
// no task is selected (this is the last possible situation), let's assign the
// PathNone preprocessor variable to the Result
Result := '{#PathNone}';
// finally prepend to the Result the {app} constant and expand all the constants
Result := ExpandConstant('{app}\' + Result);
end;
Similar you can do with many section parameters, but not all (this is quite a broad topic). Beware also, that some of the parameters are evaluated eariler (when the tasks were not yet seen by the user), some of them later. Also some of the parameters are evaluated more than once (assigned getter functions may execute more than once).
So it depends which parameters are you going to specify that way. For your mentioned [Files] section DestDir parameter and [Icons] section Parameters parameter you are fine with this approach.

Adding a mapped drive with WNetAddConnection2 is not accessible

I'm trying to map a drive using WNetAddCOnnection2 but there's something not quite right. The code that I am using from pinvoke.net and seems to work at first. If I am stepping through the code I get a 0 for a response and I am able to use System.IO.Directory.GetFiles() to inspect the new mapped drive which leads me to believe that credentials are fine.
The problem is that the drive is not available outside of the application. When I type net use from a command prompt I see the drive listed like this:
Unavailable L: \\<server>\<share> Microsoft Windows Network
When I try to access the drive I get either:
The system cannot find the drive specified.
or
The system cannot find the path specified.
Any help would be greatly appreciated.
Here's the nutshell of the code in question:
NETRESOURCE res = new NETRESOURCE();
res.iScope = RESOURCE_GLOBALNET;
res.iType = RESOURCETYPE_DISK;
res.iDisplayType = RESOURCEDISPLAYTYPE_SHARE;
res.iUsage = RESOURCEUSAGE_CONNECTABLE;
res.sRemoteName = share;
res.sLocalName = drive;
res.sProvider = null;
int iFlags = 0;
iFlags = CONNECT_UPDATE_PROFILE;
int iResult = WNetAddConnection2( ref res, psPassword, psUsername, iFlags );
The iResult always ends up equaling 0.
MSDN articles that may assist:
* WNetAddConnection2 - [http://msdn.microsoft.com/en-us/library/aa385413%28VS.85%29.aspx][1]
* NETRESOURCE - [http://msdn.microsoft.com/en-us/library/aa385353%28VS.85%29.aspx][2]
I reckon your problem is the display type where "res.iDisplayType = RESOURCEDISPLAYTYPE_SHARE". Perhaps try changing to a value of "0" (RESOURCEDISPLAYTYPE_GENERIC). So for example, what I generally use to map drives appears:
With Res
.dwScope = RES_SCOPE_GLOBALNET 'value 2
.dwType = RES_TYPE_DISK 'value of 1
.dwUsage = RES_USE_CONNECT 'value of 1
.localName = "x:" 'leave blank for no drive
.RemoteName = "\\\"
End With
lRes = WNetAddConnection2(Res, sPassword, sDomain & "\" & sPassword, RES_CNN_UPDATE_PROFILE)
If lRes = 0 Then
'Success
Else
'Error
End If
Always check your connections before & after calls from command prompt:
1a) From system making connection, list current connections:
net use
1b) From system connected too, list current sessions:
net session
To disconnect session, use API 'WNetCancelConnection2', my code following from above:
sServer = "\\\"
lRes = WNetCancelConnection2(sServer, RES_CNN_UPDATE_PROFILE, True)
If lRes `> 0 Then
'Success
Else
'Error
End If
Alternatively, simply making connections using 'net' command:
1) To map a drive letter:
net use `: \\`\` /user:`\` `
2) To map an IPC connection:
net use \\`\` /user:`\` `
Disconnecting using 'net' command:
1) Disconnecting mapped drive:
net use `: /delete
2) Disconnecting server share:
net use \\`\` /delete

Resources