検索

クリアフィルター
記事
Toshihiko Minamoto · 2021年8月24日

AWS Lambda の IRIS Python Native API IRIS

ソリューションを Amazon Web Services エコシステム、サーバーレスアプリケーション、または `boto3` を使用した Python スクリプトにスムーズに統合する方法を探している場合は、[IRIS Python Native API](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_PYNATIVE) を使用するのが最適です。 IRIS で何かを取得したり設定したりしてアプリケーションに素晴らしい機能を備える必要があるまで、本番の実装を大々的に構築する必要はありません。したがって、誰かにとって重要なものまたはあなた以外にはまったく重要でないもの(これも同じくらい重要なことです)を構築できるようにこの記事が役に立てればと思います。 ![画像](/sites/default/files/inline/images/aws_lambda_native_api_iris_0.png) これを実装する口実が必要であれば * OAUTH2 ワークフローを実装する FHIR(R) ベースソリューションで Patient ID コンテキストを調べ、SHMAHT のトークンに挿入するために Pre Token Generation Trigger を使用する必要がある。 * インスタンスタイプ、ノードグループ、Toaster、または ECS クラスタに応じてプロビジョニング後の IRIS チューニング設定を行い、リングゼロで IRIS を実行したい。 * AWS CLI でシェルを終了することなく IRIS を管理できるスキルを Zoom で家族や友人に披露したい。 ## ポイント ここでは、IRIS と対話できる AWS lambda 関数を示し、それをプロビジョニングする方法とさまざまなキャパシティで操作する方法についての例をいくつか説明します。これに関する対話を持つことで、**物事をより簡単に実現できる pip を公開**できるようになることを願っています。 ## お急ぎですか???? ![画像](/sites/default/files/inline/images/hurry.png) Stream をご覧ください。 ## そうでない方は… このお楽しみに参加するには、現在のフライトプランから項目をいくつか削除する必要があります。 ### ネットワークの構築 IRIS の実行 OS は問いませんが、きっかり 2 つのサブネットを使用する AWS VPC で実行しており、IRIS を実行しているスーパーサーバーへのアクセスを許可するセキュリティグループが必要です。懐かしさと InterSystems が時間を掛けて [IANA](https://tools.ietf.org/html/rfc6335) に登録したという単なる事実からポート 1972 を使用していますが、時間を掛けずに新しいポートを `/etc/services` に追加する場合は、新しいマットレスのタグを引きちぎるのと同じくらいに大変な思いをすることでしょう。 私たちの場合、AWS v2 ネットワークロードバランサーに適切なヘルスチェックを備えた ec2 インスタンスのミラーセットです。 ![画像](/sites/default/files/inline/images/toaster2.png) ### インポートしたサンプルのクラス どうにかして、このリポジトリのルートにクラスを作成して 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 ``` ぜひお試しあれ! ![画像](/sites/default/files/inline/images/aws_lambda_iris_python_0.png) それではお元気で!技術的な議論をお待ちしてます!
記事
Toshihiko Minamoto · 2022年10月19日

Django 入門 パート 1

しばらく前、IRIS 用 Django の新しいドライバーを導入しました。 そこで実際に、IRIS で Django を使用する方法を見てみましょう。 ![新しい Python および Django プロジェクトのベストプラクティス | Luan Pablo | Analytics Vidhya | Medium](https://miro.medium.com/max/1200/1*slHeZngyeUr7ypEz7MNL5w.png) _重要な注意事項: 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 ファイルが作られます。  ![](/sites/default/files/inline/images/images/image(4228).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 を開きます。 ![](/sites/default/files/inline/images/images/image(4229).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 に移動すると、追加のテーブルがあります。 ![](/sites/default/files/inline/images/images/image(4230).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日

IISでRESTを動かす場合の設定方法

これは、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日

DX Jetpack for VS Code(VSCodeを使用するIRIS開発者のエクスペリエンスを向上させるツール):デベロッパーツールコンテスト2023入賞作品のご紹介

開発者の皆さん、こんにちは! 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日

「妊娠トラッカ」アプリケーションを支えるIS技術

今回は、「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)で扱われます。 ![一般的なアプリケーションのアーキテクチャ](https://github.com/jrpereirajr/pregnancy-symp-tracker-fhir-app/raw/master/img/W3U0DcpPRr.png) ## 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で直接作成するのに役立ちます。 ![VS Code用OpenAPI拡張子](https://github.com/jrpereirajr/pregnancy-symp-tracker-fhir-app/raw/master/img/j9GY3tSHq5.png) 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が生成されたコードを格納するパッケージ名です。 ![REST APIクラスを生成する](https://github.com/jrpereirajr/pregnancy-symp-tracker-fhir-app/raw/master/img/75zkIcliUr.png) 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 に任せてください。 ![REST API ディスパッチクラス](https://github.com/jrpereirajr/pregnancy-symp-tracker-fhir-app/raw/master/img/Q1iBkK3GHK.png) 最後の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日

仕様ファースト(APIファースト)アプローチによるREST APIの開発 

この記事では、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の負荷を分散する」も確認してください 。
記事
Toshihiko Minamoto · 2020年12月7日

Arduino と RFID を使用したユーザー認証

    以前の記事では Arduino を使い始め、最終的には気象観測所のデータを表示できるようになりました。 この記事ではさらに掘り下げ、InterSystems Caché アプリケーションに対して RFID カードと Arduino を介した認証をセットアップします。   認証の委任 Caché には認証コードの書き込みを許可することで、[認証を委任](http://docs.intersystems.com/cache20161/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_delegated)するための仕組みがあります。 この仕組みを有効にするには、次の手順を実行する必要があります。 ZAUTHENTICATE ルーチンにユーザー認証コードを記述します。 このルーチンにはユーザー名/パスワードの取得、それらの検証と権限の割り当て、パスワード変更、トークン生成の 4 つのエントリポイントがあります。 詳細については、以下をお読みください。 1. Caché で委任認証を有効にします([SMP] → [System Administration] → [Security] → [System Security] → [Authentication/CSP Session Options] を開き、[Allow Delegated authentication] ボックスにチェックを入れて設定を保存します)。 2. 関連するサービスかアプリケーションの委任認証を有効にします(前者の場合は [SMP] → [Menu] → [Manage Services] → [Service] → [Allowed Authentication Methods] → [Delegated] を選択 → [Save]、後者の場合は [SMP] → [Menu] → [Manage Web Applications] → [Application] → [Allowed Authentication Methods] → [Delegated] を選択 → [Save])。 仕組み 委任認証は、委任認証が有効になっているサービスや Web アプリケーションに対してユーザーが認証される際に発生します。 1. ZAUTHENTICATE ルーチンが呼び出されます。 このルーチンのコードはユーザーによって書かれたものであり、[OS](http://docs.intersystems.com/cache20161/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fzf-1#RCOS_B78412) への呼び出しを含む任意の Caché ObjectScript コードである可能性があります。 2. 次のステップは、ZAUTHENTICATE の呼び出しが成功したかどうかによって決まります。 ZAUTHENTICATE の呼び出しが成功し、ユーザーが ZAUTHENTICATE で認証されたのが初めてだった場合は「委任されたユーザー」が作成されます。 ZAUTHENTICATE がロールやその他の権限をユーザーに割り当てた場合は、それらがユーザープロパティになります。 ZAUTHENTICATE の呼び出しが成功し、ユーザーが ZAUTHENTICATE で認証されたのが初めてではなかった場合はそのユーザーのレコードが更新されます。 ZAUTHENTICATE の呼び出しが成功しなかった場合、ユーザーはアクセスエラーを受け取ります。 インスタンスとサービスで 2 要素認証が有効になっている場合は、ユーザーの電話番号と事業者の検索が開始されます。 これらの情報が入力されている場合は、2 要素認証が実行されます。 入力されていない場合は、ユーザーは認証されません。 ユーザー情報の出処は? 次のように、アプリケーション/サービスで有効になっている認証方法に応じた 2 つの認証方法があります。 * 委任: ユーザー名/パスワードは ZAUTHENTICATE ルーチン(GetCredentials エントリポイント)から取得され、ZAUTHENTICATE を使用して検証されます([ユーザータイプ](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_users#GCAS_users_types): 委任)。 委任およびパスワード: ユーザー名/パスワードは GetCredentials から取得されますが、標準の Caché ツールを使用してチェックされます(ユーザータイプ: Caché)。 次に、ZAUTHENTICATE ルーチンとそのエントリポイントを見てみましょう。 ZAUTHENTICATE これはメインルーチンであり、次の 4 つのエントリポイントで構成されています。 GetCredentials このエントリポイントはサービスで委任認証が有効になっている場合に呼び出され、ユーザーにユーザー名/パスワードの入力を求める代わりに呼び出されます。 このルーチンのコードは、ユーザー名とパスワードを(何らかの方法で)取得します。 その後、(このエントリポイントの外部で)受信したユーザー名とパスワードはユーザーから通常の方法で入力されたかのように認証されます。 ユーザー名とパスワードは、キーボードからの入力、API、外部デバイスを使用したスキャンなど、任意の方法で取得できます。 この記事では、RFID カードを使用した認証を実装します。 このエントリポイントはステータスを返します。ステータスがエラーの場合は監査ログに記録され、認証試行は拒否されます。 ただし、エラーステータス $SYSTEM.Status.Error($$$GetCredentialsFailed) が返された場合は、例外的に通常のユーザー名/パスワードの入力が続きます。 シグネチャは次のとおりです。 GetCredentials(ServiceName, Namespace, Username, Password, Credentials) Public { } 説明: * ServiceName – 接続が確立されるサービスの名前 * Namespace – ネームスペース(接続時に指定されている場合) * Username – ユーザー名 * Password – パスワード Credentials – 現在使用されていません このエントリポイントの重要な機能について説明します。 委任認証とパスワード認証の両方がサービスやアプリケーションで有効になっている場合、ユーザー名とパスワードは GetCredentials エントリポイントを介して受信されますが、それらの情報は標準のパスワード認証に使用されます(ユーザーが手動で入力した場合と同じ)。また、認証が成功した場合のユーザーは委任ユーザーではなく通常の Cache ユーザーになります。 ZAUTHENTICATE 初回認証が成功すると、ZAUTHENTICATE はロールやその他のユーザープロパティを定義します。 初回認証以外の場合はプロパティが更新されます(例えば、Roles はログインのたびに指定する必要があります)。 そのために、Properties 配列のプロパティを定型化したコードで設定する必要があります。 シグネチャは以下のとおりです。 ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) Public { } Properties 配列の説明: * Properties("Comment") — コメント * Properties("FullName") — 氏名 * Properties("NameSpace") — 初期ネームスペース * Properties("Roles") — カンマ区切りのロールのリスト * Properties("Routine") — 初期ルーチン * Properties("Password") — パスワード * Properties("Username") — ユーザー名 * Properties("PhoneNumber") — ユーザーの電話番号 * Properties("PhoneProvider") — 電話会社 Properties("AutheEnabled") — 標準の 2 要素認証を有効化します(この目的のために、$$$AutheTwoFactorSMS に等しい値を設定する必要があります) ChangePassword ユーザーパスワードを変更するためのエントリポイントです。シグネチャは次のとおりです。 ChangePassword(Username, NewPassword, OldPassword, Status) Public { } 説明: * NewPassword — 新しいパスワード * OldPassword — 古いパスワード Status — パスワード変更の結果 SendTwoFactorToken 標準の 2 要素認証で使用されるものです。 リクエストの形式と認証トークンを指定します。 シグネチャは以下のとおりです。 SendTwoFactorToken(Username, ServiceName,Namespace,Application,Credentials,SecurityToken,TwoFactorTimeout,UserPhoneNumber) Public { } 説明: * Application — ユーザーが接続している CSP アプリケーションまたはルーチン * SecurityToken — ユーザーに送信されるトークン * TwoFactorTimeout — トークンの有効期限が切れる時間 UserPhoneNumber — ユーザーの電話番号 例 まずは簡単な例から始めましょう。Windows での Caché ターミナルを担う %Service_Console サービスは、ユーザー名とパスワードの入力をユーザーに要求します。 このサービスに対して委任認証を有効にしましょう。 以下は、ユーザーにユーザー名とパスワードの入力を要求する ZAUTHENTICATE ルーチン(%SYS ネームスペース内)です。 ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) PUBLIC { #Include %occStatus Quit $$$OK } GetCredentials(ServiceName, Namespace, Username, Password, Credentials) Public { #Include %occErrors #Include %occStatus Do ##class(%Prompt).GetString("USER:",.Username) Do ##class(%Prompt).GetString("PASS:",.Password) Quit $$$OK } ターミナルの場合、これは通常のユーザー名認証と同じように見えます。 >USER: _SYSTEM >PASS: SYS RFID それでは、RFID による認証を見てみましょう。 考え方は単純で、Caché が暗号化されたユーザー名とパスワードをカードに書き込み、認証中に Caché がカードをスキャンして復号化し、受け取ったユーザー名とパスワードを認証に使用するというものです。 まず、Arduino Uno と [RFID-RC522](http://playground.arduino.cc/Learning/MFRC522) モジュールの回路図をご覧ください。 ![](/sites/default/files/inline/images/tutorial_rfid.jpg) [MF522](https://github.com/miguelbalboa/rfid) ライブラリを使用した [C のコードはここにあります](https://github.com/intersystems-ru/ArduinoSnippets/blob/master/Delegated/Delegated.ino)。 このコードでは、COM ポート経由で次の 2 つのコマンドを受信できます。 * Get – RFID カードのブロック 2 / 4 / 5 / 6 の内容が COM ポートに渡されます Set@bloc2@bloc4@bloc5@bloc6 — ブロック 2 / 4 / 5 / 6 の値が受信したデータに置き換えられます Caché 側には [Arduino.Delegate](https://github.com/intersystems-ru/ArduinoSnippets/blob/master/Arduino/Delegated.cls.xml) クラスがあり、その中に次の 2 つの対応するエントリポイントがあります。 * SetCredentials — ユーザー名とパスワードの入力を取得し、それをシステムに格納されているキーを使用して AES 暗号化で暗号化し、RFID カードに書き込みます。 GetCredentials — カードから暗号化テキストを受信して復号化し、ユーザー名、パスワード、および操作のステータスを返します。 また、GetCredentials を使用して Arduino.Delegated クラスを呼び出す ZAUTHENTICATE ルーチンは以下のとおりです。 ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) PUBLIC { #Include %occStatus Quit $$$OK } GetCredentials(ServiceName, Namespace, Username, Password, Credentials) Public { #Include %occErrors #Include %occStatus Quit ##class(Arduino.Delegated).GetCredentials(.Username, .Password) } これで準備完了です! 組み立て後のデバイスは次のようになります。 ![](/sites/default/files/inline/images/8f7b06a6e43640d99569c80c3e0496fb.jpg) 次のようにターミナルでシステム暗号化キーを設定します(%SYS ネームスペースと Arduino.Delegated クラスを使用できる必要があります)。 Do ##class(Arduino.Delegated).InitEncryption(Key, IV) ここで、Key は暗号化キー、IV は初期化ベクトルです。 これらは、ユーザー名とパスワードを暗号化するために使用されます。 コマンドを使用して認証するには、Arduino を Caché に接続し、カードに情報を書き込みます。 Do ##class(Arduino.Delegated).SetCredentials("_SYSTEM", "SYS") 適切なサービスまたは Web アプリケーション(端末やシステム管理ポータルなど)で委任認証とパスワード認証を有効にすると、カードを RFID カードリーダーにかざすことで認証できるようになります。 考えられる機能強化 * [マネージド暗号化キー](http://docs.intersystems.com/cache20161/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_encrypt#GCAS_encrypt_mgmt)を使用してユーザー名とパスワードを暗号化すると、セキュリティを強化できます。 * 2 要素認証を使用すると、セキュリティを強化できます。具体的には、先にユーザー名とパスワードのペアを取得してから、ユーザーに固有のキーが格納されているカードを読み取ります。 次に、受信したキーをシステムに格納されている特定ユーザーのキーで確認する必要があります。 任意のユーザーデータを格納する方法は、InterSystems のコミュニティで[議論されています](https://community.intersystems.com/post/best-practices-store-user-informationsettings-cach%C3%A9)。 それぞれ 15 文字を超えるユーザー名とパスワードを格納する機能を追加します。 まとめ 柔軟性の高い Caché の認証システムを使えば、任意のユーザー認証ロジックを実装できます。 リンク * [ドキュメント](http://docs.intersystems.com/cache20161/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_delegated) * [GitHub リポジトリ](https://github.com/intersystems-ru/ArduinoSnippets) * SAMPLES ネームスペースには ZAUTHENTICATE ルーチンの例も含まれています
記事
Toshihiko Minamoto · 2020年11月18日

クラス、テーブル、グローバルとその仕組み

クラス、テーブル、グローバルとその仕組み InterSystems IRIS を技術的知識を持つ人々に説明する際、私はいつもコアとしてマルチモデル DBMSであることから始めます。 個人的には、それが(DBMSとして)メインの長所であると考えています。 また、データが格納されるのは一度だけです。 ユーザーは単に使用するアクセス API を選択するだけです。 - データのサマリをソートしたいですか?SQL を使用してください! - 1 つのレコードを手広く操作したいですか?オブジェクトを使用してください! - あなたが知っているキーに対して、1 つの値にアクセスしたりセットしたいですか? グローバルを使用してください! これは短く簡潔なメッセージで、一見すると素晴らしく聞こえます。しかし、実際には intersystems IRIS を使い始めるたユーザーには クラス、テーブル、グローバルはそれぞれどのように関連しているのだろうか? 互いにどのような存在なのだろうか? データは実際にどのように格納されているのだろうか?といった疑問が生じます。 この記事では、これらの疑問に答えながら実際の動きを説明するつもりです。 ## パート 1. モデルに対する偏見。 データを処理するユーザーは多くの場合、処理対象のモデルに偏見を持っています。 開発者はオブジェクトで考えます。 このようなユーザーにとって、データベースとテーブルは CRUD(Create-Read-Update-Delete、ORM の使用が望ましい)を介して操作する箱のようなものですが、その基礎となる概念モデルはオブジェクトです(これは主に私たちのような多くのオブジェクト指向言語の開発者に当てはまります)。 一方、リレーショナル DBMS に多くの時間を費やしているデータベース管理者は往々にしてデータをテーブルと見なしています。 この場合、オブジェクトはレコードの単なるラッパー扱いです。 また、InterSystems IRIS では永続クラスはデータをグローバルに格納するテーブルでもあるため、いくつかの説明が必要になります。 ## パート 2. 具体例 次のような Point クラスを作成したとします。 ```objectscript Class try.Point Extends %Persistent [DDLAllowed] { Property X; Property Y; } ``` 次のように DDL/SQL を使用して同じクラスを作成することもできます。 ``` CREATE Table try.Point ( X VARCHAR(50), Y VARCHAR(50)) ``` コンパイル後、新しいクラスがグローバルにネイティブに格納されているデータをカラム(またはオブジェクト指向のユーザーの場合はプロパティ)にマッピングするストレージ構造を自動生成します。 ``` Storage Default { %%CLASSNAME X Y ^try.PointD PointDefaultData ^try.PointD ^try.PointI ^try.PointS %Library.CacheStorage } ``` ここでは何が起きているのでしょうか? 下から順番に説明します(**太字**の単語が重要です。残りは無視してください)。 - Type - 生成されたストレージタイプ。この場合は永続オブジェクトのデフォルトストレージです。 - StreamLocation - ストリームを格納するグローバルです。 - IndexLocation - インデックス用のグローバルです。 - IdLocation - ID の自動インクリメントカウンターを格納するグローバルです。 - **DefaultData** - グローバルの値をカラム/プロパティにマッピングするストレージの XML 要素です。 - **DataLocation** - データを格納するグローバルです。 ここでは「DefaultData」が `PointDefaultData` となっていますので、その構造をもう少し詳しく見てみましょう。 基本的に、グローバルノードは次の構造を持っていると言われています。 - 1 - %%CLASSNAME - 2 - X - 3 - Y したがって、グローバルは次のようになると予想されます。 ``` ^try.PointD(id) = %%CLASSNAME, X, Y ``` しかし、グローバルを出力すると空になります。ここではデータを追加していなかったためです。 ``` zw ^try.PointD ``` オブジェクトを 1 つ追加しましょう。 ``` set p = ##class(try.Point).%New() set p.X = 1 set p.Y = 2 write p.%Save() ``` すると、グローバルはこのようになります。 ``` zw ^try.PointD ^try.PointD=1 ^try.PointD(1)=$lb("",1,2) ``` ご覧のように、期待する構造 %%CLASSNAME, X, Y はオブジェクトの X プロパティと Y プロパティに対応する `$lb("",1,2)` とセットになっています(%%CLASSNAME はシステムプロパティですので無視してください)。 次のように SQL を使用してレコードを追加することもできます。 ``` INSERT INTO try.Point (X, Y) VALUES (3,4) ``` すると、グローバルの内容は次のようになります。 ``` zw ^try.PointD ^try.PointD=2 ^try.PointD(1)=$lb("",1,2) ^try.PointD(2)=$lb("",3,4) ``` つまり、オブジェクトまたは SQL を介して追加するデータは、ストレージ定義に従ってグローバルに格納されます(補足:PointDefaultData の X と Y を置き換えることでストレージ定義を手動で変更できます。その場合に新しいデータがどうなるかを確認してください!)。 では、SQL クエリを実行したい場合はどうなるのでしょうか? ``` SELECT * FROM try.Point ``` これは `^try.PointD` グローバルを反復処理し、ストレージ定義(正確にはその `PointDefaultData` 部分)に基づいてカラムにデータを入力する ObjectScript コードに変換されます。 今度は変更を行います。 テーブルからすべてのデータを削除しましょう。 ``` DELETE FROM try.Point ``` すると、この時点でグローバルの内容は次のようになります。 ``` zw ^try.PointD ^try.PointD=2 ``` ここでは ID カウンターのみが残っているため、新しいオブジェクト/レコードの ID は 3 になることに注意してください。 また、クラスとテーブルは引き続き存在します。 しかし、次を実行するとどうなるでしょうか。 ``` DROP TABLE try.Point ``` これはテーブルとクラスを破棄し、グローバルを削除します。 ``` zw ^try.PointD ``` 皆さんがこの具体例に従い、グローバル、クラス、テーブルがどのように統合され、相互に補完しているかをより深く理解できたことを願っています。 手元の仕事に適切な API を使用すれば、開発がより高速かつアジャイルになり、バグが少なくなります。
記事
Toshihiko Minamoto · 2022年12月21日

Django 入門 パート 3

Django の可能性と IRIS の使用方法を引き続き観察しています。 [初めに](https://jp.community.intersystems.com/node/527776)モデルの定義方法と、IRIS に存在しているテーブルへの接続方法を確認し、[次に](https://jp.community.intersystems.com/node/527781)組み込みの Django 管理ポータルを拡張して、モデルに含まれるデータの表示、フィルタ、編集、そしてページネーションの機能を追加しました。 では、実際の動作を確認しましょう。posts-and-tags パッケージで使用したデータで Django に REST API を作成します。 それには、[Django REST Framework](https://www.django-rest-framework.org/) を使用します。 ![Django REST Framework](https://www.django-rest-framework.org/img/logo.png) Django REST Framework は、Web API を構築するための強力で柔軟性を備えたツールキットです。 REST Framework の使用を推奨するのには、以下のような理由があります。 * Web で閲覧可能な API には、開発者のユーザビリティにおいて大きなメリットがあります。 * OAuth1a と OAuth2 の認証ポリシーを含むパッケージ * ORM と非 ORM データソースの両方をサポートするシリアル化 * すべてをカスタマイズ可能。強力な機能が必要なければ、通常の関数ベースのビューを使用できます。 * 詳細なドキュメントと優れたコミュニティサポート * Mozilla、Red Hat、Heroku、Eventbrite など、世界的に有名な企業が使用・信頼 まず、依存関係で requirements.txt を更新する必要があります。 # Django そのもの django>=4.0.0 # Django 用 InterSystems IRIS ドライバー、InterSystems の DB-API ドライバー 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 # Django REST Framework とそのオプションの依存関係 djangorestframework>=3.4.4 # 閲覧可能な API の Markdown サポート markdown>=3.0.0 # フィルタサポート django-filter>=1.0.1 そして、これらをインストールします。 python install -r requirements.txt ## API の初稿 _urls.py_ ファイルを以下に更新します。 ここでは、API のルートを **_api/_** に更新し、API リクエストに対し、http://localhost:8000/api/ がルートとして使用されるようにします。 from django.contrib import admin from django.urls import path, include from rest_framework import routers router = routers.DefaultRouter() urlpatterns = [ path('api/', include(router.urls)), path('admin/', admin.site.urls), path('api-auth/', include('rest_framework.urls')), ] Django REST Framework には、settings.py の DEBUG=True によりサーバーが開発モードで実行している場合、API の UI が組み込まれています。 この URL を開きましょう。 ![](/sites/default/files/inline/images/images/image(4289).png) 何も定義されておらず、フレームワークが URL に接続されているだけでも、すべてが機能しています。 認証を必要とするリクエストに対しては、認証がサポートされています。  $ curl http://127.0.0.1:8000/api/ {} プロジェクトの API を定義しましょう。最低限、REST Framework のいくつかの機能を使用します。 * シリアライザー - クエリセットやモデルインスタンスなどの複雑なデータをネイティブ Python データ型に変換し、`JSON` や `XML` などのコンテンツタイプに簡単にレンダリングできるようにします。 シリアライザーは逆シリアル化も提供しているため、着信データを検証してから、解析したデータを複雑な型に変換し直すことも可能です。 * ビューセット - 関連する一連のビューのロジックを 1 つのクラスにまとめることができます。 Post 用のエンドポイントを追加しましょう。 とてもシンプルではありますが、 更新された **_urls.py _** のコンテンツを見てみましょう。 from django.contrib import admin from django.urls import path, include from rest_framework import routers, serializers, viewsets from .models import CommunityPost router = routers.DefaultRouter() class CommunityPostSerializer(serializers.HyperlinkedModelSerializer): class Meta: # class with model model = CommunityPost # list of fields to show, or just '__all__' fields = '__all__' # ViewSets define the view behavior. class CommunityPostViewSet(viewsets.ModelViewSet): queryset = CommunityPost.objects.all() serializer_class = CommunityPostSerializer # connect it with API router.register(r'posts', CommunityPostViewSet) urlpatterns = [ path('api/', include(router.urls)), path('admin/', admin.site.urls), path('api-auth/', include('rest_framework.urls')), ] これで Web UI に表示されるようになりました。 ![](/sites/default/files/inline/images/images/image(4290).png) ここのリンクをクリックすると、そのレスポンスを確認できます。 ![](/sites/default/files/inline/images/images/image(4291).png) 最後までスクロールすると、新しい項目用に生成されたフォームがあります。これは POST リクエストで追加可能です。 すべてのフィールドがプロパティの型に適しています。 ![](/sites/default/files/inline/images/images/image(4292).png) 項目リストで任意の項目の URL をクリックして、これを確認します。 レスポンスのこの項目と、PUT リクエストを使った編集フォームのみです。 ![](/sites/default/files/inline/images/images/image(4294).png) ## 認証 PUT か POST でデータを変更できるようになりました。 認証の要件はまだ有効化されていません。 REST Framework には、使用できる認証の組み合わせが様々用意されているため、匿名アクセスの読み取り専用リソースを一部開放することができます。 そして、変更を行うための認証を行います。 または、アクセスを完全に閉鎖することも可能です。 ここでは、匿名の読み取り専用に構成し、変更には認証を必要とするようにしましょう。 それには、次のコードを **_settings.py_** に追加すれば完了です。 REST_FRAMEWORK = { # Use Django’s standard `django.contrib.auth` permissions, # or allow read-only access for unauthenticated users. 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', ], } これを使用すれば、Django 管理用に前に作成したユーザー名とパスワードなどでログインするまで、フォームが表示されなくなります。 ## ページネーション デフォルトではページネーションはありませんが、リストクエリに簡単に追加できます。 **_settings.py_** の **REST_FRAMEWORK** 変数を更新しましょう。 ページネーションクラスとデフォルトのページサイズをセットアップします。 REST_FRAMEWORK = { ... 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 10, ... } これにより、生成される JSON がわずかに変わりました。「次へ」や「前へ」といったページリンクや全項目数などの関連する項目が追加されています。 これで、Web UI でページを移動できるようになりました。 ![](/sites/default/files/inline/images/images/ezgif_com-gif-maker_(4).gif) ## フィルタと検索 フィルタ機能と検索機能の追加も非常に単純です。 **_settings.py_** の **REST_FRAMEWORK** 変数を更新しましょう。  REST_FRAMEWORK = { ... 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.SearchFilter', ], ... ] そして、CommunityPostViewSet をフィルタと検索用のフィールドのリストで更新します。 class CommunityPostViewSet(viewsets.ModelViewSet): queryset = CommunityPost.objects.all() serializer_class = CommunityPostSerializer filterset_fields = ['posttype', 'lang', 'published'] search_fields = ['name',] これで、Web UI で動作するようになりました。 ![](/sites/default/files/inline/images/images/ezgif_com-gif-maker_(5).gif) 最後に、完全に機能する REST API が完成しましたが、今のところ、このリソース専用の API です。 非常に単純ですが、十分にカスタマイズ可能で、他のリソースに接続したり、リンクしたリすることが可能です。
記事
Toshihiko Minamoto · 2020年11月18日

FTP ファイルのダウンロード(すべてのファイル、ファイル数)オプション: ファイルのコピーまたはファイルの移動

FTP ファイルを Intersystems Caché からダウンロードする**メソッド**を以下に示します。ご質問がある場合はメッセージをお寄せください。 ClassMethod FTPDownload(myFTP = "", myUserName = "", myPassword = "", sFileLocation = "", dLocation = "", noOfdownloadFile = 1, sourceFileDel = ){ /*---------------------------------------------------------------------------------------------------------------------------  要件に従ってファイルをダウンロードします  : FTP メソッド : 再利用可能  作成者 : Sanjib Raj Pandey、30/03/2018 に作成  downLoadFile = ファイル数またはすべてのファイル  ...... ダウンロードしたいファイルを指定します、デフォルト値は 1 です。   = 1,3,7,100 ファイルなどの値を指定します。   = すべてのファイルをダウンロードするには "*" を指定します。   SourceFileDel = ダウンロード後にソースフォルダーのファイルを削除したい場合は.....                   この値を 1 に設定します  -- ;   デフォルト値 :  0                   1= True (ダウンロード後にソースフォルダーを削除する)、 0 = False(コピーのみ)   sFileLocation = ソースファイルの場所(フォルダー)  dLocation = 宛先フォルダー例 :  以下の内容は ..... すべてのファイルをソースから宛先フォルダーに移動します。  w ##class(CW.COMMON).FTPDownload("IP アドレス","ユーザー名","パスワード","ソースフォルダー","宛先フォルダー,"*",1) 以下の内容は...... 200 ファイルをソースから宛先フォルダーにコピーします。w ##class(CW.COMMON).FTPDownload("IP アドレス","ユーザー名","パスワード","ソースフォルダー","宛先フォルダー,200,0)   ------------------------------------------------------------------------------------------------------------------------- */ // Try .. Catch のようなエラー制御をセットアップできます。   Set (count,fileNo,key,messge,fileStream,myFileName,myFile,fSave,eMessage,eSubject)="" Set fIp= myFTP  Set fUserName= myUserName  set fPassword=myPassword  set sFileLocaion=sFileLocation  set dLocation=dLocation  Set downloadFile=noOfdownloadFile  Set sourceFileDel=sourceFileDel  If $Length(fIp)=0||($L(fUserName)=0)||($L(fPassword)=0) || ($L(downloadFile)=0) Q "資格情報が無効であるか、ダウンロードファイルが 0 です!IP、ユーザー名、パスワード、FTP または宛先の場所を確認してください!"       Set myFtp=##class(%Net.FtpSession).%New() Set eMessage="FTP 接続に失敗しました。"_fIp_" またはユーザー名、パスワードをチェックしてください!" Set eSubject ="FTP 警告メッセージ。"   Set myFtp.Timeout = 60 If 'myFtp.Connect(fIp,fUserName,fPassword) Quit  w $$EVEMAIL^CW.COMMON(eSubject,eMessage) Do myFtp.SetDirectory(sFileLocaion) If 'myFtp.NameList(" ",.x) Quit "ファイルが見つかりません " Set fileStream = ##class(%Stream.FileBinary).%New() Set message ="コピー" Set myFileName="" Set fileNo=0 Set Key="" If (downloadFile = "*") {   While (x.GetNext(.Key))'="" {       Do StartCopy }Do myFtp.Logout()Quit fileNo_" ファイルが正常に"_message_"されました!"    }  If (downloadFile >0) {       Set count=1        While ((count 
記事
Mihoko Iijima · 2020年12月15日

IIS で REST を動かす場合の設定

これはInterSystems FAQ サイトの記事です。 REST のベース URL を /rest とした場合の IIS と Webゲートウェイの設定内容は以下の通りです。 IIS の設定内容 アプリケーション /rest を作成し、ハンドラーマッピングで * = CSPms を定義します。 ハンドラーマッピングの設定手順は以下の通りです。 IIS > Default Website > アプリケーション /rest > "ハンドラーマッピング" をダブルクリック > モジュールマップの追加 要求パス : * モジュール: CSPms 名前   : CSPGW (任意の名前) 要求の制限 > [要求のマップ先が次の場合のみハンドラーを呼び出す] のチェックはオフにします。※こちらの設定はデフォルトでは「チェックあり」になっていますのでご注意ください。 この構成により、IIS は /rest アプリケーションを Web ゲートウェイに転送します。 Webゲートウェイの構成 Web Gateway のアプリケーションアクセス から /rest を追加します。→既存アプリケーションをクリックしてコピーし、アプリケーションパスを /rest に設定しIRISサーバを指定します。 この構成により、Webゲートウェイは /rest アプリケーションをIRISサーバに転送します。 IRISサーバの構成 管理ポータルを開き、システム構成 > セキュリティ > アプリケーション > ウェブ・アプリケーション より /rest アプリケーションを追加します。 RESTディスパッチクラス名を「ディスパッチクラス」に指定します。 この構成により、IRIS は /rest アプリケーションを対象ネームスペースに転送し、対象ディスパッチクラスを呼び出します。 IIS経由でテスト 以下のURLで呼び出します。 http://localhost/rest/test REST ディスパッチクラスが以下のようなサンプルコードだった場合 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 } } 以下の結果が得られます。
記事
Mihoko Iijima · 2021年3月9日

ダイナミックオブジェクトの JSON 変換で MAXSTRING エラーが出る場合の対処方法

これは InterSystems FAQ サイトの記事です。 ダイナミックオブジェクトから JSON 文字列を生成するときに使用する %ToJSON() の引数にストリームオブジェクトを指定することでエラーを回避できます。 コード例は以下の通りです。 USER>set temp=##class(%Stream.TmpCharacter).%New() USER>set jsonobj={} USER>set jsonobj.pro1=["a","b","c","d"] USER>set jsonobj.pro2=["あ","い","う","え"] USER>do jsonobj.%ToJSON(temp) USER>write temp.Size 51 USER>write temp.Read() {"pro1":["a","b","c","d"],"pro2":["あ","い","う","え"]} 詳細はドキュメントもご参照下さい。 【IRIS】大きいダイナミック・エンティティからストリームへのシリアル化 大きいダイナミック・エンティティからストリームへのシリアル化 こんにちは、mihokoさん %Stream.TmpCharacterクラスを使用した場合、IRISTEMPデータベースにデータが書き込まれると理解してよろしいでしょうか? また、作成したストリームデータは明示的に削除する必要はありますか? こんにちは、Amanoさん ご質問ありがとうございます! 確認してみたところ、ご質問いただいたように、IRISTEMP に書くには書くのですが、大きなサイズになったときにプロセスプライベートグローバル(=PPG。こちらも IRISTEMP に格納される変数)へセットしていました。 ちなみに、上に書いているサンプルだと、PPG は使用していませんでした。 削除についてですが、ストリームを格納した変数を消去すると使用していた PPG も消えますので、「ストリームをセットした変数を kill する」を入れるのが確実だと思います(メソッド終了でローカル変数も消えますが、明示的に書いた方がわかりやすいかな、と思いました)。 確認するまで私も知りませんでしたので、ご質問いただきありがとうございました! mihokoさん %Stream.TmpCharacterクラスは便利そうですね。 回答ありがとうございました。