Сервер данных Apollo с использованием DynamoDB «Ошибка типа: response.http.headers не повторяется» - PullRequest
0 голосов
/ 16 апреля 2020

Я следую учебному пособию: Как использовать DynamoDB с Apollo GraphQL и NodeJS Без сервера в автономном режиме и в работе

Я создал следующие файлы:

compose.yml

version: "3"

services:
  knocker-lambda-api:
    build:
      context: ./knocker-lambda-api/
      dockerfile: Dockerfile
    expose:
      - 3000
    ports:
      - 3000:3000
    restart: on-failure
    volumes:
      - ./knocker-lambda-api/:/app
  # here could be the other applications
  db:
    image: amazon/dynamodb-local
    entrypoint: java
    command: "-jar DynamoDBLocal.jar -sharedDb -dbPath /data"
    expose:
      - 8000
    ports:
      - 8000:8000
    volumes:
      - ./.db-files/:/data

  db.gui:
    image: aaronshaf/dynamodb-admin
    expose:
      - 8001
    ports:
      - 8001:8001
    environment:
      - DYNAMO_ENDPOINT=http://db:8000

обработчик. js

export { default as main } from "./index.js";

index. js

import { ApolloServer } from "apollo-server-lambda";
import resolvers from "./graphql/resolvers";
import typeDefs from "./graphql/types";

import { PinballMachineAPI } from "./graphql/data-sources/pinball";
import KnockerDB from "./graphql/data-sources/knocker-db";
// set up any dataSources our resolvers need
const dataSources = () => ({
  pinballMachineAPI: new PinballMachineAPI(),
  knockerDB: new KnockerDB(),
});

// Set up Apollo Server
const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources,
  introspection: true,
  playground: true,
});

const handler = server.createHandler({
  cors: {
    origin: "*",
    credentials: true,
  },
});
/* const handler = (event, context, callback) => {

  // tell AWS lambda we do not want to wait for NodeJS event loop
  // to be empty in order to send the response
  context.callbackWaitsForEmptyEventLoop = false;

  // process the request
  return handler(event, context, callback);
}; */

export default handler;

база данных. js

import AWS from "aws-sdk";

import tables from "./tables";

export default class Database {
  async connect() {
    if (!this._connection) {
      let params = {};
      console.log(__DEV__);
      if (__DEV__) {
        params = {
          endpoint: process.env.DB_URL,
          region: "local",
          accessKeyId: "local",
          secretAccessKey: "local",
        };
      } else {
        params = {
          region: "us-east-2",
          apiVersion: "2012-08-10",
        };
      }

      this._connection = new AWS.DynamoDB(params);

      if (__DEV__) {
        // will create tables through lambda only in development
        await this.createTables(tables);
      }
    }
    console.log(this._connection);
    return this._connection;
  }

  async get() {
    return new Promise((resolve, reject) => {
      this._connection.get(params, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  async putItem(params) {
    return new Promise((resolve, reject) => {
      this._connection.putItem(params, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  async getItem(params) {
    return new Promise((resolve, reject) => {
      this._connection.getItem(params, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  async updateItem(params) {
    return new Promise((resolve, reject) => {
      this._connection.updateItem(params, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  async scan(params = {}) {
    return new Promise((resolve, reject) => {
      this._connection.scan(params, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  async deleteItem(params) {
    return new Promise((resolve, reject) => {
      this._connection.deleteItem(params, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  async createTables(tables) {
    for (let k = 0; k < tables.length; k++) {
      const table = tables[k];

      await new Promise((resolve, reject) => {
        this._connection.createTable(table, (err) => {
          if (err) {
            if (err.code !== "ResourceInUseException") {
              console.dir(err);
              reject(err);
            } else {
              console.dir(`Table "${table.TableName}" exists`);
              resolve();
            }
          } else {
            console.dir(`Created table "${table.TableName}"`);
            resolve();
          }
        });
      });
    }
  }
}

knocker-db. js

import Database from "../../db/database";
import stringGen from "crypto-random-string";

export default class KnockerDB {
  // our methods go here, we are going to discuss them below
  async put(data) {
    const item = {
      username: {
        S: data.username.toString(),
      },
      name: {
        S: data.name.toString(),
      },
      machinesPlayed: {
        L: [],
      },
      scores: {
        L: [],
      },
      locationsVisited: {
        L: [],
      },
      email: {
        S: data.email.toString(),
      },
    };

    if (data.id) {
      item.id = { S: data.id.toString() };
    } else {
      // as we mentioned before, we need to specify a new key explicitly
      item.id = { S: stringGen(12) };
    }

    const db = await this.getDatabase();
    await db.putItem({
      TableName: "player",
      Item: item,
    });
  }

  async get() {
    const db = await this.getDatabase();
    return db.get({
      TableName: "player",
      Key: {
        HashKey: "hashkey",
      },
    });
  }

  async getById(id) {
    const db = await this.getDatabase();
    return db.getItem({
      TableName: "player",
      Key: {
        id: {
          S: id.toString(),
        },
      },
    });
  }

  async getByUsername(username) {
    const db = await this.getDatabase();
    return db.getItem({
      TableName: "player",
      Key: {
        username: {
          S: username.toString(),
        },
      },
    });
  }
  /* 
async getForCharacter(id) {
    const db = await this.getDatabase();
    const result = await db.scan({
        TableName: 'player',
        ExpressionAttributeValues: {
            ':cId': {
                S: id,
            },
        },
        FilterExpression: 'contains(characters, :cId)',
    });

    if (result && result.Items) {
        // need to "decode" the items, i know this is annoying
        return result.Items.map((item) => {

            const p = item.parameters ? item.parameters.M : {};
            const parameters = [];
            Object.keys(p).forEach((k) => {
                parameters.push({
                    name: k,
                    value: p[k].S,
                });
            });

            return {
                name: item.name.S,
                damage: item.damage.N,
                id: item.id.S,
                type: item.type.S,
                parameters,
            };
        });
    }

    return [];
} */

  async delete(id) {
    const db = await this.getDatabase();
    await db.deleteItem({
      TableName: "player",
      Key: {
        id: {
          S: id.toString(),
        },
      },
    });
  }
  async getDatabase() {
    if (!this._db) {
      console.log("db created");
      this._db = new Database();
      await this._db.connect();
    }

    return this._db;
  }
}

Я создал репозиторий для этого проекта: Ссылка

Чтобы создать бэкэнд проекта, я выполните следующее:

npm i

docker-compose -f compose.yml build

Чтобы запустить бэкэнд проекта, я запускаю следующее:

docker-compose -f compose.yml up

Я получаю доступ к пользовательскому интерфейсу GraphQL с помощью: http://localhost:3000/dev/knocker-api

Примечание. URL-адрес в пользовательском интерфейсе GraqhQL совпадает с приведенным выше

При доступе к пользовательскому интерфейсу GraphQL возникает следующая ошибка: брошено:

В https://cdn.jsdelivr.net/npm/@apollographql / graphql-playground-react@1.7.32/build/static/node_modules/apollo-link-http-common/lib/index.js

enter image description here

"TypeError: response.http.headers is not iterable"

Кто-нибудь знает, почему я у меня проблема?

...