Find

Question
· May 20, 2024

ObjectScript: Cannot Read OpenId, Result Doesn't Display

Hello My Friends,

I have a problem with my script, In another code it works fine, but in this script It not display anything in InterSystems portal, when I try to debug, it gives the data, but why in portal it show nothing?

Can anyone help me please? Thank you

in Portal it show nothing

5 Comments
Discussion (5)3
Log in or sign up to continue
Article
· May 16, 2024 9m read

インターシステムズ製品をバックアップする前に確認したいこと

開発者の皆さん、こんにちは。

この記事では、InterSystems製品のバックアップ方法(4種類)のご紹介と、バックアップを行う前に確認しておきたい内容について解説します。

また、この記事に続くシリーズ記事では、それぞれのバックアップの仕組みと操作例を交えたバックアップとリストア手順を解説していきます。

シリーズ記事の中で行っているバックアップ練習、リストア練習の内容については、インターシステムズの講師付きトレーニングコースの中でも取り入れている内容で、一人1環境の演習環境内で実際にお試しいただいています。

参加者の皆さんと一緒に演習を進めて行きますと、データベースリストアの後に行うジャーナルリストアについては、ユーティリティから出力される確認項目が多いため、皆さん慎重に確認されながらリストアの指示を入力されています。そのため、リストア開始前の手続きや準備に意外と時間がかかっています。(実行例

普段なかなかバックアップやリストアを行う機会がないかと思いますが、万が一の場合に備え練習いただくことで実際のリストア作業が発生した際も落ち着いて対処できるようになるかと思います。

また、試さなくてもどんな感じになるのかをお読みいただくことで手続きの大枠がつかめ、心の準備ができるかと思います。まだ体験されたことない方はぜひご興味あるバックアップ手法に対するバックアップ・リストア方法をご確認下さい!


最初は、バックアップの種類について簡単にご紹介します。

 

InterSystems製品のバックアップ方法(4種類)

以下の4種類の方法を選択できます。

推奨方法) 外部バックアップは主に、論理ディスク・ボリュームの有効な ”スナップショット” を迅速に作成するテクノロジと共に使用します。 ここでは、バックアップ時、データベースへの書き込みをフリーズさせてからスナップショットを実行します。

この ”スナップショット” のようなテクノロジは、ストレージアレイからオペレーティング・システム、ディスクの単純なミラーリングに至るまで、さまざまなレベルで存在します。

InterSystems製品が用意するバックアップ機能を利用する方法で、バックアップ対象に設定した全データベースの使用済ブロックをバックアップする方法です。

管理ポータルメニューやタスクスケジュールにも含まれるメニューでお手軽ですが、データベースの使用済ブロック数が多くなればなるほどバックアップ時間も長くなりバックアップファイルサイズも大きくなってしまう方法です。

バージョン2024.1以降では、実験的機能としてオンラインバックアップの高速化機能を提供しています。

データベースファイル(DATファイル)の退避(ファイルコピーのような対退避方法も含)とオンライン・バックアップを組み合わせて利用する方法で、外部バックアップで使用するスナップショットのようなテクノロジが利用できない環境に対して素早くバックアップを取得できる方法です。(オンラインバックアップより高速にバックアップを実行できますが、手順は複雑です。)

InterSystems製品を停止できる場合に利用できる方法で手順がシンプルです。

 

シリーズ記事では、それぞれのバックアップ方法とリストア方法を解説していきますがその前に、 バックアップを取得する前に必ず確認しておきたい事 があります。

さて、それは何でしょうか・・・・・

 

👇

👇

それは、バックアップを取るデータベースの物理的な整合性が保たれているかどうかをバックアップ前に確認すること です。

万が一に備えてバックアップをとっていたとしても、バックアップ対象データベースの物理的な整合性が崩れている状態のバックアップはリストア対象として利用できません。

物理的な整合性=ディスク上のデータベース・ブロックの整合性

整合性が崩れた状態で取得したバックアップでは、正常な状態のデータベースに復旧させることができないためデータ喪失につながり、バックアップの目的が果たせず、非常に危険です。

バックアップが正常に成功していることを確認することも重要ですが、最も重要なことは データベースの整合性が保たれているデータベースをバックアップしているか ということです。

種類別のバックアップ方法、リストア方法紹介の前に、まずはデータベースの整合性チェックについて確認していきましょう。

 

データベースの物理的な整合性チェックツール

整合性チェックは、管理ポータルメニュー([システムオペレーション] > [データベース] > [整合性チェック]ボタン)や ^Integrity 、タスクスケジュールを使用して実行することができます。

管理ポータルメニューは以下の通りです。

システムルーチン:^Integrity を使用する場合は、IRISにログインし%SYSネームスペースに移動します。

チェックしたいデータベースを指定した実行もできますが、まずはシステム全体のチェックを実行してみます。

カレントデバイスに出力することもできますが、整合性チェックのログは長くなるので、例ではファイル出力先をフルパスで指定しています。

USER>set $namespace="%SYS"
%SYS>do ^Integrity
This utility is used to check the integrity of a database
and the pointer structure of one or more globals.

Output results on
Device: /usr/irissys/mgr/integ0424.log
Parameters? "WNS" =>
Stop after any error?  No=>
Do you want to check all databases?  No=> yes

ログ例は以下の通りです。

整合性チェックは全てのデータベースブロックについての検査結果を出力する長いログのため、一部省略+コメントを追記しています。

Intersystems IRIS Database Integrity Check on 04/08/2024 at 15:23:20
System: 733fea287670  Configuration: IRIS
IRIS for UNIX (Ubuntu Server LTS for x86-64 Containers) 2024.1 (Build 263U) Wed Mar 13 2024 15:21:28 EDT


《コメント》データベースディレクトリ毎に中身に含まれるデータベースブロックのつながりを検査します。

---Directory /usr/irissys/mgr/---

Global: %
 トップ/ボトムポインタレベル: ブロック数=1      8kb (充填率 0%)
 データレベル:           ブロック数=2      16kb (充填率 79%)
 合計:                ブロック数=3      24kb (充填率 53%)
 経過時間 = 0.204/08/2024 15:23:26
Global: %IRIS.SASchema
 トップ/ボトムポインタレベル: ブロック数=1      8kb (充填率 0%)
 データレベル:           ブロック数=1      8kb (充填率 11%)
 合計:                ブロック数=2      16kb (充填率 5%)
 経過時間 = 0.204/08/2024 15:23:26
<省略>

Global: rOBJ
 トップ/ボトムポインタレベル: ブロック数=1      8kb (充填率 37%)
 データレベル:           ブロック数=205      1,640kb (充填率 73%)
 ビッグストリング:          ブロック数=1,374      10MB (充填率 81%) カウント = 539
 合計:                ブロック数=1,580      12MB (充填率 80%)
 経過時間 = 0.204/08/2024 15:23:40
---Total for directory /usr/irissys/mgr/---
        95 Pointer Level blocks         760kb (16% full)
     6,634 Data Level blocks             51MB (83% full)
     1,574 Big String blocks             12MB (79% full) # = 696
     8,318 Total blocks                  64MB (81% full)
     1,279 Free blocks                10232kb

Elapsed time = 14.3 seconds 04/08/2024 15:23:40
《コメント》データベースディレクトリに対してチェックが終わると上記サマリを出力し、エラーがない場合は以下出力します。

No Errors were found in this directory.


《コメント》次のデータベースに対する整合性チェックを開始します。

---Directory /usr/irissys/mgr/HSCUSTOM/---

Global: EnsDICOM.Dictionary
 トップ/ボトムポインタレベル: ブロック数=1      8kb (充填率 0%)
 データレベル:           ブロック数=1      8kb (充填率 0%)
 合計:                ブロック数=2      16kb (充填率 0%)
 経過時間 = 0.204/08/2024 15:23:40
<省略>

Global: rOBJ
 トップ/ボトムポインタレベル: ブロック数=1      8kb (充填率 2%)
 データレベル:           ブロック数=19      152kb (充填率 76%)
 ビッグストリング:          ブロック数=259      2,072kb (充填率 60%) カウント = 259
 合計:                ブロック数=279      2,232kb (充填率 61%)
 経過時間 = 0.104/08/2024 15:23:47
---Total for directory /usr/irissys/mgr/HSCUSTOM/---
        48 Pointer Level blocks         384kb (11% full)
     2,150 Data Level blocks             16MB (70% full)
       272 Big String blocks           2176kb (60% full) # = 272
     2,485 Total blocks                  19MB (67% full)
       203 Free blocks                 1624kb

Elapsed time = 7.6 seconds 04/08/2024 15:23:47
No Errors were found in this directory.

《コメント》以降同様にデータベース毎にチェックを行い、エラーがある場合はその対象ブロックに対してエラー情報を出力します。エラーがない場合は各データベースのチェックの終わりに「No Errors were found in this directory.」と出力します。

すべてのデータベースの検査を終え、エラーがない事を確認すると以下1行出力し、整合性チェックが終了します。

No Errors were found.

 

タスクスケジュールについては、インストール時に毎週月曜日深夜2時に実行するタスクとして登録されていますが、一時停止状態で登録されています。

管理ポータル > [システムオペレーション] > [タスクマネージャ] > [タスクスケジュール] > Integrity Check 

整合性チェックは指定したデータベースの全データベースブロックを検査するため、チェック時間は、データ量、チェックするデータベース数、HWスペックに依存します。

目安となるような計測時間は特になく、実際稼働中の環境で実測した値を参考に予測していただく必要があります。

万が一の場合に備え、データベース整合性チェック時間のおおよその検討が付けられるように、実環境でテスト実行を行っていただくことを推奨します。

また、整合性チェック時間は環境により異なりますので、実稼働環境の状況に合わせ実行のタイミングをご検討ください。

もし、整合性チェックでエラーが出た場合は、すぐにサポートセンターまでご連絡ください。

 

以上、整合性チェックツールの使い方でした。

次は、いよいよバックアップ種類別のバックアップとリストア方法について解説します。

​​​​​​

Discussion (0)1
Log in or sign up to continue
Discussion (8)2
Log in or sign up to continue
Announcement
· May 12, 2024

[Webinar in Hebrew] Maximize Your Success with InterSystems Support

Hi Community,

We're pleased to invite you to the upcoming webinar in Hebrew:

👉 Maximize Your Success with InterSystems Support 👈

📅 Date & time: May 21st, 3:30 PM IDT


Unlock the full potential of your InterSystems solutions in this engaging webinar! Join us to explore the capabilities of the Worldwide Response Center (WRC). Learn about our streamlined support procedures and policies designed to deliver the help you need, when you need it. Dive into the WRC application with a live demo, discover insider tips for effective communication, and much more.

Presenter:
🗣@Asaf Sinay, Senior Support Manager, Product Support, InterSystems 

➡️ Register today and enjoy!
 

1 Comment
Discussion (1)1
Log in or sign up to continue
Article
· May 9, 2024 9m read

Microsoft 365 - Events and Tasks

 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. 

If you still have your client ID, client secret, and application ID from the last exercise, you may continue utilizing them. We will reuse the globals we stored from before with the GetToken method. Most of the setup in Microsoft Entra will not need to be repeated. The only exception would be that you will have to go back to your application permissions and add the correct permissions for each item. We will start with adding a Task that requires permission Tasks.ReadWrite.All. Add that permission and grant admin consent employing the same process we described in the previous article.

As always, our API request will be built from a %Net.HttpRequest object. In this case, we will have to make two additional requests besides the one to get the token. Since Tasks in Outlook currently use Microsoft ToDo, your tasks can be organized into lists now. To create a task we must know the ID of the list to which we should send it. We will utilize the default task list for that. To find out its ID we need to create a request to the ToDo lists API. It will be a simple HTTP get request with no entity body, so it will be very easy.

set listRequest = ##class(%Net.HttpRequest).%New()
set listRequest.Server = "graph.microsoft.com"
set listRequest.SSLConfiguration = "O365"
set listRequest.Https = 1
set listRequest.Location = "/v1.0/users/youremail@domain.com/todo/lists"
do listRequest.SetParam("$filter","displayName eq 'Tasks'")
set sc = ..GetToken(.token)
do listRequest.SetHeader("Authorization","Bearer "_token)
set sc = listRequest.Get()

Pay attention to the parameter we set. On HTTP get requests, the graph API supports some OData filtering. By adding it, we specify that we are looking for the task list with the display name “Tasks,” which should be the default task list for most users. Since my client is not very tech savvy - he says he only ever accepts cookies with milk - he hasn’t changed the name of his default task list.

Note that we are going to employ the same basic setup that we operated for the email API request. The server will be graph.microsoft.com. We will  utilize the O365 SSL configuration. We will also get the bearer token and add it to the header. Then we will send the response to a location that includes the target user’s email address. This time we will operate the request’s Get() method rather than Post(). It will return a JSON response that will look similar to the one below:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('########-####-####-####-############')/todo/lists",
    "value": [
        {
            "@odata.etag": "#####################################",
            "displayName": "Tasks",
            "isOwner": true,
            "isShared": false,
            "wellknownListName": "defaultList",
            "id": "There will be a big long string here."
        }
    ]
}

Without the filter parameter, we would get all user’s task lists. They often have at least two lists: a default task list and a separate one for flagged emails. If they set up other lists in ToDo, there will be even more. We are only interested in the default task list today. To get its ID, we should do the following:

set respObj = ##class(%Library.DynamicObject).%FromJSON(listRequest.HttpResponse.Data)
set respArray = respObj.%Get("value")
set listObj = respArray.%Get(0)
set ListID = listObj.%Get("id")

We need to use it in the task location to complete the request, so let's set it up similarly to the previous request.

set taskRequest = ##class(%Net.HttpRequest).%New()
set taskRequest.SSLConfiguration = "O365"
set taskRequest.Https = 1
set taskRequest.Server = "graph.microsoft.com"
set taskRequest.ContentType = "application/json"
set taskRequest.Location = "/v1.0/users/youremail@domain.com/todo/lists/"_ListID_"/tasks"

The body of this request will have a JSON structure. We could set just the title property of this JSON object and send the request. If we did it, it would give the user a new task with only that title and nothing else.

set mytask = ##class(%Library.DynamicObject).%New()
do mytask.%Set("title","Merry Christmas!")

However, my client’s needs are more advanced than that! He has a recurring annual task that requires a bit more information, including a small description, a due date, a reminder, and some recurrence. We will start with the body which will contain a very basic reminder. He is getting old, you know. So, the body will be a JSON object itself. Just like the email body, it will have two properties. One of them will be contentType which can be either “text” or “html”, and the other will be content, which will consist of the actual content of the body. Let's stick to a simple text.

set body = ##class(%Library.DynamicObject).%New()
do body.%Set("content","Don't forget to deliver!")
do body.%Set("contentType","text")
do mytask.%Set("body",body)

Since my client is a very influential man in charge of some crucial work, we will also set the importance of this task.

do mytask.%Set("importance","high")

He has also requested to add a due date and a reminder on the task. Additionally, his dates should be easy enough to remember. The dueDateTime property of the task should also be a JSON object with both a timestamp and a time zone. The reminder will likewise be the same kind of object, so we should set the isReminderOn property to true.

set duedate = ##class(%Library.DynamicObject).%New()
do duedate.%Set("dateTime","2024-12-25T00:00:00")
do duedate.%Set("timeZone","Etc/GMT")
do mytask.%Set("dueDateTime",duedate)
set reminder = ##class(%Library.DynamicObject).%New()
do reminder.%Set("dateTime","2024-12-24T00:00:00")
do reminder.%Set("timeZone","Etc/GMT")
do mytask.%Set("reminderDateTime",reminder)
do mytask.%Set("isReminderOn","true","Boolean")

Next, we need to deal with the recurrence. The recurrence object typically contains two more objects: a pattern and a range. The pattern tells us when this task recurs, and the range suggests how long it recurs. My client’s task is annual and will continue without an end, so I guess he will be doing this forever! 

We will start with the pattern. The type of pattern can be daily, weekly, relativeMonthly, absolutelyMonthly, relativeYearly, or absoluteYearly. For the monthly and yearly options, the absolute indicates that the event will occur on the exact same date, for example, September third of every year or on the tenth of every month. Relative means a certain occurrence of a day, like the third Thursday of every month or the fourth Thursday of every November. In my client’s case, it is an absolute yearly recurrence. The interval is based on the type. If it is a yearly event, 1 means this event will repeat every year. If it were a weekly event, an interval of 1 would mean one week. We should also set the month and dayOfMonth. For other types of recurrence, you may need to set the daysOfWeek property as well. It is a collection that can contain any day of the week. However, it does not apply in our case.

set pattern = ##class(%Library.DynamicObject).%New()
do pattern.%Set("type","absoluteYearly")
do pattern.%Set("interval",1)
do pattern.%Set("month",12)
do pattern.%Set("dayOfMonth",25)

The range can have one of three types: noEnd, endDate, and numbered. Since we are going to use the noEnd type, we only need to specify a start date and the type. If we were using the endDate type, we would have to specify an endDate, and if we were employing the numbered type, we would need to specify numberOfOccurrences as a positive integer telling us how many times the event will repeat.

set range = ##class(%Library.DynamicObject).%New()
do range.%Set("type","noEnd")
do range.%Set("startDate","2024-12-25")

Then we need to add the following code to a recurrence object and later add that object to our task. 

set recurrence = ##class(%Library.DynamicObject).%New()
do recurrence.%Set("pattern",pattern)
do recurrence.%Set("range",range)
do mytask.%Set("recurrence",recurrence)

Now we are finally ready to send off our request!

do taskRequest.EntityBody.Write(mytask.%ToJSON())
do taskRequest.Post()

If we have done everything correctly, we should get an HTTP response with a Status Code of 201 telling us that the task was created. The user should be able to see it in Outlook as soon as their Outlook syncs again.

 

After my client’s big annual event, they usually have a company party. He would like me to create an event on his Outlook calendar for it. To do it, you will need to go back to Entra and give your application the Graph API permission Calendars.ReadWrite. Simply sending an event request without specifying a calendar ID will add the event to the user’s default calendar. Once again, we will create a %Net.HttpRequest with our usual setup.

set evtRequest = ##class(%Net.HttpRequest).%New()
set evtRequest.Server = "graph.microsoft.com"
set evtRequest.SSLConfiguration = "O365"
set evtRequest.Https = 1
set evtRequest.Location = "/v1.0/users/"_..UserAddress_"/calendar/events"
set evtRequest.ContentType = "application/json"
set sc = ..GetToken(.token)
do evtRequest.SetHeader("Authorization","Bearer "_token)

Next, we have to set up the body of the request. This object will contain some properties that should look very familiar to you. The subject will be a simple string with a description of the event. The body will be the same kind of object as email and task bodies. There we must specify the content type as either "html" or "text". Additionally, remember to mention the start and end dates and times with the time zone included.

set evtObj = ##class(%DynamicObject).%New()
do evtObj.%Set("subject","After Party")
set bodyObj = ##class(%DynamicObject).%New()
do bodyObj.%Set("contentType","html")
do bodyObj.%Set("content","Party after the big day! <br /><b>BRING YOUR OWN NOG!</b>")
do evtObj.%Set("body",bodyObj)
set start = ##class(%Library.DynamicObject).%New()
do start.%Set("dateTime","2024-12-26T20:00:00")
do start.%Set("timeZone","Etc/GMT")
do evtObj.%Set("start",start)
set end = ##class(%Library.DynamicObject).%New()
do end.%Set("dateTime","2024-12-27T00:00:00")
do end.%Set("timeZone","Etc/GMT")
do evtObj.%Set("end",end)

There is a reminderMinutesBeforeStart field that will set a reminder for an event.

do evtObj.%Set("reminderMinutesBeforeStart",60)

We can also add a location, which is an object that can contain a name, address, coordinates, and some contact information.

set locObj = ##class(%Library.DynamicObject).%New()
do locObj.%Set("displayName","SeasonalSpirits")
set coordObj = ##class(%Library.DynamicObject).%New()
do coordObj.%Set("latitude",90)
do coordObj.%Set("longitude",0)
do locObj.%Set("coordinates",coordObj)
do evtObj.%Set("location",locObj)

It will not be much of a party if no one comes, so we should add some attendees too. It will be the same style of email address we used before, plus a type field specifying whether the attendee is required, optional, or a resource.
 

set attendees = ##class(%Library.DynamicArray).%New()
set myatt1 = ##class(%Library.DynamicObject).%New()
do myatt1.%Set("type","required")
set myemail1 = ##class(%Library.DynamicObject).%New()
do myemail1.%Set("name","Mr C.")
do myemail1.%Set("address","youremail@domain.com")
do myatt1.%Set("emailAddress",myemail1)
do attendees.%Push(myatt1)
set myatt2 = ##class(%Library.DynamicObject).%New()
do myatt2.%Set("type","optional")
set myemail2 = ##class(%Library.DynamicObject).%New()
do myemail2.%Set("name","Mrs. C.")
do myemail2.%Set("address","youremail2@domain.com")
do myatt2.%Set("emailAddress",myemail2)
do attendees.%Push(myatt2)
do evtObj.%Set("attendees",attendees)

Finally, we are ready to write the request body and post it.

do evtRequest.EntityBody.Write(evtObj.%ToJSON())
do evtRequest.Post()

Once again, if we have done all of the abovementioned correctly, we should get an HTTP response with a status code of 201, and the user should now have the event on their default calendar.

 

That is all for now! However, it seems to me that I have just got another email from my mysterious client. It is something related to managing big and busy teams of builders, stable managers, etc. This guy’s business is really weird! I have a feeling I will be back soon, talking about Teams.  

Discussion (0)1
Log in or sign up to continue