Eduard Lebedyuk · Feb 20, 2018 go to post

I'd recommend developing a REST API and Web application (you can pack it via Cordova, etc.)?

Even better, you can write a responsive web application and not bother with packers altogether. Unless you need things like camera/gps... a responsive web site is easier to develop and support.

Eduard Lebedyuk · Feb 17, 2018 go to post

Have you modified Import method in your class to show errors?

 if $$$ISOK(tStatus) { set tCounter = tCounter + 1 } else { w $System.Status.GetErrorText(tStatus) return}

Most likely you're getting an error and import stops.

Eduard Lebedyuk · Feb 15, 2018 go to post

All datatypes go through the transformation.

The collation is determined in this order:

  1. If an index definition includes an explicitly specified collation for a property, the index uses that collation.
  2. If an index definition does not include an explicitly specified collation for a property, the index uses the collation explicitly specified in the property definition.
  3. If the property definition does not include an explicitly specified collation, then the index uses the collation that is the default for the property data type.
  4. If the property data type does not include an explicitly specified collation, then the index uses namespace default collation
  5. If the namespace default collation is not specified, then SQLUPPER is used.

Documentation.

Eduard Lebedyuk · Feb 13, 2018 go to post

The general approach is:

  1. XData to ZEN objects
  2. Modify object(s)
  3. Serialize back into XData.

Sample code (SAMPLES namespace):

/// This is the Desktop Demonstration page for the Zen demonstration application.
Class ZENDemo.AutoDemo Extends %ZEN.Component.page
{

/// Class name of application this page belongs to.
Parameter APPLICATION = "ZENDemo.Application";


/// This XML block defines the contents of this page.
XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ]
{
<page xmlns="http://www.intersystems.com/zen" title="Zen Desktop Demo">
<tablePane sql="SELECT ID,Name FROM Sample.Person WHERE Name %STARTSWITH ? ORDER BY Name">
<column colName="ID" hidden="0"/>
<column colName="Name"/>
<parameter value="Z"/>
</tablePane>
</page>
}

/// Toggles visibility of the ID column.
/// w $System.Status.GetErrorText(##class(ZENDemo.AutoDemo).Modify())
ClassMethod Modify() As %Status
{
    #Dim pSC As %Status = $$$OK
    #Dim tPage As %ZEN.Component.page
    #Dim tHidden As %Boolean
    Set tSC = ..GetPageObject(, .tPage)
    Quit:$$$ISERR(tSC) tSC

    Set tHidden = tPage.children.GetAt(1).columns.GetAt(1).hidden

    Write "Old ID column hidden value: ", tHidden, !

    Set tPage.children.GetAt(1).columns.GetAt(1).hidden = 'tHidden

    Write "New ID column hidden value: ", 'tHidden, !

    Set tNewStream = ##class(%Stream.TmpCharacter).%New()
    Set tSC = ##class(%ZEN.Utils).%ObjectToXML(tNewStream, tPage)

    Write "New XData: ", 'tHidden, !
    Do tNewStream.Rewind()
    Do tNewStream.OutputToDevice()
    Do tNewStream.Rewind()

    Set tSC = ..SavePage(,tNewStream)

    Do $system.OBJ.Compile($classname())

    Quit tSC
}

ClassMethod GetPageObject(pClassName = {$classname()}, Output pPage As %ZEN.Component.page) As %Status
{
    Set tReader = ##class(%XML.Reader).%New()
    Set tSC = tReader.OpenStream(##class(%Dictionary.CompiledXData).%OpenId(pClassName _ "||" _ "Contents").Data)
    If $$$ISERR(tSC) Quit tSC

    Do tReader.Correlate("page","%ZEN.Component.page")

    #; there should only be one page defined
    Do tReader.Next(.pPage,.tSC)
    Quit:$$$ISERR(tSC) tSC
    Quit:'$IsObject(pPage) $$$ERROR($$$GeneralError,"No <page> element defined in Contents block.")

    Quit tSC
}

ClassMethod SavePage(pClassName = {$classname()}, pStream) As %Status
{
    Set tCDef = ##class(%Dictionary.ClassDefinition).%OpenId(pClassName)
    If '$IsObject(tCDef) {
        Set tSC = $$$ERROR($$$GeneralError,"Unable to open class definition: " _ tClass)
    }
    #; find XDATA Contents block
    Set tIndex = tCDef.XDatas.FindObjectId(pClassName _ "||" _ "Contents")
    If (tIndex '= "") {
        #; get XDATA as stream
        Set tStream = tCDef.XDatas.GetAt(tIndex).Data
        Do tStream.CopyFrom(pStream)
    }
    Else {
        #; create a new XData block !!!
    }
    Set tSC = tCDef.%Save()
    Quit tSC
}

}

Calling:

Write $System.Status.GetErrorText(##class(ZENDemo.AutoDemo).Modify())

Toggles visibility of the ID column.

Eduard Lebedyuk · Feb 8, 2018 go to post

Okay.

Another idea: you can define your session event class, and  set it as a session event class for your web application. That way you can track:

  • What pages and partials are requested
  • How long does the session lasts
  • How long does it take to execute the request
Eduard Lebedyuk · Feb 8, 2018 go to post

If I do a direct link without using the router it works fine

How fo you test directly? If you're doing it from a terminal, then that terminal process works as your OS user. Ensemble works under another OS user. Check that this user has access to file.

Eduard Lebedyuk · Feb 7, 2018 go to post

In what context are you encountering IDKEY?

While Primary Key, ID  and IDKEY refer to the same concept, there are some nuances:

  • Primary Key - is something that uniquely identifies a row or an object (it's usually a property or several properties)
  • ID is a default name of the Primary Key property (persistent cache classes/tables have ID property by default and it's a Primary Key) 
  • IDKEY is an index over Primary Key property (regardless of the actual property name)
Eduard Lebedyuk · Feb 6, 2018 go to post

Angular pages are often templates filled with data (via REST) and so it' possible to use that. In the root broker of the application add request tracking (who accessed what and when).

OnPreDispatch method works for that purpose.

/// This method Gets called prior to dispatch of the request. Put any common code here
/// that you want to be executed for EVERY request. If pContinue is set to 0, the
/// request will NOT be dispatched according to the UrlMap. If this case it's the
/// responsibility of the user to return a response.
ClassMethod OnPreDispatch(pUrl As %String, pMethod As %String, ByRef pContinue As %Boolean) As %Status
{
    Quit $$$OK
}
Eduard Lebedyuk · Feb 5, 2018 go to post

My assumption is that you are suggesting to define a class and create a list of objects which then get passed back to the BPL

Yes, something like that. Or you can pass only id's of saved objects back. That makes ensemble messages lighter.

Eduard Lebedyuk · Feb 3, 2018 go to post

Check this article on delegated authentication.

I am setting the software defined username in the Properties("Comment") array and wanting to reference it in the Rest Service Dispatch class.

Do you see delegated user getting created?

Properties("Comment") should be available as this user Comment property.

is there a way to return more specific messaging regarding the failure to the calling web application?

iirc both ZAUTHENTICATE main entry point and GetCredentials entry point return %Status so you can pass the error there.

Eduard Lebedyuk · Feb 2, 2018 go to post

Snapshot is a very technical abstraction.

If possible, it's better to convert it to business object(s) and pass that.

Eduard Lebedyuk · Feb 1, 2018 go to post

Use SET command:

set ^A("ID1")="Name\Abb\Map"
set ^A("ID2")="Name\Abb\Map"
set ^A("ID3")="Name\Abb\Map"

Why do you want to set classes storage directly?

Eduard Lebedyuk · Feb 1, 2018 go to post

You can use this SQL directly:

SELECT COUNT(1)
FROM Your.Table

Or if you want to pass class name as an argument, you can wrap it in SQL procedure:

Class Utils.Dictionary
{

/// Call from SQL: SELECT Utils.Dictionary_GetExtentSize('Utils.Persistent') As ExtentSize
/// write ##class(Utils.Dictionary).GetExtentSize("Utils.Persistent")
ClassMethod GetExtentSize(class) As %Integer [ SqlProc ]
{
  /// Convert class name to table name.
  /// You can skip this step if you have table name already
  #define ClassSQLTable(%c) ($$$comClassKeyGet(%c,$$$cCLASSsqlschemaname)_"."_$$$comClassKeyGet(%c,$$$cCLASSsqltablename))
  set table = $$$ClassSQLTable(class)

  /// Quoter2 is called to escape table name if required
  set table = ##class(%CSP.UI.Portal.SQL.Home).Quoter2(table)

  /// Execute dynamic SQL
  /// Really %sqlcq.<NAMESPACE>.cls<NUMBER>
  #dim rs As %SQL.ISelectResult
  set rs = ##class(%SQL.Statement).%ExecDirect(,"SELECT COUNT(1) AS extentSize FROM " _ table)

  /// Get first result
  do rs.%Next()
  set extentSize = rs.extentSize

  quit extentSize
}

}

And call like this:

SELECT Utils.Dictionary_GetExtentSize('Utils.Persistent') As ExtentSize
Eduard Lebedyuk · Jan 31, 2018 go to post

Okay, what does GUID means in your case? I get how people have GUIDs and holidays have GUIDs, but the purpose for the GUID of a person+holiday combo escapes me.

Eduard Lebedyuk · Jan 31, 2018 go to post

Can you elaborate on your data model? What are your two tables, and what information joining them  generates.

Consider the following database: it has clients and products -and each client and each product has a guid.

The join between clients and products would mean semantically - what client bought which products.

But it's probably be better to store this information in another table - orders and just add properties/fk/relationships to clients and products.

You want GUIDs - a mark of persistency,  but you want them in a transient query. I think it would be better to create another table and populate it with the relevant data and new  GUIDs and return that new GUIDs.

Another approach would be exposing hash function as an sql procedure and passing both GUIDs into it and returning a hash to a client.

Eduard Lebedyuk · Jan 31, 2018 go to post

Post your isc.REST code.

Your web app config looks ok, but you need to check that:

  • UnknowUser is enabled
  • UnknownUser can access USER namespace (if it's a dev box just give him %ALL role)
  • License consumption is not 100%
Eduard Lebedyuk · Jan 30, 2018 go to post

This is not what the customer wants. He have a use case where he only needs to be able to create and decompress raw DEFLATE-compressed content.

What's the use case?

 

Also, why use node instead of just calling zlib directly:

zlib deflate string ?level?

I'm not talking about callout here (which could also be another valid approach) but just direct $zf(-1, "zlib ...") call