20200513のNode.jsに関する記事は15件です。

RaspberryPiで撮った写真をCustom Vision Serviceで画像判定してどの猫がいるかLINEに返す

はじめに

LINEBotからRaspberryPiで写真を撮ってLINEにおくる!で作ったLINEBotにAI機能を追加してどの猫がいるか教えてくれるものを作りました。

概要

LINEBotからRaspberryPiを動かして写真を撮り、Gyazoに送って画像判定で何が映っているかと撮影した写真をLINEに送るものです。
ppt.png

画像判定

画像判定はMicrosoft Custom Vision Serviceを使用。
茶色と白黒とグレーの3匹のねこを学習させ、mixとして色々な猫を追加学習させて、3匹のどの猫に近いかを教えてくれます。
スクリーンショット 2020-05-13 22.28.03.png
3匹以外の猫を判定するとmixが一番高い値で出ます!(catタグは全ての猫に追加)
スクリーンショット 2020-05-13 22.26.38.png

できたもの

使い方

RaspberryPiを猫がよくいる場所に置きます。
猫が見たいので LINEBotに「ねこ」と入れます。
(「ねこ」以外のワードには「何が見たいの?」と返してきます。)
写真を撮るのに時間がかかるので、「写真撮ってくるから待っててねー」、とつなぎのワードが入ります。
写真を撮って送るときに Azure Cognitive Vision ServicesのCustom Vision Serviceを使用してどの猫がいるかを画像を分類して、10秒ほどすると猫の種類と写真が送られてきます。

環境

MacBook Pro macOS Mojave
 Visual Studio Code 1.44.0
Azure Cognitive Vision Services
 Custom Vision Service
RaspberryPi 3B
 Release: 10
 Codename: buster
 Node.js v12.16.3
 npm v6.14.4
 ngrok
Pi camera
洗濯バサミ

コード

node.js
'use strict';
const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;
const Gyazo = require('gyazo-api');
const gyazoclient = new Gyazo('***');
const config = {
  channelSecret: '***',
  channelAccessToken: '***'
};
const app = express();
let whichCat;

app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/webhook', line.middleware(config), (req, res) => {
  console.log(req.body.events);
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result));
});
const client = new line.Client(config);
async function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }
  let mes = event.message.text;
  if (event.message.text.match("ねこ")) {

    await client.replyMessage(event.replyToken, {
      type: "text",
      text: '写真を撮ってくるからちょっと待っててね'
    });
    await nyanpi();
    return nyancoPic(event.source.userId);
  }
  else {
    return client.replyMessage(event.replyToken, {
      type: "text", text: '何が見たいのかな?'
    });
  }
}
const nyanpi = async (userId) => {
  const PiCamera = require('pi-camera');
  const myCamera = new PiCamera({
    mode: 'photo',
    output: `${__dirname}/nyan.jpg`,
    width: 640,
    height: 480,
    nopreview: true,
  });
  await myCamera.snap()
  await gyazoclient.upload('./nyan.jpg', {
    title: "my picture",
    desc: "upload from nodejs"
  })
}
//Custom Vision Service
const sendCustomVision = async (imageURL) => {
  const CUSTOM_VISION_API_ENDPOINT_URL = '***';
  // Prediction-Keyの値を Prediction-Key ヘッダーに入れる
  // JSONで送るので Content-type ヘッダーに application/json 指定
  const configCustomVisionAPI = {
    url: CUSTOM_VISION_API_ENDPOINT_URL,
    method: 'post',
    headers: {
      'Content-type': 'application/json',
      'Prediction-Key': '***'
    },
    data: {
      url: imageURL
    }
  };
  // axiosの送信設定
  let responseCustomVision;
  try {
    // POSTリクエストで送る
    responseCustomVision = await axios.request(configCustomVisionAPI);
    console.log("post OK");
    // データ送信が成功するとレスポンスが来る
    console.log(responseCustomVision.data);
    // 全てのタグにcatが含まれているので、近似値が高いものがcatタグの時は2番目を返す
    const catTag = responseCustomVision.data;
    if (catTag.predictions[0].tagName === "cat") {
      whichCat = catTag.predictions[1].tagName
    } else {
      whichCat = catTag.predictions[0].tagName;
    }
  } catch (error) {
    console.log("post Error");
    // ダメなときはエラー
    console.error(error);
  }
}
//gyazoから最新の写真URLを取得
const nyancoPic = async (userId) => {
  const response = await gyazoclient.list()
  const gyazoimgUrl = response.data[0].url;
//画像判定に送る
  const sendcstmvison = await sendCustomVision(gyazoimgUrl);
//タグの名前を猫の見た目に置き換え
  if (whichCat === "chairo") {
    whichCat = "ちゃいろ";
  } else if (whichCat === "shirokuro") {
    whichCat = "しろくろ";
  } else {
    whichCat = "グレー";
  }
  return client.pushMessage(userId, [{
    type: 'image',
    originalContentUrl: gyazoimgUrl,
    previewImageUrl: gyazoimgUrl
  },
  {
    type: "text", text: `${whichCat}の猫がいるよ`
  }]);
}
app.listen(PORT);
console.log(`Server running at ${PORT}`);

参考サイト

Computer Vision サービスを使用した画像処理 - Learn | Microsoft Docs

感想

AIが簡単に実装できた
グレーがシンガプーラという猫種で他のことめちゃくちゃ顔が同じなのでシンガプーラ判定やってみたい。
ngrokなので早く何かにdeployしたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Win10 + node.js + SQL Server 2019 Express で Tedious Request を少し試してみる

Win10 + node.js + SQL Server 2019 Express で Tedious Request を少し試してみる

目的

Tedious Request
Package - mssql-tedious-int64
を参考に少し試してみる(select count(*) & select ~ order by)
対象はWin10 + node.js -> Ubuntu 18.04 + SQL Server 2019 Express

対象テーブル

データの並びは以下に準拠
郵便番号データの説明
対象データにユニークキーになりそうなデータが無いので
8桁のテキストを追加する

CREATE TABLE [dbo].[ZIPCODE](
    [SEQ] [nchar](8) NOT NULL,
    [PREFCODE] [nchar](3) NULL,
    [KUBUNCODE] [nchar](8) NULL,
    [POSTAL5] [nchar](5) NULL,
    [POSTAL] [nchar](8) NULL,
    [PREFKANA] [nchar](20) NULL,
    [CITYKANA] [nchar](40) NULL,
    [ADDRKANA] [nchar](80) NULL,
    [PREFKANJI] [nchar](20) NULL,
    [CITYKANJI] [nchar](40) NULL,
    [ADDRKANJI] [nchar](80) NULL,
    [FLG1] [int] NULL,
    [FLG2] [int] NULL,
    [FLG3] [int] NULL,
    [FLG4] [int] NULL,
    [FLG5] [int] NULL,
    [FLG6] [int] NULL
) ON [PRIMARY]

サンプルコード

地元県だと名前感あるので 17ISHIKA.CSV を使う

var Connection = require('tedious').Connection; 
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;

var connectionConfig = {
    userName: 'demo',
    password: 'demo',
    server: '192.168.5.49',
    options: {
        database: 'demo'
    }
};

var connection = new Connection(connectionConfig);

connection.on('connect', function(err) {  
    if (err) {
        console.log(err);
        console.log('err:connect');
        connection.exit;
        process.exit(0);
    }
    console.log("Connected");

    itemcount();
    console.log("done:truncate");  
});

function itemcount() {
    request = new Request("SELECT COUNT(*) FROM ZIPCODE;", function(err, rowCount) {
        if (err) {
            console.log(err);
            console.log('err:request:itemcount');
            connection.exit;
            process.exit(0);
       }
    });

    request.on('row', function(columns) {
        console.log('value: ' + columns[0].value);
    });

    request.on('doneInProc', function (rowCount, more, rows) {  
        console.log('doneInProc:itemcount: '+ rowCount + ' row(s) returned');  
    });

    request.on('requestCompleted', function (err) { 
        console.log("requestCompleted:itemcount");
        itemselect()
    });

    connection.execSql(request); 
}

function itemselect() {
    request = new Request("SELECT * FROM ZIPCODE WHERE SEQ < '00000003' ORDER BY SEQ DESC;", function(err, rowCount) {
        if (err) {
            console.log(err);
            console.log('err:request:itemselect');
            connection.exit;
            process.exit(0);
       }
    });
    // 1行毎にイベントが発生する
    request.on('row', function(columns) {
        console.log('value: ' + columns[0].value);
        console.log('value: ' + columns.length);
        // var i = 0;
        // while (i < columns.length) {
        //     console.log(columns[i++].value);
        // }
    });

    request.on('doneInProc', function (rowCount, more, returnStatus, rows) {  
        console.log('doneInProc: '+ rowCount + ' row(s) returned');  
    });

    request.on('requestCompleted', function (err) { 
        console.log("requestCompleted:itemselect");
        connection.exit;
        process.exit(0);
    });

    connection.execSql(request); 
}

参考にしたのは以下のサイト

Tedious Request
Package - mssql-tedious-int64
Win10 + node.js から SQL Server 2019 Express に CSVファイルを書き込んでみる
Win10 + node.js から SQL Server 2019 Express に CSVファイルを書き込んでみる 修正版

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Slack SDKの公式ドキュメントのコードをそのまま利用したらサーバー障害で地獄を見た

Overview

みんな大好きSlack、私ももれなく処理結果等の通知で使用しています。
今まで何の不具合もなく使っていたはずなのですが、障害発生によってSlack SDKがやらかしてくれたので残しておきます。(今まで発生していたのを見逃していたのかもしれない)

最初に言っておきますが、絶対にドキュメント読むことなく公式ドキュメントのコードを使用することは避けてください。

Target reader

  • Slack SDKを利用していて、Slack SDKのリトライポリシーを理解していない方。

Prerequisite

  • Node.jsのバージョンはGoogle Cloud Function(GCF)に依存し、現時点ではV10系とする。
  • Node.jsの例ではあるが、各SDKでもWebClientを利用していることから恐らく同様と考えられる。
  • Node.jsのソースコードはesmというパッケージを導入し、import/exportで記述している。
  • @slack/web-api: "5.8.0"

Body

何が起きたのか?

Slackでサーバートラブルが発生しました。
https://status.slack.com//2020-05/147dad376c8946ff

その結果簡単に終わる処理が9分動作しても完了せずタイムアウトしました。
更にFunctions自体もリトライでフォローするようになっているため、合計20分程度かけましたが処理はタイムアウトで終了しました。
私の場合は1つのインスタンスが20分動いただけですが、これが100とかだとすると結構いたいです。
また、正常終了が大前提だった場合は焦ることになったでしょう。

その時実行していたログが以下です。
間隔をあけながらリトライを繰り返しているようです。
間隔が次第に大きくなっていることから、意図的に作られた再試行だというのが読み取れます。

GCFは1回につき最大10分しか実行できないため、9分でタイムアウトさせています。

CloudFunctions.log
D 2020-05-13T00:01:01.868207720Z myfunction xxxxxxxx Function execution started myfunction xxxxxxxx 
A 2020-05-13T00:01:01.936Z myfunction xxxxxxxx start myfunction. 
A 2020-05-13T00:01:07.279Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:01:08.898Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:01:12.311Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:01:17.444Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:01:35.508Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:01:56.226Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:02:55.448Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:04:45.895Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
A 2020-05-13T00:07:47.685Z myfunction xxxxxxxx [WARN]  WebClient:0 http request failed An HTTP protocol error occurred: statusCode = 503 myfunction xxxxxxxx 
D 2020-05-13T00:10:01.933989541Z myfunction xxxxxxxx Function execution took 540067 ms, finished with status: 'timeout' myfunction xxxxxxxx 

ソースコードは以下になります。
Slack関連の処理はほぼ公式ドキュメントそのままです。

utils/slack.js
import { WebClient } from '@slack/web-api';
import { token, conversationId as channel } from '../configs/slack';

const web = new WebClient(token);

const postMessageToChat = async (text) => {
    try {
        // See: https://api.slack.com/methods/chat.postMessage
        const res = await web.chat.postMessage({ channel, text });
        console.log('Message sent: ', res.ts);
    } catch (e) {
        console.error(e);
        // ignore error
        // throw e;
    }
};

export { postMessageToChat };
index.js
const requireEsm = require('esm')(module);
const { postMessageToChat } = requireEsm('./utils/slack');

exports.myfunction = async (data, context, callback) => {
    console.log(`start myfunction`);
    try {
        await postMessageToChat(`Start myfunction`); // ここがエラーの発生元

        // my code

        await postMessageToChat('End myfunction');

        console.log(`end myfunction`);
        callback();
    } catch (e) {
        console.error(e);
        callback(e);
    }
};

本来ならSlackに通知してすんなり終了する処理ですが、タイムアウトしてしまう悲劇に遭遇しました。
Slackがトラブルなら送信できないのはやむなしですが、どうしてweb.chat.postMessage()は終了しなかったのかが問題です。

どうやらリトライポリシーの設定が良くないようだ

issueを出そうと思い、issueをチェックしたりソースをざっと見ていた。
timeoutでは見つからなかったが、retryで検索するとどうもRetryPolicyはあるようだ。
これはpostMessage()でも使えるんじゃないか?と調査したら見えてきた不都合な真実…

自動リトライ
https://slack.dev/node-slack-sdk/web-api#automatic-retries

The WebClient comes with this queuing system out of the box, and its on by default! The client will retry a failed API method call up to 10 times, spaced out over about 30 minutes.

Google翻訳先生曰く

WebClientにはこのキューシステムが標準で付属しており、デフォルトでオンになっています。クライアントは、失敗したAPIメソッド呼び出しを最大10回再試行し、約30分以上間隔を空けます。

最大約30分使ってリトライとかおま:joy::joy::joy:

おれも何言っているかよくわからないんだがもう一度言っておく。
web.chat.postMessage()は障害発生時、最大約30分間応答しないということだ。
システムによってはどんどん応答待ちのスレッドが増加してくという怖いことになりかねない:scream:
(GCFは同時起動数が最大1000なので、10分間に1000回以上リクエストが来たらあふれそう)
時としておせっかい程始末に負えないものはないというのはこのことか!

ドキュメントを更に読むとオプションでこのリトライポリシーを変更可能とある。

const { WebClient, retryPolicies } = require('@slack/web-api');

const web = new WebClient(token, {
  retryConfig: retryPolicies.fiveRetriesInFiveMinutes,
});

よく見て頭に入れてほしい。
WebClientにリトライポリシーオプションを含んだ第二引数のないプログラムは間違っているといっても過言ではない。
このコードこそが導入部のサンプルコードとしてあるべきコードだと私は考える。
もちろん、場合によってはメッセージの送信が重要な位置づけになっているため、30分にわたりリトライが適している場合もある。
その場合は恐らくエラーの際にフォローするためのコードが入っているはず。

ちなみにSlackWebClientのキーワードでQiitaの記事をググってみたが、各言語のサンプルあれど一つもリトライポリシーのオプション指定はなかった。
つまり、相当見落としているとみてもいいだろう。私自身も送信だけなので、公式ドキュメントをコピーして使っていただけだ。
公式ドキュメントを読むということはやっぱり大切だと痛感した。

リトライポリシーは以下の種類がある。Slackへの送信が失敗しても問題ないなら迷わずretryPolicies.rapidRetryPolicyだろう。
気に入るものがないなら自分でカスタマイズできそうなので、リンク先を見るといいと思われる。

retryConfig 説明
retryPolicies.tenRetriesInAboutThirtyMinutes (デフォルト)
retryPolicies.fiveRetriesInFiveMinutes 5分間で5回試行
retryPolicies.rapidRetryPolicy テストを高速に実行するために使用
{ retries: 0 } 再試行なし(その他のオプション

ちなみにrapidRetryPolicyは高速というけどどの程度?というのが気になるところ。
これは以下のissueに答えがあって、約10msで10回リトライとあるので、1ms待機後にリトライしていると思われる。
再試行は10回するのに応答速度は再試行なしと比較してもそんなに劣らないと思われる。
https://github.com/slackapi/node-slack-sdk/issues/734

try 10 times in about 10ms

Conclusion

今回もまた改めて公式ドキュメントにしっかり目を通すことの大切さを学んだ。
公式ドキュメントの導入部のサンプルコードが時として牙をむくとか想像できなかった:disappointed_relieved:

これからレビューの際に見つけたらびしっと指摘できますね!
「再試行時も想定できないあなたはプロじゃない」

Have a great day!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Slack】レガシーテストトークンを使わずに絵文字を一括でエクスポートする

はじめに(対処した問題)

Slackの新しいワークスペースを作成するに伴い,現在使用しているワークスペースから絵文字を一括でエクスポート&インポートしようと企てていました.
参考サイトを基にエクスポートしようとしたら,Slackのトークン作成のところで躓きました.

参考にさせていただいたサイト↓
Slackの絵文字(emoji)を一括エクスポート&インポートする

というのも,今まで紹介されているエクスポート方法ではSlackのレガシートークンが使われているものが主でした.
しかし,2020/5/5にレガシーテストトークンが作れなくなりました.(すでに作成されているレガシートークンは使用&再生成できますが,新規作成ができないようになっています.)
The creation of legacy test tokens is deprecated

We're deprecating legacy test tokens and will disallow the creation of new test tokens beginning May 5th, 2020.

2020/5/13現在のSlackで新しいトークンを使用するにはSlackアプリを作成するようにと記述されています.
API トークンの生成と再生成

注意 :レガシーテストトークンを新しく作成することはできなくなりました。Slack API を操作するために新しいトークンが必要な場合は、代わりに Slack アプリを作成してください。

今回はレガシートークンで今まで絵文字をエクスポートしていたものを上記に紹介したSlackアプリで代替することで実現していきます.

手順(一括でエクスポートする)

基本的にはレガシートークンを使用してエクスポートする手順と同様のため,先程も紹介させて頂いた記事(Slackの絵文字(emoji)を一括エクスポート&インポートする)に沿って進めていきます.

  1. Node.jsをインストールする
  2. 必要なモジュールをインストールする
  3. エクスポート元のSlackチームのAPIトークンを取得する(←ここが変わる)
  4. スクリプトファイルを作成する
  5. スクリプトファイルを実行する

1. Node.jsをインストールする

ターミナルでnode -vを打つことで確認できます.

ターミナル

> node -v

インストールされていればバージョンが表示されます(筆者はv12.16.3).
インストールされてない場合は以下のような記事を参考にインストールしてください.
windows10にNode.jsをインストールする
MacにNode.jsをインストール

2. 必要なモジュールをインストールする

今回使用するモジュールをインストールします.

ターミナル

> npm install slack-node
> npm install request
> npm install fs

3. エクスポート元のSlackチームのAPIトークンを取得する(←ここが変わる)

ここまでは筆者も順調に進んでいましたが,参考記事にあるようなレガシートークンを発行できないところで詰みました.
そこで今回はSlackアプリを作成することで乗り越えます(他にも良い方法があるかもしれません.)

Slack APIにアクセスし,右上の「Your Apps」をクリックする.
image.png

「Create New App」をクリックして,「App Name」に適当なアプリ名(例:import-emoji)を入れて,「Development Slack Workspace」にエクスポートしたいワークスペースを選択する(ここでエクスポートしたいチャンネルが出ない場合はログインが必要).

作成したらページが遷移するので「Permission」をクリック.

image.png

「Scopes」の「User Token Scopes」の下にある「Add an OAuth Scope」をクリックし,「emoji:read」を選択する("emoji"と入力すると候補で上位に出てくる).
選択すると下図の状態になる.

image.png

ページの上部に進むと先程までグレーでクリックすることができなかった「Install App to Workspace」が緑色になり,インストール可能となっているため,クリックしてインストールする.
(※ここで無料のワークスペースの場合は10個アプリを連携しているとエラーになってしまうので注意)

作成を許可するとトークンが発行されるのでこちらを次の作業のためにコピーして用意しておく.
image.png

これでレガシートークンの代わりとなるトークン作成が終わりました.

4. スクリプトファイルを作成する

VSCodeなどのエディターツールで以下のコードをコピー&ペーストする.(ここは下記サイトにある@kureさんや@ne-peerさんの記事にあるコードを拝借致します.)

参考にさせて頂いたサイト
Slackのカスタム絵文字を全てダウンロードする
Slackの絵文字(emoji)を一括エクスポート&インポートする

ファイル名は自分が認識できれば何でもOKです.(下記のファイル名は例)

slack-emoji-export.js
var Slack = require('slack-node');
var request = require('request');
var fs = require('fs');

apiToken = "<apitoken>"; // ここにAPIトークンを貼り付ける。
slack = new Slack(apiToken);

slack.api("emoji.list", function (err, response) {
    for(key in response.emoji){
        url = response.emoji[key];
        //エイリアスは無視
        if(url.match(/alias/)){
            continue;
        }

        // 取得対象の拡張子
        extention = url.match(/\.[^\.]+$/);

        request
        .get(url)
        .on('response', function (res) {
        })
        .pipe(fs.createWriteStream('image/' + key + extention));
    }
});

上記コードにある// ここにAPIトークンを貼り付けるの行にある"<apitoken>"の部分に先程コピーして用意していたトークンを貼り付ける("xoxp-00000~"という形になればOK).

5. ディレクトリを作成し,スクリプトファイルを実行する

スクリプトファイルを作成&保存した階層と同階層にimageというディレクトリを作成する.(これをしないとエラー発生)

ディレクトリ作成後,以下のコマンドを入力することでエクスポートが完了する(imageディレクトリにすべての絵文字が保存されます).

【補足】一括インポート

筆者が確認時点(2020/5/13)では,一括インポートは従来どおりの方法で可能なことが確認できています.

従来どおりの方法

Chomeの拡張機能をインストール
Neutral Face Emoji Tools

上記ツールをインストール後,
Slackで「ワークスペースをカスタマイズ」の「絵文字」のタブに行くとドラック&ドロップで一括で絵文字をインポートできます.

image.png

筆者がインポートに使用しようとしたときには上の画像のように拡張ツールのインポート画面が出てこなくて非常に焦りましたが,適当なカスタム絵文字を1つ追加するだけで上記のような画面が出てきます

おわりに

これで今まで通りSlackのカスタム絵文字を簡単に移行させて使い倒していきましょう.

参考サイト一覧

Slackの絵文字(emoji)を一括エクスポート&インポートする
The creation of legacy test tokens is deprecated
windows10にNode.jsをインストールする
MacにNode.jsをインストール
Slack API
Slackのカスタム絵文字を全てダウンロードする

おすすめの記事

【note】ひとりSlackを2週間運用してみて

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】node-mysqlをたった3行で操作する 


はじめに

Node.jsでmysqlを使おうとした時、大体はnode-mysqlかpromise-mysqlを使うと思うのですが、どちらにしても大変面倒くさいのでnode-mysqlをラップしてmysqlを簡単に操作するためのコードを書いてみました。

結論から言うと、ネストをほぼ発生させずに処理を3行~にまとめる事が出来ました。モジュールにしたら保守とか色々面倒くさそうなのでコードのままここに上げておきます。promiseを返したかったり、足りない部分があった場合は各位で調整してください。

また動作確認はしていますが、下記コードを使用したことによる損害等は一切保証しませんので悪しからず。
「これコピペしても動かないんだけど!」とか言う輩はNode.js向いてないので辞めたほうが良いと思うよ




使い方

test.js
var test =  require("./DbAdapter");
var hoge = new test();
hoge.getData("select * from ?","hoge",function(err,result){console.log(result)});

実際に使う時はこんな感じ、データの取得に関しては、ほぼnode-mysqlの使い勝手で行ける。プレースホルダ使わない場合は値の代わりにnullぶち込むだけでOK




test2.js
var test =  require("./DbAdapter");
var hoge = new test();
test.setDataAddSql("insert into hoge value('110');");
test.setDataAddSql("insert into hoge value(?);",120);
try{
    test.setDataExe();
}catch(e){
   //エラー処理
}

データを書き込む時は、SQLを追加してから実行メソッドを叩くだけでOKです。まあ失敗した時はエラー返すのでその都合でちょっと長くなりますがSELECT文は3行で行けるので許して
setDataAddSql()は配列に値をぶち込むだけなので、同期処理とか考えなくても良いのは便利ですね。

本体の実装

DbAdapter.js (クラス本体)
/**DBを操作するクラス。
 * newしてメソッドを叩くだけ!!! */
class DbAdapter{

    constructor(){
        this.sqlArray = new Array();
        this.valueArray = new Array();
    }


    /**単一の情報取得クエリを実行します(コミット無し) */
    getData(SQL,value = null,callback = function(err,result){}){

        var pool = require("./pool").getPool();
        var error = null;
        pool.getConnection(function(err,connection){
            if(err){
                error = err;
            }
            connection.beginTransaction(function(err){
                if(err){
                    error = err;
                }
                connection.query(SQL,value,function(err,results,fields){
                    if(err){
                        error = err ;
                    }
                    connection.rollback(function(err){
                        if(err){
                            error = err;
                        }
                        connection.release();
                        callback(error,results);

                    });

                })
            })
        })
    }



    /**SQL文をインスタンスに追加します */
    setDataAddSql(SQL,value = null){
        this.sqlArray.push(SQL);
        this.valueArray.push(value);
    }



    /**追加されたSQL文を実行します */
    setDataExe(){

        //thisの値とメソッドを取得
        var nowSqlArray = this.sqlArray;
        var nowValueArray = this.valueArray;
        var recursiveQuery = this._recursiveQuery;
        //コネクションを実行
        var pool = require("./pool").getPool();
        pool.getConnection(function(err,connection){
            if(err){
                throw err;
            }
            //トランザクション開始
            connection.beginTransaction(function(err){
                if(err){
                    throw err;
                }
                recursiveQuery(nowSqlArray,nowValueArray,connection,recursiveQuery);
                //jsのクラスはメソッドの中から自身への参照を取れないので引数で渡す
            })
        })
    }

    /**connectionの処理をラップしたメソッド。クラス外部からは使用禁止 */
    _recursiveQuery(sqlArray,valueArray,connection,self){
        var newSqlArray = sqlArray;
        var newValueArray = valueArray;
        var myself = self;

        //エラー制御
        if(newSqlArray.length == 0){
            console.log("SQLが設定されていません");
            connection.rollback(function(err){
                if(err){
                    console.log(err);
                }
                connection.release();
            });
            return;
        }

        //クエリを送信
        connection.query(newSqlArray.shift(),newValueArray.shift(),function(err,results,fields){

            //エラー処理
            if(err){
                connection.rollback(function(err){
                    if(err){
                        console.log(err);
                    }
                    connection.release();
                });
                throw err;
            }

            //SQLを全て実行し終わった時
            if(newSqlArray.length == 0){
                connection.commit(function(err){
                    if(err){
                        console.log(err);
                        connection.rollback(function(err){
                            if(err){
                                console.log(err);
                            }
                            connection.release();
                        });
                        throw err;
                    }
                    console.log("SQL exe");
                    return;
                })

            //まだSQLが残っている時(再帰処理)
            }else{
                myself(newSqlArray,newValueArray,connection);
            }
        })
    }

}


module.exports = DbAdapter;





………まあこのコードを見たJS使いの諸兄が言いたいことは分からんでも無いですが、元々JavaとかC#とか書いてた民なのでPromiseそんなに好きじゃないんだ。「Promiseを返す」って所までは分かるけど、再帰処理とか値を返す処理とか(そもそもjsでそれを書くべきではない)を書き始めると一気に難読化しない???

という与太はそこら辺にしておいて解説に移りたいと思います。
と言ってもgetData()とかその辺はコード読めば分かりますね。ライブラリの呼び出しをただ単にラップしてるだけです。beginTransactionとか何回も書くのは嫌なので、そういう何やかんやは全てメソッドの中にまとめました。

途中で出てくるvar pool = require("./pool").getPool();は別クラスで定義したコネクションプールの呼び出しです。

実装はこんな感じ


pool.js
class Pool{

    constructor(){
        var mysql = require("mysql");
        this.pool = mysql.createPool({
            connectionhoLimit:100,
            host :"host",
            user :"hoge",
            password : "password",
            database:"huga"
        });
    }

    getPool(){
        return this.pool
    }

}

module.exports = new Pool()



特段解説する所は無いですね。よりセキュアな実装を求めるならPASSはベタ書きじゃなくて、より安全な保管場所から拾うべきです。ちなみmodule.exportsしてるのはクラスじゃなくてインスタンスなので、地味にシングルトン風味だったりします。




で話をDbAdapter本体に戻すのですが、書き込み側は配列にSQLを追加するsetDataAddSql()と実行処理のトリガー、再帰処理を含むクエリの実行と言った感じに3つのメソッドで実装しました。使用感としてはJavaのStringBuilderを参考にしたので、一部の方には馴染みが深いと思います。


と言った風に、まあ書いてしまえばそれだけなのですが、この実装にたどり着くまで5回書き直して丸3日かかりました。
意外かもしれませんが、最初はSELECT文発行してデータを受け取る所で詰まりましたね。今思えば何で詰まってたんだろうって感じですが、Node.jsはJavaみたいにスタックでコードを考えると十中八九うまく行かないみたいです。

というかどれだけ糖衣錠しようが実際に動かす時はスクリプトチックな挙動をするのでコールスタックなんて幻想ですよ。ちょっとした関数でも値を返すより、その後の処理を値で受け取るのがベター(まあ素直にpromise返せばそれで良い気がするけど……)。Node.js の return と for を信じてはいけない(戒め)

あと詰まった所といえばクラスプロパティのスコープですね。コールバック関数の中でthis.hoge = huga とか書いても値入らないしエラー吐かないのにはビビる。

あとコードの中にコメント書いてるけど、メソッドの中から自分自身をthisで取得しようとするとエラるね。これは引数で渡してご強引に解決しました。

他にも色々詰まった気がするけど、正気を保つためにコーディング中の記憶を全て消去したので、あとは覚えてないです。



おわりに

現在はNode.jsでWebサービスを開発中です。
開発メンバーを募集してるので興味のある方はTwitter@hoge19194545までご連絡ください

↑とかやると金関係人間関係その他その他の問題がこじれて開発どころの話では無くなるので募集しません。1度言ってみたかっただけ

その代わりと言ってはなんですが、サービス開発してるのは本当なのでツイッター等フォローして下さるとありがたいです。
またどこかでお会いしましょう。ご安全に!


参考資料とか

Node.jsでMySQLを使うメモ
https://qiita.com/PianoScoreJP/items/7ed172cd0e7846641e13
[Node.js][MySQL]Promiseを使ってトランザクションを書きやすくする
https://tech.chakapoko.com/nodejs/mysql/promise.html
Node.jsでシングルトンなクラスモジュール
https://memo.appri.me/programming/nodejs-singleton-class

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像分析サイトを20分で立ち上げる

IBM Cloud を使って、画像分析サイトを瞬時に立ち上げる方法をお伝えします。
デプロイまで15分前後でだいたいあげられてます。

この内容を実行するには、IBM Cloudアカウントが必要になります。
ライトアカウント(クレカ登録不要、期限なし)で実行可能です。
→登録はこちら

この手順で作成されるのは、次のサービスです。
- Cloud Foundry Application Node.js
- Service(Continuous Delivery, Visual Recognition)
- Cloud Application(Visual Recognition)
- Toolchain
*ライトアカウントの方は、メモリの制限にかからないか事前に確認してください

IBM Cloud Login

Watson Visual Recognition Basicを立ち上げる

  1. 「カタログ」メニューから「ソフトウエア」「Webとアプリケーション」にチェックで対象のサービスを絞り込み、「Watson Visual Recognition Basic」をクリックします。
    image.png

  2. 画面下部の「はじめに」をクリック(または「作成」タブを選択で同じ画面に遷移します)

  3. アプリ名を書き換え(初期値でもOK、Uniqueにする必要があります)
    image.png

  4. 「作成」をクリックします。

  5. アプリが起動します。
    image.png

アプリのデプロイ環境を構築立ち上げます

  1. 「デプロイメント自動化」のパネルから「アプリのデプロイ」をクリックします。 image.png
  2. デプロイメント自動化のターゲットに「IBM Cloud Foundry」を選択します。
  3. IBM Cloud API キーの「新規」作成をします。
  4. 他の入力項目を確認し(初期値可)「次へ」をクリックします。
  5. 次ページでDevOps Toolchainの項目を確認し(初期値可)「作成」をクリックします。
  6. コードの生成を待ちます(BUILD・DEPLOY共に緑になれば作成ずみ) image.png
  7. Delivery pipeline が表示されたら「名前」のリンクをクリックします。 image.png
  8. DEPLOY パネルの最終実行の結果の「コンソールの表示」をクリックします。 image.png
  9. (3)で指定した名前のアプリケーションが表示されるので実行中であることを確認し、「アプリURLにアクセス」をクリックします。

作成したサイトで画像分析を試しましょう

image.png
- 左画面下部、青枠の画像を切り替えると、右画面の結果が変わります。
- 左画面下部、ピンク枠をクリックすると、画像のアップロードができ、結果が試せます。
image.png
こんな感じ!!
で、どうするのかって突っ込まれそうですが、ここからどうするかはまた改めて考えます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

この解決策→nvm:854: no such file or directory: /Users/suin/.nvm/nvm-exec

Homebrewでインストールしたnvmで下記のようなエラーが出た場合、

nvm:854: no such file or directory: /Users/suin/.nvm/nvm-exec

シンボリックリンクを作り直すと治る:

mkdir -p ~/.nvm
cd ~/.nvm
ln -s $(brew --prefix nvm)/nvm.sh
ln -s $(brew --prefix nvm)/nvm-exec

バージョン情報

nvm --version
0.35.3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでDiscordBotを作る

初の記事ですねはい。
今回は、Node.jsを使ってDiscordでBotを作っていこうと思います。

インストールするもの、必要なもの

node.js(https://nodejs.org/ja/)
discord.js
VScode(エディタならVScodeである必要はありません)
discordBotのTOKEN
(当たり前だけどDiscordのアカウント)

node.jsをインストールする

https://nodejs.org/ja/
ここへ行くとこのような画面になると思います

キャプチャ.PNG
(2020/5/13現在の表示です。この画像とバージョンが変わっている可能性があります)
今回は、左の「推奨版」でいいかと思います
するとインストーラーがダウンロードされると思うので、実行しましょう
(インストーラーでダウンロードする手順はそこまで難しくないので割愛します。画面に表示されるとおりに進めていけば大丈夫なはずです)

discord.jsのインストール

インストールができたら、エディター(私の場合VScode)を起動しましょう
どこでも構わないので適当にファイルを作り、開いてください
そしたら、ターミナルで

npm init

と打ちましょう。
なんか色々出てきますが、とりあえずEnterで飛ばしちゃって大丈夫かと思います
最後にIs this OK? (yes)ってのが出るので「yes」と打ちましょう
そうすると、package.jsonというファイルが作られているはずです
それが確認できたら、今度は

npm install discord.js --save

と打ってください
package-lock.jsonというファイルとnode-modulesというフォルダ(中にめちゃくちゃなんかある)ができていれば成功です!
ここまで出来たら今度はBotのアカウントを作ってTOKENを取得します!

TOKENの取得

discord Developer Portal(https://discordapp.com/developers/applications/)
へ行きましょう
するとこんな画面になります
キャプチャ.PNG
そしたら、右上のNew Applicationを押してください。
キャプチャ.PNG
Bot名を決めて、ここに入れましょう(厳密にいうとBot名ではなくアプリケーション名です。ここに入れた名前はBotを招待するときに表示されます)
キャプチャ.PNG
こんな画面になるので、左のBotというところを押してください
キャプチャ.PNG
Add Botを押しましょう。ほんとにBotを追加する??的なのが出るのでyes, Do it !
キャプチャ.PNG
てんててーん!これでBotアカウントの作成は終わりです!
そしたら、copyというのを押して、TOKENをコピーして取っておいてください。
TOKENの例
NzA5OTM3Njg2ODY5MTgwNTMz.XrtLnQ.4wBNgKLc14aeOtDy3468Xs7STwo
(ここ最近で作ったならNから始まってるかな...?)
キャプチャ.PNG
ここで注意!
ここでコピーしたTOKENは誰にも見せてはいけません!
他人に取られると、BOTを乗っ取られてしまいます!

(ここでちょっと余談。よく間違えがちなのが、Bot名を決めた後にページにあるCLIENT SECRETというところのコピーしてしまい、「TOKEN入れてコードもおかしくないのに動かない!」ということになることがあります。間違えないように!)

コードを書いていく

さぁそしたらエディタ-に戻りましょう。
index.jsというファイルを作り、書いていきます。

index.js
const discord = require('discord.js');
const client = new discord.Client();
/*必須*/

client.on('ready', () => {
    console.log('bot ready!');
});
/*起動に成功したらコンソールに"bot ready!"と表示します*/

client.on('message', message =>{

    if(message.content === "こんにちは"){
        message.reply("こんにちは!")
    }
/*もし送られたメッセージがこんにちはならこんにちは!と返します*/

})
/*messageが送られたときに反応します*/

client.login('TOKEN');
/*ここにさっきコピーしたTOKENを入れましょう*/

このこーどは、「こんにちは」というメッセージが送られたらメンション付きで「おはよう!」と返すコードです

Botをサーバーに招待する

先ほどのbotアカウント管理画面にいきましょう
キャプチャ.PNG
OAuth2というところを押してください
そうしたら、Botというところにチェックを入れてください
キャプチャ.PNG
そしたら下になんかでるのでAdministratorを押して、でできたURLをコピーしてください
(このURLは今後別のサーバーにBOTを招待するときにも使えるので取っておくといいかもしれません)
キャプチャ.PNG
そしたらコピーしたURLに飛びましょう
キャプチャ.PNG
招待するサーバーを選択してはいを押してください。
権限を与えるとこに確認を求められるので確認し、認証を押しましょう。
私はボットではありませんってやつがでるのでチェックしてください
(指定した画像を押すことを求められる事があります)
これで招待は成功したはずです!
サーバーを見てみましょう
キャプチャ.PNG
確かにいますね!
そうしたらあとは起動するだけ!
ターミナルで

ターミナル
node index.js

とうってコンソールにbot ready!と出れば成功です!
キャプチャ.PNG
無事オンラインになりました!
キャプチャ.PNG
ちゃんと動いてますね!

終わり

お疲れ様でした!
これでbotができました!
今後別の記事で機能を追加方法なども出すつもりです!
長い記事を読んで下さりありがとうございました!

(もしかしたら抜けてるところとか間違えてるところがあるかもしれません。もしも間違えがあったらコメントで指摘してください)

参考にしたサイトやページなど

https://www.youtube.com/watch?v=9CDPw1lCkJ8&t=251s

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

axiosを使用してLiquid APIを叩き、node.jsで実行

初学者ですが自分へのメモの意も含め、誰かのお役に立てたらと思い執筆します。
タイトルの通りaxiosを使用して、APIを叩き、node.jsで実行してBTC/JPYをコンソール上に出力します。
今回は認証不要なPublic APIを使用。
補足情報やアドバイスがあれば、ぜひコメントいただければ幸いです。

参考元

Liquid API ドキュメント:https://developers.liquid.com/
https://kennejs.com/entry/2019/01/23/001715
https://qiita.com/shisama/items/61cdcc09dc69fd8d3127

環境

macOS Mojave 10.14.6

事前準備

node.jsのインストール
https://qiita.com/oreo3@github/items/622fd6a09d5c1593fee4

yarnのインストール
https://classic.yarnpkg.com/ja/docs/install/#mac-stable

axiosのインストール
https://github.com/axios/axios#installing

axiosって何?

https://www.willstyle.co.jp/blog/2751/
公式:https://github.com/axios/axios

ソースコード

node.js
const axios = require("axios").default;

let url = "https://api.liquid.com";
let path = "/products/5";
let query = "";

axios
  .get(url + path + query) //指定した引数へリクエストを送る
  .then((res) => {
    const items = res.data;
    console.log(items.last_traded_price, items.market_bid, items.market_ask);
  })
  .catch((error) => {
    const { status, statusText } = error.response;
    console.log(`Error! HTTP Status: ${status} ${statusText}`);
  });

まとめ

APIを叩くことも初めてだったので、最初は"request"と"axios"を2つとも使用してしまっていました。汗
また、「axios.create」などでインスタンスも作成できるようですが、どんな必要性 or 用途で使用するのかもこれから勉強したいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【axios】axiosを使用してLiquid APIを叩き、node.jsで実行

初学者ですが自分のメモの意も含め、誰かのお役に立てたらと思い執筆します。
タイトルの通りaxiosを使用して、APIを叩き、node.jsで実行してBTC/JPYをコンソール上に出力します。
今回は認証不要なPublic APIを使用。
補足情報やアドバイスがあれば、ぜひコメントいただければ幸いです。

参考元

Liquid API ドキュメント:https://developers.liquid.com/
https://kennejs.com/entry/2019/01/23/001715
https://qiita.com/shisama/items/61cdcc09dc69fd8d3127

環境

macOS Mojave 10.14.6

事前準備

node.jsのインストール
https://qiita.com/oreo3@github/items/622fd6a09d5c1593fee4

yarnのインストール
https://classic.yarnpkg.com/ja/docs/install/#mac-stable

axiosのインストール
https://github.com/axios/axios#installing

axiosって何?

https://www.willstyle.co.jp/blog/2751/
公式:https://github.com/axios/axios

ソースコード

sample.js
const axios = require("axios").default;

let url = "https://api.liquid.com";
let path = "/products/5";
let query = "";

axios
  .get(url + path + query) //指定した引数へリクエストを送る
  .then((res) => {
    const items = res.data;
    console.log(items.last_traded_price, items.market_bid, items.market_ask);
  })
  .catch((error) => {
    const { status, statusText } = error.response;
    console.log(`Error! HTTP Status: ${status} ${statusText}`);
  });

まとめ

APIを叩くことも初めてだったので、最初は"request"と"axios"を2つとも使用してしまっていました。汗
また、「axios.create」などでインスタンスも作成できるようですが、どんな必要性 or 用途で使用するのかもこれから勉強したいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Netlify Functionsのローカル環境をyarn startで起動する設定

Netlify Functionsの開発、検証用としてローカルでの実行環境が用意されている。netlify-lambdaパッケージをローカルにインストールしている場合、以下のコマンドを実行することでhttp://localhost:9000/.netlify/functions/ファンクション名にアクセスできるようになる。

% npx netlify-lambda serve src/lambda

通常は、ファンクションだけではなく、webページとあわせて起動する必要がある。たとえばwebページをVue.jsで開発している場合、webページは以下のコマンドで起動する。

% vue-cli-service serve

これらをpackage.jsonに登録することで、npmあるいはyarnコマンドで実行できるようになる。さきにファンクションの環境を起動して、しばらくしてからwebページを起動しないと、ファンクションが起動できない。startにwebページの起動、prestartにファンクションの起動と3秒の待機(sleep 3)を設定することで、yarn startでwebページとファンクションが起動できるようになる。

package.json
...
  "scripts": {
    "start": "vue-cli-service serve",
    "prestart": "npx netlify-lambda serve src/lambda & sleep 3",
...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Promise async function result / return value VS then result / #javascript #node

async function f() {
  return "XXX";
}

const result = f()
console.log(result)
// Promise { 'XXX' }

f().then(result => {
  console.log(result)
});
// XXX

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3164

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Promise { <pending> } in Javascript / async await function / return value VS then result #node #javascript

非同期の関数 ( async ) では返り値が Promise となるので、直接返り値を利用できない
then で結果が出たことを待ち受けて、その中で関数の返り値を利用する

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function f() {
  await sleep(1000);
  return "XXX";
}

const result = f()
console.log(result)
// Promise { <pending> }

f().then(result => {
  console.log(result)
});
// XXX

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3163

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Build an app using SQL Server 目次

はじめに

image.png

今回は、SQL Server を使ったアプリを開発しようということで、Building an app using SQL Server の内容をベースに、SQL Server を使ったアプリ開発関連の記事を一元管理できるように目次ページを作成しました。
※ページは、随時更新予定です。

C#


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GASでReactを実行するテンプレート

はじめに

最近Google Apps ScriptでWebアプリを作ることが多いです。無料で作れるWebサーバー的用途で使えるので便利なんですよね。ただGoogle Apps Scriptで構築する際に諸々制限も発生します。これを回避したテンプレートの開発環境を公開します。

GASでReactを使う制限と対策

  • WebpackでバンドルしたHTMLをUpload出来ない

制限はこれにつきます。普通にGoogle Claspでアップロードすると重すぎてブラウザが固まっておちるのと何とかWebアプリケーションとして公開しても全く動きません。よってWebpackバンドル時に各々node_moduesをバンドルしないようにしてReactがうまく動くような環境にします。

前提

  • Google ClaspでGASの開発を既に行なっている方を対象とします

開発環境構成

  • Node.js 13.13.0
  • Visual Studio Code

- 利用モジュールは以下

{
 "name": "web-app-template",
 "main": "./src/Index.tsx",
 "license": "MIT",
 "private": true,
 "scripts": {
   "dev": "NODE_ENV=development npx webpack-dev-server --open --hot",
   "shell": "rm build/app.js & cp ./src/gas/app.js ./build/app.js",
   "build": "NODE_ENV=development npx webpack && npm run shell && clasp push --force && clasp open",
   "deploy": "NODE_ENV=production npx webpack && npm run shell && clasp push --force && clasp open"
 },
 "dependencies": {
   "@types/google-apps-script": "1.0.12",
   "@material-ui/core": "4.9.10",
   "@types/react": "16.9.34",
   "@types/react-dom": "16.9.6",
   "@types/react-router-dom": "5.1.4",
   "html-webpack-inline-source-plugin": "1.0.0-beta.2",
   "html-webpack-plugin": "4.2.0",
   "react": "16.13.1",
   "react-dom": "16.13.1",
   "react-router-dom": "5.1.2",
   "ts-loader": "6.2.2",
   "typescript": "3.8.3",
   "webpack": "4.42.1",
   "webpack-cli": "3.3.11",
   "webpack-dev-server": "3.10.3"
 }
}

ソースコード

以下Githubにアップロードしました。
web-app-template

基本的な使い方

  • プロジェクトをダウンロードしたディレクトリでまずはApp Scriptプロジェクトを紐付けましょう。
clasp create --rootDir ./src
  • まずは以下コマンドでテストします
npm run dev

これにてローカルでWebサーバーが立ち上がり、Web画面のイメージが表示されます。

  • ./src/pages内のtsxファイルを編集しましょう
  • 編集が完了したら以下コマンドでApp Scriptに反映しましょう npm run deploy 上記を行った後、対象のApp ScriptでWebアプリケーションを公開すれば構築したReactのサイトが表示される筈です。

ReactからApp Scriptを呼ぶ時

./src/gas/googleScriptRun.jsに以下のようなスクリプトがあります。

google.script.run.withSuccessHandler(function () {
  console.log("success");
}).recieveSpreadsheet();

これはWebアプリケーション側よりGAS上の関数を実行する記載方法で以下のような構文になります。

google.script.run.withSuccessHandler(__コールバック関数__}).実行するGASの関数名();

よって上記google.ScriptRun.jsにてWebアプリ側で呼びたい関数を記載し、./src/gas/app.js側に呼ばれる関数を記載すればWebアプリケーション側よりGAS上の関数を実行することが可能です。

上記、もし何かのお役に立てばお使い頂ければ幸いです。
宜しくお願い致します。

以下でも他詳細を記載しています。
Blogger

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む