查找

问题
· 一月 17, 2024

How to export a project via Visual Studio Code?

Hi everyone, 

Does anyone know how to export projects via VSC? 

I opened the project through the "InterSystems Tools" plugin (command is "Edit Code in Project") and I can correctly work on it.

However, when I try using the "ObjectScript" plugin to export the project (right click on the project -> "Export Project Contents")

This message appears and it is not possible to export the project:

I've tried also to open a new window, then a folder and finally the project, but nothing changes. 

This is an example of my workspace: 


Anyone knows how to do it? 

Thank you! 

2 条新评论
讨论 (8)5
登录或注册以继续
问题
· 一月 16, 2024

VSCode "Go To Definition" Does Nothing

When using VS Code to develop with Object Script, the Server Credentials prompt for connecting to the IRIS instance is happy accept a blank for UnknownUser. See screen shot:

From here VS Code happily connects to the IRIS instance using unauthenticated access. But the standard VS Code "Go To Definition" appears to do nothing. Whilst looking through the documentation I found that in the Language Server extension, there is a setting to output verbose trace details. With verbose output switched on, trying the "go to definition" of some code, the output shows this:

"[Warn  - 12:16:39 PM] Cannot make required REST request because the configured server connection has a username but no password."

Has anyone encountered this before? There is a bit of inconsistency between the Server Manager extension being happy to communicate with IRIS using unauthenticated access, but then the Language Server extension struggles. I'm happy to just instruct all users to have a username and password, but I'm wondering if there is a fix or something I can configure to make unauthenticated access.

5 Comments
讨论 (5)3
登录或注册以继续
问题
· 一月 5, 2024

Problème de connexion SFTP

Bonjour,

Je fais face à un problème depuis plusieurs mois, j'ai plusieurs flux qui fonctionnent en SFTP avec la classe d'iris EnsLib.FTP.PassthroughService

Le problème est que de temps en temps, j'ai un message d'erreur qui dit : 

"Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée" 

J'ai eu le cas pour un serveur chez nous et nous avons solutionné le problème en rallongeant le paramètre intervalle entre appels à 60 secondes,

Cependant j'ai la même erreur pour un serveur extérieur avec exactement la même configuration que le premier flux, j'ai contacté leur service infrastructure qui m'ont dit qu'il manquait bien un connexion SFTP à l'heure de l'erreur mais ils n'ont aucune information et rien dans leur logs. 

Concrètement cela n'empêche pas le flux de fonctionner car au prochain appel, tout refonctionne, cependant le noeud reste en rouge jusqu'à ce qu'il y ait un nouveau "ConfigItem" et cela nous envoie donc un mail d'alerte et surtout nous passe notre production en erreur sur notre outil de supervision qui est relié à l'api monitor d'Iris

D'ailleurs je ne comprend pas à quoi correspond ce fameux ConfigItem qui arrive assez aléatoirement comme ici par exemple.

Est-ce que d'autres personnes possèdent des flux SFTP en continu ? Avez-vous déjà eu ce problème ?

Merci par avance 

7 Comments
讨论 (7)2
登录或注册以继续
文章
· 十二月 26, 2023 阅读大约需 3 分钟

CSPアプリケーションをReactを使って書き換えるその4

IRIS側の処理は、IRISでREST APIを実装する方法を理解していれば、簡単です。

前回のログイン処理でユーザー認証をIRIS側でどのように実装されているか確認して見ましょう。

まずはディスパッチクラスの定義です。

Shop.Brokerというクラスの中で定義されています。

checkpasswordというメソッドが最後に定義されていて、最終的にShop.Rest.Customer:checkPasswordという(クラス)メソッドが呼ばれているのがわかると思います。

 

ここで定義しているパラメータは、とりあえずおまじない的に含めておくことをお勧めします。

(説明し出すと少し長くなるので)

Class Shop.Broker Extends %CSP.REST
{
Parameter CONVERTINPUTSTREAM = 1;
Parameter HandleCorsRequest = 1;
XData UrlMap
{
<Routes>
  <Route Url="/product/:code" Method="GET" Call="Shop.Rest.Product:getRecord"/>
  <Route Url="/products" Method="GET" Call="Shop.Rest.Product:listRecords"/>
  <Route Url="/deleteproduct/:code" Method="GET" Call="Shop.Rest.Product:deleteRecord"/>
  <Route Url="/addproduct" Method="POST" Call="Shop.Product:createRecord"/>
  <Route Url="/modifyproduct" Method="POST" Call="Shop.Rest.Product:modifyRecord"/>
  <Route Url="/customer/:id" Method="GET" Call="Shop.Rest.Customer:getRecord"/>
  <Route Url="/customers" Method="GET" Call="Shop.Rest.Customer:listRecords"/>
  <Route Url="/deletecustomer/:id" Method="GET" Call="Shop.Rest.Customer:deleteRecord"/>
  <Route Url="/addcustomer" Method="POST" Call="Shop.Customer:createRecord"/>
  <Route Url="/modifycustomer" Method="POST" Call="Shop.Rest.Customer:modifyRecord"/>
  <Route Url="/addorder" Method="POST" Call="Shop.Rest.POrder:createRecord"/>
  <Route Url="/order/:id" Method="GET" Call="Shop.Rest.POrder:getRecord"/>
  <Route Url="/orders" Method="GET" Call="Shop.Rest.POrder:listRecords"/>
  <Route Url="/checkpassword/:userid/:password" Method="GET" Call="Shop.Rest.Customer:checkPassword"/>
</Routes>
}

}

 

 

Shop.Rest.CustomerクラスのcheckPasswordメソッドの中は以下のような感じです。

IRISがわかっている人ならば中身の説明は不要ですよね。

ClassMethod checkPassword(pUserId As %String, pPassword) As %Status
{
    set status = $$$OK
    
    try {
        
      if $data(%request) {
        set %response.ContentType="application/json"
        set %response.CharSet = "utf-8"
      }
      
      set cust=##class(Shop.Customer).CheckPasswd(pUserId, pPassword)
      set return = {}

      if cust = "" {
        set return.authorized = "ng"
      }
      else {
        set return.authorized = "ok"
        set return.ID = cust.%Id()
      }

      do return.%ToJSON()
    
    }
    catch e {
        
      set status = e.AsStatus()
    }
                        
    Quit status
}

 

大体こんな感じです。

結構色々なことを理解しないとなかなか前に進めませんが、やはり理解するには自分で何か作ってみるのが一番です。 

讨论 (0)1
登录或注册以继续
文章
· 十二月 26, 2023 阅读大约需 7 分钟

CSPアプリケーションをReactを使って書き換えるその3

それでは、今回はより具体的にReact開発方法について解説します。

ショップデモのリポジトリの配下にreactというディレクトリがあります。

この下にReactのコードがあります。

ここのreact-setup.mdに記載されている通り、前準備としてreactのテンプレートを作ります。

npx create-react-app shopdemo --template typescript

 

あとはこのReactプロジェクトを動かすためのライブラリのインストールを行います。

詳細は、react-setup.mdに書いてあります。

まず3つのディレクトリがあって、これは絶対こうしなければならないというものでもないのですが、基本的なお作法として用意するのが一般的なようです。

  • public
    • ここにはindex.htmlだけ置くのが一般的なようです。
    • テンプレートが自動生成するものでも良いのですが、Bootstrapを使用する場合は、テンプレートのindex.htmlにそのライブラリのロードを付け加えています。
  • components
    • ここに自分で開発するreactコンポーネントを配置します。
  • hooks
    • hookを用意する場合は、ここに配置します。 ​​​​

ここでは、まずログインをするためのユーザー認証を行うコンポーネントの処理について説明します。

Login.tsxに処理を記述しています。

tsxという拡張子は、jsx形式のファイルで、typescriptで記述する際は、tsxにするのがお作法のようです。


このファイルの終わりの方にreturn文以降に以下のような記述があります。

これがjsx記法と呼ばれるもので、javascriptの中にHTML文を埋め込むことができます。

return (
<div>

<h1>ログイン</h1>

<form onSubmit={handleSubmit}>

<table>

<tbody>

<tr>

<td><label>利用者ID:</label></td>

<td><input name="userid" type="text" placeholder="userid" /></td>

</tr>

<tr>

<td><label>パスワード</label></td>

<td><input name="password" type="password" placeholder="password" /></td>

</tr>

<tr><td><button type="submit">ログイン</button></td></tr>

</tbody>

</table>

</form>

{isError && <p style={{ color: "red" }}>{`${errorText}`}</p>}

</div>

);

};

 

ここでユーザー名とパスワードを入力してもらって、ログイン認証を行う処理を作っていきます。

前回の記事でReactは基本SPAでサブミットはないという説明をしましたが、このログイン処理のように複数のデータをフォーム形式で入力してサブミットするというようなケースは多々あります。

方法は色々あるのですが、ここではreact-router-domというものを使っています。

それ以外にもnext.jsというフレームワークが有名です。

実際にサブミットしているように見えますが、実際にPOSTしているわけではなく、react-router-domというフレームワークの中であくまでもSPAの枠組みの中で処理は実装されている感じです。

(もしかしたらこの理解は間違っているかも)

ここでonSubmitコールバックとしてhandleSubmitというメソッドが呼ばれています。

ここの中括弧は、jsxの作法の1つで、そのカッコ内にjavascriptを記述できます。

ここでは、handleSubmitはJavaScriptの変数で、以下のように定義されています。

ちなみにReactでは変数定義は、基本const、たまにletが使われ、varは使いません。

古いJavaScriptしか知らない人にとってはここが1つのハードルかもしれません。

そして以下のコードは厳密に言うと、TypeScriptですが、あんまり褒められたTypeScriptコードではありません。

eventという変数の型としてanyを使っていますが、本当は適切なイベント型を指定するべきです。

そのイベントに紐づいた(ここはフォームデータなので)データ要素としてユーザー名とパスワードを取得しています。

そしてuserLoginCheckというメソッドを呼び出しています。

  const handleSubmit = (event: any) => {
    event.preventDefault();
    const { userid, password } = event.target.elements;
    userLoginCheck(userid.value, password.value).finally(() => 
    {
      if (login.status) {
        navigate("/Shop", { state: { customerId: login.customerId } })
      }
        
    })
  };

userLoginCheckの中身は以下のようになっています。

const userLoginCheck = async (userid: any, userpassword: any) => {
let status = false;

let customerId = 0;

setIsLoading(true);

setIsError(false);

await axios

.get<any>(`http://${serverAddress}:${serverPort}${applicationName}/checkpassword/${userid}/${userpassword}?IRISUsername=${username}&IRISPassword=${password}`)

.then((result: any) => {

if (result.data.authorized === 'ok') {

login.status = true;

login.customerId = result.data.ID;

}

else {

setIsError(true);

setErrorText('ログインが失敗しました');

}

})

.catch((error: any) => {

setIsError(true)

if (error.response) {

setErrorText(error.response.data.summary);

}

else if (error.request) {

setErrorText(error.request);

}

else {

setErrorText(error.message);

}

})

.finally(() => setIsLoading(false))

};



この後、RESTのインタフェースによりサーバーのAPIを呼び出します。

RESTのインタフェースの実装も複数ありますが、ネット上でサンプルがたくさん見つかるaxiosというライブラリを使用しています。

ここでREST APIのurlを指定します。 

このurlにcheckpasswordというメソッド名が含まれているのがわかると思います。

これがIRIS側で呼ばれるメソッド名となります。

ログインチェックがOKだったら、react-router-domに含まれるnavigateメソッドを呼び出して、/Shopにページ遷移します。

(実際にページ遷移しているわけではなくあくまでもエミュレーション)

あと、axiosの呼び出しは非同期なので、結果が返る前に呼び出しが戻ってくるので、最初はその動きになかなか慣れないかもしれません。

 

ログインが成功するとShop.tsxが呼ばれます。

このページはさらに複数のコンポーネントで構成されています。

そしてショッピングカートの機能を実装するためにcreateContextを使いコンテキスト情報を管理します。

navigate経由でページ遷移する際にデータを引き継ぐ仕組みとしてuseLocationというフックが用意されているので、それを使って情報を引き継ぎます。

jsx記法によるHTML定義の中で動的にデータを変更したい部分にはuseStateというフックを使用します。

import React from 'react';
import { createContext, useState, Dispatch,SetStateAction } from "react";
import { Header } from './Header';
import { ShoppingCart } from './ShoppingCart';
import { ProductList } from './ProductList';
import { useLocation } from "react-router-dom"
export type shopItem = {
        productCode: string;
        productName: string;
        price: number;
        units: number;
  };
  
export const ShopContext = createContext({} as {
    orderItems: shopItem[];
    setOrderItems: Dispatch<SetStateAction<shopItem[]>>;
    }
  ); 

export const Shop = () => {

  const [orderItems, setOrderItems] = useState<shopItem[]>([]);
  
  const location = useLocation();
        
  const values={orderItems,setOrderItems};
  
  return (
    <>
    <div className="title">
    <Header customerId = {location.state.customerId} /> 
    </div>
    <ShopContext.Provider value={values}>
    <div className="shoppingcart" style = {{ float: "left",width: "40%",height: "100%",overflow: "auto",border: "solid #000000 1px"}}>    
    <ShoppingCart customerId = {location.state.customerId} />
    </div>
    <div id="productlist" style = {{ width: "60%",height: "100%",overflow: "auto",border: "solid #000000 1px"}}>
    <ProductList />
    </div>
    </ShopContext.Provider>
    </>    
  );    
}
export default Shop;
讨论 (0)1
登录或注册以继续