You can get all globals used by classes by running these two queries:

SELECT 
  CounterLocation, 
  DataLocation,
  ExtentLocation, 
  IdLocation, 
  IndexLocation,  
  StreamLocation, 
  VersionLocation
FROM %Dictionary.StorageDefinition

And:

SELECT 
  Location
FROM %Dictionary.StorageIndexDefinition

Are the files in a local folder? 

I think the simplest would be customizing EnsLib.File.PassthroughService to drop all files except for the last.

Or writing  a new adapter/BS which does it for you.

You can run irissession:

C:\InterSystems\IRIS\bin\irissession.exe iris -U "%SYS" "classmethod"

As for EmbeddedGit you can commit using EmbeddedGit and deploy using gitlab runners. I suppose you can call EmbeddedGit methods to import code, probably SourceControl.Git.Utils:ImportAll.

I'd use a Business Operation approach. Did it for Community Python Gateway, and it worked pretty well.

Is your BS enabled via SDS by any chance? If yes, can you try enabling it directly and remove Enabled SDS to test?

WebServerPort=0 only stops internal apache which has been removed many years ago. It does not affect an external webserver.

Change it in CSP.ini, leaving only your web app entry.

I suppose you can do it via Merge CPF too if you really want:

[Actions]
ModifyApplication:Name=/api/atelier,Enabled=0
ModifyApplication:Name=/api/deepsee,Enabled=0
ModifyApplication:Name=/api/docdb,Enabled=0
ModifyApplication:Name=/api/healthshare-rest/hssys,Enabled=0
ModifyApplication:Name=/api/iam,Enabled=0
ModifyApplication:Name=/api/iknow,Enabled=0
ModifyApplication:Name=/api/interop-editors,Enabled=0
ModifyApplication:Name=/api/mgmnt,Enabled=0
ModifyApplication:Name=/api/monitor,Enabled=0
ModifyApplication:Name=/api/security-config,Enabled=0
ModifyApplication:Name=/api/uima,Enabled=0
ModifyApplication:Name=/csp/broker,Enabled=0
ModifyApplication:Name=/csp/documatic,Enabled=0
ModifyApplication:Name=/csp/fhir-management,Enabled=0
ModifyApplication:Name=/csp/fhir-management/api,Enabled=0
ModifyApplication:Name=/csp/fhirsql,Enabled=0
ModifyApplication:Name=/csp/fhirsql/api/repository,Enabled=0
ModifyApplication:Name=/csp/fhirsql/api/ui,Enabled=0
ModifyApplication:Name=/csp/healthshare,Enabled=0
ModifyApplication:Name=/csp/healthshare/hcc,Enabled=0
ModifyApplication:Name=/csp/healthshare/hcc/bulkfhir,Enabled=0
ModifyApplication:Name=/csp/healthshare/hcc/bulkfhir/api,Enabled=0
ModifyApplication:Name=/csp/healthshare/hcc/services,Enabled=0
ModifyApplication:Name=/csp/healthshare/hssys,Enabled=0
ModifyApplication:Name=/csp/healthshare/hssys/app,Enabled=0
ModifyApplication:Name=/csp/healthshare/hssys/app/api,Enabled=0
ModifyApplication:Name=/csp/healthshare/hssys/services,Enabled=0
ModifyApplication:Name=/csp/hscustom,Enabled=0
ModifyApplication:Name=/csp/hssys,Enabled=0
ModifyApplication:Name=/csp/oauth2-client/api,Enabled=0
ModifyApplication:Name=/csp/oauth2-server/api,Enabled=0
ModifyApplication:Name=/csp/sys,Enabled=0
ModifyApplication:Name=/csp/sys/exp,Enabled=0
ModifyApplication:Name=/csp/sys/mgr,Enabled=0
ModifyApplication:Name=/csp/sys/oauth2,Enabled=0
ModifyApplication:Name=/csp/sys/op,Enabled=0
ModifyApplication:Name=/csp/sys/sec,Enabled=0
ModifyApplication:Name=/csp/test,Enabled=0
ModifyApplication:Name=/csp/user,Enabled=0
ModifyApplication:Name=/isc/studio/rules,Enabled=0
ModifyApplication:Name=/isc/studio/templates,Enabled=0
ModifyApplication:Name=/isc/studio/usertemplates,Enabled=0
ModifyApplication:Name=/ui/interop,Enabled=0
ModifyApplication:Name=/ui/security-config,Enabled=0

What's your Business Process class?

If you're using a message router (derived from EnsLib.MsgRouter.RoutingEngine), check Response From, Response Target Config Names, and Forward Generated Response To Target(s) settings. Although if called in a sync way, it should return a response to the caller anyway.

If you're extending an Ens.BusinessProcess, you can populate a response in any On* method (OnRequest, OnResponse, OnComplete, etc.)

If you're writing a BPL process, you should be able to access a response object everywhere.

First ^UnitTestRoot must be set to a valid directory. This resolves the initial error of: 

LogStateStatus:0:Finding directories: ERROR #5007: Directory name 'U:\internal\testing\unit_tests\' is invalid <<==== **FAILED**  (root)::

After that run:

Do ##class(%UnitTest.Manager).DebugRunTestCase(,"User.TestCase")

List several classes separated by a semicolon (;) to run them as one suite:

Do ##class(%UnitTest.Manager).DebugRunTestCase(,"User.TestCase;User.TestCase2")

I was confused a bit by the practice question 19 

check whether the caller correctly passed an instance of Sample.Person or a subclass.

I thought it meant:

check whether the caller correctly passed an instance of Sample.Person (which would be correct) or a subclass (which would be incorrect).

But it meant:

check whether the caller correctly passed an instance of Sample.Person or a subclass (both would be correct).

Use schedules or if you need just close enough you can do this:

  • Check how much time, on average BS takes to run. Let's say X seconds.
  • Set Call Interval on your BS to 86400-X.
  • Start BS at 10:00 AM.
  • Assuming the average runtime stays constant, it should work well enough.

This can possibly work if you make %Persistent a secondary superclass. In that case, new properties would go to a separate subscript, and your setup can maybe work (requires testing).

Storage works like this (parent has N properties, Child - X):

Child Extends %Persistent, Parent:

^a(id) = $lb("", prop1, prop2, ..., propX)

Child Extends Parent, %Persistent:
 

^a(id) = $lb("Child", propA, propB, ..., propN)
^a(id, "Child") = $lb(propO, propP, ..., propX)

I use return where it's important that we exit (middle of the loop, etc) immediately, in all other cases I use quit.

I think you might want to track these numbers instead:

  • Number of properties. $lb is list, so the larger the number of elements in a list the longer the time to get to the last element. Also larger larger lists exhaust the global buffer faster, so they might be less optimal. 
  • Method size in sloc. Ideally, your class shouldn't have methods spanning hundreds (thousands) of lines.

I wouldn't worry about total class length, but rather the number of individual elements and method sizes.

There's also class limits docs, but if you hit them, you probably have an issue already.

I'm thinking we may need to create a custom business operation extending the default one EnsLib.HL7.Operation.TCPOperation, and we would need to wrap up the RetryCount inside the response. What do you think?

I think you are correct. RetryCount is a runtime property, I don't think it's stored anywhere longterm, so you need a custom BO to save it. There are some workarounds you can attempt:

  • Query event log
  • Get current retry count using:  $$$GetJobMonitor(tConfigName,$$$SystemName_":"_$Job,$$$eMonitorRetry)

But I would definitely recommend subclassing.

Check ParseIOStream/ParseFramedIOStream from EnsLib.HL7.Parser. These methods take a stream (read to a specific position) and return next HL7 message. Overall loop can be seen in EnsLib.HL7.Service.FileService's OnProcessInput.

However, it is often difficult to test just the REST API without inadvertently involving the authentication scheme, the web application configuration, or even network connectivity. Those are a lot of hoops to jump through just to test the code within your dispatch class.

Another way you can do this is by splitting web (rest) api from the business logic. I think ideally your REST handler method does only the following two things:

  • Parses incoming request
  • Verifies incoming request (debatable, might also be offloaded to the underlying API)
  • Calls underlying API

This way you can focus on unit testing your underlying api layer and keep the REST parts of your implementation as simple as possible.

You can use BPs to do that automatically.

BS (pool size 1) sends file to BP (1 request, pool size 1).

BP parses the file and sends X async requests to BO (pool size >1) for processing, total request count is stored in BP.

BP OnError/OnResponse methods are called once rows are being processed, maintain count here.

Once OnError/OnResponse count matches original count send a final batch request.  

Eduard Lebedyuk · Dec 18, 2025 go to post

a-z, A-Z only. Everything else is considered non-alphabetic, although I guess non-latin characters would be a more correct term. 

Eduard Lebedyuk · Dec 17, 2025 go to post

PS: why is the correct result in the testNumbers method 321 and not 213?

Thanks for noticing! Fixed.