ZPM requires IRIS, and this is highly unlikely to change.
- Log in to post comments
ZPM requires IRIS, and this is highly unlikely to change.
@Dmitry Maslennikov it's not actually a REST service, I just want a web application where I have full control over behavior of URLs under the application root in ObjectScript. %CSP.REST is the easiest (maybe only?) way to do that.
I ended up implementing Login as follows (which at least mostly works):
/// Called for a REST page in the event of a login being required
ClassMethod Login(skipheader As %Boolean = 1) As %Status [ ProcedureBlock = 0 ]
{
// Support including logo image (most of the time...)
Set brokerApp = "/csp/broker/"
Set brokerName = $Replace(%request.URL,$Piece(%request.URL,"/portal",1),brokerApp)
If (brokerName '= brokerApp) {
Set filename = $System.CSP.GetFileName(brokerName)
If ##class(%Library.File).Exists(filename) {
Set %response.ServerSideRedirect = brokerName
Quit $$$OK
}
}
// Redirect with trailing slash (supports above)
If ($Extract(%request.CgiEnvs("REQUEST_URI"),*) '= "/") && (%request.URL = %request.Application) {
Set %response.Redirect = %request.Application
Do %response.WriteHTTPHeader()
Quit $$$OK
}
// Suppress "Access Denied" error message
If %request.Method = "GET" {
Set %request.Data("Error:ErrorCode",1) = $$$ERROR($$$RequireAuthentication)
}
Quit ##class(%CSP.Login).Page()
}@Evgeny Shvarov as we've covered in GitHub issues, the business rule issue is a product-level issue (in the new Angular rule editor only, not the old Zen rule editor). I clarified https://github.com/intersystems/git-source-control/issues/225 re: the importable format.
The non-"wrapped" XML export format is importable by git-source-control and, I believe, IPM itself, although not by $System.OBJ.Load. It's just a matter of preference/readability. In a package manager context being loadable by $System.OBJ.Load isn't as important, and while the enclosing <Export> and <Document> tags aren't as annoying for XML files as for XML-exported classes/routines/etc., they're still annoying and distract from the true content of the document.
Also - git-source-control 2.1.0 fixes issues with import of its own export format. You should try it out. ;)
There are a few more projection examples in isc-rest. Here's my favorite:
https://github.com/intersystems/isc-rest/blob/main/cls/_pkg/isc/rest/mo…
IMO if a user shouldn't modify a method that's automatically generated, it's best to put it in a separate generated class. The above example covers that behavior.
@Kari.Vatjus-Anttila5072 I'm just seeing this now. I'm a bit mystified - currently I'm using the same flags with latest-enough IRIS and the latest package manager, and it's fine.
It's possible that the mocking framework is causing issues. It looks like it has its own unit test manager; how does that play alongside the custom TestCoverage unit test manager? Have you made further changes to TestCoverage.Manager?
This looks amazing!!
Here's a quick example. In short:
It would be nice if this was available via quick pick based on the isfs context (including the routine, not just the tag+offset).

I agree on most of this. A few comments:
"Triple slash - Great idea. TestCoverage - Great tool. Why are they not in a single "powerful" package?"
These tools solve different, distinct problems (although both are related to unit testing). Tools that solve different, distinct problems should be separate packages. If you want to write a single "powerful" package that depends on both of them and maybe adds some glue between them, feel free!
Dependencies:
Packages should use semantic versioning, and dependencies in IPM can be declared in a way that adheres to the semantic versioning contract. It's the responsibility of the dependency repo owner to follow this, and if you don't trust them you can just lock down to the version you tested with.
Also, I'm hoping to get around to reviewing your TestCoverage PR soon - just trying to deal with some CI infrastructure issues, and my day job keeps interfering. ![]()
@Stefan Cronje re: existing class definitions to Swagger, that's covered in https://github.com/intersystems/isc-rest
@Evgeny Shvarov you're correct that no further configuration is needed - although if you want to commit direct from the IDE / Web UI you should set up the username/email for attribution.
At a technical level, see: https://github.com/intersystems/git-source-control/blob/main/cls/Source…
git-source-control doesn't reference module.xml directly; there's a method in IPM to get the package to which a given "InternalName" (e.g., Foo.Bar.CLS) belongs, so it calls that.
I just got here via search. These days (but perhaps not in 2016) we support application/json and application/yaml. text/plain and other types (haven't fully enumerated them) actually work in VSCode, but not Studio. (Good reminder to not use Studio!)
See: https://docs.intersystems.com/iris20223/csp/docbook/DocBook.UI.Page.cls…
I got way too much air time last Summit. ![]()
The general pattern that I would recommend is:
https://github.com/intersystems/isc-perf-ui is a simple example of how this all fits together - see especially https://github.com/intersystems/isc-perf-ui/blob/main/module.xml.
I just used this toolset to build a small but meaningful IRIS-based application in about a week. I didn't hand-code a single REST endpoint in ObjectScript, and I got my OpenAPI spec and all my Angular services and TypeScript interfaces for free*.
Of course, if you already have a significant hand-coded REST API, this doesn't help much. For one application my team manages we've added a Forward in our main REST dispatch class to one using isc.rest and gradually migrated endpoints over to use the standardized approach.
* ok, not totally free - there's the small price of writing better ObjectScript and e.g. having methods that return a registered JSON-enabled type rather than e.g. %DynamicArray and %DynamicObject.
I ended up going with:
"Extend %Net.HttpRequest, override the Read method, and add support for read of individual chunks from an ongoing request (similar to what is already supported for WebSockets, but also on an HTTP 200 response with Transfer-Encoding: chunked)."
Hoping to get my example use case out on the Open Exchange at some point...
You can use parameters on the return type. For example:
Class DC.Demo.SqlProcCollation
{
ClassMethod Test() As %String [ SqlProc ]
{
return "Abe Lincoln"
}
ClassMethod Test2() As %String(COLLATION="SQLUPPER") [ SqlProc ]
{
return "Abe Lincoln"
}
}Given that:
select DC_Demo.SqlProcCollation_Test(),DC_Demo.SqlProcCollation_Test2() where DC_Demo.SqlProcCollation_Test() = 'ABE LINCOLN'
Returns no results
select DC_Demo.SqlProcCollation_Test(),DC_Demo.SqlProcCollation_Test2() where DC_Demo.SqlProcCollation_Test2() = 'ABE LINCOLN'
Returns 1 row
Set source = "NAMESPACE1" Set target = "NAMESPACE2" Kill ^|target|oddDEF(classname) Merge ^|target|oddDEF(classname) = ^|source|oddDEF(classname) New $Namespace Set $Namespace = target $$$ThrowOnError($System.OBJ.Compile(classname,"ck"))
EDIT: Don't do this. Everybody should ignore me and do what @Chad Severtson said instead.
This is exactly why my quick-and-dirty approach didn't (seem to?) work in a first quick attempt. ExportToStream/LoadStream it is!
(for context, this is moving class definitions over in conjunction with changing routine mappings)
The standard approach for this in ObjectScript is the $$$FormatText macro - for example:
Class Demo.Text
{
ClassMethod Sample()
{
Write $$$FormatText("Watch out %1, it's a %2!","Superman","large pizza made of Kryptonite")
}
}Results in:
d ##class(Demo.Text).Sample()Watch out Superman, it's a large pizza made of Kryptonite!
I'll add - this is particularly helpful in conjunction with localization and used heavily in IRIS' own localization of e.g. error messages.
Hi @Michael Davidovich - it's been a while! Here's a quick sample for how I'd do this:
Class Mike.Demo.REST Extends %CSP.REST
{
/// 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. In 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
{
#dim %request As %CSP.Request
Set pContinue = 0
Set version = %request.GetCgiEnv("HTTP_X_API_VERSION","unspecified; use X-API-VERSION header")
Set class = $Case(+version,
1:"Mike.Demo.v1",
2:"Mike.Demo.v2",
:"")
If (class = "") {
Set error = $$$ERROR($$$GeneralError,$$$FormatText("Invalid API version: %1",version))
// Shoud be HTTP 400, but you probably want to report this differently/better.
Do ..ReportHttpStatusCode(..#HTTP400BADREQUEST,error)
Quit $$$OK
}
Quit $classmethod(class,"DispatchRequest",pUrl,pMethod,1)
}
}
Class Mike.Demo.v1 Extends %CSP.REST
{
Parameter VERSION = 1;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/version" Method="GET" Call="GetVersion" />
</Routes>
}
ClassMethod GetVersion() As %Status
{
Write {"version":(..#VERSION)}.%ToJSON()
Quit $$$OK
}
}
Class Mike.Demo.v2 Extends Mike.Demo.v1
{
Parameter VERSION = 2;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/version" Method="GET" Call="GetVersion" />
</Routes>
}
}The full log I get (running without -d) is exactly the same as above.
I'm running a container based on intersystemsdc/iris-community:latest. Was able to get into the filesystem and found weird ??????s in permissions, similar to https://stackoverflow.com/questions/52195175/strange-file-permission-in… - I'm going to try rebuilding and if that doesn't work try restarting the docker service on the host where things aren't working.
https://stackoverflow.com/questions/72917224/ls-l-in-docker-shows-quest… has better info and I was able to work around the issue in question by doing something I don't want to admit to doing. (Ultimately the root cause is old infrastructure where I'm deploying the container.)
That's a fair question. In my specific case, I'm trying to write a tag-based CSP page that extends %CSP.WebSocket, which involves overriding instance methods.
Turns out that it's as simple as putting classmethod="0" in your <script> tag... and then getting a helpful error message on compilation that you can't actually do that in a CSP page. 😂
On the other hand, if you use:
classmethod="##(+$Piece($STACK($STACK-3,"PLACE"),"+",2)<10)##"
Then it'll happily compile instance methods, and your successor and someone in the WRC will have probably a good laugh when your application randomly breaks in 2-10 years.

This is a much cleaner option, of course. :)
(I have other constraints that led me to not go this route in the first place.)
@Michael Breen - is a 10 on the scale of 1-10 "super crazy hard" or "super crazy easy"?
@Evgeny Shvarov I have a detailed writeup here (although Dmitry already hit the important point re: IPM): https://community.intersystems.com/post/unit-tests-and-test-coverage-ob…
A few other notes:
Unit test class instances have a property (..Manager) that refers to the %UnitTest.Manager instance, and may be helpful for referencing the folder from which unit tests were loaded (e.g., to load additional supporting data or do file comparisons without assuming an absolute path) or "user parameters" / "user fields" that were passed in the call to run tests (e.g., to support running a subset of tests defined in unit test code). Sure, you could do the same thing with PPGs or % variables, but using OO features is much better.
I'll also often write unit tests that do setup in OnBeforeAllTests and cleanup in %OnClose, so that even if something goes very horribly wrong it'll have the best chance of actually running. Properties of the unit test are useful to store state relevant to this setup - the initial $TLevel (although that should always be 0), device settings, global configuration flags, etc.