Eduard Lebedyuk · Jan 25, 2018 go to post

You can use Ens.Response class for an empty response and you need to init it:

Method OnMessage(request As Ens.StreamContainer, Output response As Ens.Response) As %Status
{
    set response = ##class(Ens.Response).%New()
    quit $$$OK
}

If you only want to see streams why not use <trace> in BP?

Eduard Lebedyuk · Jan 24, 2018 go to post

Postconditionals are to be used only for the most simple checks, for example:

set:a="" a=10

instead of

if a="" {
  set a=10
}

But several conditions (or several lines inside) should be as if, I agree.

Eduard Lebedyuk · Jan 24, 2018 go to post

For one-line if I prefer postconditionals:

set:a b=a

 

As for For - the option with braces. It's more readable. And one line inside For loop quickly and often becomes several lines, so braces become mandatory anyway.

Eduard Lebedyuk · Jan 24, 2018 go to post

Why do you need to check it at runtime? If source and target are from different but related  classes you can copy by their common ancestor (or by specified ancestor class). Or do you copy properties between two unrelated classes?

Eduard Lebedyuk · Jan 24, 2018 go to post

Nice example of using method generators.

Why do you need ExistsProperty? Here's a check I usually employ for class properties in generators:

if prop.Internal || prop.Calculated || prop.ReadOnly || prop.Private || prop.Identity || prop.MultiDimensionalCONTINUE

It filters out all systems properties like Concurrency, %%OID and so on.

Eduard Lebedyuk · Jan 23, 2018 go to post

When talking about speed, one of the most important questions is what exactly do you need to speed up? You need to determine:

  • what queries are the most popular
  • what queries take the most time

to fix it. Some ways to fix performance are (easy to hard):

  • Add indices
  • Change classes
  • Change application architecture

Because in your case I see at least two request types which require different actions to speed them up:

  • Get last X changes
  • Get last X modified subjects
Eduard Lebedyuk · Jan 22, 2018 go to post

Header itself is another layer because it's meta information for the BPL state machine, which you're inside of. So I don't see how you can get header Id from inside the state machine without violating abstractions in place.

Still, %Context, %Process and %LastError are documented and acceptable to use.

Eduard Lebedyuk · Jan 22, 2018 go to post

You can access %Ensemble("%Process") as ..%Process so:

..%Process.%PrimaryRequestHeader.%Id()
Eduard Lebedyuk · Jan 19, 2018 go to post

Why would you want to do that?

Here's sample production class:

Class Test.Production Extends Ens.Production
{

XData ProductionDefinition
{
<Production Name="Test.Production" TestingEnabled="true" LogGeneralTraceEvents="true">
  <Description></Description>
  <ActorPoolSize>1</ActorPoolSize>
ion>
}

}

Ens.Config.Production serialized is XData ProductionDefinition and not the class itself.

To create production class automatically you need to:

  1. Create %Dictionary.ClassDefinition object for your test production
  2. Create Ens.Config.Production object
  3. Create %Dictionary.XDataDefinition
  4. Serialize (2) into (3)
  5. Insert XData (3) into (1)
  6. Save and compile (1)
Eduard Lebedyuk · Jan 19, 2018 go to post

Use any of the existing Git hooks for Studio or Atelier.  They would work with GitHub and GitLab both.

Eduard Lebedyuk · Jan 18, 2018 go to post

Check EvaluateRules method of Ens.Rule.Definition class. It evaluates rule by name and context. You can call it from Studio or Terminal.

Eduard Lebedyuk · Jan 17, 2018 go to post

Got it. Usually after enabling HTTPS I add server-wide redirect from HTTP to HTTPS. So in cube I specify port 80 and it automatically redirects to HTTPS.

Eduard Lebedyuk · Jan 17, 2018 go to post

If Caché does not work through IIS, you need to configure it.  On Windows you can configure Caché to work with IIS, Apache or Nginx.

After you have configured the web server to work with Caché, add https support.  For example use Let's encrypt as a CA.

Eduard Lebedyuk · Jan 17, 2018 go to post

You can use DATEPART function:

w $SYSTEM.SQL.DATEPART("year", $h)
>2018
w $SYSTEM.SQL.DATEPART("month", $h)
>1

The advantage of this approach is more readable code.

Eduard Lebedyuk · Jan 17, 2018 go to post

while i was using your code, i am getting this error in response, which says "read" method is unknown

You're getting this error because list of objects does not have this method. Check your list of objects for contents. Seems like json is already converted so you don't need to do that.

Eduard Lebedyuk · Jan 16, 2018 go to post

For SQL, use LAST_DAY function - it's a date function that returns the date of the last day of the month for a date expression:

SELECTLAST_DAY('2004-02-25')

It's also available in Caché ObjectScript:

Write $SYSTEM.SQL.LASTDAY("2004-02-25")
Eduard Lebedyuk · Jan 16, 2018 go to post

Convert json to proxy object (list of in your case) and iterate over it

set json = "[{""Name"":""bat""},{""Name"":""Cat""},{""Name"":""rat""},{""Name"":""mat""},{""Name"":""hat""},{""Name"":""chat""},{""Name"":""please""},{""Name"":""help""},{""Name"":""me""},{""Name"":""in""},{""Name"":""getting""},{""Name"":""the""},{""Name"":""value""},{""Name"":""of""},{""Name"":""nameFromObjectInsideArray""}]"
set sc = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(json,,.obj,1)
for i=1:1:obj.Count() { 
    write obj.GetAt(i).Name,!
}

To get json string from  request:

set json = %request.Content.Read($$$MaxStringLength)

You may also need to convert request into UTF8:

set json = $ZCVT(json,"I","UTF8")
Eduard Lebedyuk · Jan 16, 2018 go to post

Sorry I'm lost here. and not sure what you in 1,2 or 3 !?

It's an iterative process to pinpoint and remove errors. You start by establishing a channel which can send valid requests. For example using Postman. Next you compare valid request (which you can now consistently send) with your request which does not pass. Next you iteratively break valid request or fix failed request or send failed request via Postman. Sooner or later you understand what causes your request to fail and fix that.

the other difference is that Now they also require a Header, before they they only had a Body in their API.  

You need to add all required headers via SetHeader method.

I think my date is already like that!? do you mean removing the double quotes or changing it to YYYY-DD-MM?

You're sending:

"PaymentRecordTransactionD":"PaymentRecordTransactionD"

But API expects:

PaymentRecordTransactionD": "2014-12-31"

Looks different for me.

Eduard Lebedyuk · Jan 16, 2018 go to post

The error says parse error, but in this case I think it means either datatype validation (probably)  or fixed property order (less probable, but that can actually happen). My default advice in cases like this is as follows:

  1. Set up a way to send valid requests
  2. Send request via Caché/Ensemble
  3. Compare (DiffDog, etc) requests from 1 and 2, here you need to:
    • Modify valid request till it fails
    • Modify invalid request  till it succeeds

In your case the first modification I would do is replacing dates with real dates, for example with 2014-01-11.

Eduard Lebedyuk · Jan 15, 2018 go to post

Doubtful. Web browsers security policies probably forbid this kind of thing.

Why do you need that?

Eduard Lebedyuk · Jan 15, 2018 go to post

Well, yes, it returns the sender of the message. If you're sending message from BS to BO directly sender would be BS. If you're sending BS-BP-BO, for BO the sender would be BP because it is.

If you want to get BS from BO in BS-BP-BO you can implement it via several approaches:

  1. Add this info to BP-BO message.
  2. You have BP-BO header, use it, SessionId and BusinessProcessId to locate  BS-BP header in the header table.
  3. If you have unique identifier for BP-BO message and you know it in BS you can store this information during BS processing (to a temp holder class) and then retrieve it from BO by the identifier.

I'd like to add that second approach without careful planning and optimizing may negatively impact performance  if stored messages are in millions. Starting new SessionId for each new message sent from BS may help. (because BS MessageId would then be equal to SessionId in BO).

Eduard Lebedyuk · Jan 12, 2018 go to post

I think that's for services only. They store information about processed rows in: these 2 globals

$$$DoneRowTable(key)
^CacheTemp.Adapter.sqlrow(..BusinessHost.%ConfigName, ..%InstKey, key)

Where DoneRowTable macro is resolved into

^Ens.AppData(..BusinessHost.%ConfigName, "adapter.sqlrow", key)

You can check if there's anything for your BO.

The globals are set in OnTask method of inbound adapter.

Eduard Lebedyuk · Jan 12, 2018 go to post

What happens if you don't declare a Persistent value when you call ExecuteQuery()?

What is " Persistent value"?

What does Ensemble set as the key value for your query?

What is "key value"?

That said, if you execute the same query against external database, and the data in the external database does not change you should get the same results.

After you finish working with resultset the first time (and before creating it a second time) you can call:

Set sc = rs.Close()

Maybe that would help.