質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.38%

Node.jsでGoogleSpreadSheetと連携しようとした際にcallbackエラー

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,193

score 11

前提・実現したいこと

コードのgetInfo関数で引数がコールバックになっているのですが、そこの部分でコールバックが存在しないというエラーが起こる

発生している問題・エラーメッセージ

/Users/user/dev/project/node_modules/google-spreadsheet/index.js:84
      cb();
      ^
TypeError: cb is not a function
    at /Users/user/dev/project/node_modules/google-spreadsheet/index.js:84:7
    at /Users/user/dev/project/node_modules/google-spreadsheet/node_modules/google-auth-library/lib/auth/jwtclient.js:119:5
    at /Users/user/dev/project/node_modules/google-spreadsheet/node_modules/google-auth-library/lib/auth/jwtclient.js:138:16
    at Request._callback (/Users/user/dev/project/node_modules/google-spreadsheet/node_modules/gtoken/lib/index.js:228:14)
    at Request.self.callback (/Users/user/dev/project/node_modules/request/request.js:185:22)
    at Request.emit (events.js:197:13)
    at Request.<anonymous> (/Users/user/dev/project/node_modules/request/request.js:1161:10)
    at Request.emit (events.js:197:13)
    at IncomingMessage.<anonymous> (/Users/user/dev/project/node_modules/request/request.js:1083:12)
    at Object.onceWrapper (events.js:285:13)
  /Users/user/dev/atom-tools/node_modules/google-spreadsheet/index.js

  this.setAuthToken = function(auth_id) {
    if (auth_mode == "anonymous") auth_mode = "token";
    setAuthAndDependencies(auth_id);
  };

...中略

75:  function renewJwtAuth(cb) {
76:    auth_mode = "jwt";
77:    jwt_client.authorize(function(err, token) {
78:      if (err) return cb(err);
79:      self.setAuthToken({
80:        type: token.token_type,
81:        value: token.access_token,
82:        expires: token.expiry_date
83:      });
84:      cb();
85:    });
86:  }

・・・中略

  function setAuthAndDependencies(auth) {
    google_auth = auth;
    if (!options.visibility) {
      visibility = google_auth ? "private" : "public";
    }
    if (!options.projection) {
      projection = google_auth ? "full" : "values";
    }
  }

・・・中略

  // public API methods
  this.getInfo = function(cb) {
    self.makeFeedRequest(["worksheets", ss_key], "GET", null, function(
      err,
      data
    ) {
      if (err) return cb(err);
      if (data === true) {
        return cb(new Error("No response to getInfo call"));
      }
      var ss_data = {
        id: data.id,
        title: data.title,
        updated: data.updated,
        author: data.author,
        worksheets: []
      };
      var worksheets = forceArray(data.entry);
      worksheets.forEach(function(ws_data) {
        ss_data.worksheets.push(new SpreadsheetWorksheet(self, ws_data));
      });
      self.info = ss_data;
      self.worksheets = ss_data.worksheets;
      cb(null, ss_data);
    });
  };

・・・中略

  // This method is used internally to make all requests
  this.makeFeedRequest = function(url_params, method, query_or_data, cb) {
    var url;
    var headers = {};
    if (!cb) cb = function() {};
    if (typeof url_params == "string") {
      // used for edit / delete requests
      url = url_params;
    } else if (Array.isArray(url_params)) {
      //used for get and post requets
      url_params.push(visibility, projection);
      url = GOOGLE_FEED_URL + url_params.join("/");
    }

    async.series({
      auth: function(step) {
        if (auth_mode != "jwt") return step();
        // check if jwt token is expired
        if (google_auth && google_auth.expires > +new Date()) return step();
        renewJwtAuth(step);
      },
      request: function(result, step) {
        if (google_auth) {
          if (google_auth.type === "Bearer") {
            headers["Authorization"] = "Bearer " + google_auth.value;
          } else {
            headers["Authorization"] = "GoogleLogin auth=" + google_auth;
          }
        }

        headers["Gdata-Version"] = "3.0";

        if (method == "POST" || method == "PUT") {
          headers["content-type"] = "application/atom+xml";
        }

        if (
          method == "PUT" ||
          (method == "POST" && url.indexOf("/batch") != -1)
        ) {
          headers["If-Match"] = "*";
        }

        if (method == "GET" && query_or_data) {
          var query = "?" + querystring.stringify(query_or_data);
          // replacements are needed for using structured queries on getRows
          query = query.replace(/%3E/g, ">");
          query = query.replace(/%3D/g, "=");
          query = query.replace(/%3C/g, "<");
          url += query;
        }

        request(
          {
            url: url,
            method: method,
            headers: headers,
            gzip: options.gzip !== undefined ? options.gzip : true,
            body: method == "POST" || method == "PUT" ? query_or_data : null
          },
          function(err, response, body) {
            if (err) {
              return cb(err);
            } else if (response.statusCode === 401) {
              return cb(new Error("Invalid authorization key."));
            } else if (response.statusCode >= 400) {
              var message = _.isObject(body)
                ? JSON.stringify(body)
                : body.replace(/&quot;/g, '"');
              return cb(
                new Error(
                  "HTTP error " +
                    response.statusCode +
                    " (" +
                    http.STATUS_CODES[response.statusCode]
                ) +
                  ") - " +
                  message
              );
            } else if (
              response.statusCode === 200 &&
              response.headers["content-type"].indexOf("text/html") >= 0
            ) {
              return cb(
                new Error(
                  "Sheet is private. Use authentication or make public. (see https://github.com/theoephraim/node-google-spreadsheet#a-note-on-authentication for details)"
                )
              );
            }

            if (body) {
              var xml_parser = new xml2js.Parser({
                // options carried over from older version of xml2js
                // might want to update how the code works, but for now this is fine
                explicitArray: false,
                explicitRoot: false
              });
              xml_parser.parseString(body, function(err, result) {
                if (err) {
                  xml_parser = null;
                  body = null;
                  return cb(err);
                }
                if (cb.length == 3) {
                  cb(null, result, body);
                } else {
                  body = null;
                  cb(null, result);
                }
              });
            } else {
              if (err) cb(err);
              else cb(null, true);
            }
          }
        );
      }
    });
  };

該当のソースコード

"use strict";

const googleSpreadSheet = require("google-spreadsheet");

const doc = new googleSpreadSheet(
  "1X98eannyhE"
);

let sheet;

const setAuth = () =>
  new Promise((resolve, reject) => {
    const creds = require("./903c28f.json");
    doc.useServiceAccountAuth(creds, getInfoAndSheets());
  });

const getInfoAndSheets = () =>
  new Promise((resolve, reject) => {
    doc.getInfo((err, info) => {
      sheet = info.worksheets[0];
    });
    resolve();
  });

const workingWithCells = () => {
  const COLUMS = {
    name: 1,
    price: 2
  };
  sheet.getCells(
    {
      "min-row": 2,
      "max-row": 5,
      "return-empty": true
    },
    (err, cells) => {
      for (let i = 0; i < cells.length / sheet.colCount; i += 1) {
        const name = cells[i * sheet.colCount + COLUMS.name].value;
        const price = cells[i * sheet.colCount + COLUMS.price].value;
      }
    }
  );
};

const a = async () => {
  await setAuth();
  await workingWithCells();
};

const main = () => {
  a();
};

main();

試したこと

const getInfoAndSheets = () =>
  new Promise((resolve, reject) => {
    doc.getInfo((err, info) => {
      sheet = info.worksheets[0];
    });
    resolve();
  });


上記の関数を下記に変更

const getInfoAndSheets = () =>
  new Promise((resolve, reject) => {
    doc.getInfo((err, info) => {
      if(!info) info = function(){}
      sheet = info.worksheets[0];
    });
    resolve();
  });


infoがないからエラーが起きているのかと思い、条件分岐を追加しましたが、あまり意味はなかったです。

補足

一応同じ症状の方が一人いたのですが、あまり解決策が出されていなかった
https://github.com/theoephraim/node-google-spreadsheet/issues/105

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kotaro_nagano

    2019/09/27 10:09

    プラグイン側のコードが文字数制限で表示できないので、別途質問作ります。

    キャンセル

  • kotaro_nagano

    2019/09/27 10:12

    2万文字以上になってしまうので、プラグインのURL貼っておきます
    https://www.npmjs.com/package/google-spreadsheet

    キャンセル

  • kotaro_nagano

    2019/09/27 10:17

    該当プログラム部分だけ貼っておきました。

    キャンセル

回答 1

check解決した方法

0

無事解決しました。

問題はモジュール側にあったみたいです。

https://scrapbox.io/shokai/Node.js%E3%81%A8async-await%E3%81%A7Google_Spreadsheet%E3%81%AB%E6%9B%B8%E3%81%8F

google-spreadsheet-as-promised npm https://www.npmjs.com/package/google-spreadsheet-as-promised
 これを使うのが良かった
 google-spreadsheet npm https://www.npmjs.com/package/google-spreadsheetをpromise化したやつなので、async-awaitで書ける


モジュール自体がasyncにしか対応していない書き方をしていたからPromiseやasync-awaitで書くことができなかった。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る