Steve Pisani · Nov 26, 2018 go to post

Hi Evgeny,

Any new on when the Community Edition of IRIS will be available ?

Steve

Steve Pisani · Mar 6, 2019 go to post

Hi,

My guess is you are calling the query with more arguments than it expects on those times that it is failing..

Providing or inspecting again  the code making the call from the client side, would be a good start to fixing your issue. 

Steve

Steve Pisani · May 2, 2019 go to post

Hi,

In reading this - I'm wondering - what is the real advantage of #2 ?   

- Steve

Steve Pisani · May 27, 2019 go to post

Hi,

Can you also post a screenshot of your Web Application setup on the IRIS server that defines the rest endpoint ?

Thanks-  Steve

Steve Pisani · May 4, 2016 go to post

Hi David, 

The type %ListOfObjects implements GetAt (), not Get () to return an object from the list.

Also, this will return an object. I would have expected you to need to specify the property of the returned object, for the condition 

Something that will eventually look like

...   condition='Document.myList. GetAt(1).myProperty="AA"'

Steve

Steve Pisani · Jun 13, 2016 go to post

Yes this is a great feature to have.

Just a small clarification. To enable the alternative component to run, you need to select it (now it's configuration settings show up) and Enable it from settings

Enabling the alternative component automatically disable the previous running version and starts the one that currently selected (the one in the drop down). 

Steve 

Steve Pisani · Jun 21, 2016 go to post

Hi Scott,

As far as I'm aware, you do not need a license to use the DeepSee User Portal, as long as you don't need to use DeepSee Cubes, or DeepSee Queries (which needs to be licensed).

So if the information you want to present in a DeepSee User Portal type dashboard, can be sought directly via SQL Queries, etc - you can use iether:

(a) Ensemble DeepSee Dashboards (receiving data from Business Metric classes). See http://docs.intersystems.com/ens20152/csp/docbook/DocBook.UI.Page.cls?KEY=EGDV_bmetric#EGDV_bmetric_dashboard 

or

(b) DeepSee Dashboards based on KPI Class which are designed to use only SQL to extract a result set.

See:  http://docs.intersystems.com/ens20152/csp/docbook/DocBook.UI.Page.cls?KEY=D2MODADV_ch_kpi

Of course if you want more control over the UI, then go down the ZEN, ZEN Mojo, etc route.

Steve

Steve Pisani · Oct 5, 2016 go to post

Hi David

Defining the SQL Gateway connection is only one part of the problem solved.

Once you have ano SQL gateway defined, you can then create 'proxy' classes in Cache that  know something about, and represent the tables in the remote database system.

With these proxy classes on hand, you can then use them to query and update data as you would with any other persistant Cache vlass yout have.

Steve Pisani · Oct 27, 2016 go to post

Hi Mike. 

Have you already tried setting up  a Credentials record (with username and password paid via Ensemble > Configure > Credentials), and then specifying the credentials record which will be a setting in the Ensemble business operation ?

This has worked form me before with Web services that require basic authentication.

Regards

Steve

Steve Pisani · Oct 27, 2016 go to post

Hi Mike. 

Have you already tried setting up  a Credentials record (with username and password paid via Ensemble > Configure > Credentials), and then specifying the credentials record which will be a setting in the Ensemble business operation ?

This has worked form me before with Web services that require basic authentication.

Regards

Steve

Steve Pisani · Oct 27, 2016 go to post

Hi Mike

Have you tried setting up a Credentials  record with username and password pair in Ensemble  (Configure>Credentials) - then specifying the credentials name in Settings for the Ensemble u business operation ?

This has worked for me before for Web services that require basic authentication.

Steve

Steve Pisani · Mar 23, 2017 go to post

Hi Ryan,

The message that you pass to your SOAP-based business operation should (as you indicated in your 3rd bullet point), contain both the extracted HL7 data, and the authorization key you retrieved from the previous step.

I'm assuming your SOAP Business operation you are using in the last step has been automatically generated by the Studio wizards, so, you will have a Class Method for each web method of your SOAP service.

You need to edit the default generated versions of these methods the wizard gives you, in order to add your SOAP Header.

You can access   ..Adapter.%Client in this business operation to get access to the private instance of the web service client class, so, using

  Do ..Adapter.%Client.HeadersOut.SetAt(...)

You can set the headers for that particular message invocation.

Sincerely - Steve

Steve Pisani · May 29, 2017 go to post

Hi Vitaliy,

Can you please elaborate ?  Are you saying that Atelier does this already, and if so - what version are you using and how do I get to view this information ?

Thanks

Steve

Steve Pisani · May 31, 2017 go to post

Hi,

Upcoming 'Show Inherited' feature will definitely be welcome - thanks, and, using ctrl+space to invoke intellisense to get a list of options for class elements such as: indices, properties, methods, XData elements, Foreign Keys, Projections and Queries  does cover a lot of what is apparently missing..

I have not - however, been able to use the ctrl+<space> to list out options for Parameter of a class.

I have not managed to get Atelier to show me the parameters of a property type, (like MAXLEN, etc)

Property Title As %String (MAXLEN = 50, POPSPEC = "Title()")

This is available in Studio's Inspector window, and is very handy to have: both as users are starting out and can't remember exactly the name of a given property's parameters (VALUELIST, DISPLAYLIST, MAXLEN, etc...), but even and more so I guess, when using custom property types. 

I couldn't find a way of bringing these up.

thanks - 

Steve

Steve Pisani · Jun 7, 2017 go to post

Thank you for all the replies.

I'll take all comments on board, and attempt to set this up myself.

Steve

Steve Pisani · Jun 9, 2017 go to post

Hi,

There are a number of reasons for it.  

One that comes to mind, for example, is if your user accounts expire, (also perhaps, if you just forgot passwords, or there is some other user authentication issue) -  you will not be able to get into Cache to extend the expiry date, effectively - you will be locked out.

Starting Cache in Emergency Access Mode will allow you to specify a one-time, single-use account you can use to log into Cache, then into security settings, and, re-enable those account that should no longer be disabled.

You can then restart Cache with emergency access and you will be able to access Cache again using your newly re-enabled user accounts.

Steve

Steve Pisani · Sep 21, 2017 go to post

Hi Kenneth

Your container is based on a ubuntu distribution per the "FROM ubuntu" command in the dockerfile.

I believe ubuntu by default comes with and uses the application "apt-get" to do a similar function as yum, so, your yum commands running inside the ubuntu container you are starting with needs to retrieve yum, or instead, use apt commands.

however .. you say you believe that yum is installed because you executed:

which yum
/usr/bin/yum

- so the question is - did you execute this inside the container or outside of it, ie, on your VM.  Your VM (although running UBUNTU16.04), may have had yum downloaded into it - but the base ubuntu container you are using for your container definitely thinks you need this.

Equivalent apt-get commands you can use perhaps,  in place of the yum ones would be:

RUN apt-get update \
  && apt-get install which tar hostname net-tools wget \
  && apt-get clean all\ 
  && ln -sf /etc/locatime /usr/share/zoneinfo/Europe/Prague

Try that -

sincerely,

Steve

Steve Pisani · Nov 2, 2017 go to post

Hi Soufiane

The graphical rules editor used in the UI generates a class, and an xml block in that class, to represent your rules.

If you want a target to be different, based on some code, why don't you create multiple conditions for the different targets you have, then, base each condition on some database setting.

The rule itself remains static (except when you need to define a new target)  and it will show all the possible paths that can be taken by the rule.

Then.. programatically...you change that value in the database which is behind all the conditional statements you have and thus - programatically, you effect a target change.

The other alternative is to use a Business Process. You can send your message to be handled and routed by a Business Process in BPL.  The process, can programatically resolve the name of the target component in a context variable (let's say, context.TargetName). After context.TargetName has been programatically resolved, make a BPL Call action, and for the Call action's property "target" don't hardcode a value.

Instead supply "@context.TargetName", and the message will be sent to whatever the value of context.TargetName is at that point in time.

Steve

Steve Pisani · Nov 2, 2017 go to post

Hi,

If I understand you correctly, I think you want to know whether there is a function that you can invoke, from the condition field of a Business rule, that checks if a given date is in between two other dates.

If you want to see what functions are available, the best way is to get assistance from the Expression editor (that is invoked when you double-click in the Condition field, or click 'fx', on the main Business Rules definition window when the condition field is in focus)

Whilst in the Expression Editor, clicking 'fx' again, will list the available functions, and the editor will help you through the process of building an expression that includes the use of these functions, and wrapping them around AND, OR and other operators.

I would suspect, however, that by default, we do not supply an in-built Business Rules function for your current scenario - however - (and I think this is a powerful feature), you can create whatever custom function and add these to the ones already available.   When you create your function, this will appear in the Expression Editor and can be used just as if it came with Ensemble.

Building your own function for this purpose is easy, it is just a class that extends Ens.Rule.FunctionSet, and must follow some basic rules.  See the documentation here: 

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EGDV_adv_custom_utilfunctions 

Your function would take 3 arguments, value, date1 and date2 to determine whether the date in 'value' is in between date1 and date2.  Here I'm assuming the date format is CCYYMMDD, and I'm converting the incoming date to a numeric $horolog date, using the $zdh (or $zdateh) function.  Change accordingly if your date format is different.

Class User.CustomFunctions Extends Ens.Rule.FunctionSet
{
/// take 3 date values in CCYYMMDD format, (value, date1 and date2) and verify if value falls between date1 and date2
ClassMethod DateBetween(value, date1, date2) As %Boolean
{
if ($zdh(date1,8)<$zdh(value,8)) && ($zdh(value,8)<$zdh(date2,8)) quit 1
quit 0
}
}

Defining a class like the sample above in your Ensemble-enabled namespace and compiling it, will expose this function (and the comment after the 3 '/') to the Ensemble Expression editor like any other function that already comes with Ensemble.

Hope this helps.

Steve

Steve Pisani · Dec 11, 2017 go to post

Hi,

If you want to protect the database, start by creating a resource via the options Security Management > Resources  in  the management portal,  giving it an appropriate name that makes sense to you - for example, if your database is called "myAppDB", create a security resource "%DB_MYAPPDB".

Prefixing with '%DB' in the name is convention, not a requirement.  During setup, add a description, and, select whether by default, users have Read, Write and/or Use privileges.  

This is only the first step.  Now that you have an identifiable security asset you want to secure, you can proceed.

You need to decide how users that fall under this new role of yours, will interact with this DB, so you need to build up this role definition accordingly. Using the Security Management > Roles section, select your new role, and, add the Database resource that protects your database (in my example above '%DB_MYAPPDB'), identifying if users of this role can only READ or can also WRITE data in this database.

This action assigns the privilege for this database afforded to users who belong to this new role.

Actually working with this database, however, would require that you add some resources to this role.  For example, if these users are developers, and you want to give these developers access for development, then, add the %Development resource to your new role too.

You will also need to more than likely add a %Service_ type resource that allows users of this role service access into Cache, for example, via TELNET, or via ODBC, etc.  Your requirements will differ from others, but is Studio access is required, definitely include %Service_Object (Use).

Finally - have a look at a pre-defined Role on the system called a "%Developer" which is setup by default on most installations., and is something you can use for reference.  Have a look at this role, and its resources+permissions (privileges) you will see it has some databases under protection, and allows %Development, and a bunch of %Service_ resource types for allowing different access, as explained above.

Sincerely,

Steve

Steve Pisani · Dec 13, 2017 go to post

Hi Tom,

For much of what you requested, you should look at InterSystems IRIS's new Document database model.

The Document databases accepts and persists collections of JSON Documents inside of Cache . Additionally, it allows you to define a set of properties for documents in this document databse.  These properties correspond to elements in inserted documents. (ie, you can define the "LastName" as being the element 'lastname') - which effectively updates an index for every document added to the collection that has an element 'lastname'.

You can then perform an SQL query, using any one of these columns, like 'lastname' within the query to restrict/select the JSON documents you are after.

I think this new feature will satisfy your requirements . There is even a generic REST API that allows you to Add/Delete/Update JSON documents.

See: https://learning.intersystems.com/course/view.php?id=687 

HTH, 

Steve

Steve Pisani · Jan 16, 2018 go to post

Hi Satheesh,

ECP allows you to 'remotely mount' a database on one instance from another.  For example, let's say you define (as you have) a Cache instance with a Database 'C' and you have a second instance running Ensemble now.

When the basic ECP configuration is done, you will then be able to define a 'remote' database on the Ensemble instance - the remote database being, the 'C' database managed by the Cache instance. The contents within the 'C' database on Cache will be available as a database on Ensemble just as though it was a locally mounted one.

In this scenario, the Cache instance is the ECP Server (or ECP Database server),  and the Ensemble instance is the ECP Client (or ECP Application server instance).

The basic ECP configuration is carried our via ECP settings accessible through the management portal, and are done on the ECP client side (Ensemble side)  - see Admin-> Settings -> ECP Settings. You need to tell Ensemble where the ECP Database Server (Cache) is.

See the documentation for details, but when that is done, your Cache databases will be available to Ensemble as databases that can be defined as remote databases. 

Note: that ECP works at the database level - not at the namespace level... so the configuration of Ensemble Namespace (where you are building your production), needs to be enhanced such that the Ensemble namespaces sees the globals, packages and routines it needs. Edit the Ensemble namespace's Global, Package, and Routine mapping definitions.

Regarding SQL projections when you are on the ECP Client (Ensemble), if you do not Package Map your Class definition, then, you wont have those classes available for use. With only Global Mapping defined between your Ensemble namespace and the remotely mounted Cache database,  you will only get direct global access.

The online documentation for ECP overview is here: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GDDM_overview 

Finally, be aware that ECP is an enterprise feature, and that the corresponding license keys for your instances should include the Multi-Server feature. and be ECP capable.

Sincerely,

Steve

Steve Pisani · Feb 13, 2018 go to post

Hi,

Rather than programmatically modifying the source code and recompiling the class -  if your page already defines, by default, the full set of columns available, you can show and hide each and every one of them programmatically by manipulating the table columns collection which is already part of the DOM:

Save this class in the SAMPLES namespace.  In this example, the Buttons hide some columns and un-hide  others on client and server:

/// Created using the page template: Default
Class sp14.tableTest Extends %ZEN.Component.page
{
/// Class name of application this page belongs to.
Parameter APPLICATION;
 
/// This Style block contains page-specific CSS style definitions.
XData Style
{
<style type="text/css">
</style>
}
/// This XML block defines the contents of this page.
XData Contents [ XMLNamespace = "http://www.intersystems.com/zen]
{
<page xmlns="http://www.intersystems.com/zentitle="">
<tablePane id="tblPerson" sql="Select ID,Age,DOB,Name,Home_City,Home_State from Sample.Person" >
<column id="c0" colName="ID"  hidden="false"/>
<column id="c1" colName="Age"  hidden="false"/>
<column id="c2" colName="DOB"   hidden="false"/>
<column id="c3" colName="Name"   hidden="true"/>
<column id="c4" colName="Home_City" hidden="true"/>
<column id="c5" colName="Home_State" hidden="true"/>
</tablePane>
<hgroup width="350" align="left">
<button id="clearColumns" caption="Clear Columns (client)" onclick="zenPage.clearTableColumns()/>
<spacer width="10px"/>
<button id="basicColumns" caption="Add other Columns (client)" onclick="zenPage.addBasicTableColumns()"/>
</hgroup>
<spacer height="5px"/>
<hgroup width="350" align="left">
<button id="clearColumnsSS" caption="Clear Columns (Server)" onclick="zenPage.clearTableColumnsSS()/>
<spacer width="10px"/>
<button id="basicColumnsSS" caption="Add other Columns (Server)" onclick="zenPage.addBasicTableColumnsSS()"/>
</hgroup>
</page>
}
/// clearTableColumns
ClientMethod clearTableColumns() [ Language = javascript ]
 {
  tbl=zen("tblPerson")
  for (i 0; i tbl.columns.length; i++) {
     tbl.columns[i].setProperty("hidden",true)
   }
  tbl.executeQuery()
  return
 }
ClientMethod addBasicTableColumns() [ Language = javascript ]
 {
  // Add ID, Name, and City columns 
  tbl=zen("tblPerson")
  for (i 0; i tbl.columns.length; i++) {
  if ((tbl.columns[i].getProperty("colName")=="Name") ||
      (tbl.columns[i].getProperty("colName")=="Home_City") ||
      (tbl.columns[i].getProperty("colName")=="Home_State")) {
           tbl.columns[i].setProperty("hidden",false)
   }
 }
  tbl.executeQuery()
  return
 }
Method clearTableColumnsSS() [ ZenMethod ]
 {
    set tbl=%page.%GetComponentById("tblPerson")
    for i=1:1:tbl.columns.Count() {   
        set column=tbl.columns.GetAt(i)
        set column.hidden=1
    }
    set tbl.refreshRequired=1
    Quit
 }
Method addBasicTableColumnsSS() [ ZenMethod ]
 {
      set tbl=%page.%GetComponentById("tblPerson"
      for i=1:1:tbl.columns.Count()
          set column=tbl.columns.GetAt(i)
          if "Name,Home_City,Home_State"[column.colName {
             set column.hidden=0
          }
      }
      set tbl.refreshRequired=1
      Quit
 }
}
 

Even if your default <table> definition did not hard-code the full set of columns in the XDATA block, which you later manipulate, you can also programmatically add the table and column objects of the table, something like this - from server side instance method - 

        // Add Table of transactions
        set nTable=##class(%ZEN.Component.tablePane).%New()
        set nTable.id="tblTrans"
        set nTable.queryClass="myAccounts.QryUtils"
        set nTable.queryName="Transaction"

        // Define a column and add to table

        set column=##class(%ZEN.Auxiliary.column).%New() 
        set column.colName="Narrative"
        set column.hidden=1
        do nTable.%AddColumn(column)

        do %page.%AddComponent(nTable)

- Steve

Steve Pisani · Oct 4, 2018 go to post

Sure thing...

Use the %Net.HttpRequest class, to make the HTTP request, and, take the response's data

Set httprequest=##class(%Net.HttpRequest).%New()
Set httprequest.Server="http://ws.audioscrobbler.com"
set URL="/2.0/?method=chart.gettopartists&api_key=65218c8cdd03ba3836f9fc8491fb6957&format=json&limit=1000&page=10"
Do httprequest.Get(URL)

The httprequest object has a property 'HttpResponse', now containing the http response.  The HttpResponse in turn, has a stream property 'Data' containing the entire http response body -

so, you can read off this Data stream to get your raw json and setup jstring variable, however, as I see you want to call %db.FromJSON, and, that method takes a stream object anyway - you can skip setting up the jstring variable and just do this directly which should work: 

DO db.%FromJSON(httprequest.HttpResponse.Data)

Steve

Steve Pisani · Nov 6, 2018 go to post

Hi Eric

If you want your service t0 be part of the framework, but not actually use any specific connection functionality typically offered by adapters (SQL, FILE, ..etc)  Just ensure that the adapter is first set to  'Ens.InboundAdapter':

Parameter ADAPTER ="Ens.InboundAdapter" ;

And - set your PoolSize is set to 1, so a job is started. with the production.  Note that for every cycle of the Call Interval setting, the OnProcessInput method will be called.

If you want to regularly do your work (ie:  "go through a list of values in a global and compare dates. If criteria is met, it will send an email."),  then, do this in the OnProcessInput method at your desired CallInterval.

However - As you said "on start..." I'm assuming you meant, on start of the production as a whole -   In this case, leave the OnProcessInput method empty with just a 

Quit $$$OK

statement, and, (as others mentioned here), put the logic in the OnInit() method of your service, which will be invoked on production startup or enabling/disabling of the service.

Note that without the Adapter parameter setting above, and the pool size set to 1 - neither OnInit, nor OnProcessInput are called.

Now - Productions are meant to keep on running. You may eventually move away from putting this logic in the OnInit code or somewhere which requires a Production re-start in order to execute, as this effects other running business hosts ....  To explore other options further you can

(a) Work with the CallInterval which calls OnProcessInput after n seconds, and build in logic that determines if a particular cycle should just do nothing, or (say, on the change of the day, or other controlling factors, like, the size of your global entries) - would go ahead and do the emails.  Note that you can set Properties for your business service, to record state - which you can initialise  a value for in the OnInit, and update regularly during the running state of the service if you need to.

(b) Look at the Schedular feature.  The Schedular feature controls the running state of a business host. With the schedular you can elect to Enable/Disable any service on a pre-defined schedule. So - You can enable  your service, (with OnInit code to check globals and send emails), at an interval of choice without needing to stop/start the production. click : here for documentation.

Sincerely -

Steve

Steve Pisani · Nov 21, 2018 go to post

Hi,

No sooner have I asked the question, then I found the solution myself !.

and that is,  when added to a Production, the EnsLib.MsgRouter.RoutingEngine class that executes the rule has a production settings "Force Sync Send", to force all SENDS to be Synchronous.

Steve