go to post Eduard Lebedyuk · Jan 12, 2018 Some possible code improvements: 1. Instead of: s sc = httprequest.Post("http://127.0.0.1:57772/api/v0/bwxpert/visiocheck/account") it's enough to specify: s sc = httprequest.Post("/api/v0/bwxpert/visiocheck/account") as the rest of the address is defined beforehand. 2. Instead of: s httprequest.Authorization = "Basic X1N5c3RlbTpTWVM=" you can specify: s httprequest.Username = "_SYSTEM" s httprequest.Password = "SYS" Which is more readable 3. When specifying server, protocol in not required, incorrect even: s httprequest.Server="127.0.0.1" 4. Finally, don't use short syntax (s, d) and use one naming convention consistently - in your example some commands are in lowercase and some start with a capital letter.
go to post Eduard Lebedyuk · Jan 12, 2018 Private is not important in that case - it only affects from where method could be called. Private methods can be invoked only by methods of this class or its subclasses. The difference between class methods and instance methods is that instance methods can access object context. For example you can access current object property from instance methods. Use instance methods only when you need access to object context. In all other cases use class methods. Here's a sample code, highlighting the difference: Class Test.Person Extends %RegisteredObject { Property Name As %String; Method PrintName() { Write ..Name } ClassMethod PrintText(text) { Write text } }
go to post Eduard Lebedyuk · Jan 10, 2018 To pinpoint the error, please:Create app /csp/test with the same brokerRestart Caché and IISAfter that try to open:SMP: http://localhost/csp/sys/%25CSP.Portal.Home.zenYour old app: http:// localhost/myapi/checkYour new app: http:// localhost/csp/test/check
go to post Eduard Lebedyuk · Jan 10, 2018 %ZEN.Auxiliary.jsonProvider is available since 2009.2. You'll need to upgrade.
go to post Eduard Lebedyuk · Jan 9, 2018 Please ask this as a separate question.What, and from where do you want to kill? Why? Consider expanding your question.
go to post Eduard Lebedyuk · Jan 8, 2018 and by the way, is Set Request.Https=1 correct given that Set Request.Https=$$$YES not supported in Ensemble 2014? Yes, it's the same thing $$$YES macro resolves into 1. but I get this error : {"Errors":["The request entity's media type 'text/html' is not supported for this resource."],"StatusCode":415} Specify content type. For example: Set Request.ContentType = "application/json" or whatever content type the api requires.
go to post Eduard Lebedyuk · Jan 8, 2018 They are various jobs required for Caché to function. They run tasks, write to database, etc.Is there any specific reason you ask this question?
go to post Eduard Lebedyuk · Jan 8, 2018 Not really. KeyFieldName only affects the tracking of processed records. Example 5 shows configuration where each row is passed each time the service querying the database is ran.SQL Inbound Adapter always passes rows one by one.Your options are:Create a buffer and check if this is a last row. If it's a last row - call bulk API. If it's not a last row then add row to buffer.Call bulk API after every N (i.e. 100) of rows.Extend SQL Inbound Adapter to pass a whole resultset or to pass batches of rows.Write changes locally row by row. Create a separate service that calls bulk API from local data.What option to choose? Here are several questions, which may help you:What are the throughout rates (per second/minute/day/peaks?)?New rows in the source systemHow much rows Ensemble can processHow much requests can API process (does it matter if you pass several rows? For example you can pass 100 individual requests per second or 10 bulk requests with 1000 rows each)Administrative limits for the above-mentioned external systems?Can you normalize some of these rates? (For example you receive updates during the work hours, but send the 24/7) What's the bulk error strategy (good to bad)?You get an error list which you can easily match to individual recordsYou get an error list but you're unable to match it to the individual recordsYou get one (first/last/random) errorShould you group your records before sending (can be useful for visual tracing, pinpointing error source)?By some criteria (hospital, country, day, etc)What's the resend strategy in cause of errors?Let's say you sent a batch of 1000 records: 999 ok, 1 failed. Can you easily resend only one record? Would sending a whole batch again break data consistency or just fail altogether? Can you send the records individually?
go to post Eduard Lebedyuk · Jan 8, 2018 so is there a way to get it working with SSLConfigAs I said:Where SSLConfiguration is the name of your ssl config. You need to create a SSL config. Docs explain how. test 0 or 1 means as the doc doesn't mention it!?Test values0 - send as usual1 - do not send, output request to the current device2- send the request and output response to the current devicelocation in Post method is optional given that we set Set Request.Location = "/STP_IF/rest/Employee/Create"? Yes, it's enough to specify Location property.
go to post Eduard Lebedyuk · Jan 6, 2018 I'd move Write !?(%lev*3) into a separate method: ClassMethod Log(text) { Write !?(%lev*3), text }
go to post Eduard Lebedyuk · Jan 5, 2018 Google implies that your request is not complete.Generally, when debugging web services you should follow these steps:Have a way to send valid requests (i.e. SoapUI)Send request via Caché/EnsembleCompare (DiffDog, etc) requests from 1 and 2, here you need to:Modify valid request till it failsModify invalid request till it succeedsAlso, don't forget to send new request after each modification. Web services often have GUID/ID by which they identify incoming requests. If incoming request has the same identifier they don't execute it again, but just return cached result. Identifier can be a header or inside message body.
go to post Eduard Lebedyuk · Jan 5, 2018 Try to remove reNo altogether and get the root node from the xml document: do ..ShowNode(document.GetDocumentElement()) In GetDocumentElement method, you can see that for node to exist it needs 2 things: DocumentPointer to the exact node When you created the node manually, you've done the first but not the second.
go to post Eduard Lebedyuk · Jan 5, 2018 Use static classmethods, unless you need them to be instance methods.
go to post Eduard Lebedyuk · Jan 5, 2018 SET JArray=["somthing "] OR Set Array1 = ##class(%Library.DynamicArray).%New() SET Array1=[] so in both cases Array1 or JArray is not understanding [] so should be including or extending a classe(s) or ...etc That's available from version 2016.2. Refer to your local documentation or online documentation for 2014 version. FYI Srever name & port are not required for this Wed Service. It's not server name - it's host and it's required. You also need to specify HTTPS and SSLConfig. If I execute your code in test mode ( Set Status = Request.Post(,1) ) I receive: POST /https%3A//devtest.altus.net.au/STP_IF/rest/Employee/ HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; Cache;) Host: localhost Accept-Encoding: gzip Content-Length: 239 Content-Type: text/html; charset=UTF-8 { "Id":123, "PayerAustralianBusinessNumbe":"PayerAustralianBusinessNumbe", "PayerBusinessHoursPhoneNumbe":"PayerBusinessHoursPhoneNumbe", "PayerEmailAddress":"email", "PayerWithholdingPayerNumber":"PayerWithholdingPayerNumber" } You should probably modify your code like this this: ClassMethod Test(test As %String(VALUELIST="0,1,2") = 1) { Set Body = ##class(%ZEN.proxyObject).%New() Set Body.Id = "123" Set Body.PayerEmailAddress = "email" Set Body.PayerBusinessHoursPhoneNumbe = "PayerBusinessHoursPhoneNumbe" Set Body.PayerAustralianBusinessNumbe = "PayerAustralianBusinessNumbe" Set Body.PayerWithholdingPayerNumber = "PayerWithholdingPayerNumber" Set Request= ##class(%Net.HttpRequest).%New() Set Request.Server = "devtest.altus.net.au" Set Request.Location = "STP_IF/rest/Employee/" Set Request.Https = $$$YES Set Request.SSLConfiguration = "YourSSLConfig" Set Status = ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(Request.EntityBody, Body) Set Status = Request.Post(,test) } Where SSLConfiguration is the name of your ssl config. Afterwards with test=1 I get this: POST /STP_IF/rest/Employee/ HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; Cache;) Host: devtest.altus.net.au Accept-Encoding: gzip Content-Length: 239 Content-Type: text/html; charset=UTF-8 { "Id":123, "PayerAustralianBusinessNumbe":"PayerAustralianBusinessNumbe", "PayerBusinessHoursPhoneNumbe":"PayerBusinessHoursPhoneNumbe", "PayerEmailAddress":"email", "PayerWithholdingPayerNumber":"PayerWithholdingPayerNumber" } And the response with test=2 is as follows: HTTP/1.1 405 Method Not Allowed ALLOW: GET CACHE-CONTROL: no-cache CONNECTION: keep-alive CONTENT-LENGTH: 73 CONTENT-TYPE: application/json; charset=utf-8 DATE: Fri, 05 Jan 2018 08:16:20 GMT EXPIRES: -1 PRAGMA: no-cache SERVER: Microsoft-IIS/8.5 {"Message":"The requested resource does not support http method 'POST'."} You should check the API docs for correct location (or HTTP verb) I'm basically looking to handcraft the Body.%ToJSON() content to to be contained in [] Wrap Body in %ListOfObjects for that: Set List = ##class(%ListOfObjects).%New() Do List.Insert(Body) Set Status = ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(Request.EntityBody, List) And your request body would look like this: [ { "Id":123, "PayerAustralianBusinessNumbe":"PayerAustralianBusinessNumbe", "PayerBusinessHoursPhoneNumbe":"PayerBusinessHoursPhoneNumbe", "PayerEmailAddress":"email", "PayerWithholdingPayerNumber":"PayerWithholdingPayerNumber" } ]
go to post Eduard Lebedyuk · Jan 4, 2018 Why not?It's an oref, you can even get the object by this string if it exists somewhere in process memory, here's an example.
go to post Eduard Lebedyuk · Jan 4, 2018 passing sbJSON by reference would make this recursive construct easier to understandObject are always passed by reference.
go to post Eduard Lebedyuk · Jan 4, 2018 replacement with %ToJSON() does it as well.Is there a way to call %ToJSON, so it would return formatted output?
go to post Eduard Lebedyuk · Jan 4, 2018 Export it as XML. Store in VCS with code. Import with the rest of the code. Other option is to store it as XData, here's sample code: XData DisplayProperies { <Export generator="Cache"> <Global> <Node><Sub>^Test</Sub> <Node><Sub>1</Sub> <Data>123</Data> </Node> <Node><Sub>Val2</Sub> <Data>prop3</Data> </Node> <Node><Sub></Sub> <Data>prop1</Data> </Node> </Node> </Global> </Export> } /// Set global /// do ##class(Class.Installer).setGlobal() ClassMethod setGlobal() As %Status { #dim sc As %Status = $$$OK #dim className As %String = $classname() // search for XData: DisplayProperies #dim xdata As %Dictionary.CompiledXData = ##class(%Dictionary.CompiledXData).IDKEYOpen(className, "DisplayProperies",, .sc) quit:$$$ISERR(sc) sc set stream = xdata.Data set sc = $system.OBJ.LoadStream(stream, "/displaylog=0 /displayerror=0") quit sc }