Eduard Lebedyuk · Aug 1, 2017 go to post

There are several ways to do that.

  1. Add from/to arguments to your existing REST servise, so your client asks for a data within a specified time slice.
    • Specifying no arguments yields all data
    • Specifying only from yields data starting at from and till now
    • Specifying both from and to yields only data acquired between from and to
  2. Use websockets.
Eduard Lebedyuk · Jul 31, 2017 go to post

If it's a part of Ensemble Production, you need to create Business Operation. Here's a sample BO that does POST request:

/// This operation does a POST request to a REST API and receives Auth token
Class Production.Operation.NLPAuthOperation Extends Ens.BusinessOperation
{

Parameter ADAPTER = "EnsLib.HTTP.OutboundAdapter";

Property Adapter As EnsLib.HTTP.OutboundAdapter;

Parameter INVOCATION = "Queue";

/// Get Auth token
Method GetAuth(request As Ens.Request, Output response As Ens.StringResponse) As %Status
{
    #dim sc As %Statis = $$$OK
    
    // Form request body (using Credentials)
    set input = {"user": ( ..Adapter.%CredentialsObj.Username), "pass": (..Adapter.%CredentialsObj.Password)}
    
    // Send post request
    set sc = ..Adapter.Post(.httpResponse,,input.%ToJSON())
    quit:$$$ISERR(sc) sc
    
    // Get token from response
    set token = {}.%FromJSON(httpResponse.Data).token

    //
    set response = ##class(Ens.StringResponse).%New(token)
    quit sc
}

XData MessageMap
{
<MapItems>
    <MapItem MessageType="Ens.Request">
        <Method>GetAuth</Method>
    </MapItem>
</MapItems>
}

}

If you're outside of Ensemble, you need to use %Net.HttpRequest class. Here's an example.

Eduard Lebedyuk · Jul 27, 2017 go to post

Let's say you have called this url:

http://localhost:57772/rest/users/1?fields=list

And you have this route:

<Route Url="/users/:id" Method="GET" Call="Test"/>

Then in your Test method call:

write %request.Get("fields")

it would output list.

Eduard Lebedyuk · Jul 26, 2017 go to post

Turns out, I forgot to setup DispatchClass for /passthrough  application. After setting it to  EnsLib.SOAP.GenericService, the following URL works:

http://localhost:57773/passthrough/PassthroughService/CurrencyConvertor.asmx
Eduard Lebedyuk · Jul 25, 2017 go to post

That's $$$defClassDefined(class). It shows that class definition exists (or doesn't), but it doesn't show if a class is compiled.

Eduard Lebedyuk · Jul 24, 2017 go to post

%ExistsId does not open an object for %Dictionary package. Just checks the globals (see %Dictionary.CompiledClass for example).

The fastest way to check if a class exists would be:

write $$$comClassDefined(class)
Eduard Lebedyuk · Jul 19, 2017 go to post

If you have access to Caché database you can create custom query that accepts date as an argument and based on that returns specific table. It can be used via ODBC like this:

SELECT * FROM Package.Class.MyQuery(DATE)

or

Call Package.Class.MyQuery(DATE)

Here's a sample query that returns Ids from a table or a class and has an argument - class name (or table name):

Class Utils.CustomQuery2
{

/// Return ids from a table or a class
Query GetTable(Table) As %Query(CONTAINID = 1, ROWSPEC = "Id:%String") [ SqlProc ]
{
}

ClassMethod GetTableExecute(ByRef qHandle As %Binary, Table) As %Status
{
    #Dim Status As %Status = $$$OK

    If ##class(%Dictionary.ClassDefinition).%ExistsId(Table) {
        // Got a class, we need to calculate a table name and quote it
        #define ClassSQLTable(%c)    ($$$comClassKeyGet(%c,$$$cCLASSsqlschemaname)_"."_$$$comClassKeyGet(%c,$$$cCLASSsqltablename))
        Set Table = ##class(%CSP.UI.Portal.SQL.Home).Quoter2($$$ClassSQLTable(Table))
    }
    

    Set qHandle = ##class(%SQL.Statement).%ExecDirect(,"SELECT ID FROM " _ Table)
    If qHandle.%SQLCODE'=0 {
        Set Status = $$$ERROR($$$SQLError, qHandle.%SQLCODE, qHandle.%Message)
    }
    Quit Status
}

ClassMethod GetTableFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status
{
    If qHandle.%Next() {
        // Same as in ROWSPEC
        Set Row = $Lb(qHandle.ID)
    } Else {
        /// No more data
        Set AtEnd = 1
        Set Row = ""
    }
    Quit $$$OK
}

ClassMethod GetTableClose(ByRef qHandle As %Binary) As %Status
{
    Kill qHandle
    Quit $$$OK
}

}

Call samples from ODBC:

SELECT * FROM Utils.CustomQuery2_GetTable('Cube.Cube.Fact')
Call Utils.CustomQuery2_GetTable('Cube_Cube.Fact')

Code on GitHub.

Eduard Lebedyuk · Jul 18, 2017 go to post

You can use %Dictionary package to do that. Here's a method that sets selectivity of a specified class/property (assuming Default storage) to an arbitrary value:

/// w $system.Status.DisplayError(##class(User.Selectivity).ModifySelectuvity())
ClassMethod ModifySelectuvity(class As %Dictionary.CacheClassname = {$classname()}, property As %String = "field1", selectivity As %Integer(MINVAL=0,MAXVAL=100) = {$random(101)}) As %Status
{
    #dim sc As %Status = $$$OK
    set id = $lts($lb(class, "Default", property), "||")
    set strategy = ##class(%Dictionary.StoragePropertyDefinition).%OpenId(id)
    set strategy.Selectivity = selectivity _ ".0000%"
    set sc = strategy.%Save()
    quit:$$$ISERR(sc) sc
    
    set sc = $system.OBJ.Compile(class)
    quit sc
}
Eduard Lebedyuk · Jul 18, 2017 go to post

Here's a code snippet:

// Really %sqlcq.<NAMESPACE>.cls<NUMBER>
#dim rs As %SQL.ISelectResult
set rs = ##class(%SQL.Statement).%ExecDirect(, "SELECT * FROM Sample.Person")
//set rs = ##class(Sample.Person).ExtentFunc()
//do rs.%Display()

while rs.%Next() {
    write rs.ID,!
}
Eduard Lebedyuk · Jul 18, 2017 go to post

Why do you want to change selectivity?

You can run TuneTable to recalculate selectivity.

Eduard Lebedyuk · Jul 18, 2017 go to post

You can use method generators:

ClassMethod OnCompile() [ CodeMode = objectgenerator ]
{
    do $system.OBJ.Compile("class")
    quit $$$OK
}

If method generator produces no code, then it would not be created, only ran at compile time.

Eduard Lebedyuk · Jul 18, 2017 go to post

That would send request to the remote server using http. To use https, try adding:

Set aa.Https = $$$YES
Set aa.SSLConfiguration = "NameOfSSLConfiguration"

You'll also need client SSL Configuration, here's documentation.

Eduard Lebedyuk · Jul 18, 2017 go to post
set stream = aa.HttpResponse.Data
while 'stream.AtEnd {
    write stream.Read($$$MaxStringLength)
}

Read argument is the number of charachers to read from the list. 32000 by default.

Eduard Lebedyuk · Jul 17, 2017 go to post

Do you sign \r\n on Solaris? In your previous post you wrote that you need to sign:

{Date}{newline}{Password}{newline}{etc}{Message Body}

Maybe {newline} can only be \r\n?

 

Also, compare (using hex compare tool) {Message Body} you sign and {Message Body} you actually send. It seems like in your case they should be identical (that is not always the case).

Eduard Lebedyuk · Jul 17, 2017 go to post

To convert to Big Endian and Little Endian you can use $zcvt:

>zzdump $zcvt("12","O","UnicodeBig")
0000: 00 31 00 32                                             .1.2
>zzdump $zcvt("12","O","UnicodeLittle")
0000: 31 00 32 00                                             1.2.
Eduard Lebedyuk · Jul 17, 2017 go to post

It depends on the signing method.  I'd try to sign a stream without new lines at all.

Do you know if openssl uses the line-end to know up the where to sign and does it line by line, or does it include the line endings in the value that should be signed?

openssl signs incoming byte stream, not a character stream, so there's no distinction.

Eduard Lebedyuk · Jul 16, 2017 go to post

It depends on a queries you want to run. For example you want to get all parents' ids which have a child with a specific property value. In a child class you can index for one or several properties, and execute this query:

SELECT parentId
FROM child
WHERE propertyA = 'value'

Documentation on indexes.

Or you can define a computed property in your parent class and index that.

Eduard Lebedyuk · Jul 14, 2017 go to post

I thought you needed an alphabetical consumption. What order do you need?

You can get one of these fairly easy:

  • Name - the name of the file (the default)
  • Type - file type
  • DateCreated - the date the file was created
  • DateModified - the date the file was last modified
  • Size - the file size
Eduard Lebedyuk · Jul 14, 2017 go to post

Pool size = 1 should be enough.

Call stack:

  • EnsLib.File.InboundAdapter class, OnInit method
  • EnsLib.File.Common class, DeepList query
  • EnsLib.File.Common class, FileList query
  • %File class, FileSet  query (without providing sortby argument)

FileSet  query then sorts by filename in lowercase (with added whitespace in the beginning).

Eduard Lebedyuk · Jul 14, 2017 go to post

Code should be stored in source control and there should be some sort of installer that does all required steps to get an application running.

That said,  ^%qCacheMsg is stored in CACHELIB, which is Read-Only by default. <PROTECT>  means either read or write error, so you need to add roles to your current user and/or make  CACHELIB writable temporaly.

Eduard Lebedyuk · Jul 14, 2017 go to post

Thank you.

I thought about using prepare, but I'm searching for solutions without it.

"ON AXIS" should work

Eduard Lebedyuk · Jul 14, 2017 go to post

Object serialized to $lb does not contain metainformation about properties, etc.

You can write a method generator that would iterate over storage schema and generate a method that gets id of object as an input, retrieves $lb value and outputs it as JSON.