[Node.js] GraphQLで更新する

var schema = buildSchema(`
	type Query {
		course(id: Int!): Course
		courses(topic: String):[Course]
	},
	type Mutation {
		updateCourseTopic(id: Int!, topic: String!): Course
	}
	type Course {
		id: Int
		title: String
		author: String
		description: String
		topic: String
		url: String
	}
`);

// 省略

var updateCourseTopic = function({id, topic}){
	coursesData.map(course => {
		if(course.id === id){
			course.topic = topic;
			return course;
		}
	});
	return coursesData.filter(course => course.id === id)[0];
}
var root = {
	course: getCourse,
	courses: getCourses,
	updateCourseTopic: updateCourseTopic
};

$ node server2.js

mutation updateCourseTopic($id: Int!, $topic: String!) {
  updateCourseTopic(id: $id, topic: $topic) {
    ... courseFields 
  }
}
fragment courseFields on Course {
 title
 author
 description
 topic
 url
}

{
 "id": 1,
 "topic": "JavaScript"
}

何となく使い方はわかりました。

Node.jsでGraphQLを動かす

$ npm init
$ npm install graphql express express-graphql -save

server.js

var express = require('express');
var express_graphql = require('express-graphql').graphqlHTTP;
var { buildSchema } = require('graphql');

var schema = buildSchema(`
	type Query {
		message: String
	}
`);

var root = {
	message: () => 'Hello World!'
};

var app = express();
app.use('/graphql', express_graphql({
 schema: schema,
 rootValue: root,
 graphiql: true
}));
app.listen(8000, () => console.log('Express GraphQL Server Now Running On 192.168.34.10:8000/graphql'))

{
	message
}
{
  "data": {
    "message": "Hello World!"
  }
}

$ curl -XPOST -H “Content-Type:application/json” ‘http://192.168.34.10:8000/graphql’ -d ‘{“query”: “query { message }”}’
{“data”:{“message”:”Hello World!”}}
L jsonでクエリをリクエストすると返ってくる

### パラメータを受け取る

var express = require('express');
var express_graphql = require('express-graphql').graphqlHTTP;
var { buildSchema } = require('graphql');

var schema = buildSchema(`
	type Query {
		course(id: Int!): Course
		courses(topic: String):[Course]
	},
	type Course {
		id: Int
		title: String
		author: String
		description: String
		topic: String
		url: String
	}
`);
var coursesData = [
{
	id: 1,
	title: '初めてのGraphQL ―Webサービスを作って学ぶ新世代API',
	author: 'Eve Porcello',
	description: '本書で紹介するGraphQLは2015年にFacebookによって公開されたRESTとは異なるアプローチのアーキテクチャです。',
	topic: 'GraphQL',
	url: 'https://www.amazon.co.jp/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AEGraphQL-%E2%80%95Web%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E5%AD%A6%E3%81%B6%E6%96%B0%E4%B8%96%E4%BB%A3API-Eve-Porcello/dp/487311893X/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=graphql&qid=1633041782&sr=8-1'
},
{
	id: 2,
	title: '基礎からはじめるGraphQL',
	author: '志村翔太',
	description: '本書ではGraohQLの基礎文法や概念を学び、実際にGraohQLを利用したアプリケーションの完成を目指していきます。',
	topic: 'GraphQL',
	url: 'https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E3%81%AF%E3%81%98%E3%82%81%E3%82%8BGraphQL-%E5%BF%97%E6%9D%91%E7%BF%94%E5%A4%AA-ebook/dp/B08PC8H5HF/ref=sr_1_2?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=graphql&qid=1633071074&sr=8-2'
},
{
	id: 3,
	title: 'Node.js超入門 第3版',
	author: '掌田津耶乃',
	description: 'Node.jsの入門者向け書籍です。2018/8に出た「Node.js超入門 第2版」の改訂版です。改訂内容は新バージョン14対応、データベースはSQLite3、ORMはSequelizeに変更しています。CSS関連はBootstrap利用、Expressは最初からGeneratorを使う形で解説しています。',
	topic: 'NodeJS',
	url: 'https://www.amazon.co.jp/Node-js%E8%B6%85%E5%85%A5%E9%96%80-%E7%AC%AC3%E7%89%88-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83/dp/479806243X/ref=sr_1_1_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=nodejs&qid=1633071225&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyNEMxVzdLVUtWOU1ZJmVuY3J5cHRlZElkPUEwNjIwNjAzMkhaN1dYSzZVSEw2NiZlbmNyeXB0ZWRBZElkPUExSkVDRkoyVzMwTlBBJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ=='
}
]
var getCourse = function(args){
	var id = args.id;
	return coursesData.filter(course => {
		return course.id == id;
	})[0];
}
var getCourses = function(args){
	if(args.topic){
		var topic = args.topic;
		return coursesData.filter(course = course.topic === topic);
	} else {
		return coursesData;
	}
}
var root = {
	course: getCourse,
	courses: getCourses
};

var app = express();
app.use('/graphql', express_graphql({
 schema: schema,
 rootValue: root,
 graphiql: true
}));
app.listen(8000, () => console.log('Express GraphQL Server Now Running On 192.168.34.10:8000/graphql'))
query getSingleCourse($courseID: Int!) {
 course(id: $courseID) {
  title
  author
  description
  topic
  url
 }
}

{ 
 "topic":"Node.js"
}

$ curl -XPOST -H “Content-Type:application/json” ‘http://192.168.34.10:8000/graphql’ -d ‘{“query”: “query getSingleCourse($courseID:Int!){course(id:$courseID){title author description topic url}}”, “variables”: {“courseID”:1}}’
{“data”:{“course”:{“title”:”初めてのGraphQL ―Webサービスを作って学ぶ新世代API”,”author”:”Eve Porcello”,”description”:”本書で紹介するGraphQLは2015年にFacebookによって公開されたRESTとは異なるアプローチのアーキテクチャです。”,”topic”:”GraphQL”,”url”:”https://www.amazon.co.jp/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AEGraphQL-%E2%80%95Web%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E5%AD%A6%E3%81%B6%E6%96%B0%E4%B8%96%E4%BB%A3API-Eve-Porcello/dp/487311893X/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=graphql&qid=1633041782&sr=8-1″}}}

query getCourses($topic: String!) {
 courses(topic: $topic) {
  title
  url
 }
}

{
  "topic": "GraphQL"
}

{
“errors”: [
{
“message”: “course is not defined”,
“locations”: [
{
“line”: 2,
“column”: 2
}
],
“path”: [
“courses”
]
}
],
“data”: {
“courses”: null
}
}

なんやろう、ようわからんね

GraphQLをより理解する[PHP編]

### Resolver
The resolver is basically a call back function for each field. There is always a default resolver for all fields, when we define own resolve function for a field, we simply override the default resolver.

### How to define Schema for Query

$schema_obj = new Schema {
	"query" => $queryType,
	"mutation" => $mutationType,
	"subscription" => $subscriptionType
}

index.php

require_once __DIR__ . '/vendor/autoload.php';

use GraphQL\GraphQL;
use GraphQL\Type\Schema;
try {
	$schema_obj = new Schema([
		'query' => $queryType
	]);
	$Input = file_get_contents('php://input');
    $input_data = json_decode($Input, true);
    $query_data = $input_data['query'];
    $variable_values = isset($input_data['variables']) ? $input_data['variables'] : null;
    $value = ['prefix' => 'Output: '];
    $resultant = GraphQL::executeQuery($schema_obj, $query_data, $value, null, $variable_values);
    $output_value = $resultant->toArray();
} catch(\Exception $e){
	$output_value = [
		'errors' => [
			[
				'message' => $e->getMessage()
			]
		]
	];
}
header('Content-Type: application/json');
echo json_encode($output_value);

$ curl http://192.168.34.10:8000 -d ‘{“query”: “query { echo(message: \” Hi Knowband, this is my first GraphQL program \”) }” }’
{“errors”:[{“message”:”Schema does not define the required query root type.”,”extensions”:{“category”:”graphql”},”locations”:[{“line”:1,”column”:1}]}]}

うむ… 実践的にやらないとあかんな

PHPでGraphQLを試したい

まずubuntuにcomposerを入れます。

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer
$ sudo chmod +x /usr/local/bin/composer
$ source ~/.bashrc
$ composer -v
Composer version 2.1.8 2021-09-15 13:55:14

### ライブラリのインストール
$ composer require webonyx/graphql-php

public/graphql/index.php

require_once __DIR__ . '/../../vendor/autoload.php';

use GraphQL\Type\Definition\ObjectType;

class Query extends ObjectType {
	public function __contruct(){
		parent::__construct([
			'name' => 'Query',
			'fields' => [
				'number' => [
					'type' => Type::int(),
					'args' => [
						'number' => Type::int(),
					],
					'resolve' => function($value, $args, $context, ResolveInfo $resolveInfo){
						return $args['number'];
					}
				],
			],
		]);
	}
}

$schema = new GraphQL\Type\Schema([
	'query' => new Query(),
]);

$server = new Graph\Server\StandardServer([
	'schema' => $schema
]);

$ php -S 192.168.34.10:8000
$ curl -X POST -H “Content-Type: application/json” “http://192.168.34.10:8000/graphql/” \
> -d ‘{“query”: “query { number(number: 20210928) }”}’

### Repositoryを使う
src/Type/User/User.php

namespace Hpscript\Type\User;

use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use Hpscript\Repository\UserRepository;
use Hpscript\Type\Definition\DomainType;

class User extends ObjectType {

	private $userRepository;

	public function __construct(){
		$this->userRepository = new UserRepository;
		parent::__construct([
			'name' => 'User',
			'fields' => function(){
				return [
					'id' => [
						'type' => Type::int(),
						'resolve' => function($id){
							return $id;
						},
					],
					'name' => [
						'type' => Type::string(),
						'resolve' => function($id){
							return $this->getUser($id)->getName();
						}
					],
					'profile' => [
						'type' => Type::string(),
						'resolve' => function($id){
							return $this->getUser($id)->getProfile();
						}
					],
					'address' => [
						'type' => Type::string(),
						'resolve' => function($id){
							return $this->getUser($id)->getName();
						}
					],
				];
			},
		]);
	}
}

private function getUser($id){
	return $this->userRepository->getUser($id);
}

src/Type/Definition/DomainType.php

namespace Hpscript\Type\Definition;

use Hpscript\Type\User\User;

class DomainType {

	private static $user;

	public static function user(){
		if(!isset(static::$user)){
			static::$user = new User();
		}
		return static::$user;
	}
}

src/Repository/UserRepository.php

namespace Hpscript\Repository;

class UserRepository {

	private $dummyData;

	public function __construct(){
		$sakamoto = new User(1, 'Sakamoto', '坂本太郎', '東京都');
		$sato = new User(2, 'Sato', '佐藤和子', '神奈川');
		$tanaka = new User(3, 'Tanaka', '田中はじめ', '愛知県');
		$this->dummyData = [
			1 => $sakamoto,
			2 => $sato,
			3 => $tanaka
		];
	}

	public function getUser($id){
		data_default_timezone_set('Asia/Tokyo');
		error_log(date('Y-m-d H:i:s') . "\t" . __METHOD__ . "\n", 3, "/tmp/php-graphql-sample.log");
		return $this->dummyData[$id];
	}
}

class User {
	private $id;
	private $name;
	private $profile;
	private $address;

	public function __construct($id, $name, $profile, $address){
		$this->id = $id;
		$this->name = $name;
		$this->profile = $profile;
		$this->address = $address;
	}

	public function getId(){
		return $this->id;
	}
	public function getName(){
		return $this->name;
	}
	public function getProfile(){
		return $this->profile;
	}
	public function getAddress(){
		return $this->address;
	}
}

public/grapql/index.php

require_once __DIR__ . '/../../vendor/autoload.php';

use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ResolveInfo;
use Hpscript\Type\Definition\DomainType;

class Query extends ObjectType {
	public function __contruct(){
		parent::__construct([
			'name' => 'Query',
			'fields' => [
				'user' => [
					'type' => DomainType::user(),
					'args' => [
						'id' => Type::int(),
					],
					'resolve' => function($value, $args, $context, ResolveInfo $resolveInfo){
						return $args['id'];
					}
				],
			],
		]);
	}
}

$schema = new GraphQL\Type\Schema([
	'query' => new Query(),
]);

$server = new Graph\Server\StandardServer([
	'schema' => $schema
]);

$server->handleRequest();

composer.json

{
	"autoload": {
		"psr-4": {
			"Hpscript\\": "src"
		}
	},
    "require": {
        "webonyx/graphql-php": "^14.9"
    }
}

$ curl -X POST -H “Content-Type: application/json” “http://192.168.34.10:8000/graphql/” -d ‘{“query”: “query { user(id: 2){id name} }”}
POST /graphql/ – Uncaught ArgumentCountError: Too few arguments to function GraphQL\Type\Definition\ObjectType::__construct(), 0 passed in /home/vagrant/dev/graphql/public/graphql/index.php on line 30 and exactly 1 expected in /home/vagrant/dev/graphql/vendor/webonyx/graphql-php/src/Type/Definition/ObjectType.php:86
エラーになるな。mutationはいらないから、querytypeだけで良いような気がするんだが。。

[GraphQL]入門

フロントエンドエンジニアの間ではGraphQLはもはや必須スキル?
ということでGraphQLを入門

### GraphQLとは
– APIのためのクエリ言語(Graph Query Language)
– SQLのようなクエリでAPIを定義する
– REST APIとはエンドポイントが異なる(特定の値を取得するために、余分なデータも取得されてしまう、あるいは、1つのエンドポイントでは必要なデータが取得できない といった問題を解決できる 必要なデータだけ要求する)
– 型付スキーマで記述する
– GraphQLのSubscriptionsはWebSocketを使用しているので、簡単にリアルタイム通信ができる
– Client側がGraphQL Query Languageで、Server側がGraphQL Schema Language

注) サーバ側とクライアント側 両方を学ぶ必要がある

### GraphQL Query Language(GET)
Githubの例: https://docs.github.com/en/graphql/overview/explorer

query { 
  viewer { 
    login
  }
}
query { 
  user(login: "hpscript"){
    id
    name
    url
  }
}

おおおお、これは凄い
POST, PUT, DELETEはmutationを使用する

“!”は必須という意味

type User {
	login: String!
	id: String!
	name: String!
	age: Int
	bio: String
	url: String
}

実際に動かしてみないとわからないね

[Python3 x React.js] S3にアップロードしたjsonファイルをリアルタイムで読み込む

pythonでS3にアップロードします
 L S3 full accessのcredentialは別ファイルからimportにします。

import boto3
import json
import credentials

str = {
	"speech":"最高"
}

with open("sample.json", "w") as f:
	json.dump(str, f, ensure_ascii=False)

s3 = boto3.client('s3', aws_access_key_id=credentials.Accesskey, aws_secret_access_key= credentials.Secretkey, region_name=credentials.Region)
 
filename = "sample.json"
bucket_name = "hogehoge"
 
s3.upload_file(filename,bucket_name,filename, ExtraArgs={'ContentType': "application/json",'ACL':'public-read'})
print("upload {0}".format(filename))

React.jsでJsonをsetIntervalで読み込みます

function App(){
			const [data, setData] = React.useState([]);

			React.useEffect(() => {
				const fetchData = async() => {
				fetch("https://hoge.s3.ap-northeast-1.amazonaws.com/sample.json")
					.then((res) => res.json())
					.then((data) =>{
						setData(data);
				});
				}

				const id = setInterval(() => {
				    fetchData();
				 }, 2000);
				
			}, []);

			console.log(data);

			return(
				<h1>コメント:{data.speech}</h1>
			);
		}

		const target = document.getElementById('app');
		ReactDOM.render(<App />, target);

pythonでuploadしたワードがブラウザの更新なくHTML側に表示されます。
OK、後はUIを作っていく

[Python3] jsonを生成してS3にuploadする

strのところは、mufj, 1375で作ってますが、本来はpython3で処理した値をuploadする想定です。

import boto3
import json

accesskey = ""
secretkey = ""
region = ""

// 機械学習等の処理

str = {
	"三菱UFJフィナンシャル・グループ":668.3,
	"日経ダブルインバース上場投信":367
}

with open("stock.json", "w") as f:
	json.dump(str, f, ensure_ascii=False)

 
s3 = boto3.client('s3', aws_access_key_id=accesskey, aws_secret_access_key= secretkey, region_name=region)
 
filename = "stock.json"
bucket_name = "speech-dnn"
 
s3.upload_file(filename,bucket_name,filename, ExtraArgs={'ACL':'public-read'})
print("upload {0}".format(filename))

result
{“三菱UFJフィナンシャル・グループ”: 668.3, “日経ダブルインバース上場投信”: 367}

OK これをreactと接続して、インタラクティブに表示されるか確認する

### 修正
‘ContentType’: “application/json”で指定しないと、jsonファイルとして保存されない

s3.upload_file(filename,bucket_name,filename, ExtraArgs={'ContentType': "application/json",'ACL':'public-read'})

[Python3] botoでS3にpublicでuploadする方法

s3.upload_file で、 ExtraArgs={‘ACL’:’public-read’}を追加する

s3 = boto3.client('s3', aws_access_key_id=accesskey, aws_secret_access_key= secretkey, region_name=region)
 
filename = "a.json"
bucket_name = "hoge"
 
s3.upload_file(filename,bucket_name,filename, ExtraArgs={'ACL':'public-read'})
print("upload {0}".format(filename))

※追加しないとaccess denyとなるので注意が必要

[音声認識] RaspberryPI 4(model B)でJuliusを動かす

音声認識をwavファイルではなく、ラズパイでやります。

### 前準備
– ラズパイ4 model B (秋葉原で電源、microSDなどセットで1万くらい)
– モニター、キーボード, マウス(メルカリでセットで3500円くらい)
– USBマイク(amazonで300円くらい)
※初期設定でwifiの設定とキーボードをJapaneseにする必要がある

$ mkdir julius
$ cd julius
$ wget https://github.com/julius-speech/julius/archive/v4.4.2.1.tar.gz
$ tar xvzf v4.4.2.1.tar.gz
$ cd julius-4.4.2.1
$ sudo apt-get install libasound2-dev libesd0-dev libsndfile1
※libsndfile1-devはRaspberryPI 4ではinstallできなかった
$ ./configure –with-mictype=alsa
$ make
$ sudo make install

$ cd ../
$ mkdir julius-kit
$ cd julius-kit
$ wget https://osdn.net/dl/julius/dictation-kit-v4.4.zip
$ unzip dictation-kit-v4.4.zip

$ sudo vim /etc/modprobe.d/alsa-base.conf

options snd slots=snd_usb_audio,snd_bcm2835
options snd_usb_audio index=0
options snd_bcm2835 index=1

$ sudo vim ~/.profile
一番最後の行に追加

export ALSADEV="plughw:0,0"

$ sudo apt-get install alsa-utils sox libsox-fmt-all
$ sudo sh -c “echo snd-pcm >> /etc/modules”

ラズパイ再起動

$ cd ~/julius/julius-kit/dicration-kit-v4.4/
$ julius -C main.jconf -C am-gmm.jconf -demo

AWSにjsonを送信したい
-> S3に保存すれば良いのかな
そこさえできれば、ほぼ基本的な挙動は完成

[ TypeScript ] 環境構築でWebsocketServer.js:10エラーになる時

### コンパイラ導入
$ node -v
v10.19.0
$ sudo npm install -g typescript
$ tsc -v
Version 4.4.3

hello.ts

const message:string = 'Hello! TypeScript!'
console.log(message);

$ tsc hello.ts
$ ls
hello.js hello.ts
$ node hello.js
Hello! TypeScript!

### npm環境
$ npm install typescript ts-loader webpack webpack-cli webpack-dev-server –save-dev

package.json

  // 省略
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode=development",
    "start": "webpack-dev-server --mode=development"
  },
    // 省略

webpack.config.js

const path = require('path');
module.exports = {
	entry: {
		bundle: './src/app.ts'
	},
	output: {
		path: path.join(__dirname,'dist'),
		filename: '[name].js'
	},
	resolve: {
		extensions:['.ts','.js']
	},
	devServer: {
		contentBase: path.join(__dirname,'dist')
	},
	module: {
		rules: [
			{
				test:/\.ts$/,loader:'ts-loader'
			}
		]
	}
}

$ tsc –init
-> tsconfig.json が生成される

dist, srcフォルダを作成する

src/app.ts

import {Item} from './item';
var elem = document.getElementById('output');
var aBook = new Item('はじめてのTypeScript', 2020);
aBook.say(elem);

src/item.ts

export class Item {
	constructor(private name:string, private price:number){}

	public say(elem: HTMLElement | null) : void {
		if(elem){
			elem.innerHTML = '書名:' + this.name + ' 価格: ' + this.price + '円';
		}
	}
}

dist/index.html

<body>
	<div id="output"></div>
	<script src="bundle.js"></script>
</body>
</html>

$ npm run start

> tutorial@1.0.0 start /home/vagrant/dev/typescript/tutorial
> webpack-dev-server –mode=development

[webpack-cli] /home/vagrant/dev/typescript/tutorial/node_modules/webpack-dev-server/lib/servers/WebsocketServer.js:10
static heartbeatInterval = 1000;

何でだろう? なんかエラーになるな。。

$ sudo apt-get purge nodejs

▼この記事を参考に最新版を入れる
https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-ja

$ cd ~
$ curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
$ sudo bash nodesource_setup.sh
$ sudo apt install nodejs
$ node -v
v14.17.6
$ npm run start

http://192.168.34.10:8000/

nodeがv10だとwebpack-dev-serverでエラーになるみたい 
https://github.com/VickScarlet/lifeRestart/issues/176
v14にupgradeするとエラー解消
マジかよ~~~~ もうやだー