You're running into the fact that calling %Get on a dynamic object doesn't return an undefined when data at the index you specify doesn't exist.  Instead, it returns "".

Both the answers you have here are great if you need a generalized solution.  If all you're looking for is a simple work around in a non-general case, all you need to do is check if your %Get returned "",  and if so, kill the value before you pass it in:

 ClassMethod passIncompleteJSON()
{
  set json = ["arg1 val", "arg2 val"]
  set arg1 = json.%Get(0)
  kill:(arg1="") arg1
  set arg2 = json.%Get(1)
  kill:(arg2="") arg2
  set arg3 = json.%Get(2)
  kill:(arg3="") arg3
  write ..threeArgs(.arg1, .arg2, .arg3)
}

ClassMethod threeArgs(a, b, c)
{
  set:($get(c)="") c = "default"
  return a_"."_b_"."_c
}

Just be sure to pass in the parameters by reference since you're going to be passing in undefined variables.

(This does remove the distinction between undefined and "" though; you wouldn't be able to intentionally pass "" with this solution)

I'm building classes which contain representations of REST resources.  For example, a full (likely for administrators) representation of the "person" resource might contain all fields, and all of them are writable.  A limited (maybe authenticated, but non admin) representation might contain most fields, and some like name and email are writable, but others like employment status are not.  A minimal (maybe for unauthenticated users) representation would contain only a few, read-only fields.

The property parameters would be things like the name for the property we use in the JSON, whether the field is writable, other flags regarding how to represent relationships or object properties, etc.

I think what I'm imagining is very similar to the %XML.PropertyParameters class used by %XML.Adaptor

(If you're probably wondering why I didn't include all this in my original post, I've been discussing it with Tim Leavitt and my ideas are a lot more formulated than they were 2 hours ago.  I suspect that an analogous architecture to what's used with XML will work well.)

Oh okay, I see.  JSON_ARRAYAGG takes whatever results it gets back, and puts them into a JSON array.  Since JSON_OBJECT returns a bunch of results that are json objects, using JSON_ARRAYAGG(JSON_OBJECT( says to take all those json objects, and put them into a json array.

As far as the non select part of the query, it looks like you can specify it with a new "select", or without.  I.e.:

SELECT JSON_ARRAYAGG(JSON_OBJECT('state':Home_State)) FROM Sample.Person WHERE Home_State %STARTSWITH 'A'
SELECT JSON_ARRAYAGG(JSON_OBJECT('state':Home_State)) FROM (select state from Sample.Person WHERE Home_State %STARTSWITH 'A')

Thanks a lot for taking the time to explain.  It's very clear to me what is happening now.

Thanks a lot.  This worked.

I found the page about this function for anyone who sees this later:

https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page...

Would you mind explaining how exactly this particular line is working?  Why do you have to use both JSON_ARRAYARG and JSON_OBJECT and what does that syntax mean?  It looks like it's essentially "selecting" the data from one result set into another as JSON, but I'm not sure how.  What does the JSON_OBJECT call do here, and what does the JSON_ARRAYAGG call do?

Ah, using base-href is a great idea, thanks.  A few questions about this:

Why did you rename the files to .csp?

Why are you using ahead of time compilation?

By using:

ng build --base-href = /webapp_name/index.html

I was able to access <cname>/webapp_name/index.html successfully; the index.html file could find the scripts like polyfills.js without me modifying those src arguments by hand.

I'm guessing you're looking for the %%ID special reference.  Something like this:

Class Testing.TestingCalcProps Extends %Persistent
{

Property user As %String;

Property a As %String [ Calculated, SqlComputeCode = {set {*} = {user}_{%%ID}}, SqlComputed];

}

I'd recommend the Calculated keyword, as elements only gain id's after they're saved, which could perhaps lead to inaccurate values in the calculated property before the saving happens.

Here's the resource which talks about these properties: https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...

And here's the one that mentions %%ID: https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...

The post build script executes on the node that the Jenkins build ran on.  Therefore, that node machine is “local” for the purposes of copying remote files, and the machine where the XML file you want to copy lives is the “remote” machine.

You’ve probably seen this, but here’s the documentation for the Secure Copy Protocol (scp) command:

https://linuxize.com/post/how-to-use-scp-command-to-securely-transfer-files/

The relevant form for you should be:

scp user@hostOfFileToCopy:/path/to/fileToCopy /path/to/newCopy

“user” needs to be the name of a user who has permissions to copy the file. hostOfFileToCopy identifies the machine on which that XML file lives (could be an IP address). /path/to/fileToCopy is the absolute path, on that machine, of the file you want to copy.

/path/to/newCopy is the absolute path for the destination of the copy on the machine the Jenkins build is running on, not just the relative path from within the Jenkins workspace.  For example, while your path within the Jenkins workspace might be “/ReportFiles/Results.xml", the absolute path on the machine is probably something like “/Jenkins/workspace/ReportFiles/Results.xml”

Hope this helps.

If you're curious, this is why what you tried originally got you unexpected results.

You passed the embedded sql statement an array with a missing subscript,  :IDArray(), hoping it would put your first result in IDArray(1), your second in IDArray(2), etc.  The problem with that is that a single embedded sql statement like the one you used will only ever return one result row: the first row that matches the criteria.  So what you were likely seeing is your first valid ID stored in IDArray(1), and none of the other ones you wanted present.

So why can it accept an array as an output argument at all, then?  When embedded sql sees an array as an output argument, it gives that array a subscript for each of the columns in the result row.  So IDArray(1) wasn't the first ID, but rather the first column of the returned sql row.  For example, &sql(select id, name, age into :result() from Person) would return the id into result(1), name into result(2), and age into result(3).

Hope this helps.  Good luck!

It appears that, for whatever reason, the System files are not automatically included for every class you write in Cache 2010.  When a class inherits from %RegisteredObject, you don't need to worry, because they'll be included via inheritance.  For other classes, like the one where I was seeing this issue, the system files are unavailable unless included by hand.

Adding Include.%occStatus to the top of the non-compiling classes fixes the issue for the $$$OK macro, although a better practice is to use Include.%occInclude, to get some other standard stuff with the status routines.

<?xml version="1.0" encoding="UTF-8"?>
<Export generator="Cache" version="25">
<Class name="CACHE2010.Build">
<TimeCreated>64952,32335.711823</TimeCreated>

<Parameter name="SrcVer">
<Description>
Location and Revision of this file in Perforce (Auto-updating)</Description>
<Default>$Id: //custom_ccrs/_common/tools/CCRToolsBuild/BuildManifests/Cache2010/Build.xml#1 $</Default>
</Parameter>

<XData name="BuildManifest">
<Description>
This is a build manifest. Here we describe build steps in XML format.</Description>
<Data><![CDATA[
<Manifest>

<!-- Change into namespace -->
<Namespace Name="CCRClientTools" Ensemble="0" Code="CCRTOOLS-CODE" Data="CCRTOOLS">
<Configuration>
<!-- Have  database for code -->
<Database Name="CCRTOOLS-CODE" Dir="c:\intersystems\cache\mgr\ccrtools-code\" Create="overwrite" InitialSize="200" ExpansionSize="60" />
</Configuration>

</Namespace>

</Manifest>
]]></Data>
</XData>

<Method name="Build">
<Description>
This method is called to pass parameters into generated build method</Description>
<ClassMethod>1</ClassMethod>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
     Set pLogLevel=3
     Set sc=..RunBuildManifest(.vars, .pLogLevel)

     Quit sc
]]></Implementation>
</Method>

<Method name="RunBuildManifest">
<Description>
This is a standard %Installer method generator whose code is generated by XGL.</Description>
<Internal>1</Internal>
<ClassMethod>1</ClassMethod>
<CodeMode>objectgenerator</CodeMode>
<FormalSpec><![CDATA[&pVars,pLogLevel:%Integer,pInstaller:%Installer.Installer]]></FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
     #; Let our XGL document generate code for this method.
     quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "BuildManifest")
]]></Implementation>
</Method>
</Class>
</Export>