0 フォロワー · 79 投稿

Representational state transfer (REST) は、Webサービスを作成するために使用される一連の制約を定義するソフトウェアアーキテクチャスタイルです。 RESTful Webサービス(RWS)と呼ばれる、RESTアーキテクチャスタイルに準拠したWebサービスは、インターネット上のコンピュータシステム間の相互運用性を提供します。 REST準拠のWebサービスを使用することで、要求されたシステムは、統一され事前定義されたステートレスな操作のセットを使用して、Webリソースのテキスト表現にアクセスし操作することができます。 SOAP Webサービスなどの他の種類のWebサービスは、独自の任意の操作セットを公開します。 詳細はこちら。

記事 Shintaro Kaminaka · 11月 27, 2020 5m read

背景

InterSystems IRIS 2019 では、新たに魅力的な機能が導入される予定です。 ぜひ知っておくべき魅力的な新機能の一つには、API 管理があります。

OpenAPI Initiative(https://www.openapis.org/)は、API を定義するための標準仕様(https://github.com/OAI/OpenAPI-Specification)をサポートする組織です。 OpenAPI 仕様(OAS)は、REST API 向けのプログラミング言語に依存しない標準的なインターフェースの記述を定義するもので、人間とコンピューターの両方が、ソースコードへのアクセス、追加ドキュメント、またはネットワークトラフィックの検査を必要とせずに、サービスの機能を検出して理解できるようにしています。 OpenAPI を使用して適切に定義されている場合、消費者は最小限の実装ロジックでリモートサービスを理解して対話できます。 低レベルのプログラミングに対するインターフェース記述と同様に、OpenAPI 仕様によってサービスを呼び出す際の当て推量が排除されます。

InterSystems は InterSystems IRIS で API 設計優先のアプローチをサポートしており、それによって先に仕様を設計してからサーバーサイドを生成できるようにしています。 API を先に設計する場合、通常は Swagger Editor やその他同様のツールを使用して仕様を作成し、必要に応じて JSON 形式で OAS 仕様を取得します。

API を設計して実装する準備ができたら、OAS 仕様を使用してサーバーサイドの API ロジックを作成できるようになります。 InterSystems IRIS 2019.1 では、新しいルーチンである ^%REST を使用し API を土台にして、ビジネスロジックを呼び出すコードを配置するクラスを自動的に生成できます。 このクラスのメソッドは命名規則に基づいて生成されますが、メソッドとクラスを仕様(operationId)で定義することもできます。

InterSystems IRIS REST コマンドラインインターフェースの使用例を以下に示します。

USER>do ^%REST
 
REST Command Line Interface (CLI) helps you CREATE or DELETE a REST application 
Enter an application name or (L)ist all REST applications (L): acmeapi
REST application not found: acmeapi
Do you want to create a new REST application? Y or N (Y):
 
File path or absolute URL of a swagger document.
If no document specified, then create an empty application.
OpenAPI 2.0 swagger: C:\myspec\acme.swagger.json
 
OpenAPI 2.0 swagger document: C:\myspec\notification.swagger.json
Confirm operation, Y or N (Y):
-----Creating REST application: acmeapi-----
CREATE acmeapi.spec
GENERATE acmeapi.disp
CREATE acmenapi.impl
REST application successfully created.
 
Create a web application for the REST application? Y or N (Y):
Specify web application name. Default is /csp/api/acme
Web application name: /csp/api/acme/v1
 
-----Deploying REST application: acmeapi-----
Application acmeapi deployed to /csp/api/acme/v1
 

現時点では、REST API を作成する際に API の土台の構築に使用できるのは OpenAPI 2.0 Swagger 仕様だけです。

ご覧のとおり、このルーチンは以下の 3 つのクラスを作成します。

  • .spec: このクラスは swagger 仕様(XData OpenAPI ブロック)のコンテナです。 このクラスは読み取り専用です。
  • .disp: CSP アプリケーションで使用できるディスパッチクラスです。 %CSP.REST を拡張し、XData UrlMap を定義するものです。 また、このクラスは読み取り専用であり、システムクラスとして扱われます(デフォルトでは Atelier で非表示になっています)。
  • .impl: 必要なすべての署名メソッドを定義するクラスです。 API を機能させるには、このクラスを完成させる必要があります。

すでに開発済みの API がある場合

InterSystems は、開発者がリモートで API 機能を探索できるサービス検索機能を InterSystems IRIS 2018.1 で導入しました。 また、Swagger を統合することで、既存の REST アプリケーションから Open API 仕様(OAS)を生成できるようにしています。 そのため、InterSystems IRIS で変更される API はすべて swagger 仕様を自動生成できます。

次のように管理 API を使用すると、システムで使用可能なすべての API を照会することができます。

HTTP GET http://:/api/mgmnt/ 

次の結果が返ってきます。


[
...,
    {
        "name": "/csp/petstore/v2",
        "dispatchClass": "petstore.disp",
        "namespace": "USER",
        "resource": "",
        "swaggerSpec": "/api/mgmnt/v1/USER/spec/csp/petstore/v2",
        "enabled": true
    }
]

また、API の Swagger 仕様は swaggerSpec プロパティによって示された URL に HTTP GET を発行することで取得できます。 元の swagger 仕様で定義されている API 操作には、アクションを実装する必要があるメソッドの名前を定義する新しいプロパティが追加されています。

例:

"x-ISC_ServiceMethod": "getPetById",

この api/mgmnt は検索だけではなく、次のように API の作成/照会/削除にも使用できるのが非常に魅力的です。

HTTP POST to /api/mgmnt/v2//
HTTP GET to /api/mgmnt/v2//
HTTP DELETE to /api/mgmnt/v2//

IRIS API Explorer

IRIS Explorer はこの API を利用し、IRIS API を非常にわかりやすく視覚化して管理するためのツールを提供する Angular 5 アプリケーションです。 以下、簡単にご紹介します。

まず、InterSystems IRIS インスタンスにログインする必要があります(デフォルトでは、52773 番ポートでローカルインスタンスを探します)。

ログイン後、アプリがクエリを実行して既存のすべての API を取得します。

ここでは既存の API を削除することも、新しい API を作成することもできます。 新しいアプリケーションを作成するには、ネームスペース / アプリケーション名 / Swagger 仕様を含む .json ファイルを指定する必要があります。

API を作成したら、仕様を表示できるようになります。 私はさらにこれを見やすくするため、Swager-UI(https://github.com/swagger-api/swagger-ui)を埋め込みました。

もちろん、JSON 形式の OSA 仕様も取得できます。

すべてのコードはオープンであり、都合に合わせて使用または変更することができます。 このアプリは Open Exchange で入手できます。

https://openexchange.intersystems.com/package/iris-explorer

GitHub でも公開しています。

https://github.com/drechema/iris-explorer

お役に立てば幸いです。

0
0 314
記事 Tomohiro Iwamoto · 11月 23, 2020 21m read

この記事では、OData API 標準に基づいて開発された RESTful API サービスを利用するための IRIS クライアントの開発について説明します。

HTTP リクエストを作成し、JSON ペイロードの読み取りと書き込みを行い、それらを組み合わせて OData 用の汎用クライアントアダプタを構築する方法を確認するため、多数の組み込み IRIS ライブラリを説明します。 また、JSON を永続オブジェクトに逆シリアル化するための新しい JSON アダプタについても説明します。

RESTful API の操作

REST は World Wide Web の標準化に関する作業から作成された一連の設計原則です。 これらの原則はあらゆるクライアントサーバー通信に適用でき、HTTP API が RESTful であることを説明するためによく使用されます。

REST はステートレスなリクエスト、キャッシュ処理、統一した API 設計など、さまざまな原則を網羅しています。 ただし、詳細な実装については網羅していません。また、これらのギャップを埋めるための一般的な API 仕様は存在しません。

この曖昧さは、RESTful API に幾分かの理解、ツール、より厳密なエコシステムを中心によく構築されるライブラリが不足している原因となっています。 特に、開発者は RESTful API の検出と文書化のために独自のソリューションを構築する必要があります。

OData

OData は、一貫性のある RESTful API を構築するための OASIS の仕様です。 OASIS コミュニティには、Microsoft / Citrix / IBM / Red Hat / SAP などの有名なソフトウェア会社が参加しています。 2007 年に OData 1.0 が最初に導入され、最新バージョンの 4.1 が今年リリースされました。

OData の仕様は、メタデータ、一貫性のある操作の実装、クエリ、例外処理などを対象としています。 また、アクションや関数などの追加機能も対象としています。

TripPinWS OData API の説明

この記事では、Odata.org が例示している TripPinWS API を使用します。

他の RESTful API と同様に、一般的にはサービスのベース URL が必要です。 OData でこのベース URL にアクセスすると、API エンティティのリストも返されます。

https://services.odata.org:443/V4/(S(jndgbgy2tbu1vjtzyoei2w3e))/TripPinServiceRW

この API には Photos、People、Airlines、Airports、Me のエンティティと、GetNearestAirport という関数が含まれていることがわかります。

応答には、TripPinWS メタデータドキュメントへのリンクも含まれています。

https://services.odata.org/V4/(S(djd3m5kuh00oyluof2chahw0))/TripPinServiceRW/$metadata

このメタデータは XML ドキュメントとして実装されており、独自の XSD ドキュメントが含まれています。 これにより、IRIS XML スキーマウィザードから生成されるコードを使用してメタデータドキュメントを消費する可能性が広がります。

メタデータドキュメントは一見かなり複雑に見えるかもしれませんが、エンティティのスキーマ定義を構成するために使用されるタイプのプロパティを表しているだけです。

次の URL を使用すると、API から People のリストを取得できます。

https://services.odata.org/V4/(S(4hkhufsw5kohujphemn45ahu))/TripPinServiceRW/People

この URL は 8 人のリストを返します。この 8 という数値は、厳密な結果ごとのエンティティ数の上限値です。 実際には、これよりもはるかに大きな上限値を使用することになるでしょう。 ただし、OData が @odata.nextLink などの追加のハイパーテキストリンクを含んでいる例も示されています。そのリンクを使用すると、People を検索した結果の次のページを取得できます。

また、次のようにして上位 1 件の結果のみを選択するなど、クエリ文字列値を使用して結果リストを絞り込むこともできます。

https://services.odata.org/V4/(S(4hkhufsw5kohujphemn45ahu))/TripPinServiceRW/People?$top=1

FirstName でリクエストを絞り込むこともできます。

https://services.odata.org/V4/(S(4hkhufsw5kohujphemn45ahu))/TripPinServiceRW/People?$filter=FirstName eq 'Russell'

この例では eq 演算子を使用し、「Russell」に等しいすべての FirstName を抽出しました。 ここでは対象の文字列を一重引用符で囲むことが重要です。 OData では、さまざまな演算子を組み合わせて表現力の高い検索クエリを作成することができます。

IRIS %Net パッケージ

IRIS には、包括的な標準ライブラリが含まれています。 私たちは FTP / メール / LDAP / HTTP などのプロトコルをサポートする %Net パッケージを使用することになります。

TripPinWS サービスを使用するには、HTTPS を使用する必要があります。そのためには、IRIS 管理ポータルに HTTPS 構成を登録する必要があります。 複雑な証明書をインストールする必要はないため、次のように数ステップで作業を完了することができます。

  • IRIS 管理ポータルを開きます。
  • [ システム 管理] > [セキュリティ] > [SSL/TLS 構成] をクリックします。
  • [新規構成の作成] ボタンをクリックします。
  • 「odata_org」という構成名を入力し、[保存] をクリックします。
  • ここでは任意の名前を選択できますが、記事の残りの部分では odata_org を使用します。

これで、HttpRequest クラスを使用して全員のリストを取得できるようになりました。 Get() が動作すると、OK の場合に 1 が返ってきます。 その後、次のように応答オブジェクトにアクセスして結果を端末に出力できます。

DC>set req=##class(%Net.HttpRequest).%New()
DC>set req.SSLConfiguration="odata_org"
DC>set sc=req.Get("https://services.odata.org:443/V4/(S(jndgbgy2tbu1vjtzyoei2w3e))/TripPinServiceRW/People")
DC>w sc
1
DC>do req.HttpResponse.OutputToDevice()

先に進む前に、基本的な HttpRequest を自由に試してみてください。 Airlines や Airports を取得してみたり、不正な URL を入力した場合にどのようなエラーが返ってくるかを調べたりしてください。

汎用 OData クライアントの開発

HttpRequest クラスを抽象化し、さまざまな OData クエリオプションの実装を簡単にする汎用 OData クライアントを作成しましょう。

このクライアントを DcLib.OData.Client と名付け、%RegisteredObject を拡張して作成します。 また、特定の OData サービスの名前を定義するために使用できるいくつかのサブクラスと、HttpRequest オブジェクトなどのランタイムオブジェクトと値をカプセル化するいくつかのプロパティを定義します。

OData クライアントのインスタンス化を簡単にするため、%OnNew() メソッド(クラスのコンストラクタメソッド)もオーバーライドし、それを使用して実行時のプロパティを設定します。

Class DcLib.OData.Client Extends %RegisteredObject
{
Parameter BaseURL;
Parameter SSLConfiguration;
Parameter EntityName;
Property HttpRequest As %Net.HttpRequest;
Property BaseURL As %String;
Property EntityName As %String;
Property Debug As %Boolean [ InitialExpression = 0 ];
Method %OnNew(pBaseURL As %String = "", pSSLConfiguration As %String = "") As %Status [ Private, ServerOnly = 1 ]
{
   set ..HttpRequest=##class(%Net.HttpRequest).%New()
   set ..BaseURL=$select(pBaseURL'="":pBaseURL,1:..#BaseURL)
   set ..EntityName=..#EntityName
   set sslConfiguration=$select(pSSLConfiguration'="":pSSLConfiguration,1:..#SSLConfiguration)
   if sslConfiguration'="" set ..HttpRequest.SSLConfiguration=sslConfiguration
   quit $$$OK
}
}

このように DcLib.OData.Client を拡張し、BaseURL と SSL 構成パラメーターを一箇所で設定することにより、TripPinWS サービスに固有のクライアントクラスを定義できるようになります。

Class TripPinWS.Client Extends DcLib.OData.Client
{
Parameter BaseURL = "https://services.odata.org:443/V4/(S(jndgbgy2tbu1vjtzyoei2w3e))/TripPinServiceRW";
Parameter SSLConfiguration = "odata_org";
}

この基本クライアントを使用すれば、サービスで使用したいエンティティタイプごとにクラスを作成できます。 この新しいクライアントクラスを拡張すれば、EntityName パラメーターでエンティティ名を定義するだけで済みます。

Class TripPinWS.People Extends TripPinWS.Client
{
Parameter EntityName = "People";
}

次に、エンティティのクエリを簡単にするため、基本の DcLib.OData.Client クラスにさらにいくつかのメソッドを追加する必要があります。

Method Select(pSelect As %String) As DcLib.OData.Client
{
   do ..HttpRequest.SetParam("$select",pSelect)
   return $this
}
Method Filter(pFilter As %String) As DcLib.OData.Client
{
   do ..HttpRequest.SetParam("$filter",pFilter)
   return $this
}
Method Search(pSearch As %String) As DcLib.OData.Client
{
   do ..HttpRequest.SetParam("$search",pSearch)
   return $this
}
Method OrderBy(pOrderBy As %String) As DcLib.OData.Client
{
   do ..HttpRequest.SetParam("$orderby",pOrderBy)
   return $this
}
Method Top(pTop As %String) As DcLib.OData.Client
{
   do ..HttpRequest.SetParam("$top",pTop)
   return $this
}
Method Skip(pSkip As %String) As DcLib.OData.Client
{
   do ..HttpRequest.SetParam("$skip",pSkip)
   return $this
}
Method Fetch(pEntityId As %String = "") As DcLib.OData.ClientResponse
{
   if pEntityId="" return ##class(DcLib.OData.ClientResponse).%New($$$ERROR($$$GeneralError,"Entity ID must be provided"),"")
   set pEntityId="('"_pEntityId_"')"
   if $extract(..BaseURL,*)'="/" set ..BaseURL=..BaseURL_"/"
   set sc=..HttpRequest.Get(..BaseURL_..EntityName_pEntityId,..Debug)
   set response=##class(DcLib.OData.ClientResponse).%New(sc,..HttpRequest.HttpResponse,"one")
   quit response
}
Method FetchCount() As DcLib.OData.ClientResponse
{
   if $extract(..BaseURL,*)'="/" set ..BaseURL=..BaseURL_"/"
   set sc=..HttpRequest.Get(..BaseURL_..EntityName_"/$count")
   set response=##class(DcLib.OData.ClientResponse).%New(sc,..HttpRequest.HttpResponse,"count")
   quit response
}
Method FetchAll() As DcLib.OData.ClientResponse
{
   #dim response As DcLib.OData.ClientResponse
   if $extract(..BaseURL,*)'="/" set ..BaseURL=..BaseURL_"/"
   set sc=..HttpRequest.Get(..BaseURL_..EntityName,..Debug)
   set response=##class(DcLib.OData.ClientResponse).%New(sc,..HttpRequest.HttpResponse,"many")
   if response.IsError() return response
   //応答に nextLink が含まれる場合は、さらにデータを取得し続ける必要があります。
   while response.Payload.%IsDefined("@odata.nextLink") {
       //前の値を配列に退避し、新しい値をその配列にプッシュしてから
       //新しい応答にそれを設定し直し、新しい値のイテレータを作成します。
       set previousValueArray=response.Payload.value
       set sc=..HttpRequest.Get(response.Payload."@odata.nextLink",..Debug)
       set response=##class(DcLib.OData.ClientResponse).%New(sc,..HttpRequest.HttpResponse)
       if response.IsError() return response
       while response.Value.%GetNext(.key,.value) {
           do previousValueArray.%Push(value)    
       }
       set response.Payload.value=previousValueArray
       set response.Value=response.Payload.value.%GetIterator()
   }
   return response
}

ここでは 9 つの新しいメソッドを追加しました。 最初の 6 つはクエリオプションを定義するためのインスタンスメソッドであり、最後の 3 つは 1 つのエンティティ、すべてのエンティティ、またはすべてのエンティティのカウントを取得するためのメソッドです。

最初の 6 つのメソッドは、基本的に HTTP リクエストオブジェクトにパラメーターを設定するためのラッパーです。 コードを実装しやすくするため、これらの各メソッドはこのオブジェクトのインスタンスを返し、メソッドをチェーン化できるようにしています。

メインの Fetch() メソッドについて説明する前に、Filter() メソッドの動作を見てみましょう。

set people=##class(TripPinWS.People).%New().Filter("UserName eq 'ronaldmundy'").FetchAll()
while people.Value.%GetNext(.key,.person) {
 write !,person.FirstName," ",person.LastName    
}

このメソッドを使用すると、次のような結果が返ってきます。

Ronald Mundy

このサンプルコードでは、TripPinWS Peopleオブジェクトのインスタンスを作成しています。 これにより、ベース URL と基本クラスの証明書の構成が設定されます。 その後、その Filter メソッドを呼び出してフィルタークエリを定義してから FetchAll() を呼び出すと HTTP リクエストを呼び出すことができます。

ここで直接アクセスできるのは生の JSON データではなく、動的オブジェクトとしての People の結果であることに注意してください。 これは、例外処理を簡単にする ClientResponse オブジェクトも実装するためです。 また、返される結果のタイプに応じて動的オブジェクトを生成します。

まず、FetchAll() メソッドについて説明します。 この段階で、実装クラスが基本クラス構成で OData URL を定義しており、ヘルパーメソッドが追加のパラメーターを設定しています。FetchAll() メソッドは URL を組み立てて GET リクエストを発行する必要があります。 元のコマンドラインの例と同様に HttpRequest クラスで Get() メソッドを呼び出し、その結果から ClientResponse を作成します。

API が一度に 8 件しか結果を返さないため、このメソッドは複雑になっています。 コード内でこの制限に対処し、前の結果の nextLink 値を使用して最後のページに到達するまで次の結果ページを取得し続ける必要があります。 追加の各ページを取得する際に前の結果配列を保管してから、新しい結果をそれぞれその配列にプッシュしています。

Fetch() / FetchAll() / FetchCount() メソッドは、DcLib.OData.ClientResponse というクラスのインスタンスを返します。 例外を処理し、有効な JSON 応答を自動的に逆シリアル化するためにこのクラスを作成しましょう。

Class DcLib.OData.ClientResponse Extends %RegisteredObject
{
Property InternalStatus As %Status [ Private ];
Property HttpResponse As %Net.HttpResponse;
Property Payload As %Library.DynamicObject;
Property Value;
Method %OnNew(pRequestStatus As %Status, pHttpResponse As %Net.HttpResponse, pValueMode As %String = "") As %Status [ Private, ServerOnly = 1 ]
{
   //直接の HTTP エラーをチェック
   set ..InternalStatus = pRequestStatus
   set ..HttpResponse = pHttpResponse
   if $$$ISERR(pRequestStatus) {
       if $SYSTEM.Status.GetOneErrorText(pRequestStatus)["" set ..InternalStatus=$$$ERROR($$$GeneralError,"Could not get a response from HTTP server, server could be uncontactable or server details are incorrect")
       return $$$OK
   }
   
   //モードが count の場合、応答は JSON ではなく単なる数値になります。
   //数値であることを確認し、true ならばすべて ok を返しますが、それ以外の場合は
   //JSON で表現されるエラーを検出するためにフォールスルーします。
   if pValueMode="count" {
       set value=pHttpResponse.Data.Read(32000)
       if value?1.N {
           set ..Value=value
           return $$$OK
       }
   }
   
   //JSON ペイロードをシリアル化し、シリアル化エラーをキャッチします。
   try {
       set ..Payload={}.%FromJSON(pHttpResponse.Data)    
   } catch err {
       //先に HTTP ステータスコードのエラーをチェックします。
       if $e(pHttpResponse.StatusCode,1)'="2" {
           set ..InternalStatus = $$$ERROR($$$GeneralError,"Unexpected HTTP Status Code "_pHttpResponse.StatusCode)
           if pHttpResponse.Data.Size>0 return $$$OK
       }
       set ..InternalStatus=err.AsStatus()
       return $$$OK
   }
   
   //OData エラーのペイロードをチェックします。
   if ..Payload.%IsDefined("error") {
       do ..HttpResponse.Data.Rewind()
       set error=..HttpResponse.Data.Read(32000)
       set ..InternalStatus=$$$ERROR($$$GeneralError,..Payload.error.message)    
       return $$$OK
   }
   
   //すべて ok なら、必要なモード(many, one, count)に一致するように応答値を設定します。
   if pValueMode="one" {
       set ..Value=..Payload
   } else {
       set iterator=..Payload.value.%GetIterator()
       set ..Value=iterator
   }
   
   return $$$OK
}
Method IsOK()
{
   return $$$ISOK(..InternalStatus)
}
Method IsError()
{
   return $$$ISERR(..InternalStatus)
}
Method GetStatus()
{
   return ..InternalStatus
}
Method GetStatusText()
{
   return $SYSTEM.Status.GetOneStatusText(..InternalStatus)
}
Method ThrowException()
{
   Throw ##class(%Exception.General).%New("OData Fetch Exception","999",,$SYSTEM.Status.GetOneStatusText(..InternalStatus))
}
Method OutputToDevice()
{
   do ..HttpResponse.OutputToDevice()
}
}

ClientResponse オブジェクトのインスタンスが与えられた場合、最初にテストを実行してエラーがあったかどうかを確認することができます。 エラーは複数のレベルで発生する可能性があるため、単一の使いやすいソリューションでエラーを返すのが望ましいです。

set response=##class(TripPinWS.People).%New().Filter("UserName eq 'ronaldmundy'").FetchAll()
if response.IsError() write !,response.GetStatusText() quit

IsOK() メソッドと IsError() メソッドはオブジェクトのエラーをチェックします。 エラーが発生した場合は GetStatus() または GetStatusText() を呼び出してエラーにアクセスするか、ThrowException() を使用してエラーを例外ハンドラに渡すことができます。

エラーが発生していない場合、ClientResponse は生のペイロードオブジェクトを応答ペイロードのプロパティに代入します。

set ..Payload={}.%FromJSON(pHttpResponse.Data)

次に、応答の Value プロパティを単一のインスタンスとして、または多数の結果を探索するための配列イテレータとして、ペイロード内のメインデータ配列に設定します。

私はこれらすべてのコードを GitHub https://github.com/SeanConnelly/IrisOData/blob/master/README.md 上の単一のプロジェクトに格納しています。そこで全体を見直せば、より深く理解することができるでしょう。 次の例はすべて、ソースの GitHub プロジェクトに含まれています。

OData クライアントの使用

基本 Client クラスに関しては、With() メソッドも理解しておく必要があります。 すべてのエンティティのインスタンスを作成する代わりに、単一のクライアントクラスだけで With() メソッドを使用することができます。 With() メソッドは、指定されたエンティティ名で新しいクライアントを定義します。

ClassMethod With(pEntityName As %String) As DcLib.OData.Client
{
   set client=..%New()
   set client.EntityName=pEntityName
   return client
}

このメソッドを使用すれば、次のように基本 Client クラスですべての People を取得できます。

/// 基本クライアントクラスと .With("People") を使用してすべての "People" を取得します
ClassMethod TestGenericFetchAllUsingWithPeople()
{
   #dim response As DcLib.OData.ClientResponse
   set response=##class(TripPinWS.Client).With("People").FetchAll()
   
   if response.IsError() write !,response.GetStatusText() quit
   
   while response.Value.%GetNext(.key,.person) {
       write !,person.FirstName," ",person.LastName    
   }
}

または、次のようにクラスごとのエンティティを使用します。

/// People クラスを使用してすべての "People" を取得します
ClassMethod TestFetchAllPeople()
{
   #dim people As DcLib.OData.ClientResponse
   set people=##class(TripPinWS.People).%New().FetchAll()
   
   if people.IsError() write !,people.GetStatusText() quit
   
   while people.Value.%GetNext(.key,.person) {
       write !,person.FirstName," ",person.LastName    
   }
}

ご覧のとおり、これらの方法は非常に似通っています。 どちらの方法を選択すべきかは、具体的なエンティティについて自動補完がどれほど重要であるか、および具体的なエンティティクラスにエンティティ固有のメソッドを追加するかどうかによって異なります。

DC>do ##class(TripPinWS.Tests).TestFetchAllPeople()
Russell Whyte
Scott Ketchum
Ronald Mundy
… およびその他の人

次に、Airlines についても同じ処理を実装しましょう。

/// すべての "Airlines" を取得します
ClassMethod TestFetchAllAirlines()
{
   #dim airlines As DcLib.OData.ClientResponse
   set airlines=##class(TripPinWS.Airlines).%New().FetchAll()
   
   if airlines.IsError() write !,airlines.GetStatusText() quit
   
   while airlines.Value.%GetNext(.key,.airline) {
       write !,airline.AirlineCode," ",airline.Name    
   }
}

そして、コマンドラインから次の結果を得ることができます。

DC>do ##class(TripPinWS.Tests).TestFetchAllAirlines()
AA American Airlines
FM Shanghai Airline
… およびその他の航空会社

次は Airports の実装です。

/// すべての "Airports" を取得します
ClassMethod TestFetchAllAirports()
{
   #dim airports As DcLib.OData.ClientResponse
   set airports=##class(TripPinWS.Airports).%New().FetchAll()
   
   if airports.IsError() write !,airports.GetStatusText() quit
   
   while airports.Value.%GetNext(.key,.airport) {
       write !,airport.IataCode," ",airport.Name    
   }
}

そして、コマンドラインから次の結果を得ることができます。

DC>do ##class(TripPinWS.Tests).TestFetchAllAirports()
SFO San Francisco International Airport
LAX Los Angeles International Airport
SHA Shanghai Hongqiao International Airport
… およびその他の空港

これまでは FetchAll() メソッドを使用してきました。 次のように Fetch() メソッドを使用し、エンティティの主キーを使用して単一のエンティティを取得することもできます。

/// 人の識別子を使用して単一の "People" エンティティを取得します
ClassMethod TestFetchPersonWithID()
{
   #dim response As DcLib.OData.ClientResponse
   set response=##class(TripPinWS.People).%New().Fetch("russellwhyte")
   
   if response.IsError() write !,response.GetStatusText() quit
   
   //新しいフォーマッターを使用して出力を美しく整形してみましょう(最新バージョンの IRIS のみ)
   set jsonFormatter = ##class(%JSON.Formatter).%New()
   do jsonFormatter.Format(response.Value)
}

この例では動的配列またはオブジェクトを取得し、整形したJSONに出力できる新しい JSON フォーマッタークラスを使用しています。

DC>do ##class(TripPinWS.Tests).TestFetchPersonWithID()
{
 "@odata.context":"http://services.odata.org/V4/(S(jndgbgy2tbu1vjtzyoei2w3e))/TripPinServiceRW/$metadata#People/$entity",
 "@odata.id":"http://services.odata.org/V4/(S(jndgbgy2tbu1vjtzyoei2w3e))/TripPinServiceRW/People('russellwhyte')",
 "@odata.etag":"W/\"08D720E1BB3333CF\"",
 "@odata.editLink":"http://services.odata.org/V4/(S(jndgbgy2tbu1vjtzyoei2w3e))/TripPinServiceRW/People('russellwhyte')",
 "UserName":"russellwhyte",
 "FirstName":"Russell",
 "LastName":"Whyte",
 "Emails":[
   "Russell@example.com",
   "Russell@contoso.com"
 ],
 "AddressInfo":[
   {
     "Address":"187 Suffolk Ln.",
     "City":{
       "CountryRegion":"United States",
       "Name":"Boise",
       "Region":"ID"
     }
   }
 ],
 "Gender":"Male",
 "Concurrency":637014026176639951
}

OData の永続化

最後のいくつかの例では、新しい JSON アダプタクラスを使用して OData JSON を永続オブジェクトに逆シリアル化する方法を説明します。 ここでは Person、Address、City の 3 つのクラスを作成しますが、いずれも Person のデータ構造を OData メタデータに反映します。 また、@odata.context のような追加の OData プロパティが逆シリアル化エラーをスローしないよう、1 に設定された %JSONIGNOREINVALIDFIELD を使用します。

Class TripPinWS.Model.Person Extends (%Persistent, %JSON.Adaptor)
{
Parameter %JSONIGNOREINVALIDFIELD = 1;
Property UserName As %String;
Property FirstName As %String;
Property LastName As %String;
Property Emails As list Of %String;
Property Gender As %String;
Property Concurrency As %Integer;
Relationship AddressInfo As Address [ Cardinality = many, Inverse = Person ];
Index UserNameIndex On UserName [ IdKey, PrimaryKey, Unique ];
}
Class TripPinWS.Model.Address Extends (%Persistent, %JSON.Adaptor)
{
Property Address As %String;
Property City As TripPinWS.Model.City;
Relationship Person As Person [ Cardinality = one, Inverse = AddressInfo ];
}
Class TripPinWS.Model.City Extends (%Persistent, %JSON.Adaptor)
{
Property CountryRegion As %String;
Property Name As %String;
Property Region As %String;
}

次に、OData サービスから Russel Whyte を取得し、Person モデルの新しいインスタンスを作成した後に応答値を使用して %JSONImport() メソッドを呼び出します。 これにより、Address と City の詳細とともに Person オブジェクトにデータが入力されます。

ClassMethod TestPersonModel()
{
   #dim response As DcLib.OData.ClientResponse
   set response=##class(TripPinWS.People).%New().Fetch("russellwhyte")
   
   if response.IsError() write !,response.GetStatusText() quit
   
   set person=##class(TripPinWS.Model.Person).%New()
   
   set sc=person.%JSONImport(response.Value)
   if $$$ISERR(sc) write !!,$SYSTEM.Status.GetOneErrorText(sc) return
   
   set sc=person.%Save()
   if $$$ISERR(sc) write !!,$SYSTEM.Status.GetOneErrorText(sc) return
}

次に、次のように SQL コマンドを実行してデータが永続化されていることを確認できます。

SELECT ID, Concurrency, Emails, FirstName, Gender, LastName, UserName
FROM TripPinWS_Model.Person
ID                          Concurrency                      Emails                                                                                    FirstName    Gender    LastName    UserName
russellwhyte    637012191599722031    Russell@example.com Russell@contoso.com    Russell            Male         Whyte            russellwhyte

最終的な考え

上記のように、組み込みの %NET クラスを使用して RESTful な OData サービスを利用するのは簡単です。 少しばかりの追加ヘルパーコードを使用すれば、OData クエリの構築を単純化し、エラーレポートを統合し、JSON を動的オブジェクトに自動的に逆シリアル化できます。

そして、必要に応じてベース URL と HTTPS 構成を指定するだけで、新しい OData クライアントを作成できます。 さらに、この単一のクラスと .With('エンティティ') メソッドを使用してサービス上の任意のエンティティを利用するか、関心のあるエンティティの名前付きサブクラスを作成することができます。

また、新しい JSON アダプタを使用して JSON 応答を永続クラスに直接逆シリアル化できることも説明しました。 現実的には最初にこのデータを非正規化することを検討し、JSON アダプタクラスがカスタムマッピングで機能することを確認する必要があります。

最後になりますが、OData の操作は非常に簡単です。 私が特注の実装でよく経験する場合よりもはるかに少ないコード量でサービス実装の一貫性を維持することができました。 私は RESTful 設計の自由さを楽しんでいますが、次のサーバーサイドソリューションでは標準を実装することを検討したいと思います。

0
0 301
記事 Toshihiko Minamoto · 11月 16, 2020 6m read

InterSystems API Management(IAM)は、IT インフラストラクチャ内の Web ベースの API との間のトラフィックを監視、制御、および管理できる InterSystems IRIS Data Platform の新機能です。 アナウンスを見逃した方は、こちらのリンクを参照してください。 また、IAM の使い方を説明した記事もあります。

この記事では、InterSystems API Management を使用して API の負荷を分散します。

この例では、2 つの InterSystems IRIS インスタンスを使用し、クライアントに /api/atelier REST API を公開したいと思います。

そのようにしたいと思う理由は、次のようにさまざまです。

  • 負荷分散により、サーバー間でワークロードを分散する。
  • ブルーグリーンデプロイの実践。片方を「本番用」、もう片方を「開発用」とする 2 台のサーバーを用意し、それらを切り替えられるようにする。
  • カナリアデプロイの実践。1 台のサーバーでのみ新しいバージョンを公開し、クライアントの 1 %をそこに移動する。
  • 高可用性構成の実現。
  • その他。

しかし、いずれの場合も実行する必要のある手順は同じようなものです。

前提条件

  • 2 つの InterSystems IRIS インスタンス
  • InterSystems API Management インスタンス

作業開始

必要な作業を以下に掲載します。

1. アップストリームの作成

アップストリームは仮想ホスト名を表し、複数のサービス(ターゲット)で着信リクエストの負荷を分散するために使用されます。 例えば、service.v1.xyz という名前のアップストリームはホストが service.v1.xyz であるサービスのリクエストを受け付けます。 このサービスのリクエストは、アップストリーム内で定義されたターゲットにプロキシされます。

アップストリームには、リクエストを処理する能力の有無に基づいてターゲットを有効または無効にできるヘルスチェッカーも含まれています。

はじめに、次の手順を実行します。

  • IAM 管理ポータルを開く
  • ワークスペースに移動する
  • ワークスペースを選択する
  • アップストリームを開く
  • [New Upstream] ボタンをクリックする

[New Upstream] ボタンをクリックすると、アップストリームに関する基本情報を入力できるフォームが表示されます(他にも多くのプロパティがあります)。

name にサービスが使用する仮想ホスト名を入力します。 この値は DNS レコードとは無関係です。 混乱を避けるために、存在しない値に設定することをお勧めします。 残りのプロパティについて確認したい場合は、ドキュメントを参照してください。 スクリーンショットでは、新しいアップストリームに myupstream という仮の名前を付けています。

2. ターゲットの作成

ターゲットは、リクエストを実行して結果をクライアントに送り返すバックエンドサーバーです。 アップストリームに移動し、作成したアップストリーム名をクリックします([Update] ボタンではありません)。

既存のすべてのターゲット(現時点では存在しません)と [New Target] ボタンが表示されます。 このボタンを押します。

そして、新しいフォームでターゲットを定義します。 ここで使用できるパラメーターは次の 2 つだけです。

  • target - バックエンドサーバーのホストとポート
  • weight - このサーバーに与えられた相対的な優先度(wight の値が大きいほど、より多くのリクエストがこのターゲットに送信されます)

ここでは次のように 2 つのターゲットを追加しました。

3. サービスの作成

アップストリームを作成しましたので、今度はそのアップストリームにリクエストを送信する必要があります。 そのためにはサービスを使用します。
サービスエンティティは、その名前が示すように各アップストリームサービスを抽象化したものです。 サービスの例としては、データ変換、
マイクロサービス、課金 API などがあります。

IRIS インスタンスをターゲットとするサービスを作成してみましょう。[Service] に移動し、[New Service] ボタンを押します。

次の値を設定します。

フィールド説明
namemyserviceこのサービスの論理名
hostmyupstreamアップストリーム名
path/api/atelier提供したいルートパス
protocolhttpサポートしたいプロトコル

上記以外はデフォルト値のままにしてください(port: 80 を含みます)。

作成したサービスは、サービスのリストに表示されます。 サービスの id をどこかにコピーしておいてください。後で必要になります。

4. ルートの作成

ルートは、クライアントのリクエストに一致するルールを定義します。 各ルートはサービスに関連付けられ、サービスには複数のルートを関連付けることができます。
特定のルートに一致するすべてのリクエストは、関連するサービスにプロキシされます。

ルートとサービスを組み合わせる(およびそれらの関係を分離する)ことで強力なルーティング機構を実現し、インフラストラクチャのさまざまなアップストリームサービスにつながる IAM できめ細かなエントリポイントを定義することができます。

それでは、ルートを作成しましょう。 [Routes] に移動し、[New Route] ボタンを押します。

ルートの作成フォームで次の値を設定します。

フィールド説明
path/api/atelier提供したいルートパス
protocolhttpサポートしたいプロトコル
service.id3のguidサービス id の値(前のステップの guid)

以上です!

http://localhost:8000/api/atelier/(最後のスラッシュに注意)に送信されたリクエストは、2 つのバックエンドのいずれかによって処理されます。

まとめ

IAM は詳細にカスタマイズ可能な API 管理インフラストラクチャを提供し、開発者と管理者が API を制御できるようにします。

リンク

質問

IAM を使用してどのような機能を実現してほしいですか?

0
0 255
記事 Mihoko Iijima · 10月 27, 2020 4m read

皆さん、こんにちは!

InterSystems IRIS には [Interoperability(相互運用性)]というメニューがあります。

このメニューには、システム統合を簡単に作成できる仕組み(アダプタ、レコードマップ、BPM、データ変換など)が用意されていて、異なるシステムを簡単に接続することができます。

例えば、普段繋がっていないシステムを繋げるために相手の仕様に合わせてデータを受信(または送信)したり、データ送信前に別システムから情報を取得して追加したり、データベース(IRIS でもそれ以外でも)から情報を取得したり更新したり、データ中継の流れの中に様々な処理を含むことができます。

この記事のシリーズでは、Interoperability(相互運用性)でシステム統合を行う際、どのような仕組みで動作するのか、またどのような開発が必要になってくるのか、をご理解いただくためにサンプルコードをご覧いただきながら以下の項目を解説します。

まずはこのシリーズで使用するテーマをご紹介します。

0
2 1412
記事 Mihoko Iijima · 10月 27, 2020 10m read

この記事はこちらの投稿の続きの内容です。

前回の記事では、システム統合に必要なコンポーネントの中から、プロダクション内の処理の調整役となるビジネス・プロセスの作成について解説しました。

今回の記事では、プロダクションの情報入力窓口である、ビジネス・サービスの作成について解説します。

いよいよ「Interoperability(相互運用性)を使ってみよう!」の最後のコンポーネントです。

ビジネス・サービスは、IRIS 外部からの送信される情報の入力窓口で、外部 I/F に対してアダプタを使用する/しないを選択できます。

サンプルでは、3 種類のビジネス・サービスを用意しています(括弧内のリンクはサンプルコードへのリンク)。

  1. ファイルインバウンドアダプタを利用したファイル用ビジネス・サービスStart.FileBS) 
  2. SOAP インバウンドアダプタを利用する Web サービス用ビジネス・サービスStart.WS.WebServiceBS
  3. アダプタを使用せず、ストアドプロシージャや REST で呼び出されるビジネス・サービスStart.NonAdapterBS
0
0 916
記事 Akio Hashimoto · 10月 25, 2020 5m read

Docker環境下でWebアプリのコンテナとIRISのコンテナをRESTfulAPIで連携した時の SSL化方法。
ちなみにこの方法で管理ポータル等もSSL通信が可能になります。

参考サイト:

構成

※ Vue.jsはローカル上でホットリロードを利用した開発環境。 ※ IRISはローカル上に構築したDockerコンテナ下で稼働中。

今回、IRISをDockerコンテナで稼働させる方法は割愛します。

WebアプリケーションをHTTPSで利用する

Vue.jsで開発中のアプリケーションをhttpsでホットリロード利用出来るように起動する。

npm run serve -- --https

これで開発中のデバッグ画面をhttpsで呼び出して利用する事ができるようになるが、このままでは、httpsからhttpにアクセスする事が出来ない為、IRISのRESTを呼び出すことが出来ない。

Nginxでプロキシサーバーを立てる

Nginxでプロキシサーバーを立てて、httpsでアクセスされたら全てIRISのサーバーにリダイレクトするようにする。

https://192.168.10.100/ -> http://192.168.10.100:9092/

Dockerfile

FROM nginx

ENV _NGINX_CONF /etc/nginx

RUN mkdir -p $_NGINX_CONF/ssl
RUN apt-get update
RUN apt-get install openssl

※ NginxのコンテナにOpenSSLをインストールしておく

docker-compose.yml

version: '3.7'
services:
    proxy:
        build:
            context: .
        image: proxyiris:latest
        ports:
            - "443:443"
        restart: always
        environment:
            - TZ=Asia/Tokyo

Nginxのコンテナを作成し起動する。

docker-compose up -d

自己証明書を作成する

コンテナにログイン

docker exec -it proxy_proxy_1 /bin/bash

作業ディレクトリを準備

  • 作業ディレクトリに移動
cd /etc/nginx/ssl
  • openssl.cnfの変更
cp /etc/ssl/openssl.cnf /etc/ssl/openssl.cnf.org

※ openssl.cnfのバックアップ

openssl.cnf

・・・
[ CA_default ]

#dir		= ./demoCA		# Where everything is kept
dir		= .		# Where everything is kept
・・・

※編集方法は適宜。私の場合はローカルにコピーしてエディタで修正しました。

ローカルへのコピー方法

docker cp proxy_proxy_1:/etc/ssl/openssl.cnf {コピー先のパス}
  • CA署名時用のシリアルナンバーファイルを準備する
touch index.txt
echo 00 > serial

CA(証明書認証局)を作成

CA秘密鍵の作成。

openssl genrsa -aes256 -out cakey.pem 2048

※パスワードを入力します。

CA証明書リクエストの作成。

openssl req -new -key cakey.pem -config /etc/ssl/openssl.cnf -out cacert.csr

※Common name (e.g, server FQDN or YOUR name) []:192.168.10.100

x509 v3拡張領域の追加内容をファイルで指定する。

touch cav3.txt
echo "basicConstraints = critical, CA:true" > cav3.txt
echo "keyUsage = critical, cRLSign, keyCertSign" >> cav3.txt
echo "subjectKeyIdentifier=hash" >> cav3.txt

CA証明書の作成。

openssl ca -in cacert.csr -selfsign -keyfile cakey.pem -notext -config /etc/ssl/openssl.cnf -outdir . -days 365 -extfile cav3.txt -out cacert.pem

※ iOS13以降、有効期限は13ヶ月となっているので、今回は1年にしています。

サーバー証明書を作成

サーバー秘密鍵の作成。

openssl genrsa -aes256 -out server.key 2048

※パスワードを入力します。

サーバー証明書リクエストの作成。

openssl req -new -key server.key -config /etc/ssl/openssl.cnf -out server.csr

※「Organization Name」と「Common Name」は認証局の時と同じにしておく。

x509 v3拡張領域の追加内容をファイルで指定する。

touch v3server.txt
echo "[SAN]" > v3server.txt
echo "basicConstraints = CA:false" >> v3server.txt
echo "keyUsage = critical, digitalSignature, keyEncipherment" >> v3server.txt
echo "extendedKeyUsage = serverAuth" >> v3server.txt
echo "authorityKeyIdentifier=keyid,issuer" >> v3server.txt
echo >> v3server.txt
echo "subjectAltName=@alt_names" >> v3server.txt
echo "basicConstraints=CA:FALSE" >> v3server.txt
echo "[alt_names]" >> v3server.txt
echo "DNS.1=localhost" >> v3server.txt
echo "IP.1=192.168.10.100" >> v3server.txt
echo "IP.2=127.0.0.1" >> v3server.txt

サーバー証明書の作成。

openssl ca -in server.csr -config /etc/ssl/openssl.cnf -keyfile cakey.pem -outdir . -extfile v3server.txt -extensions SAN -out server.crt -days 365

パスワード無しサーバー秘密鍵の作成。

openssl rsa -in server.key -out nopass_server.key

Nginxに設定ファイルを追加して再起動する

設定ファイル

設定ファイルの作成

touch /etc/nginx/conf.d/irisproxy.cnf

irisproxy.cnf

server {
  listen 443 ssl http2 default_server;
  ssl_certificate         /etc/nginx/ssl/server.crt;
  ssl_certificate_key     /etc/nginx/ssl/nopass_server.key;
  location / {
    proxy_pass http://192.168.10.100:9092;
    proxy_redirect          http:// https://;
    proxy_set_header        Host            $host;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-Proto $scheme;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

コンテナの再起動

docker restart proxy_proxy_1

以上で、ローカル上のIRISへHTTPS接続出来るようになります。
Webアプリから「http://...:9092/xxx/」とアクセスしてRESTを呼び出している箇所を 「https://.../xxx/」と置き換えるだけです。

2
0 1380
質問 Akio Hashimoto · 10月 19, 2020

Vue.jsのホットリロードを使った環境においてWebアプリケーションを開発しています。Webアプリケーションから開発環境と同じローカルPCにあるDocker上のIRISへ%RESTを使用しRESTfulAPI通信をしていますが、Webアプリケーションをhttpsで利用した場合は、httpsからhttpへはAPI通信ができないので、間にプロキシサーバーを立ててリダイレクトしないといけないと思います。しかし上手くいきません。

○ npm run serve で起動したWebアプリ http -> httpでIRISの%RESTを呼び出せる。

× npm run serve -- --https で起動した場合  https -> http は呼び出しできない。

  https -> プロキシサーバ(https-httpへリダイレクト)-> httpでIRISの%RESTが呼びさせるはず。

環境

Webアプリ:Vue.js 例)https://localhost:3014/

IRIS:Docker上のコンテナで稼働 例)http://localhost:9091 -> 57772

やってみた事

1. Docker上にnginxのコンテナを作成。openssl にてSAN付きオレオレ証明書の作成

2. リダイレクトの定義を作成

現象

7
0 1312
記事 Toshihiko Minamoto · 10月 22, 2020 7m read

InterSystems IRIS 2019.1は公開されてからしばらく経ちますが、気づかれていない可能性のある、JSONの処理の強化機能について説明したいと思います。 最新のアプリケーションを構築する際、特にRESTエンドポイントを操作する際は、JSONをシリアル化形式として扱うことが重要です。

JSONの書式

まず、JSONに書式設定を適用すると、人の目で読みやすくなります。 コードをデバックする際に、特定のサイズでJSONコンテンツを確認する場合に非常に役立ちます。 構造が単純化されていれば、ざっと目を通すことが容易にはなりますが、ネストされている複数の要素に遭遇すると、あっという間に読みづらくなります。 以下に簡単な例を示します。

{"name":"Gobi","type":"desert","location":{"continent":"Asia","countries":["China","Mongolia"]},"dimensions":{"length":1500,"length_unit":"km","width":800,"width_unit":"km"}}

人の目でより読みやすい形式を適用すると、コンテンツの構造を調べやすくなります。 適切な改行やインデントを使用した同一のJSON構造を見てみましょう。

{
  "name":"Gobi",
  "type":"desert",
  "location":{
    "continent":"Asia",
    "countries":[
      "China",
      "Mongolia"
    ]
  },
  "dimensions":{
    "length":1500,
    "length_unit":"km",
    "width":800,
    "width_unit":"km"
  }
}

この単純な例だけでも、出力がかなり大きくなるため、多くのシステムでこの書式がデフォルト設定となっていない理由は明確でしょう。 ただし、この詳細な書式により、基礎構造を簡単に読み取れるようになり、何かが間違っているかどうかを見つけやすくなります。

InterSystems IRIS 2019.1では、%JSONという名前のパッケージが導入されました。 パッケージには、上記で示したとおり、動的なオブジェクトと配列、そしてJSON文字列をより読みやすくできる整形ツールなど、いくつかの便利なユーティリティがあります。 %JSON.Formatterは非常に単純なインターフェースを持つクラスです。 すべてのメソッドはインスタンスメソッドであるため、必ずインスタンスを取得するところから始めます。

USER>set formatter = ##class(%JSON.Formatter).%New()

この選択の背景には、インデント(空白またはタブなど)特定の文字や行末記号の特定の文字を使えるように整形ツールを1回構成し、それ以降、必要な個所に使用できるようになるという理由があります。

Format()メソッドは、動的なオブジェクト化配列またはJSON文字列を取ります。 では、動的なオブジェクトを使用した簡単な例を見てみましょう。

USER>do formatter.Format({"type":"string"})
{
  "type":"string"
}

そして、以下は、同じJSONコンテンツでJSON文字列を使った例です。

USER>do formatter.Format("{""type"":""string""}")
{
  "type":"string"
}

Format()メソッドは、整形された文字列を現在のデバイスに出力しますが、直接変数に出力する場合のFormatToString()FormatToStream() も表示されます。

変速を変える

上記は素晴らしい機能ですが、それだけでは記事にする価値はないかもしれません。 InterSystems IRIS 2019.1では、永続オブジェクトと一時オブジェクトをJSONとの間でシリアル化する便利な方法も導入されています。 ここで調べるクラスは %JSON.Adaptorです。 概念が%XML.Adaptorに非常に似ているため、その名前が付けられています。 JSONとの間でシリアル化するクラスは、%JSON.Adaptorをサブクラス化する必要があります。 クラスは、いくつかの便利なメソッドを継承しますが、中でも%JSONImport()%JSONExport()の継承は非常に役立ちます。 これについては例で示すのが一番良いでしょう。 次のクラスがあったとします。

Class Model.Event Extends (%Persistent, %JSON.Adaptor)
{
 Property Name As %String;
 Property Location As Model.Location;
}

および

Class Model.Location Extends (%Persistent, %JSON.Adaptor)
{
 Property City As %String;
 Property Country As %String;
}

ご覧の通り、永続的なイベントクラスがあり、ロケーションにリンクしています。 両方のクラスは%JSON.Adaptorから継承されています。 このため、オブジェクトグラフを作成し、それをJSON文字列として直接エクスポートすることができます。

USER>set event = ##class(Model.Event).%New()
 
USER>set event.Name = "Global Summit"
 
USER>set location = ##class(Model.Location).%New()
 
USER>set location.City = "Boston"
 
USER>set location.Country = "United States of America"
 
USER>set event.Location = location
 
USER>do event.%JSONExport()
{"Name":"Global Summit","Location":{"City":"Boston","Country":"United States of America"}}

もちろん、%JSONImport()を使用して、逆の方向にエクスポートすることもできます。

USER>set jsonEvent = {"Name":"Global Summit","Location":{"City":"Boston","Country":"United States of America"}}
 
USER>set event = ##class(Model.Event).%New()
 
USER>do event.%JSONImport(jsonEvent)
 
USER>write event.Name
Global Summit
USER>write event.Location.City
Boston

インポートメソッドとエクスポートメソッドは、任意のネストされた構造で機能します。 %XML.Adaptorと同様に、対応するパラメーターを設定して、個々のプロパティのマッピングロジックを指定できます。 Model.Eventクラスを次の定義に変更してみましょう。

Class Model.Event Extends (%Persistent, %JSON.Adaptor)
{
 Property Name As %String(%JSONFIELDNAME = "eventName");
 Property Location As Model.Location(%JSONINCLUDE = "INPUTONLY");
}

上記の例とオブジェクト構造が変数eventに割り当てられていると仮定した場合、%JSONExport()を呼び出すと、次の結果が返されます。

USER>do event.%JSONExport()
{"eventName":"Global Summit"}

Nameプロパティは、eventNameフィールド名にマッピングされ、Locationプロパティは %JSONExport()呼び出しから除外されますが、存在する場合は%JSONImport()呼び出し中にJSONコンテンツに入力されます。 マッピングを調整するには、次のようなパラメーターを使用できます。

  • %JSONFIELDNAME: JSONコンテンツのフィールド名に対応します。
  • %JSONIGNORENULL: 開発者が文字列プロパティの空の文字列のデフォルト処理をオーバーライドできるようにします。
  • %JSONINCLUDE : このプロパティがJSON出力/入力に含まれるかどうかを制御します。
  • %JSONNULL: これがtrue(=1)である場合、未指定のプロパティはnull値としてエクスポートされます。 そうでない場合は、プロパティに対応するフィールドはエクスポート中に省略されます。
  • %JSONREFERENCE: オブジェクト参照の処理方法を指定します。 デフォルトは「OBJECT」で、参照先クラスのプロパティが参照先オブジェクトを表すために使用されることを示します。 その他のオプションは「ID」、「OID」、および「GUID」です。

これにより高度な制御が可能になり、非常に便利です。 オブジェクトを手動でJSONにマッピングする時代は終わりました。

あともう一つ

マッピングパラメーターをプロパティレベルで設定する代わりに、XDataブロックにJSONマッピングを定義することも可能です。 次に示すOnlyLowercaseTopLevelという名前のXDataブロックには、上記のeventクラスと同じ設定が行われています。

Class Model.Event Extends (%Persistent, %JSON.Adaptor)
{
 Property Name As %String;
 Property Location As Model.Location;
 XData OnlyLowercaseTopLevel
 {
 <Mapping xmlns="http://www.intersystems.com/jsonmapping">
     <Property Name="Name" FieldName="eventName"/>
     <Property Name="Location" Include="INPUTONLY"/>
 </Mapping>
 }
}

重要な違いが1つあります。それは、XDataブロックのJSONマッピングはデフォルトの動作を変更しないが、対応する%JSONImport()%JSONExport()の呼び出しで最後の引数として参照する必要があるということです。例を示します。

USER>do event.%JSONExport("OnlyLowercaseTopLevel")
{"eventName":"Global Summit"}

指定された名前のXDataブロックが存在しない場合、デフォルトのマッピングが使用されます。 このアプローチを使用すると、複数のマッピングを構成し、各呼び出しに必要なマッピングを個別に参照することができます。そのため、マッピングをより柔軟で再利用可能にしながら、より高い制御性を得ることができます。

これらの機能強化によって作業が楽になることを願っています。皆さんからのフィードバックを楽しみしています。 ぜひコメントを残してください。

0
0 607
お知らせ Mihoko Iijima · 10月 12, 2020

開発者の皆さんこんにちは!

第6回 InterSystems IRIS プログラミングコンテスト(Full Stackコンテスト) への応募、投票が全て終了しました。コンテストへのご参加、またご興味をお持ちいただきありがとうございました。

今回のお知らせでは、見事受賞されたアプリケーションと開発者の方々を発表します!

🏆 審査員賞 - 特別に選ばれた審査員から最も多くの票を獲得したアプリケーションに贈られます。

🥇 1位 - $1,500 は npm-iris を開発された Henrique Gonçalves Dias さんに贈られました!

🥈 2位 - $1,000 は apptools-admin を開発された Sergey Mikhailenk さんに贈られました!

🥉 3位 - $500 は realworld-intersystems-iris を開発された Dmitriy Maslenniko さんに贈られました!

🏆 開発者コミュニティ賞 - 最も多くの票を獲得したアプリケーションに贈られます。

🥇 1位 - $1,000 は npm-iris を開発された Henrique Gonçalves Dias さんに贈られました!

🥈 2位 - $250 は apptools-admin を開発された Sergey Mikhailenk さんに贈られました!

1
0 143
記事 Henrique Dias · 10月 8, 2020 2m read

npm-iris とは何ですか? 

N.P.Mは "No Project Mess "の略です。

N.P.M.は、InterSystems IRISとBootstrap 4を使用したプロジェクト&タスク管理アプリです。

No Project Messは、シンプルで直感的なプロジェクトとタスクの管理ソフトウェアで、開発者や中小企業が日々の複雑な問題を軽減できるように作成されています。 
スプレッドシート、カンバン、カレンダー、ガントチャートなど、タスクのためのさまざまなビューを提供しています。

2
0 175
お知らせ Mihoko Iijima · 9月 13, 2020

開発者の皆さんこんにちは!IRIS プログラミングコンテストも 6 回目を迎えました!

今回のコンテストのテーマは

「InterSystems IRIS をバックエンドとし Web またはモバイル・ソリューションをフロントエンドとして使用する⚡️フル・スタック・アプリケーション⚡️」

です。日本からのご応募お待ちしております!

Open Exchange(アプリケーション登録/参考となる開発テンプレート)のページはこちら➡ ⚡️ InterSystems Full Stack Contest ⚡️

応募期間は 2020年9月21日~10月4日 です!

(投票期間は 2020年10月5日~11日、勝者発表は 10月12日を予定しています)

優勝特典

1、審査員から多く票を集めたアプリケーションには、以下の賞金が贈られます。

🥇 1位 - $2,000 

🥈 2位 - $1,000 

🥉 3位 - $500

2、Developer Community で多く票を集めたソリューションには、以下の賞金が贈られます。

🥇 1位 - $1,000 

🥈 2位 - $500 

複数の参加者が同数の票を獲得した場合、全参加者が勝者となり賞金は勝者間で分配されます。

参加資格

どなたでもご参加いただけます!(InterSystems 開発者コミュニティのアカウントを作成するだけでご応募いただけます)

コンテストのスケジュール

1
0 325
お知らせ Mihoko Iijima · 9月 15, 2020

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

Full Stack コンテストについての続報をお伝えします!

投票期間に追加ポイントを獲得できる「テクノロジーボーナス」について紹介します。

対象となる技術は、以下の通りです。

  • InterSystems IRIS REST API の使用
  • InterSystems Native API の使用
  • InterSystems JDBC の使用
  • ZPMパッケージによる公開
  • Docker コンテナの使用

詳細は以下の通りです。

InterSystems IRIS REST API の使用 - 1 point

フルスタック・アプリケーションから、REST API 経由で InterSystems IRIS にアクセスるとボーナスポイントを獲得できます。REST API をご自身で開発されるか、組み込みのものを利用したり、ZPM 経由でインストールするなど、選択できます。

RESTサービス作成方法については、ドキュメントや IRIS で作成する REST サーバの仕組み をご参照ください。

InterSystems Native API の使用 - 1 point

0
0 185
記事 Toshihiko Minamoto · 8月 13, 2020 3m read

皆さん、こんにちは。

InterSystems System Alerting and Monitoring (SAM)をご存知でしょうか。InterSystems IRIS 2020.1以降に対応し、IRISやそのアプリケーションの監視を行うソリューションです。といってもシステム監視を行うPrometheus、アラートを管理するAlertManager、ダッシュボードとしてグラフ等を表示させるGrafanaなどを組み合わせたものですが、IRISの利用者に合わせて設定しやすくなっています。

なお、これらのコンポーネントはDockerコンテナを使用しますので、Docker(19.3.098以降)ならびにDocker compose(1.25以降)をインストールいただく必要があります。

IRISの監視APIについてはこちらをご覧ください。

インストール手順

1. アプリケーションのインストール

アプリケーション自体はDockerコンテナにて提供されますので、DockerComposeのファイルやシェルスクリプトを以下のGitHubリポジトリからダウンロードします。
https://github.com/intersystems-community/sam

以下のgzipファイルをダウンロードし、展開します。
sam-1.0.0.XXX-unix.tar.gz

1
1 346
お知らせ Mihoko Iijima · 8月 6, 2020

開発者のみなさん、こんにちは!

InterSystems IRIS Data Platform を使用してオープンソースソリューションを作成するコンテストへようこそ!

今回のコンテスト用テンプレートはこちら!(8/10 更新) ➡️ IRIS-FHIR-Template ⬅️
(InterSystems IRIS for Health のプレビューリリース版:2020.3 が利用されている開発テンプレートです)

テンプレートの日本語 Readme をご用意しています。

応募期間は 2020年8月10日~23日 です!

優勝特典

1、審査員から多く票を集めたアプリケーションには、以下の賞金が贈られます。

🥇 1位 - $2,000 

🥈 2位 - $1,000 

🥉 3位 - $500

2、Developer Community で多く票を集めたソリューションには、以下の賞金が贈られます。

🥇 1位 - $1,000 

🥈 2位 - $500 

複数の参加者が同数の票を獲得した場合、全参加者が勝者となり賞金は勝者間で分配されます。

参加資格

どなたでもご参加いただけます!(InterSystems 開発者コミュニティのアカウントを作成するだけでご応募いただけます)

コンテストのスケジュール

8月10日~23日 応募期間
8月24日~30日 投票
8月31日 優秀者発表

コンテストのテーマ

1
0 415
お知らせ Mihoko Iijima · 8月 3, 2020

開発者のみなさん、こんにちは!

インターシステムズは、2020年7月21日(火)に オンラインにて開催された開発者向けイベント「Developers Summit 2020 Summer」(主催:翔泳社)に協賛し、セミナーで IRIS プログラミングコンテストについてご紹介しました。

ご紹介内容は、コンテストに限らず、コンテナを使用した開発環境の整備にも使えますので、セミナーを見逃された方もぜひご覧ください!

もくじ

※ YouTubeでご覧いただくと目次の秒数にジャンプできます。

0:00~ InterSystems が開催しているプログラミングコンテストの概要

2:38~ 本セッションの目標

4:21~ ≪腕試しの準備その1≫開発環境の「テンプレート」で何が提供されるか

8:26~ テンプレート起動迄の手順(各コンテスト共通)

10:02~ 優勝作品のご紹介(2020年5月分開催 Native APIの回)

11:40~ ≪腕試しの準備その2≫ご応募までの実際の流れ
 RESTサーバ作成用のテンプレート

13:00~ テンプレートで準備されるREST サーバ作成環境について

16:47~ テンプレートの中身解説+コンテナの開始とサンプルコードの実行(実演)

23:02~ GET要求 /test で動作する testMethod()の追加と実行

0
0 158
記事 Shintaro Kaminaka · 7月 30, 2020 11m read

この記事では、RESTFormsプロジェクト(モダンなWebアプリケーション用の汎用REST APIバックエンド)を紹介します。

プロジェクトの背後にあるアイデアは単純です。私はいくつかのREST APIを書いた後、REST APIが一般的に次の2つの部分で構成されていることに気付きました。

  • 永続クラスの操作
  • カスタムビジネスロジック

また、独自のカスタムビジネスロジックを書く必要はありますが、RESTFormsには永続クラスの操作に関連するすべての機能を提供しています。

使用例

  • Cachéにすでにデータモデルがあり、REST API形式で情報の一部(またはすべて)を公開したい
  • 新しいCachéアプリケーションを開発しており、REST APIを提供したい

クライアントサイド

このプロジェクトはWebアプリケーションのバックエンドとして開発されているため、JSだけで事足ります。 形式の変換は必要ありません。

補足:CRUD

オブジェクトまたはコレクションに対し、次の4つの操作を実行できます。

  • Create(作成)
  • Read(読み込み)
  • Update(更新)
  • Delete(削除)

機能

RESTFormsを使用して以下を実行できます。

  • 公開されたクラスに対するCRUD - クラスのメタデータを取得し、クラスのプロパティを作成 / 更新 / 削除できます。
  • オブジェクトに対するCRUD - オブジェクトを取得 / 作成 / 更新 / 削除できます。
  • オブジェクトコレクションに対するRead(SQL経由) - SQLインジェクションから保護します。
  • 自己検出 – 最初に使用可能なクラスのリストを取得し、その後でクラスのメタデータを取得し、そのメタデータを基にしてオブジェクトに対するCRUDを実行できます。

パス

以下の表には、主なパスとRESTFormsを使用して実行できる操作を掲載しています。

<td>
  説明
</td>
<td>
  利用可能なすべてのクラスを一覧表示します
</td>
<td>
  すべてのクラスのメタデータを取得します
</td>
<td>
  クラスのメタデータ
</td>
<td>
  プロパティをクラスに追加します
</td>
<td>
  クラスのプロパティを変更します
</td>
<td>
  クラスのプロパティを削除します
</td>
<td>
  オブジェクトを取得します
</td>
<td>
  オブジェクトの1つのプロパティを取得します
</td>
<td>
  オブジェクトを作成します
</td>
<td>
  動的オブジェクトからオブジェクトを更新します
</td>
<td>
  オブジェクトからオブジェクトを更新します
</td>
<td>
  オブジェクトを削除します
</td>
<td>
  (SQL)クエリでクラスのオブジェクトを取得します
</td>
<td>
  (SQL)カスタムクエリでクラスのオブジェクトを取得します
</td>
URL
info
info/all
info/:class
field/:class
field/:class
field/:class/:property
object/:class/:id
object/:class/:id/:property
object/:class
object/:class/:id
object/:class
object/:class/:id
objects/:class/:query
objects/:class/custom/:query

**RESTFormsを使い始めるには?**
  1. GitHubからプロジェクトをインポートします(お勧めの方法は独自リポジトリにサブモジュールとして追加する方法ですが、単にリリースをダウンロードしても良いです)。
  2. RESTFormsを介して公開したい各クラスについて以下を実施します。
  • アダプタクラスから継承する
  • 権限を指定します(一部のクラスを読み取り専用として公開する場合などに実施)。
  • オブジェクトの表示値として使用されるプロパティを指定します。
  • 表示したいプロパティの表示名を指定します。

セットアップ

  1. [リリースページ](https://github.com/intersystems-ru/RESTForms/releases/tag/v1.0)で最新リリースである20161.xml( Caché 2016.1用)または201162.xml(Caché 2016.2以降用)をダウンロードして任意のネームスペースにインポートします。
  2. 新しいWebアプリケーション /forms をDispatchクラス Form.REST.Main を使用して作成します。
  3. http://localhost:57772/forms/test?Debug をブラウザで開き、インストールを検証します({"Status": "OK"} が出力され、場合によってはパスワードの入力が求められます)。
  4. テストデータが必要な場合は、次を呼び出します:

do ##class(Form.Util.Init).populateTestForms()

最初に、利用可能なクラスを知る必要があります。 この情報を取得するには、次を呼び出します。

http://localhost:57772/forms/form/info

次のような応答が返されます。

[
   { "name":"Company",     "class":"Form.Test.Company" },
   { "name":"Person",      "class":"Form.Test.Person"  },
   { "name":"Simple form", "class":"Form.Test.Simple"  }
]

現在3つのサンプルクラス(RESTFormで提供)があります。Person(Form.Test.Personクラス)のメタデータを見てみましょう。 この情報を取得するには、次を呼び出します。

http://localhost:57772/forms/form/info/Form.Test.Person

次のように、クラスのメタデータが応答として返されます。

{  
   "name":"Person",
   "class":"Form.Test.Person",
   "displayProperty":"name",
   "objpermissions":"CRUD",
   "fields":[  
      { "name":"name",     "type":"%Library.String",    "collection":"", "displayName":"Name",          "required":0, "category":"datatype" },
      { "name":"dob",      "type":"%Library.Date",      "collection":"", "displayName":"Date of Birth", "required":0, "category":"datatype" },
      { "name":"ts",       "type":"%Library.TimeStamp", "collection":"", "displayName":"Timestamp",     "required":0, "category":"datatype" },
      { "name":"num",      "type":"%Library.Numeric",   "collection":"", "displayName":"Number",        "required":0, "category":"datatype" },
      { "name":"аge",      "type":"%Library.Integer",   "collection":"", "displayName":"Age",           "required":0, "category":"datatype" },
      { "name":"relative", "type":"Form.Test.Person",   "collection":"", "displayName":"Relative",      "required":0, "category":"form"     },
      { "name":"Home",     "type":"Form.Test.Address",  "collection":"", "displayName":"House",         "required":0, "category":"serial"   },
      { "name":"company",  "type":"Form.Test.Company",  "collection":"", "displayName":"Company",       "required":0, "category":"form"     }
   ]
}

これらの情報は次のような意味を持ちます。

クラスのメタデータ:

  • name - クラスの表示名。
  • class - 基本となる永続クラス。
  • displayProperty - オブジェクトを表示するときに使用するオブジェクトのプロパティ。
  • objpermissions - ユーザーがオブジェクトを使用して実行できる操作。 この例では、ユーザーは新しいオブジェクトを作成し、既存のオブジェクトを変更し、既存のオブジェクトを削除し、次を取得できます。

プロパティのメタデータ:

  • name - プロパティ名 - クラスの定義と同じです。
  • type - プロパティのクラス。
  • * コレクション - リスト/配列のコレクションです。 * displayName - 表示プロパティ名。 * required - このプロパティが必須であるかどうか。 * category - プロパティのタイプクラスのカテゴリ。 RESTForms対応のすべてのクラスが「form」として表示されることを除き、通常のCachéクラスのカテゴリに従います。

    クラス定義では次のようになります。

    /// テストフォーム: Person
    Class Form.Test.Person Extends (%Persistent, Form.Adaptor, %Populate)
    {
    
    /// フォーム名。グローバルキーではないため、何でもかまいません。
    /// クラスをフォームとして持たないようにするには(ここのように)空の文字列に設定します。 
    Parameter FORMNAME = "Person";
    
    /// デフォルトの権限
    /// このフォームのオブジェクトは、作成、読み取り、更新、削除できます。
    /// すべてのユーザーの権限を変更するには、このパラメーターを再定義します。
    /// このクラスのcheckPermissionメソッドを再定義します(Form.Securityを参照してください)。
    /// ユーザーやロールなどに基づいて独自のセキュリティを追加します。
    Parameter OBJPERMISSIONS As %String = "CRUD";
    
    /// オブジェクトの基本情報に使用されるプロパティ
    /// デフォルトでは、getObjectDisplayNameメソッドはここから値を取得します。
    Parameter DISPLAYPROPERTY As %String = "name";
    
    /// このパラメーターの値をSQLでORDER BY句の値として使用します。 
    Parameter FORMORDERBY As %String = "dob";
    
    /// Personの名前。
    Property name As %String(COLLATION = "TRUNCATE(250)", DISPLAYNAME = "Name", MAXLEN = 2000);
    
    /// Personの生年月日。
    Property dob As %Date(DISPLAYNAME = "Date of Birth", POPSPEC = "Date()");
    
    Property ts As %TimeStamp(DISPLAYNAME = "Timestamp") [ InitialExpression = {$ZDATETIME($ZTIMESTAMP, 3, 1, 3)} ];
    
    Property num As %Numeric(DISPLAYNAME = "Number") [ InitialExpression = "2.15" ];
    
    /// Personの年齢。<br>
    /// これは、 <property>DOB</property> から派生した値を持つ計算されたフィールドです。
    Property аge As %Integer(DISPLAYNAME = "Age") [ Calculated, SqlComputeCode = { set {*}=##class(Form.Test.Person).currentAge({dob})}, SqlComputed, SqlComputeOnChange = dob ];
    
    /// このクラスメソッドは、誕生日 <var>date</var> が与えられた場合に現在の年齢を計算します。
    ClassMethod currentAge(date As %Date = "") As %Integer [ CodeMode = expression ]
    {
    $Select(date="":"",1:($ZD($H,8)-$ZD(date,8)\10000))
    }
    
    /// Personの配偶者。
    /// これは別の永続オブジェクトへの参照です。
    Property relative As Form.Test.Person(DISPLAYNAME = "Relative");
    
    /// Personの自宅住所。 埋め込みオブジェクトを使用します。
    Property Home As Form.Test.Address(DISPLAYNAME = "House");
    
    /// このPersonが働いている会社。
    Relationship company As Form.Test.Company(DISPLAYNAME = "Company") [ Cardinality = one, Inverse = employees ];
    }

    クラスでRESTFormsを有効にする

    そして、このクラスでRESTFormsを有効にするため、通常の永続クラスから始めて次のことを行いました。

    1. Form.Adaptor から拡張しました。
    2. 値を含むパラメーター FORMNAME(クラス名)を追加しました。
    3. OBJPERMISSIONS パラメーター(すべての権限のCRUD)を追加しました。
    4. DISPLAYPROPERTY パラメーター(オブジェクト名の表示に使用されるプロパティ名)を追加しました。
    5. FORMORDERBY パラメーター(RESTFormsを使用するクエリでソートするデフォルトのプロパティ)を追加しました。
    6. メタデータで確認したいプロパティごとに DISPLAYNAME プロパティのパラメーターを追加しました。

    以上です。 コンパイル後、RESTFormsを含むクラスを使用できるようになります。

    いくつかのテストデータを生成しましたので(インストールのステップ4を参照)、IDが1のPersonを取得してみましょう。 オブジェクトを取得するには、次を呼び出します。

    http://localhost:57772/forms/form/object/Form.Test.Person/1

    その応答は以下のとおりです(生成されるデータは異なる場合があります)。

    {
       "_class":"Form.Test.Person",
       "_id":1,
       "name":"Klingman,Rhonda H.",
       "dob":"1996-10-18",
       "ts":"2016-09-20T10:51:31.375Z",
       "num":2.15,
       "аge":20,
       "relative":null,
       "Home":{
          "_class":"Form.Test.Address",
          "House":430,
          "Street":"5337 Second Place",
          "City":"Jackson"
       },
       "company":{
          "_class":"Form.Test.Company",
          "_id":60,
          "name":"XenaSys.com",
          "employees":[
             null
          ]
       }
    }

    オブジェクト(具体的にはnumプロパティ)を変更するには、次を呼び出します。

    PUT http://localhost:57772/forms/form/object/Form.Test.Person

    このボディを使用します。

    {
       "_class":"Form.Test.Person",
       "_id":1,
       "num":3.15
    }

    速度を上げるには、_class_id、および変更対象のプロパティのみをリクエストのボディに含める必要があります。

    では、新しいオブジェクトを作成しましょう。 以下を呼び出します。

    POST http://localhost:57772/forms/form/object/Form.Test.Person

    このボディを使用します。

    {
       "_class":"Form.Test.Person",
        "name":"Test person",
        "dob":"2000-01-18",
        "ts":"2016-09-20T10:51:31.375Z",
        "num":2.15,
        "company":{ "_class":"Form.Test.Company", "_id":1 }
    }

    オブジェクトの作成が成功した場合、RESTFormsは以下のようにIDを返します。

    {"Id": "101"}

    成功しなかった場合、エラーがJSON形式で返されます。 すべての永続オブジェクトのプロパティは、 _class および _id プロパティによってのみ参照する必要があります。

    そして最後に、新しいオブジェクトを削除しましょう。 以下を呼び出します。

    DELETE http://localhost:57772/forms/form/object/Form.Test.Person/101

    これがForm.Test.Personクラスに対する完全なCRUDです。

    デモ

    [現在デモ環境はお試しいただくことができません。] こちらでRESTFormsをオンラインで試すことができます(ユーザー名:Demo、パスワード:Demo)。

    また、RESTFormsUIアプリケーション(RESTFormsデータエディタ)もあります。こちらをご確認ください(ユーザー名:Demo、パスワード:Demo)。 クラスリストのスクリーンショットを以下に掲載しています。

    まとめ

    RESTFormsは永続クラスに関する、REST APIから要求されるほとんどの機能を提供します。

    次の内容

    この記事では、RESTFormsの機能について説明しました。 次回の記事では、いくつかの高度な機能(クライアントからSQLインジェクションのリスクを冒さずにデータの一部を安全に取得できるクエリなど)についてお話ししたいと思います。 この記事のパート2でクエリに関する情報をお読みください

    RESTFormsUI(RESTFormsデータエディタ)もあります。

    リンク

    0
    0 344
    記事 Mihoko Iijima · 7月 21, 2020 2m read

    IRIS で REST サーバを作成する際に準備する REST ディスパッチクラスを API ファーストの手順で作成する方法を解説します。
    (OpenAPI 2.0に基づいて作成したアプリケーション定義を使用してディスパッチクラスを作成する手順を解説します)

    このビデオには、以下の関連ビデオがあります。

    もくじ

    最初~ 復習ビデオ/関連ビデオについて など

    2:36~  作成するディスパッチクラスの内容

    4:15~ RESTディスパッチクラス:APIファーストで作成する方法(手順説明)

    5:55~ アプリケーションの仕様を定義する (例)

    6:19~ IRISにアプリケーション仕様を登録する(説明)など

    7:40~ POST要求の実行 (例)

    8:24~ 実演

     ↓実演で使用したURL↓
     http://localhost:52773/api/mgmnt/v2/user/crud2

    10:32~ (POST要求の)実行結果

    11:50~ ベースURLの設定(管理ポータルでの設定)+ 実演

    0
    0 358
    記事 Mihoko Iijima · 7月 20, 2020 1m read

    IRIS で作成する REST サーバの仕組みを解説します。


    このビデオには、以下の関連ビデオがあります。

    もくじ

    0:55~ 今回の説明内容解説と関連ビデオについて

    3:00~  RESTとは?

    3:52~  動作の仕組み

    6:23~ RESTディスパッチクラスとは

    8:04~ RESTディスパッチクラスの実装方法

    8:34~ RESTディスパッチクラス:全て手動で作成する方法 概要

    9:30~ RESTディスパッチクラス:APIファーストで作成する方法 概要

    11:12~ 共通:ベースURLの設定(管理ポータルでの設定)

    11:37~最後まで 確認できたこと

    ※YouTubeでご覧いただくと、もくじの秒数にジャンプできます。

    0
    0 764
    記事 Shintaro Kaminaka · 5月 1, 2020 14m read

    この記事では、REST API開発への仕様ファーストアプローチについて説明します。 

    従来のコードファーストREST API開発は次のようになります。 

    • コードを書く 
    • RESTを有効にする  
    • ドキュメント化(REST APIとして) 

    仕様ファーストのアプローチでは同じ手順を行いますが、順序が逆になります。 ドキュメントを兼ねた仕様書を作成し、そこからRESTアプリの定型文を生成して、最後にビジネスロジックを書きます。

    これは、次の理由でメリットがあります。 

    0
    0 1310