クリアフィルター
記事
Mihoko Iijima · 2025年6月24日
これは InterSystems FAQ サイトの記事です。
InterSystems 製品インストール時に用意されている事前定義ロールを利用して、ログインユーザ毎に管理ポータルメニュー(システム管理/システムエクスプローラー/システムオペレーション など)のアクセスを制限することができます。
管理ポータルメニューに対するアクセス制限を行うためには、管理ポータルへの「認証なし(UnknownUser)」アクセス(※1)を使用しない環境が必要です。
管理ポータル上部の「ログアウト」をクリックしてログイン画面が表示されない場合は、関連記事「管理ポータル/スタジオ/ターミナルにパスワード認証を設定するにはどうしたらいいですか?」をご覧いただき必要な設定を行ってください。
以下、インストールデフォルトで管理ポータルへのフルアクセスが許可されている事前定義ユーザ(※2): SuperUser や _SYSTEM アカウントを使用して管理ポータルにログインした状態の設定例でご説明します。
これらユーザのパスワードはインストール時に設定しています(インストール時の「初期セキュリティ」を「最小」でインストールした環境では、パスワードは大文字の SYS が設定されています)。
InterSystems 製品はロールベースのセキュリティを使用しています。ログインユーザ毎にアクセスできるメニューを制限したい場合は、付与するロールを変えることで実現できます。
InterSystems 製品内でロールとは「特権の集まり」に名前を付けたもので、「特権」は「リソース(=保護対象全般)に対する許可(READ/WRITE/USE)」を組み合わせた定義です。必要な特権を定義したロールを作成しユーザに付与することで細かなアクセス制御をユーザに対して定義できます。
言葉の定義や内容詳細については関連記事:「InterSystems 製品のセキュリティの基本:ユーザとロール」でご説明しています。ぜひご参照ください。
※1 InterSystems IRIS をインストールする際の指定で「初期セキュリティ」を「最小」とした場合に設定されるアクセスです。InterSystems IRIS for Healthでは、「初期セキュリティ」オプションは「通常」以上の設定となるため、デフォルトで「認証なし」アクセスを使用しないように設定されます。
※2 %All ロールを持つユーザです。
ここでは、以下の事前定義ロールを付与したユーザを作成し、管理ポータルログイン後の表示の違いを確認してみます。
1) %Manager ロール
システム管理に関連付けられた特権を持つロールです。管理ポータルの「システム管理」「システムエクスプローラ」「システムオペレーション」にアクセスできます。
例では、TestA ユーザにこのロールを付与します。
2) %Developer ロール
アプリケーション開発に関連付けられる特権を持つロールです。このロールでは開発者に必要なターミナルアクセスと、管理ポータルの「システムエクスプローラ」にアクセスできます。
例では、TestB ユーザにこのロールを付与します。
3) %Operator ロール
システム運用メニューに関連付けられた特権を持つロールです。管理ポータルの「システムオペレーション」にアクセスできます。
例では、TestC ユーザにこのロールを付与します。
管理ポータルの「Interoperability」メニュー については、Interoperability メニュー以下を操作する方の役割に合わせた事前定義ロールが細かく分かれています。
詳細はドキュメント「プロダクション関連の事前定義ロール」をご参照ください。
また、Interoperabilityメニューを利用する場合は、管理ポータル用ロールの他に、アクセス対象のデータベース用ロール(%DB_XXXX)も必要となります。
IRIS for Health をインストールすると表示される「Health」メニューについては、%HS_Administrator ロール と 操作対象のデーベース用ロール(%DB_XXXX)が必要です。
それでは早速、異なるロールを付与されたユーザ別の管理ポータルログイン後の表示の違いを確認します。
1) %Manager ロールを付与した TestA での表示
2) %Developer ロールを付与した TestB での表示
3) %Operator ロールを付与した TestC での表示
1) %Managerロールを付与した TestA での表示
以下の手順でTestAユーザを作成し、確認します。
手順1:TestA ユーザを作成し、%Managerロールを付与
手順2:管理ポータルにログインしてテスト
手順1:TestA ユーザを作成し、%Managerロールを付与
TestA を作成します。パスワード(3文字以上の大小文字を区別する英数字)を設定し、保存します。保存後、ロールタブで%Manager ロールを付与します。
手順2:管理ポータルにログインしてテスト
では、テストしてみます。
管理ポータルをログアウトした後で、TestA でログインします。ログイン後の見栄えは以下の通りです。
2) %Developer ロールを付与した TestB での表示
以下の手順でTestBユーザを作成し、確認します。開発者用ロールであるため、テストでは管理ポータルの他にターミナルからのアクセスも試します。また、管理ポータルからの SQL の実行も試してみます。
手順0:現在の管理ポータルをログアウトとし管理者ユーザでログイン
手順1:TestB ユーザを作成し、%Developer ロールとデータベース用ロールを付与
手順2:管理ポータルにログインして表示を確認
手順3:ターミナル(またはIRIS)へのログインテスト
手順4:管理ポータルのSQLメニューでSQLの実行テスト
手順0:現在の管理ポータルをログアウトとし管理者ユーザでログイン
TestB を作成するため、管理者ユーザ(SuperUserや_SYSTEM) でログインし直します。
手順1:TestB ユーザを作成し、%Developer ロールとデータベース用ロールを付与
作成するTestB ユーザには %Developer ロールを付与するため、管理ポータルの「システムエクスプローラ」を利用できます。
「システムエクスプローラ」はグローバル変数の参照や、クラス定義・ルーチンのインポートエクスポート、さらに SQL の実行などが行える開発者向けメニューとなるため、%Developer ロールの他にデータベース用ロールが必要となります。
データベース作成時のセキュリティ設定次第となりますが、デフォルトを選択している場合は、%DB_%DEFAULT ロールを付与します。
データベースリソースを作成している場合は、データベース用ロールを付与します。例では、USER データベース用ロール:%DB_USER を付与しています。
手順2:管理ポータルにログインして表示を確認
TestB ユーザでログインを行うと、システムエクスプローラーが選択できるように見えます。
ですが、「システムエクスプローラー」を選択してもほとんどのメニューが利用できません。
これは、管理ポータルにログインしたときのネームスペースが %SYS になっているからです。
TestB ユーザの設定にある「開始ネームスペース」に USER を指定するか、管理ポータルを開いた後ネームスペースを USER に切り替えることでサブメニューを操作できるようになります。
まずは、ネームスペースを切り替えてみます。
以下の画面は、TestB の「開始ネームスペース」に USER を設定したときの図です。
手順3:ターミナル(またはIRIS)へのログインテスト
%Developer ロールは開発者用ロールでターミナルや IRIS にログインすることができます。試しに、IRIS にログインしグローバル変数を作成してみます。
irisowner@1fed473da3f3:/opt/src$ iris session iris
ノード: 1fed473da3f3 インスタンス: IRIS
ユーザ名:TestB
パスワード:*****
USER>
USER>
USER>set ^ABC=1
USER>write ^ABC
1
ログインも、グローバルの更新も参照もできました。管理ポータルからも参照できるか確認します。
無事参照できています。
手順4:管理ポータルのSQLメニューでSQLの実行テスト
続いて、SQL画面にアクセスし、テーブルを作成してみます。
エラーが出てしまいました。
SQL 画面については、テーブルの操作に対する権限も含めてユーザに付与する必要があります。
TestB ユーザにテーブルに対する権限を追加します。これには、SuperUser でログインし直してから USER ネームスペースに対して CREATE TABLE が利用できる権限を追加します。
では、TestB ユーザにログインしなおして試してみます。
テーブルの作成とデータ登録を行ってみます。
無事、テーブル作成も INSERT 文も実行できました。
ここで、SuperUser でログインしなおし管理ポータルで TestB ユーザの情報を参照すると TestB はテーブル所有者であるため、作成したテーブルに対する SQL テーブル権限が付与されています。
所有者ではないテーブルに対しては、テーブル毎にテーブル権限を付与する必要がありますのでご注意ください。
また例では、SQL の特権を TestB ユーザに直接付与しましたが、専用ロールを作成しロールをユーザに付与する方法でも定義できます。
他のユーザに同じ SQL 特権を付与する場合は、ロールを作成したほうが便利です。
ご参考:ロールに SQL 特権を設定する例
2) %Operator ロールを付与した TestC での表示
最後に、%Operator ロールを持つ TestC を作成します。運用管理に使用するツールはターミナル(またはIRIS)へログインして実行するルーチンなどもあるため、ターミナル(またはIRIS)へのログインも試してみます。
手順0:現在の管理ポータルをログアウトとし管理者ユーザでログイン
手順1:TestC ユーザを作成し、%Operator ロールとデータベース用ロールを付与
手順2:管理ポータルにログインしてテスト
手順3:ターミナル(またはIRIS)へのログインテスト
手順4:ターミナルへのログイン失敗の原因を探る
手順0:現在の管理ポータルをログアウトとし管理者ユーザでログイン
TestC を作成するため、管理者ユーザ(SuperUserや_SYSTEM) でログインし直します。
手順1:TestC ユーザを作成し、%Operator ロールを付与
SuperUser でログインしなおして TestCを作成し、%Operatorロールを付与します。
手順2:管理ポータルにログインしてテスト
以下、TestC でログインした状態です。システムオペレーションメニューが操作できます。
手順3:ターミナル(またはIRIS)へのログインテスト
IRISに TestC ユーザでログインしてみます。
irisowner@1fed473da3f3:/opt/src$ iris session iris
ノード: 1fed473da3f3 インスタンス: IRIS
ユーザ名:TestC
パスワード:*****
アクセスが拒否されました。
TestC はアクセスが拒否されています。
これは、IRIS にログイン(Windows ならターミナルにアクセス)の許可が足りないためです。
手順4:ターミナルへのログイン失敗の原因を探る
手順3でログイン失敗の原因を探るため、一旦管理ポータルをログアウトしSuperUser でログインしなおした後、監査イベントを確認します。
システム管理 > セキュリティ > 監査 > 監査データベースの閲覧 >(検索ボタン押下後) 対象のイベントの「詳細」をクリック
エラーメッセージ: エラー #836: プログラマーアクセスの権限が不十分です
サービス名: %Service_Terminal
上記エラーが出ています。
IRIS にログイン(またはターミナルを起動)するためには、%Service_Terminal(Linux用) または %Service_Console(Windows用) サービスに対する USE 許可、アクセス対象データベースリソース(例では%DB_USER)の READ 許可、そしてプログラマーアクセスの権限として、%Development リソースのUSE許可が必要となります。
TestB ユーザに付与した %Deleveloper ロールはこれらの許可が含まれているため IRIS にログインできていました。
TestC ユーザがどの許可を持っているか?については、プロファイル画面を見ると分かりやすいです。
SuperUserでログインし、システム管理 > セキュリティ > TestCユーザの「プロファイル」をクリックします。
参照しているユーザが利用できるリソースとその許可が一覧されます。
それぞれの許可を確認します(以下の例は Linux 上 IRIS で確認しているため、%Service_Console は表示されていません)。
%DB_USER リソースと、%Development リソースに対する適切な許可が足りていません。
TestC ユーザに %DB_USER リソースの READ/WRITE 許可を持つ %DB_USER ロールと、%Development リソースの USE 許可が含まれる %Developer ロールを付与することで IRIS にログインできます。
irisowner@1fed473da3f3:/opt/src$ iris session iris
ノード: 1fed473da3f3 インスタンス: IRIS
ユーザ名:testC
パスワード:*****
USER>write $roles //このユーザがログインしたときに持っているロールを確認
%Developer,%DB_USER,%Operator
なお、例のようなロール付与以外にもリソースに対してパブリックに許可を設定することでユーザに許可を与えることもできます。プロファイル画面では「パブリックリソースにより付与」の列に情報が記載されています。
ここまでの流れで、事前定義ロールを利用した管理ポータルのアクセス制御が確認できました。
別の記事では、事前定義ロールだけでは対応できない、サブメニューに対するアクセス制御の例をご紹介しています。ぜひご参照ください。
記事
Toshihiko Minamoto · 2022年1月27日
はじめにバージョン2019.2より、InterSystems IRISは、高性能データアクセス手法としてPython用のネイティブAPIを提供してきました。 ネイティブAPIを使用すると、ネイティブのIRISデータ構造と直接対話することができます。グローバルInterSystems開発者であれば、おそらくすでにグローバルを理解していることでしょう。 ここでは、復習目的でその基本を確認しますが、次のセクションに進んでも構いません。InterSystems IRISはグローバルを使用してデータを格納しています。 グローバルは、値の有無に関係なくノードとサブノードで構成されるスパース配列です。 以下に、グローバルの概念的な例を示します。この例では、aはルートノードであり、グローバル名と呼ばれています。 各ノードには、グローバル名と1つまたは複数のサブスクリプト(サブノードの名前)で構成されるノードアドレスがあります。 aにはサブスクリプトbとcがあるため、それらのノードアドレスは、a->bおよびa->cとなります。ノードa->bとa->c->gには値(dとh)があり、ノードa->b->eとa->b->fには値がありません。 ノードa->bにはサブスクリプトeとfがあります。この構造の詳細な説明は、InterSystems dookbook『グローバルの使用法』に記載されています。グローバルの読み取りと書き込みネイティブのPython APIでは、IRISグローバルからデータを読み取りとそれへの書き込みを直接行えます。 irisnativeパッケージはGitHubで提供されています。または、InterSystems IRISがローカルマシンにインストールされている場合は、インストールディレクトリのdev/pythonサブディレクトリにあります。irisnative.createConnection関数は、IRISへの接続を作成し、irisnative.createIris関数は、この接続から、グローバルを操作するために使用するオブジェクトを取得します。 このオブジェクトには、グローバルの読み書きに使用するgetメソッドとsetメソッド、そしてノードとそのサブノードの削除に使用するkillメソッドがあります。 また、isDefinedメソッドもあり、このメソッドによって、リクエストされたノードが存在しない場合は「0」、
値があり子孫がない場合は「1」、値がなく子孫がある場合は「10」、値と子孫がある場合は「11」を返します。
import irisnative
conn = irisnative.createConnection("127.0.0.1", 51773, "USER", "", "")
iris = irisnative.createIris(conn)
iris.set("value", "root", "sub1", "sub2") # sets "value" to root->sub1->sub2
print(iris.get("root", "sub1", "sub2"))
print(iris.isDefined("root", "sub1"))
iris.kill("root")
conn.close()
また、特定のノードのサブノードをループするiteratorメソッドもあります。 (使用方法は次のセクションで説明します。)
各メソッドの詳細な説明については、APIドキュメントをご覧ください。
サンフランシスコのGTFSトランジットデータファイル
グローバルにデータを格納する
General Transit Feed Specification(GTFS)は、公共交通機関の時刻表と経路に関するフォーマットです。 IRISネイティブAPIを使用して、2019年6月10日のサンフランシスコのGTFSデータを処理する方法を見てみましょう。
まず、データファイルの情報をIRISグローバルに格納します。 (このデモでは、すべてのファイルと列を使用するわけではありません。) ファイルはCSV形式で、最初の行に列名、残りの行にデータが含まれます。 Pythonで、必要なインポートを開始し、IRISへの接続を確立します。
import csv
import irisnative
conn = irisnative.createConnection("127.0.0.1", 51773, "USER", "", "")
iris = irisnative.createIris(conn)
列名とデータに基づき、各ファイルの実用的なツリー構造を構築し、iris.setを使用してグローバルにデータを格納できます。
stops.txtファイルから始めましょう。このファイルにはサンフランシスコのすべての公共交通機関の停留所・停車駅が含まれます。 このファイルのstop_id列とstop_name列のみを使用します。 これらを、stop IDをサブスクリプト、stop nameをノードの値として使用し、1つのレイヤーのノードを持つツリー構造内のstopsというグローバルに格納します。 したがって、この構造は「stops → [stop_id]=[stop_name]」のようになります。 (この記事では、サブスクリプトがリテラルではなくデータファイルから読み取った値である場合に、角括弧を使用しています。)
with open("stops.txt", "r") as csvfile:
reader = csv.reader(csvfile)
next(reader) # Ignore column names
# stops -> [stop_id]=[stop_name]
for row in reader:
iris.set(row[6], "stops", row[4])
csv.readerは、カンマ区切りの値を保持するリストのイテレータを返します。 最初の行には列名が含まれているため、next(reader)を使ってその行をスキップします。 stop nameをstops -> [stop_id]の値として設定するために、 iris.setを使用します。
次はroutes.txtファイルです。このファイルのroute_type、route_id、route_short_name、およびroute_long_name列を使用します。 実用的なグローバル構造は、routes -> [route_type] -> [route_id] -> [route_short_name]=[route_long_name]です。 (route type(経路タイプ)は、路面電車の場合は「0」、バスの場合は「3」、ケーブルカーの場合は「5」です。) このCSVファイルからデータを読み取って、まったく同じようにグローバルに格納します。
with open("routes.txt", "r") as csvfile:
reader = csv.reader(csvfile)
next(reader) # Ignore column names
# routes -> [route_type] -> [route_id] -> [route_short_name]=[route_long_name]
for row in reader:
iris.set(row[0], "routes", row[1], row[5], row[8])
各経路には区間があります。これはtrips.txtファイルに格納されているもので、このファイルのroute_id、direction_id、trip_headsign、およびtrip_id列を使用します。 区間は、trip ID(後でstop timesファイルで確認します)で一意に識別されています。 ある経路の区間は、方向を基準とした2つのグループに分けられ、方向にはヘッドサインが関連付けられています。 これを踏まえると、ツリー構造はtrips -> [route_id] -> [direction_id]=[trip_headsign] -> [trip_id]となります。
ここでは2つのiris.set呼び出しが必要です。direction IDノードに値を設定する呼び出しと、trip IDの値のないノードを作成する呼び出しです。
with open("trips.txt", "r") as csvfile:
reader = csv.reader(csvfile)
next(reader) # Ignore column names
# trips -> [route_id] -> [direction_id]=[trip_headsign] ->[trip_id]
for row in reader:
iris.set(row[3], "trips", row[1], row[2])
iris.set(None, "trips", row[1], row[2], row[6])
最後に、stop times(停止時刻)を読み取って格納します。 これらは、stop_times.txtファイルに格納されており、このファイルのstop_id、trip_id、stop_sequence、departure_time列を使用します。 最初のオプションにはstoptimes -> [stop_id] -> [trip_id] -> [departure_time]を使用することができます。またはstop sequence(停止順)を維持する場合は、stoptimes -> [stop_id] -> [trip_id] -> [stop_sequence]=[departure_time]となります。
with open("stop_times.txt", "r") as csvfile:
reader = csv.reader(csvfile)
next(reader) # Ignore column names
# stoptimes -> [stop_id] -> [trip_id] -> [stop_sequence]=[departure_time]
for row in reader:
iris.set(row[2], "stoptimes", row[3], row[0], row[4])
ネイティブAPIを使用してデータをクエリする
次の目標は、特定の名前の停留所・停車駅のすべての出発時刻を見つけることです。
まず、特定のstop nameからstop IDを取得し、そのIDを使ってstop_timesから関連する時刻を見つけます。
iris.iterator("stops")呼び出しを使うと、stopsルートノードのサブノードを反復できます。 サブスクリプトと値のペアを反復したいため(指定された名前を持つ値を比較し、一致すればスフにサブスクリプトを知ることができるため)、イテレータで.items()を呼び出し、戻り値の型を (subscript, value) のタプルに設定します。 次に、これらすべてのタプルを反復して、正しいstopを見つけ出します。
stop_name = "Silver Ave & Holyoke St"
iter = iris.iterator("stops").items()
stop_id = None
for item in iter:
if item[1] == stop_name:
stop_id = item[0]
break
if stop_id is None:
print("Stop not found.")
import sys
sys.exit()
ノードが多数ある場合、反復によってキーの値でキーでルックアップするのはあまり効率的ではありません。 これを避けるには、サブスクリプトがstop nameで値がIDである別の配列を用意するとよいでしょう。 すると、value --> key ルックアップは、この新しい配列への1つのクエリで構成されます。
または、stop nameも一意であるため、stop IDの代わりに、コードのあらゆる箇所でstop nameを識別子として使用することもできます。
お分かりのとおり、stopsの数が膨大にある場合は、この検索に時間が掛かります。これは「フルスキャン」とも呼ばれています。 ただし、グローバルを利用して、値のIDを持つキーが名前となる逆配列を作成することができます。
iter = iris.iterator("stops").items()
stop_id = None
for item in iter:
iris.set(item[0], "stopnames", item[1])
インデックスが名前で値がIDのstopnamesグローバルがあることで、名前でstop_idを見つける上記のコードは、フルスキャン検索を行わずに実行する以下のコードに変更されます。
stop_name = "Silver Ave & Holyoke St"
stop_id=iris.get("stopnames", stop_name)
if stop_id is None:
print("Stop not found.")
import sys
sys.exit()
この時点で、stop times(停止時刻)を見つけることができます。 サブツリーstoptimes -> [stop_id]にはtrip IDサブノードがあり、このサブノードにはstop timesサブノードがあります。 trip IDではなく、stop timesのみに関心があるため、すべてのtrip IDを反復して、それぞれのすべてのstop timesを収集します。
all_stop_times = set()
trips = iris.iterator("stoptimes", stop_id).subscripts()
for trip in trips:
all_stop_times.update(iris.iterator("stoptimes", stop_id, trip).values())
ここでは、イテレータに.items()を使用していませんが、trip IDはサブスクリプトであるため.subscripts()と.values()を使用します。または、値とdeparture times(出発時刻)に関心がある場合には、下位レイヤー([stop_sequence]=[departure_time])を使用します。 .update呼び出しは、イテレータのすべての項目を既存のセットに追加します。 すると、セットにはすべての(一意の)stop timesが含まれます。
for stop_time in sorted(all_stop_times):
print(stop_time)
では、もう少し複雑にしてみましょう。 ある停車駅・停留所のすべての出発時刻を見つける代わりに、ある停車駅・停留所におけるroute IDが指定された特定の経路(両方向)のみの出発時刻を見つけることにします。 stop nameからstop IDを見つけ出すコードは、そのまま使用できます。 次に、特定の経路のすべてのtrip IDを取得します。 これらのIDは、departure timesを取得する際の追加の制限として使用されます。
trips -> [route_id]のサブセットは2つの方向に分割され、すべてのtrip IDがサブノードとなります。 前と同じようにdirectionsを反復し、すべてのdirectionsのサブノードをセットに追加します。
route = "14334"
selected_trips = set()
directions = iris.iterator("trips", route).subscripts()
for direction in directions:
selected_trips.update(iris.iterator("trips", route, direction).subscripts())
次のステップとして、取得されたstop IDを[stop_id]、選択されるtrip IDを[trip_id]とするstoptimes -> [stop_id] -> [trip_id]のすべてのサブノードの値を見つけ出します。 selected_tripsセットを反復して、すべての関連する値を見つけます。
all_stop_times = set()
for trip in selected_trips:
all_stop_times.update(iris.iterator("stoptimes", stop_id, trip).values())
for stop_time in sorted(all_stop_times):
print(stop_time)
最後の例では、isDefined関数の使用方法を示します。 以前に記述したコードを拡張し、route IDをハードコーディングする代わりに、経路の短縮名を指定すると、route IDはそれに基づいて取得されるようになります。 経路名のあるノードは、ツリーの最下位レイヤーにあります。 その上のレイヤーにはroute IDが含まれます。 すべての経路タイプを反復してからすべてroute IDを反復すると、ノードroutes -> [route_type] -> [route_id] -> [route_short_name]が存在して値がある(isDefinedが1を返す)場合は、探しているIDが[route_id]であることがわかります。
route_short_name = "44"
route = None
types = iris.iterator("routes").subscripts()
for type in types:
route_ids = iris.iterator("routes", type).subscripts()
for route_id in route_ids:
if iris.isDefined("routes", type, route_id, route_short_name) == 1:
route = route_id
if route is None:
print("No route found.")
import sys
sys.exit()
このコードは、ハードコーディングされたroute = "14334"の行の代わりになります
すべてのIRIS操作が完了したら、データベースへの接続を閉じることができます。
conn.close()
今後の内容
Python のネイティブAPIを使ってInterSystems IRIS のデータ構造にアクセスする方法を説明し、それをサンフランシスコの公共交通機関データに適用しました。 APIの詳細な説明については、ドキュメントをご覧ください。 ネイティブAPIは、Java、.NET、およびNode.jsで提供されています。
記事
Mihoko Iijima · 2020年10月25日
これはInterSystems FAQ サイトの記事です。
%Net.HttpRequest クラスの SSLConfiguration プロパティに SSL/TLS 構成の「クライアント」構成名が指定されているかご確認ください。
%Net.HttpRequest クラスを使用して、https の url にアクセスするためには、以下のドキュメントに記載されている SSL/TLS 構成 の「クライアント」構成を作成して指定した名前を SSLConfiguration プロパティに指定する必要があります。
SSL/TLS構成のクライアント構成方法
管理ポータルの [システム管理] > [セキュリティ] > [SSL/TLS構成] メニューを開き、「構成名」に任意名を設定し、「保存」ボタンをクリックします(そのほかの構成パラメータは、デフォルト値で作成します)。
実行例は以下の通りです(https://www3.nhk.or.jp/news/ にアクセスしています)。
set httprequest=##class(%Net.HttpRequest).%New()set httprequest.Https=1set httprequest.Server="www.nhk.or.jp"set httprequest.SSLConfiguration="test1" //設定したSSL/TSL構成名を指定set st=httprequest.Get()set st=httprequest.Get("/news/")write $ZCVT(httprequest.HttpResponse.Data.Read(1000),"I","UTF8") // 取得できたレスポンスの一部を表示<!DOCTYPE HTML><!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7 eq-ie6"> <![endif]--><!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8 eq-ie7"> <![endif]--><!--[if IE 8]> <html class="no-js lt-ie9 eq-ie8"> <![endif]--><!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# website: http://ogp.me/ns/website#"><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta http-equiv="X-UA-Compatible" content="requiresActiveX=true" />
<title>NHKニュースサイト 日本全国・世界の速報、最新情報|NHK NEWS WEB</title><meta name="robots" content="noodp,noarchive"><meta name="keywords" content="NHK,ニュース,NHK NEWS WEB" /><meta name="description" content="NHKのニュースサイト「NHK NEWS WEB」。国内外の取材網を生かし、さまざまな分野のニュース をいち早く、正確にお伝えします。ニュース
記事
Megumi Kakechi · 2021年3月10日
これは InterSystems FAQ サイトの記事です。
REST アプリケーションはステートレスであることが想定されています。
しかし、複数の REST 呼び出し間でデータを保持したい場合などの理由で、Webセッションを使用したい場合は、UseSession パラメータを使用することができます。
以下のように、Web アプリケーションのディスパッチクラスとして定義された %CSP.REST のサブクラスでUseSession パラメータを指定することで、CSPと同じようにWebセッションを使用することが可能となります。
Class REST.MyServices Extends %CSP.REST
{
Parameter UseSession As Integer = 1;
詳細は以下のドキュメントをご覧ください。
REST での Web セッションの使用
以下は、UseSession パラメータを使用した簡単なサンプルになります。最初に、2つのクラスを作成してください。
* REST.SessionTest.cls
Class REST.SessionTest Extends %CSP.REST
{
Parameter UseSession As Integer = 1;
XData UrlMap
{
<Routes>
<Route Url="/test/" Method="GET" Call="test"/>
</Routes>
}
ClassMethod test() As %Status
{
write "{""SessionId"":"""_%session.SessionId_"""}"
quit $$$OK
}
}
* REST.test.cls
Class REST.test Extends %CSP.Page
{
ClassMethod OnPage() As %Status
{
&html<
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<script type="text/javascript" src="http://code.jquery.com/jquery-2.2.4.js"></script>
<script type="text/javascript">
function go() {
$.ajax({
type:'GET',
dataType:'json',
url:'/csp/user/rest/test/',
success:function(data) {
ans = '';
for(var i in data) {
ans = ans + '\n' + JSON.stringify(data[i]);
}
alert(ans);
}
});
return;
}
</script>
</head>
<body>
<form name="xxx" method="post" action="">
<input type="button" name="test" onclick="go();" value="push" /></p>
</form>
Session ID: #(%session.SessionId)#<br>
</body>
</html>
サンプルの使用方法は以下になります。
1. 上記2つのクラスをUSERネームスペースに作成し、コンパイルする
2. ウェブアプリケーション /csp/user/rest を作成、 ディスパッチクラスに REST.SessionTest を設定、 セッションCookieパスを /csp/user に変更して保存
3. ブラウザで /csp/user/REST.test.cls を開いて、push ボタンを押す
記事
Megumi Kakechi · 2023年3月8日
これは InterSystems FAQ サイトの記事です。
^%GCMP ユーティリティにて2つのグローバルの内容を比較することができます。
例としてUSERとSAMPLESネームスペースにある、^testと^testを比較する場合は以下のようになります。※以下の例では、2つのネームスペースに全く同じグローバルを700個作り、その中の一つの中身を変えて検出対象としています。
USER>kill ^test // USERネームスペースに、比較元のグローバル作成(削除したあとに作成)
USER>for i=1:1:100 { for j=1:1:7 { set ^test(i,j)="テスト"_i } }
USER>zn "samples" // SAMPLESネームスペースに、比較先のグローバル作成(削除したあとに作成)
SAMPLES>kill ^test
SAMPLES>for i=1:1:100 { for j=1:1:7 { set ^test(i,j)="テスト"_i } }
SAMPLES>set ^test(50,5,1)=1 // SAMPLESネームスペースに作成したグローバルの一つを変更
SAMPLES>do ^%GCMP
Compare global ^test // 比較したいグローバル
on directory set: (this system) // Enter
in namespace: SAMPLES => // Enter(このネームスペースで良ければ)
with global ^test=> // 比較相手のグローバル
on directory set: (this system) // Enter
in namespace: SAMPLES => USER // 比較相手のネームスペース
Output differences on
Device: // 結果出力先、ターミナルで見る場合は <Enter> 押下
// ※ログファイル名をフルパスで書けば、そちらに出力される
Right margin: 80 =>
Compare global ^test in SAMPLES
with global ^test in USER
^test(50,5,1) exists in ^|"SAMPLES"|test but not in ^|"USER"|test // 相違のあるグローバルを検出
Time=.001822
SAMPLES>
同一インスタンス内ではなく、異なるサーバのインスタンス間での比較を行いたい場合は、^DATACHECKユーティリティを使用します。^DATACHECKユーティリティの使用方法については、以下の関連記事をご覧ください。
【関連】2つのデータベースにある複数のグローバルやルーチンを比較する方法
記事
Toshihiko Minamoto · 2020年9月9日
最近の大規模なベンチマーク活動で、アプリケーションのスケーリングに悪影響を与える過度の %sys CPU 時間が観察されました。
**問題**
TZ 環境変数が設定されていないため、 _localtime()_ システムコールに多くの時間が費やされていることがわかりました。 観察結果を確認するための単純なテストルーチンが作成されましたが、TZ が設定されている場合と TZ が未設定の場合とでは経過時間と必要な CPUリソースが驚くほど違っていました。 TZ が設定されていない場合、_localtime()_ から /etc/local_time への _stat()_ システムコールの継承使用は非常に負荷が高いことがわかりました。
**推奨事項**
InterSystems は、x86 または Linux on Power のいずれの Linux インストール環境でも、TZ 環境変数を適切に設定して最適なパフォーマンスを確保することを強く推奨しています。 詳細については、「man tzset」を参照してください。
現在の Caché 2016.1のフィールドテストでは日付および時刻関連の関数に関する最適化が行われており、初期テストでは大幅に改善していることがわかっています。 以下は、TZ が設定されていない場合に PowerPC上のLinuxで新しい内部関数の呼び出しをテストした結果の出力例です。
Caché 2016.1 FT 未満:
real 0m22.60s
user 0m1.64s
sys 0m20.89s
Caché 2016.1 FT の場合:
real 0m0.40s
user 0m0.37s
sys 0m0.00s
皆さんが TZ 環境変数に関してアプリケーションで経験したことや、TZ 環境変数の設定有無による Caché 2016.1 のフィールドテストへの影響についてコメントを投稿してください。
記事
Toshihiko Minamoto · 2021年8月24日
ソリューションを Amazon Web Services エコシステム、サーバーレスアプリケーション、または `boto3` を使用した Python スクリプトにスムーズに統合する方法を探している場合は、[IRIS Python Native API](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_PYNATIVE) を使用するのが最適です。 IRIS で何かを取得したり設定したりしてアプリケーションに素晴らしい機能を備える必要があるまで、本番の実装を大々的に構築する必要はありません。したがって、誰かにとって重要なものまたはあなた以外にはまったく重要でないもの(これも同じくらい重要なことです)を構築できるようにこの記事が役に立てればと思います。

これを実装する口実が必要であれば
* OAUTH2 ワークフローを実装する FHIR(R) ベースソリューションで Patient ID コンテキストを調べ、SHMAHT のトークンに挿入するために Pre Token Generation Trigger を使用する必要がある。
* インスタンスタイプ、ノードグループ、Toaster、または ECS クラスタに応じてプロビジョニング後の IRIS チューニング設定を行い、リングゼロで IRIS を実行したい。
* AWS CLI でシェルを終了することなく IRIS を管理できるスキルを Zoom で家族や友人に披露したい。
## ポイント
ここでは、IRIS と対話できる AWS lambda 関数を示し、それをプロビジョニングする方法とさまざまなキャパシティで操作する方法についての例をいくつか説明します。これに関する対話を持つことで、**物事をより簡単に実現できる pip を公開**できるようになることを願っています。
## お急ぎですか????

Stream をご覧ください。
## そうでない方は…
このお楽しみに参加するには、現在のフライトプランから項目をいくつか削除する必要があります。
### ネットワークの構築
IRIS の実行 OS は問いませんが、きっかり 2 つのサブネットを使用する AWS VPC で実行しており、IRIS を実行しているスーパーサーバーへのアクセスを許可するセキュリティグループが必要です。懐かしさと InterSystems が時間を掛けて [IANA](https://tools.ietf.org/html/rfc6335) に登録したという単なる事実からポート 1972 を使用していますが、時間を掛けずに新しいポートを `/etc/services` に追加する場合は、新しいマットレスのタグを引きちぎるのと同じくらいに大変な思いをすることでしょう。 私たちの場合、AWS v2 ネットワークロードバランサーに適切なヘルスチェックを備えた ec2 インスタンスのミラーセットです。

### インポートしたサンプルのクラス
どうにかして、このリポジトリのルートにクラスを作成して IRIS インスタンスの `%SYS` ネームスペースにインポートすることができました。 以下は、上記の出力を駆動するクラスの例です。 ここでクラスをインポートする必要がある理由に疑問を持っている場合は、以下の注意事項をご覧ください。Python で使用するラッパークラスをプロビジョニングすることがアプローチとして推奨されています。
> ドキュメントから抜粋した注意事項: これらのメソッドはクラスライブラリに定義されている InterSystems クラスと使用することもできますが、メソッドをユーザー定義クラスまたはルーチン内から間接的に呼び出す方法をベストプラクティスとしています。 多くのクラスメソッドはステータスコードのみを返し、実際の結果は引数に返されます(Native API はアクセスできません)。 システム定義関数(ObjectScript リファレンスの ObjectScript 関数に記載された関数)を直接呼び出すことはできません。
サンプルのクラス:
```
Class ZDEMO.IRIS.Lambda.Operations Extends %Persistent
{
ClassMethod Version() As %String
{
Set tSC = 0
Set tVersion = $ZV
if ( tVersion '="" ) { set tSC = $$$OK }
Set jsonret = {}
Set jsonret.status = tSC
Set jsonret.payload = tVersion
Quit jsonret.%ToJSON()
}
}
```
ここでは慣習に基づいて作業し、常に JSON オブジェクトをレスポンスとして返すことにしました。こうすればステータスを返すことも可能で、参照によって何かを返せばギャップを埋められるかもしれません。
### AWS アクセス
世界を変えようとしている Lambda 関数をプロビジョニングして呼び出せるように、IAM アクセスキーを取得してください。
飛行前チェック:
```
IRIS [ $$$OK ]
VPC [ $$$OK ]
Subnets [ $$$OK ]
Security Group [ $$$OK ]
IAM Access [ $$$OK ]
Imported Class [ $$$OK ]
```
$$$OK, **レスゴー**。
## Lambda 関数で使用する IRIS Native Python API をパッキング
AWS Lambda 関数は Linux の Boxen 上で実行するため、これは、特に Linux のみの場合に pip パッケージであれば素晴らしい部分です。 このコミットの時点では、API は pip 経由で使用できませんが、リソースは豊富に備えているため、独自のものを用意することができます。
```
mkdir iris_native_lambda
cd iris_native_lambda
wget https://github.com/intersystems/quickstarts-python/raw/master/Solutions/nativeAPI_wheel/irisnative-1.0.0-cp34-abi3-linux_x86_64.whl
unzip nativeAPI_wheel/irisnative-1.0.0-cp34-abi3-linux_x86_64.whl
```
`connection.config` を作成します。
例: [connection.config](https://raw.githubusercontent.com/basenube/iris_native_lambda/main/examples/connection.config)
ハンドラー `index.py` を作成するか、[demo github リポジトリ](https://github.com/basenube/iris_native_lambda)の examples フォルダにあるものを使用します。 デモバージョンでは、IRIS 接続情報に環境変数と外部ファイルの両方が使用されていることに注意してください。
例:
[index.py](https://raw.githubusercontent.com/basenube/iris_native_lambda/main/examples/index.py)
使用できるようどのように zip 圧縮するか。
```
zip -r9 ../iris_native_lambda.zip *
```
S3 バケットを作成し、関数の zip をそれにアップロードします。
```
cd ..
aws s3 mb s3://iris-native-bucket
s3 sync iris_native_lambda.zip s3://iris-native-bucket
```
> これで、AWS Lambda 関数として使用する API とハンドラーのパッケージ化は完了です。
では、コンソールをクリックして関数を作成するか、Cloudformation などを使用して作成します。
```
IRISAPIFunction:
Type: "AWS::Lambda::Function"
DependsOn:
- IRISSG
- VPC
Properties:
Environment:
Variables:
IRISHOST: "172.31.0.10"
IRISPORT: "1972"
NAMESPACE: "%SYS"
USERNAME: "intersystems"
PASSWORD: "lovetheyneighbor"
Code:
S3Bucket: iris-native-bucket
S3Key: iris_native_lambda.zip
Description: "IRIS Native Python API Function"
FunctionName: iris-native-lambda
Handler: "index.lambda_handler"
MemorySize: 128
Role: "arn:aws:iam::8675309:role/BeKindtoOneAnother"
Runtime: "python3.7"
Timeout: 30
VpcConfig:
SubnetIds:
- !GetAtt
- SubnetPrivate1
- Outputs.SubnetId
- !GetAtt
- SubnetPrivate2
- Outputs.SubnetId
SecurityGroupIds:
- !Ref IRISSG
```
大変な作業でしたが、これで Python でラムダ関数を使って IRI を呼び出せるようになりました。世界が大きく変わります。
## 実行!
上記の実装方法では、関数は、再利用できるように構造化されたイベントオブジェクトに渡すことが期待されています。この考え方は以下のイベントオブジェクトの例で確認できます。
```
{
"method": "Version",
# important, if method requires no args, enforce "none"
"args": "none"
# example method with args, comma seperated
# "args": "thing1, thing2"
}
```
コマンドラインの例に我慢できるのであれば、AWS CLI を使用して実行を確認できます。
```
(base) sween @ basenube-pop-os ~/Desktop/BASENUBE
└─ $ ▶ aws lambda invoke --function-name iris-native-lambda --payload '{"method":"Version","args":"none"}' --invocation-type RequestResponse --cli-binary-format raw-in-base64-out --region us-east-2 --profile default /dev/stdout
{{\"status\":1,\"payload\":\"IRIS for UNIX (Red Hat Enterprise Linux for x86-64) 2020.2 (Build 210U) Thu Jun 4 2020 15:48:46 EDT\"}"
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
```
さらに踏み込めば、AWS CLI はエイリアスをサポートしているため、エイリアスを作成して AMS コマンドに完全に統合してみることができます。 以下に CLI エイリアスの例を示します。
```
└─ $ ▶ cat ~/.aws/cli/alias
[toplevel]
whoami = sts get-caller-identity
iris =
!f() {
aws lambda invoke \
--function-name iris-native-lambda \
--payload \
"{\"method\":\""${1}"\",\"args\":\"none\"}" \
--invocation-type RequestResponse \
--log-type None \
--cli-binary-format raw-in-base64-out \
gar.json > /dev/null
cat gar.json
echo
echo
}; f
```
ぜひお試しあれ!

それではお元気で!技術的な議論をお待ちしてます!
記事
Toshihiko Minamoto · 2022年10月19日
しばらく前、IRIS 用 Django の新しいドライバーを導入しました。 そこで実際に、IRIS で Django を使用する方法を見てみましょう。

_重要な注意事項: IRIS Community Edition で Django を使用してもほぼまったく動作しません。Community Edition には利用できる接続が 5 つしかなく、Django がすぐに使い果たしてしまうためです。 残念ながらこの理由により、ライセンスの使用状況を予測するのが難しいため、新しいアプリケーションの開発にはこの方法をお勧めできません。_
## Django プロジェクトを開始する
新しい Django プロジェクトを開始しましょう。そのためにはまず、Django そのものをインストールする必要があります。
pip install django
次に、demo という名前のプロジェクトを作成します。プロジェクトフォルダが同じ名前で作成されます。
django-admin startproject demo
cd demo
または、既存のフォルダを使用することもできます。
django-admin startproject main .
このコマンドにより、いくつかの Python ファイルが作られます。
.png)
ファイルの説明:
* **manage.py:** この Django プロジェクトを様々な方法で操作するためのコマンドラインユーティリティ
* **main** ディレクトリ: プロジェクトの実際の Python パッケージ
* **main/\_\_init\_\_.py:** このディレクトリを Python パッケージと見なすように Python に伝達する空のファイル
* **main/settings.py**: この Django プロジェクトの設定/構成
* **main/urls.py**: この Django プロジェクトの URL 宣言。Django で作られたサイトの「目次」。
* **main/asci.py**: プロジェクトにサービスを提供するための ASGI 対応ウェブサーバーのエントリポイント。
* **main/wsci.py**: プロジェクトにサービスを提供するための WSGI 対応ウェブサーバーのエントリポイント。
このポイントからでもプロジェクトを開始することが可能で、何とか機能します。
$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced). You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
July 22, 2022 - 15:24:12
Django version 4.0.6, using settings 'main.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
ブラウザに移動して、URL http://127.0.0.1:8000 を開きます。
.png)
## IRIS を追加する
IRIS へのアクセスを追加しましょう。それには、プロジェクトに依存関係をいくつかインストールする必要があります. 正しい方法は、_以下の内容で、django を依存関係として追加する requirements.txt_ というファイルに定義することです。
# Django itself
django>=4.0.0
次は、公開されている Django の IRIS 用ドライバーです。 _残念ながら、InterSystems には独自のドライバーを PyPI に公開する意向がないため、以下の方法でそれを定義しなければなりません。 これがいつ削除されるかわからないため、将来的には動作しない可能性もあるので注意してください。 (pypi にあるのであれば、django-iris の依存関係としてインストールされ、明示的に定義される必要がないかもしれません)_
# InterSystems IRIS driver for Django, and DB-API driver from InterSystems
django-iris==0.1.13
https://raw.githubusercontent.com/intersystems-community/iris-driver-distribution/main/DB-API/intersystems_irispython-3.2.0-py3-none-any.whl
このファイルに定義されている依存関係を次のコマンドでインストールします。
pip install -r requirements.txt
次に、プロジェクトが IRIS を使用するように構成できます。それには、_settings.py_ ファイルの **DATABASES** パラメーターを以下のような行で更新する必要があります。NAME は IRIS のネームスペース、PORT は IRIS を利用できる SuperPort を指します。
DATABASES = {
'default': {
'ENGINE': 'django_iris',
'NAME': 'USER',
'USER': '_SYSTEM',
'PASSWORD': 'SYS',
'HOST': 'localhost',
'PORT': 1982,
}
}
Django には ORM と、プロジェクトに格納されたモデルがあり、Django モデルと Database をテーブルとして同期する必要があります。 デフォルトでは、auth に関連するモデルがいくつかあります。 ここで、migrate を実行できます。
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
IRIS に移動すると、追加のテーブルがあります。
.png)
## さらにモデルを定義する
では、モデルを追加しましょう。 それには、次のような内容で _models.py,_ という新しいファイルを追加します。
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
dob = models.DateField()
sex = models.BooleanField()
見てのとおり、様々な型のフィールがあります。 次に、このモデルを Database 用に準備する必要がありますが、 その前に、**main** プロジェクトを _settings.py_ の **INSTALLED_APPS** に追加します。
INSTALLED_APPS = [
....
'main',
]
次に、makemigrations を行います。 このコマンドは、モデルに変更があった後に呼び出される必要があります。モデルの過去の変更に対応し、インストールされているアプリケーションのバージョンに関係なく、migration はデータベースのスキーマをどのように更新すべきかを認識しています。
$ python manage.py makemigrations main
Migrations for 'main':
main/migrations/0001_initial.py
- Create model Person
もう一度 migrate を実行しますが、以前の migrations がすでに実行されたことをわかっているため、新しいものだけが実行されます。
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, main, sessions
Running migrations:
Applying main.0001_initial... OK
そして実際に、SQL ビューから migration がどのように見えるかを確認できます。
$ python manage.py sqlmigrate main 0001
--
-- Create model Person
--
CREATE TABLE "main_person" ("id" BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY, "first_name" VARCHAR(30) NULL, "last_name" VARCHAR(30) NULL, "dob" DATE NOT NULL, "sex" BIT NOT NULL);
ただし、作業アプリケーションがすでに存在する場合などには、データベースにすでに存在するテーブルにアクセスすることが可能です。 zpm package posts-and-tags がインストールされているので、community.posts テーブルのモデルを作成しましょう。
$ python manage.py inspectdb community.post
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
class CommunityPost(models.Model):
id = models.AutoField(db_column='ID') # Field name made lowercase.
acceptedanswerts = models.DateTimeField(db_column='AcceptedAnswerTS', blank=True, null=True) # Field name made lowercase.
author = models.CharField(db_column='Author', max_length=50, blank=True, null=True) # Field name made lowercase.
avgvote = models.IntegerField(db_column='AvgVote', blank=True, null=True) # Field name made lowercase.
commentsamount = models.IntegerField(db_column='CommentsAmount', blank=True, null=True) # Field name made lowercase.
created = models.DateTimeField(db_column='Created', blank=True, null=True) # Field name made lowercase.
deleted = models.BooleanField(db_column='Deleted', blank=True, null=True) # Field name made lowercase.
favscount = models.IntegerField(db_column='FavsCount', blank=True, null=True) # Field name made lowercase.
hascorrectanswer = models.BooleanField(db_column='HasCorrectAnswer', blank=True, null=True) # Field name made lowercase.
hash = models.CharField(db_column='Hash', max_length=50, blank=True, null=True) # Field name made lowercase.
lang = models.CharField(db_column='Lang', max_length=50, blank=True, null=True) # Field name made lowercase.
name = models.CharField(db_column='Name', max_length=250, blank=True, null=True) # Field name made lowercase.
nid = models.IntegerField(db_column='Nid', primary_key=True) # Field name made lowercase.
posttype = models.CharField(db_column='PostType', max_length=50, blank=True, null=True) # Field name made lowercase.
published = models.BooleanField(db_column='Published', blank=True, null=True) # Field name made lowercase.
publisheddate = models.DateTimeField(db_column='PublishedDate', blank=True, null=True) # Field name made lowercase.
subscount = models.IntegerField(db_column='SubsCount', blank=True, null=True) # Field name made lowercase.
tags = models.CharField(db_column='Tags', max_length=350, blank=True, null=True) # Field name made lowercase.
text = models.CharField(db_column='Text', max_length=-1, blank=True, null=True) # Field name made lowercase.
translated = models.BooleanField(db_column='Translated', blank=True, null=True) # Field name made lowercase.
type = models.CharField(db_column='Type', max_length=50, blank=True, null=True) # Field name made lowercase.
views = models.IntegerField(db_column='Views', blank=True, null=True) # Field name made lowercase.
votesamount = models.IntegerField(db_column='VotesAmount', blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'community.post'
**managed = False** にマークされているため、makemigrations と migrate はこのテーブルで動作しません。 テーブル名を省略すると、Django が以前に作成したテーブルも含む、大規模なモジュールのリストが生成されます。
記事
Megumi Kakechi · 2021年2月15日
これは InterSystems FAQ サイトの記事です。
「特権ルーチンアプリケーション」を使用し、コード中に $system.Security.AddRoles()メソッドを使用してロールを付与する仕組みを利用します。
※ロールベースで必要な特権を付与するため、予め特定の特権を持ったロールを作成する必要があります。
詳細は、以下ドキュメントをご参照ください。特権ルーチン・アプリケーション【IRIS】特権ルーチン・アプリケーションについて
例えば、特定ルーチン(またはクラスメソッド)実行時のみデータベースの更新を許可するための設定は、以下のとおりです。
(接続するデータベースに対してはREAD許可だけを持ち、あるルーチン実行時のみデータベースに対するREAD/WRITE許可を持つように設定します。)
1) データベース:Aのリソース定義を確認する
データベース:Aのリソースに %DB_%DEFAULT が設定されている場合は、独自リソースを作成します。
(データベースに割り当てられたリソースの確認は、管理ポータル > [システム管理] > [構成] > [システム構成] > [ローカルデータベース] > リソース で確認できます)
リソース作成は、管理ポータル > [システム管理] > [セキュリティ] > [リソース] > 新規リソース作成ボタン押下 データベースリソースは命名規則があり、 %DB_データベース名 を指定します。
例の場合は、 %DB_A を登録します。このとき、パブリック許可は全てチェックを外します。
リソース作成が完了したら、管理ポータル > [システム管理] > [構成] > [システム構成] > [ローカルデータベース] から設定対象のデータベース設定を開き、リソース名を変更し、保存します。
2) READ許可のみを付与するロールを作成する
管理ポータル > [システム管理] > [セキュリティ] > [ロール] > 新規ロール作成ボタン押下
ロール名を記入し、保存します。(ロール名を R1 とします。)
作成したロールに特権(リソースに対する許可の組み合わせ)を登録するため、追加ボタンを押下し、利用可能なリソース一覧から、1)で確認したリソース名を選択します。
選択後、リソースの許可をRのみに変更し、ロールを保存します。
3) アクセスするユーザのログインロールを確認する。
例では、ターミナルでのルーチン(またはクラスメソッド)実行で確認を行うため、ターミナルログイン時にユーザ認証を行うよう、適切なサービス(Windowsなら%Service_Console)の認証方法に「パスワード」を追加します。
任意ユーザに対して、%Developerロールと 2) で作成したロールを付与します。
4) 特権ルーチンアプリケーションの作成
実行時にREAD/WRITE許可を追加するための「特権ルーチンアプリケーション」を作成します。
管理ポータル > [システム管理] > [セキュリティ] > [アプリケーション] > [特権ルーチンアプリケーション] > 作成用ボタン押下
特権ルーチンアプリケーション名を指定し保存します。(特権ルーチンアプリケーション名を TR1 とします。)
ルーチンタブを開き、ルーチン(またはクラスメソッド)があるデータベースの指定と、名前の指定を行います。
続いて、アプリケーション・ロールタブを開き、データベース:A のDBロール(%DB_Aロール)を付与します。
※ DBロールはDBリソース作成時、自動的に作成されるロールで、DBリソースに対するREAD/WRITE許可を持つロールです。
5) コード内でロールを付与する。
4)で作成した特権ルーチン名を使用して、以下メソッドを追記します。
set st=$system.Security.AddRoles("TR1")
【実行例】
st() public { try { new $roles write "現在の値:",^ABC,! set st=$system.Security.AddRoles("TR1") // ロールの追加 write $roles,! set ^ABC="変更" write "現在の値(更新後):",^ABC,! } catch ex { zwrite ex }}
《ターミナル実行例》
A>set ^ABC="かきくけこ" // ログイン時、READ許可のみのためエラー発生SET ^ABC="かきくけこ"^^ABC,c:\intersystems\cache\mgr\a\A>write ^ABCテストA>do st^Test() // ルーチン実行期間のみ更新可現在の値:テスト%Developer,%DB_A,R1現在の値(更新後):変更
記事
Mihoko Iijima · 2021年4月15日
これは InterSystems FAQ サイトの記事です。
このエラーが発生する原因としては、アプリケーションの中で既に他のプロセスからロック対象リソースがロックされていて、何らかの理由でそのロックが解放されていないケースが考えられます。
他のプロセスがロックしている兆候がない場合は、ロックテーブルの空き領域が不足しているケースが想定されます。その場合は、メッセージログ(コンソールログ)に LOCK TABLE FULL のメッセージが出力されます。
トランザクション処理を行なっている場合には、ロック解放の延期が影響しているケースも考えられます。トランザクションとロック解放の延期については、以下のドキュメントをご参照下さい。
ドランザクション処理について【IRIS】
ドランザクション処理について
また、トランザクション中に、同一テーブルに対する大量レコードのSQL 文による更新がある場合、ロックしきい値(既定値は1000)に到達してロックエスカレーションが発生し、その結果として、テーブルロック状態になっている可能性もあります。
このように、ロックタイムアウトエラーの原因は幾つか考えられますので、まずは、管理ポータル(バージョン2010.2以前では、[システム管理ポータル])の、ロックメニューにて、現在のロックの状態をご確認下さい。
【バージョン2011.1以降】管理ポータル: [システムオペレーション] > [ロック]
【バージョン2010.2以前】システム管理ポータル: [運用] > [ロック]
関連する記事:SQLのUPDATEやDELETEで、ある程度の数のレコードを一括更新するとSQLエラー -110 が発生します。
記事
Tomoko Furuzono · 2021年6月15日
これは、InterSystems FAQサイトの記事です。
IISおよびWebゲートウェイの設定方法は以下のとおりです。
(1) IIS構成で以下の設定を行いますアプリケーション /rest を作成し、ハンドラーマッピングで * = CSPms のみ定義します。
IIS構成 > Default Website > アプリケーション /rest > "ハンドラーマッピング" をダブルクリック 要求パス: * モジュール: CSPms 名前: CSPGW (任意の名前)
要求の制限 > [要求のマップ先が次の場合のみハンドラーを呼び出す] のチェックはオフにします。※こちらの設定はデフォルトでは「チェックあり」になっていますのでご注意ください。
この構成により、IIS は /rest アプリケーションをWebゲートウェイに転送します。
(2) Webゲートウェイの構成で以下の設定を行いますWeb Gateway のアプリケーションアクセス から /rest を追加します。→既存アプリケーションをクリックしてコピーし、アプリケーションパスを /rest に設定&IRISサーバを指定します。
この構成により、Webゲートウェイは /rest アプリケーションをIRISサーバに転送します。
(3) IRISサーバの構成で以下の設定を行います管理ポータルを開き、 システム構成 > セキュリティ > アプリケーション > ウェブ・アプリケーションより /rest アプリケーションを追加します。RESTのクラスをディスパッチクラスに指定します。
この構成により、IRIS は /rest アプリケーションを対象ネームスペースに転送し、対象ディスパッチクラスを呼び出します。
(4) IIS経由で以下のように呼び出します。http://localhost/rest/test
サンプルコード:
Class User.MyREST Extends %CSP.REST{XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]{<Routes><Route Url="/test" Method="GET" Call="test"/>Routes>} ClassMethod test() As %Status{set %response.ContentType="application/json"set res={"abc":"123"}write res.%ToJSON()quit $$$OK} }
お知らせ
Toshihiko Minamoto · 2022年10月4日
開発者の皆さんこんにちは。
開発者コミュニティの新たな機能として開発者による重要な質問をご紹介します。
インターシステムズデータプラットフォームに関連する質問のうち、最もインパクトがあると思われるものに「Key Question」タグを追加します。
毎月、インターシステムズエキスパートがそのような質問を選び、その著者は栄誉ある賞を受賞することになります。
どのような栄誉なのかはつづきをご覧ください。
Key Questionタグとは?
InterSystems Data Platforms を使用したソリューションの開発、テスト、デプロイ、管理などに関する質問のうち、最も洞察に満ちた、興味深い、難問、重要などと思われるものに焦点を当てたタグです。
誰が決定するの?
インターシステムズエキスパートが開発者コミュニティの質問をレビューし、Key Questionとするかどうかを決定します。
どのような質問をレビューするの?
A開発者コミュニティ上のすべての質問は考慮されます。
Key Questions が更新頻度は?
毎月、エキスパートがKey Questionタグをつける質問を選びます。
どのような栄誉が与えられるの?
A Key Questionと呼ばれる新たなバッジがグローバルマスターズで与えられます。
開発者コミュニティでのハイライトとして@DC.Administration からKey Questionとして選ばれた質問と共にコメントがスレッドに出されます。
質問で Inquisitive Summer on Developer Community によるポイント2倍をゲットしましょう!もしかしたらあなたの質問がエキスパートにKey Questionとして選ばれるかもしれません。
Key Questionタグを購読して 今後にご期待ください!
記事
Mihoko Iijima · 2023年2月16日
開発者の皆さん、こんにちは!
InterSystems デベロッパーツールコンテスト2023 の21の応募作品の中から、Experts Nomination 第2位に輝いた @John Murray さんの DX Jetpack for VS Code (VSCodeを使用するIRIS開発者のエクスペリエンスを向上させるツール)についてご紹介します。
開発された @John Murray さんが書かれた記事「Introducing DX Jetpack for VS Code」には、3つの新しいエクステンションについて紹介されていますが、ここでは、gj :: codeSpex と ObjectScript Class View の使用例をご紹介します。
まずは、VSCodeをご用意ください。
Extentionの検索窓で、gj と入力する以下の表示になります。
この中から、をインストールしてみました。
試しにクラス定義を作成してみたところ、行番号右隣に吹き出しのマークが登場し、クリックするとクラス定義で使用しているデータタイプの解説が表示されました。
クラスメソッドの場合は、そのメソッドで使用している引数や戻り値のデータタイプを表示してくれました。
クラスリファレンスをいちいち開いて確認しなくても、エディタ上で確認ができて便利ですね。
続いて、ObjectScript Class Viewをインストールしてみます。Extensionの検索窓に「ObjectScript」と記入すると候補が出てきます。
コードエディタ上で参照したいクラスを右クリックし、を選択すると、選択したクラスの継承関係とそのクラスメンバが画面左に表示されます。
これも、どんなメンバをクラスが保持しているか確認するのに、クラスリファレンスを開かなくても確認できるので便利ですね!
VSCode上で自由にインストールできますので、ぜひ皆さんのお手元でもお試しください!
記事
Toshihiko Minamoto · 2023年4月18日
今回は、「IRIS for Health Contest」に応募するために、どのような技術を使ってアプリケーションを開発していたのか、その詳細を紹介したいと思います。
- OpenAPI仕様からのREST API生成
- APIとWebページを保護する役割ベースのアクセス(RBAC)
- InterSystems FHIR サーバー
内容
- [アプリケーション概要](#application-overview)
- [OpenAPI仕様からのREST API生成](#rest-api-generation-from-openapi-specification)
- [APIとWebページを保護する役割ベースのアクセス(RBAC)](#role-based-access-control--rbac--to-protect-api-and-web-pages)
* [REST APIの安全性確保](#securing-r-e-s-t-a-p-is)
* [Webページの安全性確保](#securing-web-pages)
* [リソースと役割の作成](#creating-resources-and-roles)
- [InterSystems FHIR サーバー](#intersystems-fhir-server)
## アプリケーション概要
まず、それらの技術に支えられたアプリケーションを簡単に紹介します。
このアプリケーションは、妊娠中の女性が簡単に症状を報告できるように設計されています。このアプリケーションはレスポンシブであるため、モバイルデバイスを使用して症状を簡単に報告することができます。このような症状は、[FHIR Observation リソース](https://build.fhir.org/observation.html) の[InterSystems FHIR サーバー](https://docs.intersystems.com/services/csp/docbookj/DocBook.UI.Page.cls?KEY=PAGE_fas)を使って記録されます。.
患者と医師は、通常のリレーショナル・テーブルを使用し、[患者](https://build.fhir.org/patient.html) と [医師](https://www.hl7.org/fhir/practitioner.html) のFHIRリソースのIDを参照して連携しています。そのため、医師は患者がどのような症状を訴えているのかを確認することもでき、万が一の事態に迅速に対応することができます。
アプリケーションは、IRIS [リソース](https://docs.intersystems.com/irisforhealthlatest/csp/docbookj/DocBook.UI.Page.cls?KEY=GAUTHZ_RSRCS) と [役割](https://docs.intersystems.com/irislatest/csp/docbookj/DocBook.UI.Page.cls?KEY=AFL_RBAC)を使用して患者/医師を識別し、アクセス権限を制御します。
FHIRリソースは、アプリケーションのフロントエンドで利用可能な[REST API](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_intro)によってアクセスされます。IRIS Interoperability Credentialsに格納されたAPI KEYを使用して、FHIRサーバーへのHTTPS接続が確立されます。
アプリケーションのWebリソースは、[IRIS Web Gateway](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCGI_intro)で扱われます。

## OpenAPI仕様からのREST API生成
IRISプラットフォームでは、手動または[OpenAPI仕様](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md)を介してRESTインターフェースを定義することができます。
OpenAPIを使用することは、デザインファーストのアプローチ、容易な変更追跡、容易な文書化、強力なモデル、設計、モッキング、テストなどのための多くのツールなど、多くの利点があります。
そこで、IRIS REST Servicesを使って、OpenAPI仕様からコードを生成する方法に焦点を当てます。
まず、OpenAPIを使ってAPIを設計する必要があります。今回は、VS Codeの拡張機能である[OpenAPI (Swagger) Editor](https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi)を使用しました。これは、エンドポイントやその他のOpenAPIリソースをVS Codeで直接作成するのに役立ちます。

OpenAPI仕様でAPIを設計したら、それをJSONファイルに保存する必要があります。ここでは、[このファイル](https://github.com/jrpereirajr/pregnancy-symp-tracker-fhir-app/blob/master/src/openapi/pregnancy-symptoms-tracker.json)にAPIを保存しています。
これで、IRIS REST Services を使用して API 用のコードを生成する準備が整いました。これには3つのオプションがあります。
- REST service[/api/mgmnt](https://docs.intersystems.com/irislatest/csp/docbookj/DocBook.UI.Page.cls?KEY=GREST_apimgmnt)を使用
- [`^%REST`ルーチン](https://docs.intersystems.com/irislatest/csp/docbookj/DocBook.UI.Page.cls?KEY=GREST_routine)を使用
- [`%REST.API` class](https://docs.intersystems.com/irislatest/csp/docbookj/DocBook.UI.Page.cls?KEY=GREST_objectscriptapi)を使用
この記事では、最後の1つである `%REST.API` クラスを使用することにします。それでは、IRIS端末を開いて、以下のコードを実行してください。
```
Set applicationName = "dc.apps.pregsymptracker.restapi"
Set swagger = "/irisrun/repo/src/openapi/pregnancy-symptoms-tracker.json"
ZW ##class(%REST.API).CreateApplication(applicationName, swagger, , .newApplication, .internalError)
ZW newApplication
ZW internalError
```
OpenApi仕様のJSONファイルの場所は、`swagger`パラメータで設定されます。
`applicationName` パラメータは、IRIS REST Servicesが生成されたコードを格納するパッケージ名です。

3つのクラスが生成されます。
- `spec.cls`: OpenAPI仕様のための単なるコンテナです。このクラスは編集しないでください。
- `impl.cls`: メソッドの実装を含むメインクラスです。このクラスは、APIロジックを開発するために、自分で編集することを意図しています。ヒント: OpenAPIのメソッドの名前は、必ずIRIS拡張属性 `operationId` を使って、[ ここで ](https://github.com/jrpereirajr/pregnancy-symp-tracker-fhir-app/blob/master/src/openapi/pregnancy-symptoms-tracker.json#L21)のように定義します。この属性を使用しない場合、IRIS はランダムな名前のメソッドを作成します。
- `disp.cls`: ディスパッチクラスは、IRIS で REST API を公開するために Web アプリケーションをバインドするクラスです。ヒント: このクラスを表示するには、生成されたアイテムを表示していることを確認します。このクラスを編集することもできますが、あまりお勧めできませんが、IRIS に任せてください。

最後の2つのパラメータ、 `newApplication` と `internalError` は出力パラメータで、それぞれ API が作成または更新されたかどうか、そして OpenAPI のパースやクラスの生成時に発生した可能性のあるエラーを返すものです。この情報をチェックするために書き出すだけです。
OpenAPI の仕様を更新した場合、コードを更新するために `CreateApplication` メソッドを再度実行する必要があります。 `impl` クラスに実装した以前のロジックコードはそのまま残し、IRIS REST Service が修正を行った箇所にはコメントを追加します。
## APIとWebページを保護する役割ベースのアクセス(RBAC)
前述したように、このアプリケーションには、患者と医師の2種類のユーザーが存在します。そこで、この2種類のユーザー間でアプリケーションのリソースに対するアクセスルールを設計するために、リソースと役割を使用しました。
ユーザーは役割を与えられ、役割にはリソースへの権限があり、リソースは例えばREST APIのようなシステムリソースにアクセスするために必要であるべきです。
### REST APIの安全性確保
IRIS REST Serviceでは、OpenAPIのIRIS拡張子である `x-ISC_RequiredResource` 属性によって、サービスにアクセスするために必要な権限を指定することができました。この属性は、API全体、または特定のエンドポイントに対して、次のように指定することができます:
```javascript
"paths": {
"/symptom": {
"post": {
"operationId": "PostSymptom",
"x-ISC_RequiredResource": ["AppSymptoms:write"],
"description": "患者さんが自分の症状を報告するために使用する",
…
"/doctor/patients": {
"get": {
"operationId": "GetDoctorPatientsList",
"x-ISC_RequiredResource": ["AppAccessDoctorPatients:read"],
"description": "現在ログインしている医師の患者を取得する",
…
```
OpenAPI仕様でAPIクラスを生成した後 - [ 前に説明したように ](#rest_api_generation_from_openapi_specification), IRIS REST Serviceが`x-ISC_RequiredResource`制約を`disp`クラスでどのように実装しているかを見ることができます:
```objecscript
ClassMethod PostSymptom() As %Status
{
Try {
Set authorized=0
Do {
If '$system.Security.Check("AppSymptoms","write") Quit
Set authorized=1
} While 0
If 'authorized Do ##class(%REST.Impl).%ReportRESTError(..#HTTP403FORBIDDEN,$$$ERROR($$$RESTResource)) Quit
…
} Catch (ex) {
Do ##class(%REST.Impl).%ReportRESTError(..#HTTP500INTERNALSERVERERROR,ex.AsStatus(),$parameter("dc.apps.pregsymptracker.restapi.impl","ExposeServerExceptions"))
}
Quit $$$OK
}
```
RBACを使ったAPIの保護方法については、[このページ](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_SECURING)をご覧ください。
### Webページの安全性確保
このアプリケーションでは、Web アプリケーションを実装するために CSP ページを使用しました。この技術は、現在の SPA に比べて古いと考えられていますが、それでもまだ利点があります。
例えば、ユーザーがページにアクセスするために、特定の役割を持つことを要求することができます。つまり、REST API のエンドポイントを保護することをまとめると、先に述べたように、アプリケーションに追加のセキュリティ・レイヤーを定義することができるのです。
ユーザがシステムにログインするとき、そのユーザに役割が割り当てられている場合、IRISはそのユーザに役割を割り当てます。このような役割は、CSP コンテキストで `$ROLE` コンテキスト変数を通してアクセスすることができ、ユーザに割り当てられた特定の役割を要求するために使用することができます。
```
<script language="cache" method="OnPreHTTP" arguments="" returntype="%Boolean">
Do ##class(dc.apps.pregsymptracker.util.Util).AssertRole("AppPatient")
Return 1
```
```
<script language="cache" method="OnPreHTTP" arguments="" returntype="%Boolean">
Do ##class(dc.apps.pregsymptracker.util.Util).AssertRole("AppDoctor")
Return 1
```
```objecscript
ClassMethod AssertRole(pRole As %String)
{
If ('$Find($ROLES, pRole)){
Set %response.Redirect = "NoPrivilegesPage.csp"
}
}
```
もし、現在のユーザーが `patient.csp` ページを評価するときに `AppPatient` 役割を持っていない場合、IRIS Web サーバはそのユーザーを `NoPrivilegesPage.csp` ページにリダイレクトし、ユーザーにセキュリティ問題を通知するメッセージを表示します。doctor.cps`ページも同様ですが、今度は `AppDoctor` 役割が必要です。
この例では、`AppPatient`と`AppDoctor`の2つの役割を持つことができます。つまり、そのユーザーは患者であると同時に医師でもあり、両方のページにアクセスすることができるのです。
### リソースと役割の作成
IRISポータルで[リソース](http://localhost:64756/csp/sys/sec/%25CSP.UI.Portal.Resources.zen), [役割](http://localhost:64756/csp/sys/sec/%25CSP.UI.Portal.Roles.zen) を作成し、[ユーザー](http://localhost:64756/csp/sys/sec/%25CSP.UI.Portal.Users.zen) に割り当てることができます - これは簡単なことです。しかし、ここではプログラムでそれらを作成する方法を紹介したいと思います:
```objecscript
ClassMethod CreateResources()
{
Do ..Log("アプリケーションリソースを作成する...")
Set ns = $NAMESPACE
Try {
ZN "%SYS"
Do $CLASSMETHOD("Security.Resources", "Delete", "AppSymptoms")
Do $CLASSMETHOD("Security.Resources", "Delete", "AppAccessDoctorPatients")
$$$TOE(st, $CLASSMETHOD("Security.Resources", "Create", "AppSymptoms", "患者の症状", "RWU", ""))
$$$TOE(st, $CLASSMETHOD("Security.Resources", "Create", "AppAccessDoctorPatients", "患者のアクセス権", "RWU", ""))
} Catch(e) {
ZN ns
Throw e
}
ZN ns
}
ClassMethod CreateRoles()
{
Do ..Log("アプリケーション役割を作成する...")
Set ns = $NAMESPACE
Try {
ZN "%SYS"
Do $CLASSMETHOD("Security.Roles", "Delete", "AppPatient")
Do $CLASSMETHOD("Security.Roles", "Delete", "AppDoctor")
$$$TOE(st, $CLASSMETHOD("Security.Roles", "Create", "AppPatient", "アプリケーション での患者の役割", "AppSymptoms:RWU", ""))
$$$TOE(st, $CLASSMETHOD("Security.Roles", "Create", "AppDoctor", "アプリケーション での医師の役割", "AppSymptoms:RWU,AppAccessDoctorPatients:RWU", ""))
} Catch(e) {
ZN ns
Throw e
}
ZN ns
}
ClassMethod CreateUsers()
{
Do ##class(dc.apps.pregsymptracker.util.Setup).Log("サンプルユーザーを作成する...")
//ある患者
&SQL(drop user MarySmith)
&SQL(create user MarySmith identified by 'marysmith')
&SQL(grant %DB_IRISAPP, %DB_IRISAPPSECONDARY, AppPatient to MarySmith)
&SQL(grant select on schema dc_apps_pregsymptracker_data to MarySmith)
//他患者
&SQL(drop user SuzieMartinez)
&SQL(create user SuzieMartinez identified by 'suziemartinez')
&SQL(grant %DB_IRISAPP, %DB_IRISAPPSECONDARY, AppPatient to SuzieMartinez)
&SQL(grant select on schema dc_apps_pregsymptracker_data to SuzieMartinez)
//ある医師
&SQL(drop user PeterMorgan)
&SQL(create user PeterMorgan identified by 'petermorgan')
&SQL(grant %DB_IRISAPP, %DB_IRISAPPSECONDARY, AppDoctor to PeterMorgan)
&SQL(grant select on schema dc_apps_pregsymptracker_data to PeterMorgan)
// 患者である医師
&SQL(drop user AnneJackson)
&SQL(create user AnneJackson identified by 'annejackson')
&SQL(grant %DB_IRISAPP, %DB_IRISAPPSECONDARY, AppDoctor, AppPatient to AnneJackson)
&SQL(grant select on schema dc_apps_pregsymptracker_data to AnneJackson)
}
```
## InterSystems FHIR サーバー
[InterSystems FHIR サーバー](https://docs.intersystems.com/services/csp/docbook/DocBook.UI.Page.cls?KEY=PAGE_fas) は、IRIS for Health と同じように、FHIR リソースへのアクセスをクラウド上で提供するサービスです。
FHIR ServerではOAuth2が可能で、[SMART on FHIR JavaScript Library](http://docs.smarthealthit.org/client-js/) などのライブラリを使ってアプリケーションから直接FHIRリソースにアクセスできますが、このアプリケーションでは、FHIR Serverをメインデータリポジトリとして使用しながら、IRISにローカルに保存されているメタデータで制御するハイブリッドアプローチを選択しました。
そこで、バックエンドがFHIR ServerでFHIRトランザクションを実行するために使用するFHIRクライアントを作成しました。このクライアントは、サーバーが生成したAPI KEYを使用して、FHIR ServerへのHTTPSコールを実行するために、`%Net.HttpRequest`を使用して実装されています。
これはHTTPクライアントのコードで、 `` を使って基本的なHTTP動詞を実装しています。
```objecscript
Class dc.apps.pregsymptracker.restapi.HTTPClient Extends %RegisteredObject
{
Property Request As %Net.HttpRequest;
Property Server As %String;
Property Port As %String;
Property UseHTTPS As %Boolean;
Property SSLConfig As %String;
Property APIKeyCred As %String;
Method CreateRequest()
{
Set ..Request = ##class(%Net.HttpRequest).%New()
Set ..Request.Server = ..Server
Set ..Request.Port = ..Port
Set ..Request.Https = ..UseHTTPS
If (..UseHTTPS) {
Do ..Request.SSLConfigurationSet(..SSLConfig)
}
}
Method SetHeaders(headers As %DynamicObject)
{
Set headersIt = headers.%GetIterator()
While (headersIt.%GetNext(.headerName, .headerValue)) {
Do ..Request.SetHeader(headerName, headerValue)
}
}
Method GetApiKeyFromEnsCredentials() As %String
{
Set apiKeyCred = ..APIKeyCred
$$$TOE(st, ##class(Ens.Config.Credentials).GetCredentialsObj(.apiKeyCredObj, "", "Ens.Config.Credentials", apiKeyCred))
Return apiKeyCredObj.Password
}
Method HTTPGet(pPath As %String) As %Net.HttpResponse
{
Do ..CreateRequest()
$$$TOE(st, ..Request.Get(pPath))
Set response = ..Request.HttpResponse
Return response
}
Method HTTPPost(pPath As %String, pBody As %DynamicObject) As %Net.HttpResponse
{
Do ..CreateRequest()
Do ..Request.EntityBody.Clear()
Do ..Request.EntityBody.Write(pBody.%ToJSON())
$$$TOE(st, ..Request.Post(pPath))
Set response = ..Request.HttpResponse
Return response
}
Method HTTPPut(pPath As %String, pBody As %DynamicObject) As %Net.HttpResponse
{
Do ..CreateRequest()
Do ..Request.EntityBody.Clear()
Do ..Request.EntityBody.Write(pBody.%ToJSON())
$$$TOE(st, ..Request.Put(pPath))
Set response = ..Request.HttpResponse
Return response
}
Method HTTPDelete(pPath As %String) As %Net.HttpResponse
{
Do ..CreateRequest()
$$$TOE(st, ..Request.Delete(pPath))
Set response = ..Request.HttpResponse
Return response
}
}
```
そしてこれがFHIRクライアントのコードで、HTTPクライアントを拡張し、CreateRequestメソッドをオーバーライドしてHTTPコールにFHIRサーバーのAPIキーを自動的に付加しています。
```objecscript
Class dc.apps.pregsymptracker.restapi.FHIRaaSClient Extends dc.apps.pregsymptracker.restapi.HTTPClient
{
Method CreateRequest()
{
Do ##super()
Do ..SetHeaders({
"x-api-key" : (..GetApiKeyFromEnsCredentials())
})
}
}
```
記事
Shintaro Kaminaka · 2020年5月1日
この記事では、REST API開発への仕様ファーストアプローチについて説明します。
従来のコードファーストREST API開発は次のようになります。
コードを書く
RESTを有効にする
ドキュメント化(REST APIとして)
仕様ファーストのアプローチでは同じ手順を行いますが、順序が逆になります。 ドキュメントを兼ねた仕様書を作成し、そこからRESTアプリの定型文を生成して、最後にビジネスロジックを書きます。
これは、次の理由でメリットがあります。
REST APIを使用したいと思っている外部開発者またはフロントエンド開発者向けの関連性のある有用なドキュメントが常に入手できます。
OAS(Swagger)で作成された仕様をさまざまなツールにインポートして、編集、クライアント生成、API管理、ユニットテスト、その他の多くのタスクの自動化または簡略化を行うことができます。
改善されたAPIアーキテクチャ。コードファーストアプローチではAPIはメソッドごとに開発されるため、開発者はAPIアーキテクチャ全体を簡単に見落としてしまう可能性があります。これに対し、仕様ファーストの開発者は通常、APIの消費者の立場としてAPIと対話するように強制されます。これは、よりクリーンなAPIアーキテクチャの設計に役立ちます。
開発の迅速化。すべての定型的なコードが自動的に生成されるため、コードを記述する必要はありません。後は、ビジネスロジックの開発をするだけです。
より高速なフィードバックループ。消費者はAPIをすぐに見ることができ、仕様を修正するだけで簡単に提案を行うことができます。
仕様ファーストのアプローチでAPIを開発しましょう!
計画
Swaggerで仕様を開発する
Docker
ローカルで
オンラインで
IRISに仕様を読み込む
API管理REST API
^%REST
クラス
IRISに読み込んだ仕様を確認する
実装
さらなる開発
考慮事項
特別なパラメータ
CORS
IAMに仕様を読み込む
仕様を作成する
最初のステップは、当然のことながら仕様を作成することです。 InterSystems IRISはOpen API仕様(OAS)をサポートしています。
OpenAPI仕様 (以前のSwagger仕様)は、REST APIのAPI記述形式です。 OpenAPIファイルを使用すると、以下を含むAPI全体を記述することができます。
利用可能なエンドポイント(/users)および各エンドポイントでの操作(GET /users, POST /users)
各操作の操作パラメータ入出力
認証方法
連絡先情報、ライセンス、使用条件およびその他の情報。
API仕様はYAMLまたはJSONで記述できます。 この形式は学びやすく、人間と機械の両方が読み取れるようになっています。 完全OpenAPI仕様はGitHubにあります。OpenAPI 3.0仕様
- Swagger docs.から
Swaggerを使用してAPIを記述します。 Swaggerを使用する方法はいくつかあります。
オンライン
Docker:docker run -d -p 8080:8080 swaggerapi/swagger-editor
ローカルインストール
Swaggerをインストールまたは実行すると、次のウィンドウがWebブラウザーに表示されます。
左側では、API仕様を編集し、右側では、レンダリングされたAPIドキュメント/テストツールをすぐに確認できます。
最初のAPI 仕様をその中にロードします(YAML)。 これは、1つのGETリクエストを使用したシンプルなAPIで、指定された範囲の乱数を返します。
数学API仕様
swagger: "2.0"
info:
description: "Math"
version: "1.0.0"
title: "Math REST API"
host: "localhost:52773"
basePath: "/math"
schemes:
- http
paths:
/random/{min}/{max}:
get:
x-ISC_CORS: true
summary: "Get random integer"
description: "Get random integer between min and max"
operationId: "getRandom"
produces:
- "application/json"
parameters:
- name: "min"
in: "path"
description: "Minimal Integer"
required: true
type: "integer"
format: "int32"
- name: "max"
in: "path"
description: "Maximal Integer"
required: true
type: "integer"
format: "int32"
responses:
200:
description: "OK"
構成は次のとおりです。
APIおよび使用されているOASバージョンに関する基本情報。
swagger: "2.0"
info:
description: "Math"
version: "1.0.0"
title: "Math REST API"
サーバーホスト、プロトコル(http、https)、およびWebアプリケーション名は次のとおりです。
host: "localhost:52773"
basePath: "/math"
schemes:
- http
次に、パス(完全なURLは http://localhost:52773/math/random/:min/:max)とHTTPリクエストメソッド(get、post、put、delete)を指定します。
paths:
/random/{min}/{max}:
get:
その後、リクエストに関する情報を指定します。
x-ISC_CORS: true
summary: "Get random integer"
description: "Get random integer between min and max"
operationId: "getRandom"
produces:
- "application/json"
parameters:
- name: "min"
in: "path"
description: "Minimal Integer"
required: true
type: "integer"
format: "int32"
- name: "max"
in: "path"
description: "Maximal Integer"
required: true
type: "integer"
format: "int32"
responses:
200:
description: "OK"
この部分では、リクエストを定義します。
CORSに対してこのパスを有効にします(詳細は後述します)
概要と説明を提供します。
operationId は、仕様内の参照を許可します。これはまた、実装クラスで生成されたメソッド名でもあります。
produces - 応答フォーマット(text、xml、 jsonなど)
parametersは、入力パラメータを指定します(URLまたは本文で)が、この場合は、2つのパラメータ、すなわち乱数ジェネレータの範囲を指定します。
responsesは、サーバーから可能な応答をリストします。
ご覧のとおり、このフォーマットはそれほど難しいものではありません。利用可能な機能は他にもたくさんありますが、仕様は次のとおりです。
最後に、定義をJSONとしてエクスポートします。 ファイルに移動→ 変換してJSON形式で保存します。 仕様は次のようになります。
数学API仕様
{
"swagger": "2.0",
"info": {
"description": "Math",
"version": "1.0.0",
"title": "Math REST API"
},
"host": "localhost:52773",
"basePath": "/math",
"schemes": [
"http"
],
"paths": {
"/random/{min}/{max}": {
"get": {
"x-ISC_CORS": true,
"summary": "Get random integer",
"description": "Get random integer between min and max",
"operationId": "getRandom",
"produces": [
"application/json"
],
"parameters": [
{
"name": "min",
"in": "path",
"description": "Minimal Integer",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "max",
"in": "path",
"description": "Maximal Integer",
"required": true,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
}
}
}
IRISに仕様を読み込む
仕様が完成したので、InterSystems IRISでこのREST APIの定型的なコードを生成できます。
この段階に進むには、次の3つが必要です。
RESTアプリケーション名:生成されたコードのためのパッケージ(たとえば math)
JSON形式のOAS仕様:前のステップで作成しました
WEBアプリケーション名:REST APIにアクセスするための基本パス( /mathの場合)
コード生成に仕様を使用するには3つの方法があります。それらは基本的に同じ方法ですが、同じ機能にアクセスするためのさまざまな方法を提供します
^%RESTルーチン (インタラクティブターミナルセッションで Do ^%RESTを実行する)を呼び出す、 ドキュメント。
%RESTクラス(Set sc = ##class(%REST.API).CreateApplication(applicationName, spec)、非インタラクティブ)を呼び出す、 ドキュメント。
API管理のREST APIを使用する、ドキュメント。
ドキュメントには必要なステップが適切に記述されていると思うので、1つだけ選択してください。 2つの注意事項を追加します。
(1) と (2) の場合、動的オブジェクトにファイル名またはURLを渡すことができます。
(2) と (3)の場合、Webアプリケーションを作成するため、次のように追加の呼び出しをする必要があります。
set sc = ##class(%SYS.REST).DeployApplication(restApp, webApp, authenticationType)
なので、今回のケースでは以下のようになります。
set sc = ##class(%SYS.REST).DeployApplication("math", "/math")
authenticationType引数の値を%sySecurityインクルードファイルから取得、関連エントリは$$$Authe*なので、認証されていない アクセスパスの場合は $$$AutheUnauthenticated。 省略した場合、パラメーターはデフォルトでパスワード認証になります。
IRISに読み込んだ仕様を確認する
アプリを正常に作成した場合、次の3つのクラスをもつ新しいmathパッケージが作成されているはずです。
Spec - 仕様をそのまま保存します
Disp - RESTサービスが呼び出されたときに直接呼び出されます。 REST処理をラップし、実装メソッドを呼び出します。
Impl - RESTサービスの実際の内部実装を保持します。 このクラスのみを編集する必要があります。
クラスの詳細に関するドキュメント 。
実装
最初は、実装クラスmath.implには/random/{min}/{max}操作に対応する1つのメソッドのみが含まれます。
/// Get random integer between min and max<br/>
/// The method arguments hold values for:<br/>
/// min, Minimal Integer<br/>
/// max, Maximal Integer<br/>
ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
{
//(Place business logic here)
//Do ..%SetStatusCode(<HTTP_status_code>)
//Do ..%SetHeader(<name>,<value>)
//Quit (Place response here) ; response may be a string, stream or dynamic object
}
小さな実装から始めましょう。
ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
{
quit {"value":($random(max-min)+min)}
}
そして最後に、ブラウザでこのページを開いてREST APIを呼び出すことができます。 http://localhost:52773/math/random/1/100
出力は次のようになります。
{
"value": 45
}
また、Swaggerエディターで試してみるボタンとリクエストパラメータを入力しても、同じリクエストが送信されます。
おめでとうございます! 仕様ファーストアプローチで作成された最初のREST APIが公開されました。
さらなる開発
もちろん、APIは静的ではないので、新しいパスなどを追加する必要があります。 仕様ファースト開発では、先ず仕様を変更し、次にRESTアプリケーションを更新し(アプリケーションの作成と同じ呼び出し)、最後にコードを記述します。 仕様の更新は安全です。パスが仕様から削除されても、コードは影響を受けません。実装クラスでは、メソッドは削除されません。
考慮事項
追加の注意事項!
特別なパラメータ
InterSystems は、swagger仕様に特別なパラメータを追加しました。
名前
データ型
デフォルト
場所
説明
x-ISC_DispatchParent
クラス名
%CSP.REST
情報
ディスパッチクラスのスーパークラス。
x-ISC_CORS
ブーリアン
偽
操作
このエンドポイント/メソッドの組み合わせに対するCORSリクエストをサポートする必要があることを示すフラグ。
x-ISC_RequiredResource
アレイ
操作
RESTサービスのこのエンドポイントへのアクセスに必要な定義済みリソースとそのアクセスモード(resource:mode)のカンマ区切りリスト。 例:["%Development:USE"]
x-ISC_ServiceMethod
文字列
操作
この操作を処理するためにバックエンドで呼び出されたクラスメソッドの名前。デフォルトはoperationIdで、これは通常適切です。
CORS
CORSサポートを有効にする方法は3つあります。
1. x-ISC_CORSを真としてルートごとに指定する方法。 これは、私たちがMath REST APIで実行した方法です。
2. APIごとに追加する方法
Parameter HandleCorsRequest = 1;
そして、クラスを再コンパイルします。 また、仕様の更新後も存続します。
3. (推奨)APIごとにカスタムディスパッチャースーパークラス (%CSP.RESTを拡張する) を実装し、CORS処理ロジックをそこに記述します。 このスーパークラスを使用するには、 x-ISC_DispatchParentを仕様に追加します。
IAMに仕様を読み込む
最後に、IAMに仕様を追加して、他の開発者向けに公開できるようにします。
IAMをまだ始めていない場合は、この記事を読んでみてください。 この記事はIAMを介したREST APIの提供についても説明しているため、ここでの説明は省きます。 InterSystems IRISインスタンスではなくIAMを指すようにするため、仕様のホストと ベースパスパラメータを変更した方が良いかも知れません。
IAM管理者ポータルを開き、 関連するワークスペースで[仕様]タブに移動します。
[仕様を追加]ボタンをクリックして、新しいAPIの名前を入力します(この例ではmath )。 IAMで新しいSpecを作成した後、編集 をクリックして仕様コードを貼り付けます(JSONまたはYAML。IAMではどちらでも構いません)。
ファイルのアップデートをクリックするのを忘れないでください
APIが開発者向けに公開されました。 開発者ポータルを開いて、右上端にあるドキュメントをクリックします。 3つのデフォルトAPIに加えて、新しい Math REST APIが利用可能になっているはずです。
それを開きます。
開発者は、新しいAPIのドキュメントを参照して、同じ場所で試すことができます。
まとめ
InterSystems IRISは、REST APIの開発プロセスを簡素化し、仕様ファーストのアプローチにより、REST APIのライフサイクル管理をさらに迅速かつ容易にします。 このアプローチでは、クライアント生成、ユニットテスト、API管理など、さまざまな関連タスクに多様なツールを使用できます。
リンク
OpenAPI 3.0 仕様
RESTサービスの作成
IAMで始める
ICMドキュメント
また、前のパート「 InterSystems API管理を使用してAPIの負荷を分散する」も確認してください 。