【Go】初歩的な出力方法いろいろまとめ【Hello world】

こちらの記事では、Goを使ってコンソールに出力を行う方法について、いくつかまとめてご紹介します。

Hello worldまで

まずは Hello world をコンソールに出力するまで。

main.go

package main

import "fmt"

func main() {
	fmt.Println("Hello world!")
}
$ go run main.go
Hello world!

文字列の連携

mainの関数の第2引数以降に文字列を指定することで、文字列を連結した状態でコンソールに出力ができます。

package main

import "fmt"

func main() {
	fmt.Println("Hello world!", "Second Message")
}
$ go run lesson.go
Hello world! Second Message

他の関数を走らせる

mainのファンクション以外を使用する方法は以下の通り。

main.go

package main

import "fmt"

func Buzz() {
	fmt.Println("Buzz")
}

func main() {
	Buzz()
	fmt.Println("Hello world!")
}
$ go run lesson.go
Buzz
Hello world!

初期化後にmain以外の関数を走らせる

基本はpackageに対応した関数が走りますが、その前に関数を走らせるための方法もあります。

main.go

package main

import "fmt"

func Buzz() {
	fmt.Println("Buzz")
}

func main() {
	Buzz()
	fmt.Println("Hello world!")
}

func init() {
	fmt.Println("init!")
}
$ go run lesson.go
init!
Buzz
Hello world!

【Go】インストール後に go コマンドが not found

GoをPCにインストール後、ターミナルから

$ go

を打つと、 command not found になってしまう。

単純にパスが通っていないだけなので、以下の通りターミナルからコマンドを入力して、パスを通しましょう。

$ export PATH=$PATH:/usr/local/go/bin

Getting Started - The Go Programming Language

【Firebase Functions】Functionsの定期実行の方法

定期的にFirebase Functionsを実行させたいという場合には、Cloud Schedulerを使用できます。

以下のようなファンクションを設定することによって、5分毎に処理が実行されます。

exports.removeMember = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
  console.log('This will be run every 5 minutes!');
  return null;
});

('every 5 minutes')の部分はcron.yaml構文というもので、以下のリンクが参考になります。
cron.yaml Reference  |  App Engine standard environment for Python 2  |  Google Cloud

注意点としては、毎回実行毎に$0.10が費用として掛かってくるという点です。
それに基づき、従量課金制の「Blasze」モードでなければCloud Schedulerは使用できません。
Schedule functions  |  Firebase

【Firebase Functions】指定したドキュメントのフィールドの値を取得する

Firebase Functionsのindex.js内で、指定したドキュメント内の値にアクセスしたい場合があります。

具体的には以下の枠線で囲まれた部分をどのように取得するのか、という話です。
f:id:nekorokkekun:20191101101145p:plain

以下のコードを例にとってみてみましょう。

exports.addFanpageMember =
functions.firestore
.document("fanPages/{fanpageId}")
.onCreate(async (snap, context) => {
  const val = snap.data();
  try {
  await admin
  .firestore()
  .collection("fanPages")
  .doc(context.params.fanpageId)
  .collection("members")
  .doc(val.userId)
  .set({ permission: "pageOwner" });
  return;
} catch (error) {
  console.log(error);
  await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });
  console.error(error);
  console.log(`user: ${context.params.userId}`);
}
});


重要なのは以下の部分です。

functions.firestore
.document("fanPages/{fanpageId}")
.onCreate(async (snap, context) => {
  const val = snap.data();

snapには現時点でのfanPages/{fanpageId}で指定したドキュメント内のデータが入っています。そこで、そのデータを取り出すために「const val = snap.data();」という形で記述しています。

このように書くことで、以下のようにオブジェクト形式でデータが入ります。

{ artistName: 'FunctionsTestファンページの紹介',
body: 'FunctionsTestファンページの紹介',
category: 'singer',
monthlyFee: '12456',
pageName: 'FunctionsTestファンページの紹介',
userId: 'aE91BXgT8fZaDiikdTRF4dmRzhF2' }

.doc(val.userId)のように書いてあげればuserIdが取得できます。その他も同様に取得できます。

【Firebase Functions】Databaseの固有IDを取得したい

以下の枠に囲われた固有IDを取得したい場合があります。
f:id:nekorokkekun:20191101100502p:plain

例えば、指定したIDのドキュメント直下に、さらにコレクションやドキュメントを作成する場合です。

その場合、以下のような書き方で固有IDを取得できます。

exports.addFanpageMember =
functions.firestore
.document("fanPages/{fanpageId}") // このように書く
.onCreate(async (snap, context) => {
  const val = snap.data();
  console.log(val);
  try {
  await admin
  .firestore()
  .collection("fanPages")
  .doc(context.params.fanpageId) // このように書く
  .collection("members")
  .doc(val.userId)
  .set({ permission: "pageOwner" });
  return;
} catch (error) {
  console.log(error);
  await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });
  console.error(error);
  console.log(`user: ${context.params.userId}`);
}
});

重要なのは、以下2点です。

.document("fanPages/{fanpageId}") // このように書く
.onCreate(async (snap, context) => {
  .doc(context.params.fanpageId) // このように書く

ドキュメントを指定する際に「{fanpageId}」といった書き方をすることで、「context.params.fanpageId」と書いてアクセスが可能となります。

【Next.js】メールフォームをFirebase Functionsで実装

f:id:nekorokkekun:20191029154326p:plain:w1000
今回は、Next.jsとFirebase Functionsを用いて、お問い合わせフォームを実装していきます。

Firebase側の実装

Firebaseとの接続を行います。

lib/db.js

import firebase from 'firebase/app'
import 'firebase/firestore'
import "firebase/auth";

let db;
try {
  const config = {
        apiKey           : xxxxxxxxxxxxxxxxxxxx,
        authDomain       : xxxxxxxxxxxxxxxxxxxx,
        databaseURL      : xxxxxxxxxxxxxxxxxxxx,
        projectId        : xxxxxxxxxxxxxxxxxxxx,
        storageBucket    : xxxxxxxxxxxxxxxxxxxx,
        messagingSenderId: xxxxxxxxxxxxxxxxxxxx,
        appId            : xxxxxxxxxxxxxxxxxxxx
    };
    if (!firebase.apps.length) {
      firebase.initializeApp(config);
    }
    // Firestoreインスタンスを作成
    db = firebase.firestore();
  } catch (error) {
    console.log(error);
}

let auth = firebase.auth();
const firestore = firebase.firestore();

module.exports = {
  // 本来、initializeAppによる初期化は一度きりのため、
  // 初期化の結果のみを切り出してexportする
  db,
  auth,
  firebase,
  firestore
};

Firerbase接続に関して、詳しくは以下をご覧ください。
nekorokkekun.hatenablog.com

Firebase Functionsの設定

ルートディレクトリにfunctionsディレクトリを作成します。

次にメーラーをインストールしましょう。

$ yarn add nodemailer
$ yarn install

そして、Firebase Functionsの環境変数に、問い合わせを受信するメールアドレスやパスワード、管理者のメールアドレスを設定しましょう。

$ firebase functions:config:set gmail.email="xxxxxx@gmail.com" gmail.password="xxxxxx" admin.email="xxxxxx@gmail.com"

最後にFirebase Functionsを実装します。
functions/index.js

const nodemailer = require("nodemailer");
const gmailEmail = functions.config().gmail.email;
const gmailPassword = functions.config().gmail.password;
const adminEmail = functions.config().admin.email;

// メールサーバー設定
const mailTransport = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: gmailEmail,
    pass: gmailPassword
  }
});

// 管理者用のメールテンプレート
const adminContents = data => {
  return `以下内容でホームページよりお問い合わせを受けました。

お名前:
${data.name}

メールアドレス:
${data.email}

内容:
${data.content}
`;
};

exports.sendMail = functions.https.onCall((data, context) => {
  // メール設定
  let adminMail = {
    from: gmailEmail,
    to: adminEmail,
    subject: "ホームページお問い合わせ",
    text: adminContents(data)
  };

  // 管理者へのメール送信
  mailTransport.sendMail(adminMail, (err, info) => {
    if (err) {
      return console.error(`send failed. ${err}`);
    }
    return console.log("send success.");
  });
});

最後に実装したFirebase FunctionsをFIrebaseにデプロイします。

$ yarn deploy

フロント側の実装

以下の記事からそのままお借りしました。
[firebase] Cloud Functionsを利用したお問い合わせ機能の実装 [react.js] - Qiita

import React, { Component } from 'react';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button'

import {firebase} from '../lib/db.js';
require("firebase/functions");

class ContactForm extends Component {
  constructor(){
    super()
    this.onSubmit = this.onSubmit.bind(this)
  }

  onSubmit(e){
    e.preventDefault()
    let data = {}
    data.name = e.target.name.value
    data.email = e.target.email.value
    data.content = e.target.content.value
    let sendMail = firebase.functions().httpsCallable('sendMail');
    sendMail(data)
    e.target.name.value = ""
    e.target.email.value = ""
    e.target.content.value = ""
    e.target.value = ""
  }

  render() {
    const textFieldStyle = {
      display: "flex",
      width: "300px",
    }

    const contactForm = {
      display: "flex",
      flexDirection: "column", 
      alignItems: "center",
      marginTop: "100px",
    }


    return (
      <React.Fragment>
        <div style={contactForm}>
          <h2>お問い合わせ</h2>
          <form onSubmit={this.onSubmit}>
            <TextField name="name" label="お名前" type="text" required style={textFieldStyle}  />
            <TextField name="email" label="メールアドレス" type="mail" required style={textFieldStyle}   />
            <TextField
              required
              name="content"
              label="お問い合わせ内容"
              multiline
              rows="8"
              margin="normal"
              variant="outlined"
              style={textFieldStyle} 
            />
            <Button variant="contained" color="primary" type="submit" style={textFieldStyle} >
              送信
            </Button>
          </form>
        </div>
      </React.Fragment>
    )
  }   
}

export default ContactForm

これで実際に、設定したメールアドレスへ問い合わせたメールの内容が届くはずです。

Firebase Functionsにエラーが出る場合

以下のようなエラーが出て悩まされました。

send failed. Error: Invalid login: 534-5.7.14 
<https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=xxxxxxxxxxx>
Please log in via your web browser and then try again.
Learn more at
https://support.google.com/mail/answer/78754 h16sm1466164ilq.18 - gsmtp

私の場合は、以下のリンクを踏み、アクセスを有効にすることで、メールが届くようになりました。
accounts.google.com