I didn't know that Caché allowed to use JOINs without specifying FROM. That's really useful to know indeed.
- Log in to post comments
I didn't know that Caché allowed to use JOINs without specifying FROM. That's really useful to know indeed.
Whoops! Fixed.
HTTP Status 301 means Moved Permanently, which happens when you request a resource and the server redirects you to another (usually equivalent), since you said it's asking to use HTTPS, I suppose you haven't configured a SSL configuration on the Caché side.
Create a new SSL Configuration mark Enabled, select Type to Client, Peer certificate verification level to None, and Protocols to TLS.
Or simply fill the Name and Description, anything else is default... click Test to see if it works and Save it.
Now after instantiating the %Net.HttpRequest, you pass your instance.SSLConfiguration = "Your SSL Configuration Name" and instance.Https = 1.
This is the minimal you usually need to do to have a working SSL aware client.
If you mean testing using the SSL configuration in the Portal, when you click Test, it asks you for your server name and port. This follows the same pattern as %Net.HttpRequest, which means: don't provide the protocol http:// or https://, only your address: fantasyfootballnerd.com
For a use case, we have service hosted on AWS using HTTPS and it connects succesfully, but only if I strip the protocol from the address. Here's a feedback from our service when using the Test button (it's portuguese though ...)
Conexão SSL bem sucedida <- Indicates success on connecting.
Protocolo: TLSv1/SSLv3
Ciphersuite:ECDHE-RSA-AES128-GCM-SHA256
Peer: <- No peer certification defined, so it's empty...
Requisição: GET / HTTP/1.0 <- Request done using HTTP 1.0 protocol
Resposta: <- Response is an empty body.
Makes sense, I think it's because OUTER JOIN lifts equivalence constraints when joining.
I'll assume that your 'where' variable is already your SQL query.
set params=1
set params(1)=$lb(,1,2,3,4,5,6,7)
"Set where(16)=InstancePrefix_"PayerName IN (?)""
do statement.%Prepare(.where)
set rows = s.%Execute(params...)
Remember that you still can concatenate SQL parts, because they aren't editable by the end-user, I just don't recommend concatenating values.
Ahh, that's because you're using IN instead of %INLIST. IN doesn't expect a list, so throws an exception.
Uhmm... what could be causing this issue is that embedded list. Your %INLIST reads one list, but the result is another list.
Or maybe I'm still missing some piece of code to understand better.
Anyway according to the documentation:
SET states=$LISTBUILD("VT","NH","ME")
SET myquery = "SELECT Name,Home_State FROM Sample.Person WHERE Home_State %INLIST ?"
SET tStatement = ##class(%SQL.Statement).%New()
SET qStatus = tStatement.%Prepare(myquery)
IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
SET rset = tStatement.%Execute(states)
I assume it should be:
set params=N
set params(1)=$lb(1,2,3)
set params(2)=hypothetical param2
...
set params(N)=paramN
I can see that your returnParams is a list instead of the format that allows variable arity.
But like I said, I might be missing some piece of code to understand better.
However, since my suggestion is not a surefire tip and you're in a hurry I think you should opt-in for the fatest working solution and investigate the error cause when you have some time, just like you said.
Now I can assume that you merged your "returnParams" subscript into a variable called filterParams.
Doesn't look wrong for me, just wondering about that SIZE. Try removing it and see if it affects your results.
Also check if you modified the display mode. Along with that status variable to see if some error happened.
P.S.: It's becoming hard to type here :x
Not exactly, this following syntax is weird, but works:
try typing instance."my_property", you can even create properties and methods with "".
Extremely useful if you are working with snake_case.
Here's the proof:
USER>zn "samples"
SAMPLES>set r = ##class(%SQL.Statement).%ExecDirect(,"SELECT TOP 1 ID AS ""my_id"" FROM SAMPLE.PERSON")
SAMPLES>w r.%Next()
1
SAMPLES>w r."my_id"
1
Are you sure?
ClassMethod TestAritySum()
{
set args = 3
set args(1) = 10
set args(2) = 20
set args(3) = 30
quit $classmethod($this, "Sum", args...)
}
ClassMethod Sum(n... As %Integer)
{
set sum = 0
for i=1:1:n {
set sum = sum + n(i)
}
quit sum
}
Yes, you must use ... to inform the method that you're are using the format compatible with variable arity.
When using . instead, the compiler assumes it should index from the subscript that you provided. It's not a bug, but a unexpected behavior caused by your input as the compiler modifies the way it treats the parameter if you put ... on the declaration.
So yep, you should always use ... for passing and receiving parameters with variable arity, unless you know exactly which parameters and their passing index, you can express it using a more "literal" format: as you have noticed, right from start I defined a param variable to hold all my values, this is really useful for populating the arity from a loop.
But what if already know what to pass? You could also call the method as:
$classmethod($this, "Sum", 1,2,3,4,5,6) // up to 255 parameters.
Also, yes, it works for %ResultSet.Execute, by the way here is the method signature:
Method Execute(args...) As %Status [ PublicList = (qHandle, args) ]
Can you post the code that's generating this error?
I noticed that the error is coming from a method called File inside your class User.zKQRest.
Though it works, it's not literal: another programmer could ask himself why you tested the method %New, thus requiring to add a comment for explaining it. However if you use ExistsId, you're literally saying about testing if the class exists.
I know, it's just a detail, but still contributes for clean code directives.
That is because the CLS Package is used internally.
InterSystems uses it to verify if the method is defined or not without throwing exception.
This is probably faster than %ExistsId, because %ExistsId tries to open the instance beforehand instead.
Because 2 years ago Caché Node JShadn't an API supporting objects.
You're right, %Dictionary.CompiledClass actually overwrites the current %Persistent implementation for %Exists (which is called by %ExistsId).
%occReference.inc
Yeah, I read that a few days ago. But I noticed it doesn't mention how to handle "?".
Hmm, I see... so according to your idea, I basically have to fetch the parameters by using %request.Get.
I think that the default convention for REST usage has been exploited too much already, to the point that most would prefer to have an extra feature than follow it strictly. Even Google and Facebook do it: though they use REST most of their services, they also allow to pass query parameters.
I think it's due to their performance or readability policies.
EDIT:
By allowing query parameters, you can also prevent another rule break:
Let's say I need to pass a lot of parameters to FETCH data from a resource. As REST demand, you need to use GET verb to fetch data from the resource if you won't change it.
However if you can't use query parameters, you need to use POST or PUT to provide the payload (that modifies data) and this would break the CRUD rule as well.
This is where I would balance the rule breaks, that is, I can't really disregard breaking a rule that actually prevents me from breaking another that's even more explicit.
I agree with @Amir Samary. Even though by using inheritance you're allowed to use method generators, it's still against against the SIngle Responsibility Principle, since the code is binding directly to the persistence (which it's SRP should be to work as a storage representation).
Binding another responsibility to it could mean that sometime in the future the persistence would need to be rewritten to match the newer requirements, thus risking to break current applications and elevating technical debt.
So my golden rule is: unless you're 120% sure that the result generated by the binded class won't change, or you're 120% sure that it still submits to the SRP, only then you can "adapt" the persistent class.
But before that, I would recommend (priority order):
1 - Use an utility method that takes the binding class name and returns the JSON or vice-versa. Can take an optional configuration object or flags to overwrite and determine the serialization behavior for the current case.
2 - Create a mirror class that extends exclusively from the Adapter.
This keeps the persistent class clean and still allows it to be serialized.
Also, there's a method for doing what I suggest on 1, I think it's been discussed here already.
Is that uhmm... an empty rectangle?
Yeah, you can't upload photos directly to this forum and it is somewhat bad, even though there's a lot of image related configs that most won't even think about using...
The only way is hosting in the cloud.
Yep, now it's displaying for me too.
I could swear it was related to this widget's toolbar.
It seems they have changed it to an image for newer releases. (Even Caché is now an image instead of pure CSS).
Well, I do agree with crediting IS, however it should be something like:
"Powered by InterSystems ENSEMBLE" like a single message, since the focus is to demonstrate your work, not theirs.
Updated to use %Stream.GlobalCharacter instead, thank you.
Using eol also didn't resolved I guess. Here's the feedback:
Port: Executing hook extension OnAfterSave from the class Port.HookTest ...
Port (OnAfterSave):
Port (OnAfterSave): First lineSecond line
Port (OnAfterSave): Third line
Port (OnAfterSave):
Port: OnAfterSave returned without errors.
ClassMethod OnAfterSave(
sourceControl As %Studio.Extension.Base = "",
InternalName,
Location,
Object) As %Status
{
// ! skips the first line leaving it empty, actual message is pushed down to the second line.
// should not even print it.
write !, "First line"
// First line is here, printed along with the second.
write "Second line", !
// Third is still third line. But ! writes another empty line.
write "Third line", !
return $$$OK
}
As you suggested, I changed it to use eol:
if $isobject(content) {
do content.OutputToDevice()
do content.MoveTo(content.Size - 1)
do content.ReadLine(, sc, .isEOL)
}
if 'isEOL write !
%Status always returned $$$OK and putting -3 instead -1 provided me the same feedback. The last line is still empty.
Maybe it's something regarding the redirection routines. I can't really see that:
wnl() do output($char(13,10)_$$$FormatText("Port (%1): ", hookName)) quit
As correct, since wnl doesn't even take a parameter where I could decide what to do.
Indeed, those redirection routines aren't called anywhere. But they're redefined extensively around the Caché Class API.
As you said it's cryptic and powerful enough to dictate the buffer workflow even though you don't use it explicitly.
From my experiments, what I notice is:
wstr handles every write
wnl handles every !
wchr handles every single char
wtab handles tab usage
wff handles every form feed
As used here.