When I was younger (a question about exactly how much younger is outside the scope of this article), the word “token” meant fun. You see, just a couple of times a year, I would get to go to an arcade and play some amusing video games with my friends.
Dear community, I have a confession to make. I have not gotten over Zen yet. Alas, all good things must come to an EOF, so I am currently learning about Angular. I am working on proving to myself that with the right back end and Angular components, I can deliver to myself and my team a very Zen-like experience in this environment. Since this is my first attempt, here is a fair warning: I will be providing some rather large code samples before discussing them. Please warm up your mouse and hand for extensive upcoming scrolling! Also, a note on the code snippets: many of them are labeled
The Good Old Days
The %Library.DynamicObject class has been in IRIS since before it became IRIS. If you have been using it since the Cache days, you may want to brush up on some of its changes.
In Cache 2018, the %Get method only had one argument. It was the key to retrieving from the JSON, meaning that if your JSON object called myObj, it would look like the following:
I need to see the full SMTP trace when a %Net.SMTP attempts to send an email to troubleshoot an issue. As far as I know, the only way to get that is to uncomment line 192 in the %Net.SMTP class.
//:#define SMTPTRACEI've done this in the past by giving myself the %DB_IRISSYS role, so I tried that. That database is also not mounted as read-only.
Of course the bigger issue here is that I can't seem to get that trace without having to edit a system class or create my own, but to fix my more immediate issue, what else do I need to check that would be preventing me from editing that class?
I'm having an issue with sending SMTP requests from a specific Microsoft 365 account to Gmail accounts. We can use other Microsoft 365 accounts, but something about this one specific account sending to Gmail fails. The error we get is just telling us that sending to the user xxxxx@gmail.com failed. In order to troubleshoot that, I'm looking for more detail than that.
As we keep updating our software, we often realize that we require more and more modern solutions. So far, only one major piece of our software relies on reading barcodes in documents and images. Since Cache did not have a means of reading barcodes in the past, we have always achieved our goals by using a Visual Basic 6 application. However, it is no longer an ideal solution because it is currently complicated to maintain it. IRIS also lacks this capability, but it has recently got an option that makes up for it: embedded Python!
Many programming languages use the try-and-catch construct to handle runtime errors gracefully. If the code within the try block encounters an error, it will throw an exception to the catch block, where the error handling occurs. Today we will dive into the ObjectScript implementation of this construct and discuss some ways to clean things up.
ObjectScript Implementation Basics
To get started, we will first look at the basic structure of the try/catch block:
try{
//some code here
}
catch ex{
//some error handling here
}When I install the numpy package on Python on my PC using:
pip install numpyI can use it in python on my machine. If I do:
pip install --target C:\InterSystems\IRIS\mgr\python numpyI get errors any time I try to import it in embedded python.
Do not let the title of this article confuse you; we are not planning to take the InterSystems staff out to a fine Italian restaurant. Instead, this article will cover the principles of working with date and time data types in IRIS. When we use these data types, we should be aware of three different conversion issues:
- Converting between internal and ODBC formats.
- Converting between local time, UTC, and Posix time.
- Converting to and from various date display formats.
Is there a wizard to override pieces of class (parameters, properties, methods, etc.) like there is in Studio? I can't find this functionality anywhere. If it's not there, it's certainly a piece I'll sorely miss as we transition to VS Code.
For programmers new to ObjectScript, one question will inevitably arise: “What is the difference between methods and class methods?” A typical answer would be: “A class method applies to a class, but a method applies to an instance of that class.” While that answer is correct, it lacks important information on how these methods differ, and how they are used in ObjectScript. Many things could be written as either. For instance, suppose we had a class called “User.Person” with a property called “Name”. If we wanted to create a method to write that name, we could do either of the following:
In our previous article, we explored how to send emails through Microsoft 365 using the Graph API. Since then, an anonymous client reached out to me about setting up some other methods of notifications through that API. He was particularly interested in Outlook’s tasks and calendar events.
I came up with a challenge for myself to come up with a way to make a variable watch itself for a certain value and do something when it hits that value without having to check it every time something touches it. Basically, a way to say "if at any point during the execution of this code, if x = 0 (or whatever the condition is) do this thing." The class I ended up with watches a %Status:
One of the most common kinds of integration we are asked to do is emailing. One of the most typical email services our customers use is Microsoft’s Office 365. After setting up the right configuration on the Microsoft side, we can email from IRIS with two HTTP requests. By the end of this article, we will be able to send an email with an attachment through our Microsoft 365 service!
“I have been waiting for thirty seconds for service. This is outrageous! I am leaving!”
“I am very sorry to hear that, sir. Perhaps, next time, you should make a reservation.”
If you heard that comment at your favorite restaurant, you would think the person saying it was being ridiculous. However, in the context of your API, it makes perfect sense. Just like your favorite eatery, your API has some regular patrons who, as you know, will be visiting one day or another. It would be great to be able to make a standing reservation for them as well.
The ideal number of table permissions to assign for your users is zero. Permissions should be granted upon sign-in based on the application used for access. For web applications, we have a simple way of doing this by appointing application roles, matching roles, and required resources in the System Management Portal.
So far, we have covered how to use ObjectScript to manage users, roles, resources, and applications. There are a few other classes in this package that work similarly to the ones mentioned above. However, these four classes are the ones everyone will have to use to manage their application security. Suppose you wanted to create your own Security management portal for this package. There would be some specific issues to think about for an API. Since the classes use similar methods, we can create fewer API endpoints using indirection. However, we should consider differences between those methods
Here in %SYS, we have already examined users, resources, and roles. Now that we know how to set all of that up, we should give it a purpose. Next we will talk about applications! As you may expect, we will see various identical class methods defined here that we have seen in the previous classes. However, some of them will have some tiny yet significant differences.
We are back to %SYS once again! Since we covered managing users and resources in the last two articles, we can finally move on to roles. As you may have guessed, there are a lot of methods of managing them that you have already seen in our previous writings. However, we can still encounter key differences in this particular class.
Welcome back to %SYS! If you read the first article in this series, you must have already seen how to manage your users through the Security.Users class programmatically. Today, we will move on to Resources! Many of the classes in this package use a very similar set of methods. That means that the more we cover, the more familiar you will feel about the subject.
Have you ever thought of creating your own systems for editing users or, perhaps, even an API that you can call? Today, you’re going to join me in the %SYS namespace and get to know Security.Users!
Is there any built-in functionality to send out an email or a text message when a mirror member fails?
We are looking at what we need to do to migrate from our current usage of Zen reports to InterSystems Reports. One of the hurdles for us is figuring out ways to interact with InterSystems reports programmatically from ObjectScript routines. There is a Java API for it, but it is possible to generate a report from InterSystems reports to a stream object in ObjectScript without diving into Java by using a %Net.HttpRequest. Here is a code example, followed by an explanation:
Is there a way to add specific table permissions to a security role programmatically? I'm working on scripting some of the initial setup work when we sell certain add-ons to our software, and I see how I can assign resources to a role and give it a description, but I don't see how I tell it that this role gives the user, for example, SELECT privileges only on the invoices table, or SELECT, INSERT, UPDATE, and DELETE.
In Cache 2018, we were using a macro in a query that looked like this:
select $$GetExtraSQL^GetExtra('B',bddtl.odnumb,bddtl.odsnum,bddtl.oddsc1) as "Description", * from sqluser.bddtl
We could save that query as a view, and there was no problem with it.
In IRIS, if we put that query into SQL in the management portal, it still works, but if we save that query as a view, when we try to run a query on that view, we get a big error message:
If I'm using an objectgenerator, is there a way to check the compile flags and have it behave differently based on which flags are set?
And as a tangent, is there a list somewhere of what compile flags there are?
Has anyone had any success reading barcodes from PDFs or images in a Cache/IRIS application? I've been looking at some possible solutions for this, including the open source ZXing libraries. I know we have the ability to create them in Zen and Intersystems Reports, but as far as I know, there's nothing built in to actually read data from a barcode. If anyone has suggestions on how to go about this, I'd love to hear them.
Is there a way for us to restrict user's ODBC permissions based on what program they're running on a client?
For example, we have some older Windows apps (.exe) that are a regular part of our software package which require the user to be able to select, insert, update, and delete. Some of our users are also using other third-party apps to connect (mostly reporting tools) but we only want them to be able to select unless we've approved the exe. Is there a way to do that?
These are not applications that were developed using CacheDirect.
Is there a way, for testing purposes at least, to change a CSP session over to a different user? We have a lot of things in our system that are allowed or restricted based on the user login, so it would be useful for me to be able to occasionally run as a different user to see how things look and work for them. I've tried using the %CSP.Session.Login function, but that still shows the CSP session as being from the original user, not the one I've switched to.
Pouring The Coffee: Creating and scheduling a task
Don't you wish a fresh, hot cup of coffee could be waiting for you right when you get into the office? Let's automate that!
Cache and IRIS come with a built-in Task Manager, which should have a familiar feel to those used to using the Windows task scheduler or using cron on Linux. Your user account will need access to the %Admin_Task resource to use it, and you can access it in the management portal under System Operation -> Task Manager. When first installed, there are roughly 20 types of task that you can schedule.