備忘メモ(React.js環 開発境セットアップ)
マシン入れ替えたので備忘録兼作業メモ
作業の流れ
- VSCode インストール
- Homebrew インストール
- anyenv & nodenv インストール
- nodenv-default-packages 設定
- nodenv 個別バージョンインストール
- create-react-app 実行
VSCode インストール
普通にここから。Visual Studio Code – コード エディター | Microsoft Azure
Homebrew インストール
これまんまで。macOS(またはLinux)用パッケージマネージャー — Homebrew
anyenv & nodenv インストール
Big Surなのでzshのはずだけど念のため確認。
$ echo $SHELL
/bin/zsh
確認できたのでインストール。
$ brew install anyenv
$ echo 'eval "$(anyenv init -)"' >> ~/.zshrc
$ anyenv install --init
$ exec $SHELL -l
$ anyenv install nodenv
$ exec $SHELL -l
nodenv-default-packages インストール
$ mkdir -p $(anyenv root)/plugins
$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
$ mkdir -p "$(nodenv root)"/plugins
$ git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages"
nodenvのdefault-packages は以下。
$ vi $(nodenv root)/default-packages
yarn
typescript
ts-node
typesync
nodenv 個別バージョンインストール
14.5で挙動変わるから、初心者は14.4.0入れとけって言われたので今もそれに倣っているが、そろそろ最新版にしてもいい気がする。
$ nodenv install -l
$ nodenv install 14.4.0
$ nodenv global 14.4.0
create-react-app 実行
作業フォルダ作ってテストプロジェクト作成。
$ mkdir -p work/ReactJS
$ cd work/ReactJS
$ npx create-react-app hello-world --template typescript
$ cd hello-world
$ yarn start
備忘メモ(Vagrant + VirtualBox + Docker + VisualStudioCode on Mac)
そのうち手順を整理することになりそうなのでメモ。
参考
Vagrantを使う「Mac最速のDocker環境」を初心者向けに解説【遅いMac for Dockerを卒業】 - Qiita
Vagrant+VirtualBoxでUbuntu環境構築 - Qiita
Vagrantバージョン
Big Surだとバージョン2.2.14はvagrant upがコケるので、2.2.13に変更。 コケない場合は作業不要。
ダウングレードは以下参考。二行目のversionと三行目のsha256 だけ変更。
Downgrade homebrew (cask) package · GitHub
$ brew edit vagrant --cask
cask "vagrant" do
version "2.2.13"
sha256 "b65adb59bef5b69be61bd79a34e3532533a172491b307718d1c4a5a46209e81f"
url "https://releases.hashicorp.com/vagrant/#{version}/vagrant_#{version}_x86_64.dmg",
verified: "hashicorp.com/vagrant/"
appcast "https://github.com/hashicorp/vagrant/releases.atom"
name "Vagrant"
desc "Development environment"
homepage "https://www.vagrantup.com/"
pkg "vagrant.pkg"
uninstall script: {
executable: "uninstall.tool",
input: ["Yes"],
sudo: true,
},
pkgutil: "com.vagrant.vagrant"
zap trash: "~/.vagrant.d"
end
作業の流れ
- Homebrewインストール
- VirtualBoxインストール
- Vagrantインストール
- Vagrantプラグインインストール
- Vagrant設定
- Mutagenインストール
- Mutagen設定
- docker-composer作成
- 確認用PHP作成
- ssh config作成(無い場合)
- Vagrant起動
- Vagrant接続(terminal)
- Docker起動
- アクセス確認
- Docker停止
- Vagrant切断
- Vagrant接続(Visual Studion Code)
- Vagrant停止
Homebrewインストール
これまんまで。macOS(またはLinux)用パッケージマネージャー — Homebrew
VirtualBoxインストール
$ brew install --cask virtualbox
セキュリティとプライバシーの設定でコケたら設定してやり直し。 設定したら再起動をする必要があるので注意。
Vagrantインストール
$ brew install --cask vagrant
Vagrantプラグインインストール
$ vagrant plugin install vagrant-disksize vagrant-hostmanager vagrant-mutagen vagrant-docker-compose
Vagrant設定
Box調査。Discover Vagrant Boxes - Vagrant Cloud 今回はubuntu/xenial64を選択。
作業フォルダ作成&移動。以後はcdしない限りここにずっと居て、各種ディレクトリ、ファイルもここに作成。 おっと、Macだとフォルダだったかな。
$ mkdir ~/Documents/Vagrant
$ cd ~/Documents/Vagrant
Vagrantfile作成。
$ vagrant init ubuntu/xenial64
編集
$ vi Vagrantfile
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.vm.hostname = "vagrant-ubuntu"
config.vm.network "private_network", ip: "192.168.33.10"
config.vm.provider "virtualbox" do |vb|
vb.gui = false
vb.cpu = 1
vb.memory = "2048"
vb.customize ['modifyvm', :id, '--natdnsproxy1', 'off']
vb.customize ['modifyvm', :id, '--natdnshostresolver1', 'off']
end
config.disksize.size = '10GB'
config.mutagen.orchestrate = true
config.vm.synced_folder './', '/home/vagrant/app', type: "rsync",
rsync_auto: true,
rsync__exclude: ['.git/', 'node_modules/', 'log/', 'tmp/']
config.vm.provision :docker, run: 'always'
config.vm.provision :docker_compose
end
Mutagenインストール
brew install mutagen-io/mutagen/mutagen
Mutagen設定
vi mutagen.yml
sync:
app:
mode: "two-way-resolved"
alpha: "./"
beta: "vagrant-ubuntu:/home/vagrant/app"
ignore:
vcs: true
paths:
- "/node_modules"
- "/log"
- "/tmp"
docker-composer作成
$ vi docker-composer.yml
version: '3'
services:
web:
image: php:7.4-apache-buster
ports:
- "80:80"
volumes:
- ./public:/var/www/html:cached
確認用PHP作成
$ mkdir public
$ vi public/index.php
<?php
phpinfo();
$ vi public/.htaccess
DirectoryIndex index.php
$ chmod -R 755 public
ssh config作成(無い場合)
まっさらな環境の場合、ファイルが無いので作る。中身は空でOK。
$ mkdir ~/.ssh
$ touch ~/.ssh/config
Vagrant起動
$ vagrant up
Vagrant接続(terminal)
$ vagrant ssh
Welcome to Ubuntu 16.04.7 LTS (GNU/Linux 4.4.0-198-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
1 package can be updated.
1 of these updates is a security update.
To see these additional updates run: apt list --upgradable
New release '18.04.5 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
Docker起動
vagrant@vagrant-ubuntu:~$ cd app
vagrant@vagrant-ubuntu:~/app$ docker-compose up
アクセス確認
ブラウザで http://192.168.33.10/ にアクセス。 この場合のIPアドレスはVagrantfileで指定したもの。
Docker停止
Ctrl-C
Vagrant切断
vagrant@vagrant-ubuntu:~$ exit
Vagrant接続(Visual Studio Code)
Visual Studio Codeにremote developmentを追加。 ssh configにvagrant用のエントリ追加。
$ vagrant ssh-config >> ~/.ssh/config
後は繋ぐのみ。 Macの場合はシステム環境設定のセキュリティとプライバシーでVisual Studio Codeにフォルダのアクセス権を設定してあげる必要があるかも。
Vagrant停止
$ vagrant halt
SAP HANAにoData via olingo(V2) with ベーシック認証でアクセス(POST)
以下はエントリー読むだけだったんで、今度はPOSTでデータ作成。
SAP HANAにoData via olingo(V2) with ベーシック認証でアクセス - ngtokuの日記
以下にあるサンプルコードだとHTTPステータスコードがBad Requestだったんで、試しにデータ本体だけ突っ込んだらレコード作成できました。
my-car-service/OlingoSampleApp.java at master · qinhaizong/my-car-service · GitHub
夜も遅いので中途半端だけど変更メソッドだけログ的に記載。
private ODataEntry writeEntity(Edm edm, String absolutUri, String entitySetName, Map<String, Object> data, String contentType, String httpMethod) throws EdmException, MalformedURLException, IOException, EntityProviderException, URISyntaxException { HttpURLConnection connection = initializeConnection(absolutUri, contentType, httpMethod); OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); // 本当はここをパラメータdataからJsonに変更する。MapでなくてJacksonを使ってBeanから変換したほうがいいかなって out.write("{\"ID\":\"GV006\",\"NAME\":\"'NEW DATA TOKU NG2'\",\"GROUP\":\"'POST DATA'\",\"CREATE_DATE\":\"\\/Date(1587911931893)\\/\"}"); out.close(); connection.connect(); final int status = connection.getResponseCode(); print("response code=" + status); ODataEntry entry = null; EdmEntityContainer entityContainer = edm.getDefaultEntityContainer(); EdmEntitySet entitySet = entityContainer.getEntitySet(entitySetName); if (status == 201) { InputStream content = connection.getInputStream(); content = logRawContent(httpMethod + " request on uri '" + absolutUri + "' with content:\n ", content, "\n"); entry = EntityProvider.readEntry(contentType, entitySet, content, EntityProviderReadProperties.init().build()); } connection.disconnect(); return entry; }
うぇーい。
SAP HANAにoData via olingo(V2) with ベーシック認証でアクセス
以下で作ったHANAに、テーブル作成。
CREATE COLUMN TABLE "SYSTEM"."TOKUTEST"( "ID" CHAR(5), "NAME" NVARCHAR(50), "GROUP" NVARCHAR(50), "CREATE_DATE" DATE, PRIMARY KEY("ID") );
データ投入。
insert into "SYSTEM"."TOKUTEST" values('GV001', 'NG TOKU', 'SYSTEM CONSULTING GROUP', CURRENT_DATE ); insert into "SYSTEM"."TOKUTEST" values('GV002', 'NG TOKU2', 'SYSTEM CONSULTING GROUP', CURRENT_DATE ); insert into "SYSTEM"."TOKUTEST" values('GV003', 'NG TOKU3', 'SYSTEM CONSULTING GROUP', CURRENT_DATE ); insert into "SYSTEM"."TOKUTEST" values('GV004', 'NG TOKU4', 'SYSTEM CONSULTING GROUP', CURRENT_DATE );
sensorjump.xsodata修正。
service { "SYSTEM"."TOKUTEST" as "TOKUTEST"; }
Javaで取得。
基本的に以下にあるサンプルコードのまんまで、最後にベーシック認証のユーザ名とパスワード入れてるだけ。
my-car-service/OlingoSampleApp.java at master · qinhaizong/my-car-service · GitHub
サンプルなのでベタ打ちだけど、URLとかベーシック認証のユーザ名とパスワードなんかはプロパティファイルに外だしが基本になるかと。
URL毎にユーザ名とパスワードが異なる場合はこんな感じで定義して、TOKUTESTはそれ用に用意したユーザ名とパスワード、TOKUTEST2は定義がないからDEFAULTから取ってくるみたいな感じになるんじゃないかと。
DEFAULT_USERNAME = xxxxxx DEFAULT_PASSWORD = xxxxxx TOKUTEST_URL = http://IPアドレス:8090/toku/oData/sensorjump.xsodata TOKUTEST_USERNAME = yyyyy TOKUTEST_PASSWORD = yyyyy TOKUTEST2_URL = = http://IPアドレス:8090/toku/oData/tokutest2.xsodata
Javaのコードは以下。
package com.servesync.olingo.clienttest; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Base64; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.olingo.odata2.api.commons.HttpStatusCodes; import org.apache.olingo.odata2.api.edm.Edm; import org.apache.olingo.odata2.api.edm.EdmEntityContainer; import org.apache.olingo.odata2.api.edm.EdmEntitySet; import org.apache.olingo.odata2.api.edm.EdmException; import org.apache.olingo.odata2.api.ep.EntityProvider; import org.apache.olingo.odata2.api.ep.EntityProviderException; import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties; import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties; import org.apache.olingo.odata2.api.ep.entry.ODataEntry; import org.apache.olingo.odata2.api.ep.feed.ODataDeltaFeed; import org.apache.olingo.odata2.api.ep.feed.ODataFeed; import org.apache.olingo.odata2.api.exception.ODataException; import org.apache.olingo.odata2.api.processor.ODataResponse; public class App { public static final String HTTP_METHOD_PUT = "PUT"; public static final String HTTP_METHOD_POST = "POST"; public static final String HTTP_METHOD_GET = "GET"; private static final String HTTP_METHOD_DELETE = "DELETE"; public static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; public static final String HTTP_HEADER_ACCEPT = "Accept"; public static final String APPLICATION_JSON = "application/json"; public static final String APPLICATION_XML = "application/xml"; public static final String APPLICATION_ATOM_XML = "application/atom+xml"; public static final String APPLICATION_FORM = "application/x-www-form-urlencoded"; public static final String METADATA = "$metadata"; public static final String INDEX = "/index.jsp"; public static final String SEPARATOR = "/"; public static final boolean PRINT_RAW_CONTENT = true; public App() { } public static void main(String[] args) throws Exception { App app = new App(); String serviceUrl = "http://IPアドレス:8090/toku/oData/sensorjump.xsodata"; String usedFormat = APPLICATION_JSON; print("\n----- Generate sample data ------------------------------"); print("\n----- Read Edm ------------------------------"); Edm edm = app.readEdm(serviceUrl); print("Read default EntityContainer: " + edm.getDefaultEntityContainer().getName()); print("\n----- Read Feed ------------------------------"); ODataFeed feed = app.readFeed(edm, serviceUrl, usedFormat, "TOKUTEST"); print("Read: " + feed.getEntries().size() + " entries: "); for (ODataEntry entry : feed.getEntries()) { print("##########"); print("Entry:\n" + prettyPrint(entry)); print("##########"); } print("\n----- Read Entry ------------------------------"); ODataEntry entry = app.readEntry(edm, serviceUrl, usedFormat, "TOKUTEST", "'GV002'"); print("Single Entry:\n" + prettyPrint(entry)); } private static void print(String content) { System.out.println(content); } private static String prettyPrint(ODataEntry createdEntry) { return prettyPrint(createdEntry.getProperties(), 0); } private static String prettyPrint(Map<String, Object> properties, int level) { StringBuilder b = new StringBuilder(); Set<Entry<String, Object>> entries = properties.entrySet(); for (Entry<String, Object> entry : entries) { intend(b, level); b.append(entry.getKey()).append(": "); Object value = entry.getValue(); if(value instanceof Map) { value = prettyPrint((Map<String, Object>)value, level+1); b.append(value).append("\n"); } else if(value instanceof Calendar) { Calendar cal = (Calendar) value; value = SimpleDateFormat.getInstance().format(cal.getTime()); b.append(value).append("\n"); } else if(value instanceof ODataDeltaFeed) { ODataDeltaFeed feed = (ODataDeltaFeed) value; List<ODataEntry> inlineEntries = feed.getEntries(); b.append("{"); for (ODataEntry oDataEntry : inlineEntries) { value = prettyPrint((Map<String, Object>)oDataEntry.getProperties(), level+1); b.append("\n[\n").append(value).append("\n],"); } b.deleteCharAt(b.length()-1); intend(b, level); b.append("}\n"); } else { b.append(value).append("\n"); } } // remove last line break b.deleteCharAt(b.length()-1); return b.toString(); } private static void intend(StringBuilder builder, int intendLevel) { for (int i = 0; i < intendLevel; i++) { builder.append(" "); } } public Edm readEdm(String serviceUrl) throws IOException, ODataException { InputStream content = execute(serviceUrl + SEPARATOR + METADATA, APPLICATION_XML, HTTP_METHOD_GET); return EntityProvider.readMetadata(content, false); } public ODataFeed readFeed(Edm edm, String serviceUri, String contentType, String entitySetName) throws IOException, ODataException { EdmEntityContainer entityContainer = edm.getDefaultEntityContainer(); String absolutUri = createUri(serviceUri, entitySetName, null); InputStream content = execute(absolutUri, contentType, HTTP_METHOD_GET); return EntityProvider.readFeed(contentType, entityContainer.getEntitySet(entitySetName), content, EntityProviderReadProperties.init().build()); } public ODataEntry readEntry(Edm edm, String serviceUri, String contentType, String entitySetName, String keyValue) throws IOException, ODataException { return readEntry(edm, serviceUri, contentType, entitySetName, keyValue, null); } public ODataEntry readEntry(Edm edm, String serviceUri, String contentType, String entitySetName, String keyValue, String expandRelationName) throws IOException, ODataException { // working with the default entity container EdmEntityContainer entityContainer = edm.getDefaultEntityContainer(); // create absolute uri based on service uri, entity set name with its key property value and optional expanded relation name String absolutUri = createUri(serviceUri, entitySetName, keyValue, expandRelationName); InputStream content = execute(absolutUri, contentType, HTTP_METHOD_GET); return EntityProvider.readEntry(contentType, entityContainer.getEntitySet(entitySetName), content, EntityProviderReadProperties.init().build()); } private InputStream logRawContent(String prefix, InputStream content, String postfix) throws IOException { if(PRINT_RAW_CONTENT) { byte[] buffer = streamToArray(content); print(prefix + new String(buffer) + postfix); return new ByteArrayInputStream(buffer); } return content; } private byte[] streamToArray(InputStream stream) throws IOException { byte[] result = new byte[0]; byte[] tmp = new byte[8192]; int readCount = stream.read(tmp); while(readCount >= 0) { byte[] innerTmp = new byte[result.length + readCount]; System.arraycopy(result, 0, innerTmp, 0, result.length); System.arraycopy(tmp, 0, innerTmp, result.length, readCount); result = innerTmp; readCount = stream.read(tmp); } stream.close(); return result; } private HttpStatusCodes checkStatus(HttpURLConnection connection) throws IOException { HttpStatusCodes httpStatusCode = HttpStatusCodes.fromStatusCode(connection.getResponseCode()); if (400 <= httpStatusCode.getStatusCode() && httpStatusCode.getStatusCode() <= 599) { throw new RuntimeException("Http Connection failed with status " + httpStatusCode.getStatusCode() + " " + httpStatusCode.toString()); } return httpStatusCode; } private String createUri(String serviceUri, String entitySetName, String id) { return createUri(serviceUri, entitySetName, id, null); } private String createUri(String serviceUri, String entitySetName, String id, String expand) { final StringBuilder absolutUri = new StringBuilder(serviceUri).append(SEPARATOR).append(entitySetName); if(id != null) { absolutUri.append("(").append(id).append(")"); } if(expand != null) { //absolutUri.append("/?$expand=").append(expand); absolutUri.append("?$(").append(expand).append(")"); } return absolutUri.toString(); } private InputStream execute(String relativeUri, String contentType, String httpMethod) throws IOException { HttpURLConnection connection = initializeConnection(relativeUri, contentType, httpMethod); connection.connect(); checkStatus(connection); InputStream content = connection.getInputStream(); content = logRawContent(httpMethod + " request on uri '" + relativeUri + "' with content:\n ", content, "\n"); return content; } private HttpURLConnection initializeConnection(String absolutUri, String contentType, String httpMethod) throws MalformedURLException, IOException { URL url = new URL(absolutUri); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("Authorization", "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", "ユーザ名", "パスワード").getBytes())); connection.setRequestMethod(httpMethod); connection.setRequestProperty(HTTP_HEADER_ACCEPT, contentType); if(HTTP_METHOD_POST.equals(httpMethod) || HTTP_METHOD_PUT.equals(httpMethod)) { connection.setDoOutput(true); connection.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, contentType); } return connection; } }
結果は以下
----- Generate sample data ------------------------------ ----- Read Edm ------------------------------ GET request on uri 'http://IPアドレス:8090/toku/oData/sensorjump.xsodata/$metadata' with content: <?xml version="1.0" encoding="utf-8" standalone="yes" ?><edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"><edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="2.0"><Schema Namespace="toku.oData.sensorjump" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://schemas.microsoft.com/ado/2008/09/edm"><EntityType Name="TOKUTESTType"><Key><PropertyRef Name="ID" /></Key><Property Name="ID" Type="Edm.String" Nullable="false" MaxLength="5" /><Property Name="NAME" Type="Edm.String" MaxLength="50" /><Property Name="GROUP" Type="Edm.String" MaxLength="50" /><Property Name="CREATE_DATE" Type="Edm.DateTime" /></EntityType><EntityContainer Name="sensorjump" m:IsDefaultEntityContainer="true"><EntitySet Name="TOKUTEST" EntityType="toku.oData.sensorjump.TOKUTESTType" /></EntityContainer></Schema></edmx:DataServices></edmx:Edmx> Read default EntityContainer: sensorjump ----- Read Feed ------------------------------ GET request on uri 'http://IPアドレス:8090/toku/oData/sensorjump.xsodata/TOKUTEST' with content: {"d":{"results":[{"__metadata": {"type":"toku.oData.sensorjump.TOKUTESTType","uri":"http://IPアドレス:8090/toku/oData/sensorjump.xsodata/TOKUTEST('GV001')"},"ID":"GV001","NAME":"NG TOKU","GROUP":"SYSTEM CONSULTING GROUP","CREATE_DATE":"\/Date(1586736000000)\/"},{"__metadata": {"type":"toku.oData.sensorjump.TOKUTESTType","uri":"http://IPアドレス:8090/toku/oData/sensorjump.xsodata/TOKUTEST('GV002')"},"ID":"GV002","NAME":"NG TOKU2","GROUP":"SYSTEM CONSULTING GROUP","CREATE_DATE":"\/Date(1586736000000)\/"},{"__metadata": {"type":"toku.oData.sensorjump.TOKUTESTType","uri":"http://IPアドレス:8090/toku/oData/sensorjump.xsodata/TOKUTEST('GV003')"},"ID":"GV003","NAME":"NG TOKU3","GROUP":"SYSTEM CONSULTING GROUP","CREATE_DATE":"\/Date(1586736000000)\/"},{"__metadata": {"type":"toku.oData.sensorjump.TOKUTESTType","uri":"http://IPアドレス:8090/toku/oData/sensorjump.xsodata/TOKUTEST('GV004')"},"ID":"GV004","NAME":"NG TOKU4","GROUP":"SYSTEM CONSULTING GROUP","CREATE_DATE":"\/Date(1586736000000)\/"}]}} Read: 4 entries: ########## Entry: GROUP: SYSTEM CONSULTING GROUP CREATE_DATE: 20/04/13 9:00 ID: GV001 NAME: NG TOKU ########## ########## Entry: GROUP: SYSTEM CONSULTING GROUP CREATE_DATE: 20/04/13 9:00 ID: GV002 NAME: NG TOKU2 ########## ########## Entry: GROUP: SYSTEM CONSULTING GROUP CREATE_DATE: 20/04/13 9:00 ID: GV003 NAME: NG TOKU3 ########## ########## Entry: GROUP: SYSTEM CONSULTING GROUP CREATE_DATE: 20/04/13 9:00 ID: GV004 NAME: NG TOKU4 ########## ----- Read Entry ------------------------------ GET request on uri 'http://IPアドレス:8090/toku/oData/sensorjump.xsodata/TOKUTEST('GV002')' with content: {"d":{"__metadata": {"type":"toku.oData.sensorjump.TOKUTESTType","uri":"http://IPアドレス:8090/toku/oData/sensorjump.xsodata/TOKUTEST('GV002')"},"ID":"GV002","NAME":"NG TOKU2","GROUP":"SYSTEM CONSULTING GROUP","CREATE_DATE":"\/Date(1586736000000)\/"}} Single Entry: GROUP: SYSTEM CONSULTING GROUP CREATE_DATE: 20/04/13 9:00 ID: GV002 NAME: NG TOKU2
うぇーい。
SCP(SAP Cloud Platform)お試し
例によってSCPの調べ物備忘録。参考サイトは以下。
SAP HANA – SAP Cloud Platform Trial版 ~環境構築編~ | Blog | グランバレイ株式会社
個人でアカウントは持ってるので、以下からログインして "Launch SAP Web IDE" をクリックして Web IDEに行き、右下にあるHelpful Linksの "SAP Cloud Platform Cockpit"から移動。
https://account.hanatrial.ondemand.com/
他は特に詰まるところもなく、問題なく作成完了。
引き続き以下をお手本に作業。
SAP HANA – SAP Cloud Platform Trial版 ~接続・開発編~ | Blog | グランバレイ株式会社
テーブル作成のSQLは以下
CREATE COLUMN TABLE "SYSTEM"."TEST"( "ID" CHAR(5), "NAME" NVARCHAR(50), "GROUP" NVARCHAR(50), "CREATE_DATE" DATE, PRIMARY KEY("ID") );
データ投入のSQLは以下
INSERT INTO "SYSTEM"."TEST" VALUES( 'GV001', 'NG TOKU', 'SYSTEM CONSULTING GROUP', CURRENT_DATE );
このエントリと直前のエントリ読めば、自分でテーブル作ってそこにアクセスするoDataサービス作れると思われ。
SAP HANAにoDataでアクセス お試し版
調べ物のついでに、SAP HANA express editionをVMWareで起動して、oDataでちょろっとアクセスしてみたので備忘録的に。
自分の備忘録なので、いつにもまして適当だけれども気にしない感じで。
ちなみにHANAであって、S/4 HANAじゃないぞ念のため。
1.SAP HANA express edition環境の構築
まぁ基本的にこちらの2記事に沿って導入しただけですな。
SAP HANAに触れてみよう! SAP HANA, express editionクイックインストールガイド | SAPジャパン ブログ
SAP HANAに触れてみよう!その2:SAP HANA 2.0, express edition + 各種ツール類のセットアップのご紹介 | SAPジャパン ブログ
あえて気をつけることと言えば、その2記事に記載してある通り、インスタンス番号が00ではなく90になっている事と、自分はeclipseバージョン2019-09を使っているので、HANA Studioは https://tools.hana.ondemand.com/2019-09/ からインストールしたことくらいかな。
ちなみに「Server only」 をダウンロードしたので、Web IDEやCockpitなんかの確認はしていない。
アドミンツールは http :// IPアドレス:8090/sap/hana/xs/admin で、ユーザ名はSYSTEM、パスワードは起動時に変更したものを使用する。
2.oDataアクセス環境の構築
こちらはVMWareの外からHANA studion on eclipseにて作業。
これはこちらのサイトの「5.1. HANA System の登録」と「5.2. リポジトリの登録」をお手本にして、
5. HANA XS Project の準備 — OData for SAP HANA セットアップガイド 初版 2016-12-01 intra-mart Accel Platform
お手軽にanonymousで認証すっ飛ばしたので、.xaccess以下はこちらを参考に
How to Expose a HANA Table via oData | SAP Blogs
なのでeclipse(HANA studio)のリポジトリはこんな感じ。
各々の内容は以下の通り。
.xaccess
{ "exposed" : true, "authentication" : null, "mime_mapping" : [{ "extension" : "jpg", "mimetype" : "image/jpeg" }], "prevent_xsrf" : false, "force_ssl": false, "enable_etags" : true, "anonymous_connection" : "toku.oData::anonuser", "cors": [{ "enabled": true, "allowMethods": ["GET","POST","PUT","DELETE", "HEAD"], "allowOrigin": ["*"] }], "allowHeaders": [ "Accept", "Authorization", "Content-Type", "X-CSRF-Token", "Access-Control-Allow-Origin" ], "exposeHeaders": [ "x-csrf-token" ], "cache_control": "no-cache, no-store" }
ちなみに "anonymous_connection" : "toku.oData::anonuser", を "authentication" :{"method": "Basic"}, に帰るとベーシック認証になるよ。
.xsapp
空っぽ
anonuser.xssqlcc
{"description":"Anonymous SQL connection"}
参照先は以下にしたので、
sensorjump.xsodataの内容はこんな感じ。エイリアスからはアンダースコア外したほうが良かったかな、と思ったりしたがとりあえずそのまま。
service { "SYS"."ADAPTERS_" as "ADAPTERS_"; }
各々のファイルは右クリックメニューでアクティベートしておかないとアドミンツールに出てこないので注意。
あと、参考サイトそのままコピーでエラーになる場合はダブルクォートがおかしかったりするので、アクティベートでエラーになる場合は打ち直してみたりすると良い。
アドミンツールは前述の http :// IPアドレス:8090/sap/hana/xs/admin で、設定。
最初は参照先サイトのようにエラーメッセージが出るけれど、右下のeditでHANA接続IDとパスワードをセットして以下の通り。
3.ブラウザから確認
APIの情報は以下で確認
http :// IPアドレス:8090/toku/oData/sensorjump.xsodata
<service xml:base="http://IPアドレス:8090/toku/oData/sensorjump.xsodata/"> <workspace> <atom:title>Default</atom:title> <collection href="ADAPTERS_"> <atom:title>ADAPTERS_</atom:title> </collection> </workspace> </service>
http :// IPアドレス:8090/toku/oData/sensorjump.xsodata/$metadata
<edmx:Edmx Version="1.0"> <edmx:DataServices m:DataServiceVersion="2.0"> <Schema Namespace="toku.oData.sensorjump"> <EntityType Name="ADAPTERS_Type"> <Key> <PropertyRef Name="ADAPTER_OID"/> </Key> <Property Name="ADAPTER_OID" Type="Edm.Int64" Nullable="false"/> <Property Name="OWNER_OID" Type="Edm.Int64" Nullable="false"/> <Property Name="ADAPTER_NAME" Type="Edm.String" MaxLength="64"/> <Property Name="PROPERTIES" Type="Edm.String" MaxLength="1000"/> <Property Name="CONFIGURATION" Type="Edm.String" MaxLength="2147483647"/> <Property Name="IS_SYSTEM_ADAPTER" Type="Edm.Byte" Nullable="false"/> </EntityType> <EntityContainer Name="sensorjump" m:IsDefaultEntityContainer="true"> <EntitySet Name="ADAPTERS_" EntityType="toku.oData.sensorjump.ADAPTERS_Type"/> </EntityContainer> </Schema> </edmx:DataServices> </edmx:Edmx>
データ内容は以下で確認。$format=json って入れないと、レスポンスはXMLで返ってくるよ。
・データ全部
http :// IPアドレス:8090/toku/oData/sensorjump.xsodata/ADAPTERS_?$format=json
・主キー指定(このテーブルの主キーはADAPTER_OIDなので、ADAPTER_OID=145525という条件で取得していることになる)
最後にLがついているのは型指定かな、あんま調べてないけど。
http :// IPアドレス:8090/toku/oData/sensorjump.xsodata/ADAPTERS_(145525L)?$format=json
・主キー&取得項目指定(ADAPTER_OIDとADAPTER_NAME)
http :// IPアドレス:8090/toku/oData/sensorjump.xsodata/ADAPTERS_(145525L)?$format=json&$select=ADAPTER_OID,ADAPTER_NAME
・条件指定(ADAPTER_NAME='aseodbc')&取得項目指定(ADAPTER_OIDとADAPTER_NAME)
http :// IPアドレス:8090/toku/oData/sensorjump.xsodata/ADAPTERS_?$format=json&$select=ADAPTER_OID,ADAPTER_NAME&$filter=ADAPTER_NAME%20eq%20%27aseodbc%27
ちなみに主キー指定の例だと、こんな感じのレスポンスが返ってくるよ。
{ "d": { "__metadata": { "type": "toku.oData.sensorjump.ADAPTERS_Type", "uri": "http://IPアドレス:8090/toku/oData/sensorjump.xsodata/ADAPTERS_(145525L)" }, "ADAPTER_OID": "145525", "OWNER_OID": "131072", "ADAPTER_NAME": "aseodbc", "PROPERTIES": "display_name=ASE (ODBC);description=Sybase ASE", "CONFIGURATION": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><RemoteSourceDescription name=\"Root\" id=\"1\">略</RemoteSourceDescription>", "IS_SYSTEM_ADAPTER": 1 } }
備忘メモ(Terraform)
NLB作ってて、「Network Load Balancers do not support Stickiness」というエラーが出たのでググったら以下がヒット。
aws_lb_target_group: When type is network, stickiness not supported · Issue #2746 · terraform-providers/terraform-provider-aws · GitHub
どうもsticknessのデフォルトがenabledの様なのでenabled = falseを明示したsticknessを追加したら動いた。
resource "aws_lb_target_group" "ecs_nlb_toku" { name = "ecs-nlb-toku" target_type = "ip" port = 8080 protocol = "TCP" vpc_id = aws_vpc.toku.id deregistration_delay = 300 health_check { interval = 30 port = "traffic-port" protocol = "HTTP" healthy_threshold = 3 unhealthy_threshold = 3 } depends_on = [aws_lb.ecs_nlb_toku] stickiness { type = "lb_cookie" enabled = false } }
公式ドキュメントは以下、デフォルトがtrueであることが確認可能。
AWS: aws_lb_target_group - Terraform by HashiCorp
ちなみに5行目は最初HTTPって記載していたんだけど、以下エラーが出たのでTCPにしてある。
Error: Error creating LB Listener: IncompatibleProtocols: The listener and its associated target group 'arn:aws:elasticloadbalancing:us-east-1:XXXXXXXXXXXX:targetgroup/ecs-nlb-toku/XXXXXXXXXXXXXXXX' have incompatible protocols
多分これだと思う。
NLB incompatible protocols · Issue #4955 · terraform-providers/terraform-provider-aws · GitHub