Stephen Wilson · Oct 4, 2018 go to post

Ok so lets kill off the AKI node in the global and instansiate the object by passing in the IDKEY

k ^CODE("TNO","BIO",291,"AKI")
set myobject= ##class(Code.TestDetails).%OpenId("BIO||291")

If you do if (myobject.AKI="") what are you really asking:

1) if ($GET(^CODE("TNO","BIO",291,"AKI"))="")
2) if ($DATA(^CODE("TNO","BIO",291,"AKI"))=0)

I think it is option 1 and I was wondering if there was a $DATA operation that would work at the object level. I see %DynamicObject has a %IsDefined("propertyName") method but this is more often seen in parsing JSON objects and used to detect { "AKI": null }

Perhaps this is what I'm looking for:

Set isNullBool = ##class(Code.TestDetails).%ObjectIsNull(myobject.AKI)
zwrite isNullBool

Any other thoughts?

Stephen Wilson · Sep 14, 2018 go to post

Same idea within a shell session. I often like to evaluate expressions to check they work as expected before writing any significant code.

set val1 = 10
set val2 = 11
set operator = "<"
// No need for extra double-quotes in logical expression
set testStatement="set testResult=(val1_operator_val2)"
xecute testStatement
zwrite testResult
Stephen Wilson · Sep 11, 2018 go to post

Rather than pass the traverseRelationships Boolean to the instance method toJSON(traverseRelationships) in User.Widget, could you change URL map/URL route to use a QueryString parameter? 
eg 1. http://{{SERVER}}:{{WEB_PORT}}/widgetsdirect/rest/Stephen?traverseRelationships=true
eg2.
http://{{SERVER}}:{{WEB_PORT}}/widgetsdirect/rest/Stephen?traverseRelationships=false

How do you map URL querystring parameters to the %CSP.REST class in HTTP Get Requests?

Stephen Wilson · Aug 16, 2018 go to post

I think Kerberos is only required if you a connecting to a MS SQL Server database from a non-Windows environment using the JDBC driver.  If you are in a Windows environment you will probably use NTLMv2. SQL Squirrel can be used for troubleshooting connection strings or you can write a simple console based Java application. You might also check the account outside of using JDBC using SQL Server Management Studio. You need to verify your system requirements are correct for the JRE/JDK/JDBC version and the MS SQL Server version. Depending on your version requirements the connection string will vary.    

Stephen Wilson · Aug 1, 2018 go to post

Consider the line in C#

CACHEObject.ContainerImco containerImco = new CACHEObject.ContainerImco(cacheConnection);

Does this 'ContainerImco' object have a constructor that takes in a CacheConnection object?  Have you opened the connection?

From the error it looks like its failing when trying to get the connection. It seems a bit weird to have a collection of CacheConnection objects.

Stephen Wilson · Aug 1, 2018 go to post

Your code is quite difficult to read without proper styling. I recommend the 'Special Container'. We created a DLL from a c# class library generated from the .NET Object Binding Wizard and placed the DLL in our bin/ folder.

Assuming lcontainerImco.Connection is a CacheConnection object from 'Intersystems.Data.CacheClient'  and you have imported 'Intersystems.Data.CacheTypes'. The following should work.

CacheConnection CacheConnect = new CacheConnection();
CacheConnect.ConnectionString = server + "Port = " + connectionPort + "; " + "Namespace = USER; "
+ "Password = " + password + ";" + "User ID = " username + "; " + "pooling = false;";
CacheConnect.Open();

There is also a little known issue about certain special characters in the password causing parsing problems, which is identifiable from the following stack trace:

at InterSystems.Data.CacheClient.CacheADOConnection.createConnectionKeyString()\r\n   at
InterSystems.Data.CacheClient.CacheADOConnection.ParseConnectionStringInternal(String connectionStri
ng)\r\n   at InterSystems.Data.CacheClient.CacheADOConnection.set_ConnectionString(String value)\r\n

Bad characters in the password include pairing and delimiting characters eg.  equals sign, single-quotes and backslashes, pound sign symbol and not symbol. This was reported to WRC two years ago but never resolved, so we created a work-a-round solution.  

Refer to the documentation relevant to your Caché version for all the valid connection string parameters (I have linked the latest release) and be sure to wrap your connection in a try-catch structure.  

Stephen Wilson · Jul 24, 2018 go to post

You would probably get a syntax error if LD resolved to an empty string ("").  I would put a trace/watchpoint on LD or alternatively log its contents to a global to verify it is not null. You might also want to check the definition #define LDAPServer $Get(^OSUMCLDAP("Server")) to see if it is correct and verify whether it should or should not contain a port number.

Here's another sample SimpleBinds() operation using server ldapserver1.mycoolcompany.com port 51000

/// Return 1 if LDAP SimpleBind successful.
/// Return 0 if LDAP SimpleBind unsuccessful
ClassMethod Connect(ByRef Username As %String, ByRef Password As %String) As %Boolean {
   set userContext = "uid="_Username_",ou=aixuser,cn=aixsecdb,cn=aixdata,o=mycoolcompany_aix"
   set ldapConnection = ##class(%SYS.LDAP).Init("ldapserver1.mycoolcompany.com",51000)
   set status =##class(%SYS.LDAP).SimpleBinds(ldapConnection,userContext,Password)
   if (status=$$$LDAPSUCCESS)
   {
      write !, "LDAP SimpleBind Successful!"
      return 1
    }
   else
   {
       write !,"LDAP SimpleBind failed!"
       write !,"Error code : ",status
       write !,"Error message : ",##Class(%SYS.LDAP).Err2String(status)
       return 0
    }
}

The Init() method documentation has some good troubleshooting tips under 'Error Codes' for connecting using SSL/TLS

Stephen Wilson · Jul 4, 2018 go to post

I am enjoying this tutorial and the cursor name thing did confuse me for a little bit. In addition, you need to have

SET widgetObj = {}

somewhere in your code otherwise, it won't compile. Is there anyway to contribute on the Github page? There doesn't appear to be repo I can submit pull requests to.

Stephen Wilson · Jul 3, 2018 go to post

Minus one (-1) is commonly used for a null reference error. Verify your certificate is in the PEM format, verify it exists in the relative path defined in your global and refer to the documentation in the %SYS.LDAP.StartTLSs  and %SYS.LDAP.SetOption methods. You might want to test passing in a raw string value pointing to the certificate file, just to rule out any directory parsing errors.

Stephen Wilson · Jul 3, 2018 go to post

Note that some of attribute values are case sensitive.

INCORRECT

<Route Url="/:name" Method="GET" Call="HelloWorld" Cors="False" />

CORRECT

<Route Url="/:name" Method="GET" Call="HelloWorld" Cors="false" />
Stephen Wilson · Jul 3, 2018 go to post

My preferred approach would be to use the ZBREAK utility but you could also potentially use ^%SYS.MONLBL. You can use ZBREAK for setting breakpoints or watchpoints or tracing line-by-line execution.  You could even set it at the beginning of your ZAUTHENTICATE routine itself rather than a shell session.  There is a bug with this utility regarding the use of round-brackets for setting a group of variables to a value, which I have documented here and with WRC.

To trace every line of execution:

ZBREAK /TRACE:ALL:"/your/file/location/trace.log"

To trace when particular lines are executed

ZBREAK /TRACE:ON:"/your/file/location/trace.log"
Error^ZAUTHENTICATE:"T"
+42^ZAUTHENTICATE:"T"

To format the log file to remove blank lines for better readability.

sed  '/^$/d' /your/file/location/trace.log
Stephen Wilson · Jul 2, 2018 go to post

I think it has something to with Unix/Linux status 0 meaning the command executed successfully. Caché ObjectScript error status 0 usually means some error has occurred. $$$LDAPSUCCESS from %syLDAP.inc is defined as 0. The LDAP Error message: LDAP error: 0 - Success is partially hard-coded. Can you verify where your code is getting into the Error label?

#define LDAPSUCCESS $zhex("00")

Stephen Wilson · Jun 28, 2018 go to post

I am really intrigued by what this device is and what kind of data you are looking to capture...  Is your host device a member of a specific multicast group? In Unix 'netstat -g' can show the multicast forwarding cache and in Windows 'route print' can be useful but might only apply if IP Routing is enabled on your interface in the 'ipconfig /all' output.

In a lab environment, when you ping a multicast address you would expect devices registered with that multicast address to reply with a single unicast response. Routers can also use specific multicast addresses for routing protocols like EIGRP and OSPF.

Stephen Wilson · Jun 15, 2018 go to post

I have checked the beta version of Atelier and I cannot see any way around this. Perhaps this should be added as a new feature request? As far as I know, the Atelier-plugin is closed source so you cannot extend it. You may be able to write your own connection adapter to achieve your goal but I am only speculating.

In the 'Server Explorer View' you must specify all of the fields.  Your saved connections are also visible via the the Preferences > Security > Secure Storage then Contents tab and 'com.intersystems.atelier.connmgr'. These saved connections are mapped to your projects.

Personally, I would opt to create a master password for eclipse so that you are prompted for some kind of password during first launch. You can also tweak the encryption algorithm used to meet you security audit requirements. Alternatively, you can launch eclipse and pass in a password file and add the file location to your eclipse.ini file as described here. At least with secure storage, your passwords can be stored securely and not in plain text.

Stephen Wilson · May 8, 2018 go to post

I was looking for something like the Class Reference guide but for the Nodejs Caché Connector . You are right, the online book I linked Using Node.js with Caché does include the parameters for ip_address and tcp_port.It would be nice if I could somehow tell eclipse's intelli-sense that these options are available.

Stephen Wilson · May 4, 2018 go to post

Can you replace the path variable in the nodeTest.js script with a remote Caché server? How would you do this?

Stephen Wilson · May 3, 2018 go to post

In practice, the majority of artefacts in our Caché instances are .int files.  We have used classes for mapping globals, .NET web development and writing stored procedures. It is nice to be able to call a classmethod from within a .NET application using a tool-generated proxy class. I detest code that jumps all over the place and Xecute statements within globals. Please stop coding this madness! Let's make the world a better place...

I confess some of the .NET software we have calls a classmethod which in turn calls a legacy .int routine with a Do statement and many subsequent Do statements. Not ideal but necessary if you want to avoid re-writing everything on a massive system. You can also use ClassExplorer with your classes and the "comment-style" self-documentation for classes is useful.  Much of the code we support uses the legacy line-based syntax and there is truly no replacement for well-structured, easy-to-read code. It just makes debugging less of a headache. I love line-spacing and curly braces - the simple things you take for granted in other programming languages. I also can't see anyone doing RESTful API development without classes.

Having human-readable, intuitively named class packages rather than hundreds of obscurely named .int routines and globals makes life so much easier. Having a class in a package in a BIO namespace called BIO.Request with CRUD methods reads much better than an interactive programme invoked by do ^REQ. I also like being able to export self-contained class packages rather than trying to work out what .int files and globals I need to export from memory.

Stephen Wilson · Apr 27, 2018 go to post

Thanks for the comments Robert. It's safe to assume there is an IF statement in each inner-for  to determine whether to do anything or not. Each object in arrayOfIds needs to be opened and a Boolean check is done against a property. I haven't had a problem with the 'N' parameter. The code appears to store 'write' records in process memory before writing everything to file after the outer-for. $ZSTORAGE has been buffed to accommodate this.

// Changes the max-limit on the process memory from the default 16384 Kilobytes
set $ZS=49152

Unique file names have been achieved using the following assumptions:

1) The process will run no more than once a day
2) Append today's date in ODBC format using $zd(+$h,3) to the end of the filename.

The 'close' statement appears after the outer-for but I'm not sure if the curly braces implicitly does it anyway.

// Null check. Close if necessary. (May be implicitly closed by outter-for loop).
if $GET(file1)'="" close file1
if $GET(file2)'="" close file2
Stephen Wilson · Apr 26, 2018 go to post

If I am modifying a routine I didn't originally write then I tend to stick to the coding style the original author adopted - even it is using legacy syntax. It is good practice not to mix old and new constructs within the same code block ie. don't mix curly braces with line-oriented syntax.  If you are completely re-writing something or writing new code then I prefer curly braces.

I would have liked to re-factor some of my code better but I found the 'use for' construct breaks when you try that. In this pseudocode, I was using the 'write' statement to write to a file on the file system. There is a condition that is checked to determine whether it gets written to file1 or file2.

// Run an extract based on arrayOfIds
for arrayIndex=1:1:arrayOfIds.Count() 
{
       // Continue to loop through array of you detect an errorStatus
      continue:errorStatus'=1
      open file1:("WNS":::):10
      // Inner-for 1
      use file1 for testIndex=1:1:testSetObj.TestCollection.Count()
       {
       }
       open file2:("WNS":::):10
       // Inner-for 2
       use file2 for testIndex=1:1:testSetObj.TestCollection.Count()
       {
       }
// end outter-for
Stephen Wilson · Apr 20, 2018 go to post

Yes @Eduard Lebedyuk, that is correct. I am thinking of a MVC Core .NET web application that consumes a RESTful API in Caché Objectscript. Thanks @Robert Cemper for your post.

Stephen Wilson · Mar 23, 2018 go to post

I have used https://www.codeproject.com/Articles/63201/TelnetSocket in a .NET web application to develop a "proof-of-concept". The code makes use of the System.Net.Sockets.TcpClient .NET class to establish and manage a telnet connection. In my case, I wanted something simple to allow a an authenticated user, to execute a non-interactive shell program over telnet to change their password using an internally hosted web application (not accessible to the public Internet). This had already been done in PHP but I wanted something a bit different and something that integrated well with our existing .NET web application. The concept worked but I was never completely happy with the design approach. 

Cache Web Terminal does provide an interactive shell with intellisense but limitations mean you can't use it like you would use something like PuTTY and the current issue log presents quite a few challenges.

Stephen Wilson · Feb 19, 2018 go to post

There is currently provision for pdf, zip, tar, gz, png and jpg file types with files up to 256 MB in size.  

Stephen Wilson · Feb 19, 2018 go to post

There have been times when I have been asked to provide a 'proof-of-concept' or a 'working example' demonstrating a problem or some unexpected behaviour.  With code snippets, it is easy to embed code in a post; however, for more complex examples,  it is beneficial  to download a working example to test the behaviour out for yourself.

Here are some references to posts where I used the 'file upload' feature.

ZEN tablePane always selects the bottom row. Why?                 

The Art of Mapping Globals to Classes  (2 of 3)

Stephen Wilson · Feb 12, 2018 go to post

For reference, you can get a list of error codes from General Error Codes and SQL Error Codes .  From the error description, it seems to be complaining about the 1st parameter.

If the 'gc' object is a %SQL.Statement then the %Prepare method only takes one parameter. What happens when you pass in pQuery to the %Prepare method?

Have you tried executing the SQL in the SQL Shell or System Management Portal?

do $System.SQL.Shell()
Stephen Wilson · Jan 12, 2018 go to post

@Wolf Koelling I had the same problem but there is a work-a-round. There's a article that describes how to download a digital certificate from your CA and import it into the cacerts store using keytool.

One of the difficulties I had was I was unable to modify the cacerts file using keytool because it required admin access to make changes to a file under C:\Program Files(x86)\Java.

Fortunately, you can copy the cacerts file to your user directory and change the eclipse.ini file to reference the new location for your cacerts certificate store.

-Djavax.net.ssl.trustStore=C:/Users/user1/cacerts

Then follow the instructions to download the atelier plugin here

Your installation requirements may be different from mine, so you may want to contact Intersystems WRC for advice specific to your needs. Please understand however that it is a problem with the Java certificate store, used by your JRE, not recognising your CA to allow HTTPS traffic to download the plugin through the eclipse IDE. It is not a firewall issue.