[PYTHON] Verwenden Sie AppSync am vorderen und hinteren Ende

Verwenden Sie AppSync am vorderen und hinteren Ende

Dieser Artikel ist ein praktischer Artikel zum Überprüfen und Korrigieren der durch die Entwicklung von Serverless Web App Mosaic gewonnenen Erkenntnisse. Es ist eines von w2or3w / items / 87b57dfdbcf218de91e2).

Es wäre schön, diesen Artikel zu lesen, nachdem man sich Folgendes angesehen hat.

Einführung

Eine API namens AppSync wird zur Datenverwaltung von hochgeladenen und verarbeiteten Bildern sowie zur Datenübertragung mit dem Client verwendet. DynamoDB wird als Datenquelle für AppSync verwendet. AppSync kann auch mit der Amplify-CLI eingerichtet werden, aber ich schien nicht in der Lage zu sein, den DynanoDB-Partitionsschlüssel oder den Sortierschlüssel anzugeben, sodass ich die Amplify-CLI nicht verwende. Erstellen Sie DynamoDB und AppSync in der AWS-Konsole. Anfrage von Vue am Frontend mit Amplify und von Lambda (Python) am Backend per HTTP.

Inhalt

AppSync-Setup

Ich kann AppSync mit der Amplify-CLI einrichten, aber anscheinend kann ich den Partitionsschlüssel und den Sortierschlüssel von DynamoDB nicht angeben. Ich kann es vielleicht tun, aber ich wusste nicht, wie ich es tun soll. Wenn jemand weiß, lassen Sie es mich bitte wissen. Es ist also etwas problematischer als die Befehlszeile, aber machen wir es mit der AWS-Konsole.

Erstellen einer DynamoDB-Tabelle

Erstellen Sie zuerst DynamoDB als Datenquelle für AppSync. AWS Console> DynamoDB> Tabelle erstellen Erstellen Sie eine Tabelle mit den folgenden Einstellungen. Tabellenname: sample_appsync_table Partitionsschlüssel: Gruppe (Zeichenfolge) Sortierschlüssel: Pfad (Zeichenfolge) Screenshot 2020-01-01 at 15.25.19.png

Erstellen der AppSync-API

Sobald DynamoDB erstellt wurde, ist es Zeit, AppSync zu erstellen. AWS Console> AppSync> API erstellen

Schritt 1: Erste Schritte Wählen Sie "DynamoDB-Tabelle importieren" und klicken Sie auf "Start". ![Screenshot 2020-01-02 at 23.36.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/0d34eb39-937e-1b41-215e- 5216cc8228dc.png) Schritt 2: Erstellen Sie ein Modell Wählen Sie die zuvor erstellte Tabelle aus (sample_appsync_table). Wählen Sie "Neue Rolle" unter "Vorhandene Rolle erstellen oder verwenden". Klicken Sie auf die Schaltfläche "Importieren". Screenshot 2020-01-02 at 23.36.23.png Klicken Sie einfach auf die Schaltfläche "Erstellen". Screenshot 2020-01-02 at 23.40.08.png Schritt 3: Ressource erstellen Legen Sie den API-Namen fest und klicken Sie auf die Schaltfläche "Erstellen". Screenshot 2020-01-02 at 23.42.14.png

Holen Sie sich schema.json

AWS Console> AppSync> sample_appsync_table> Im Menü Schema Laden Sie schema.json herunter. Diese Datei wird in der Webanwendung verwendet. Screenshot 2020-01-01 at 16.48.25.png

Anmeldeinformationen abrufen

AWS Console> AppSync> sample_appsync_table> Über das Einstellungsmenü Überprüfen Sie die Informationen von "API URL" und "API KEY" in API Detail. Diese Informationen werden in der App verwendet. ink.png

Belassen Sie den Authentifizierungsmodus als "API-Schlüssel". Das Ablaufdatum des API-Schlüssels beträgt standardmäßig 7 Tage. Sie können es jedoch durch Bearbeiten auf 365 Tage verlängern. Ich werde einen weiteren Artikel darüber schreiben, wie der Authentifizierungsmodus als Cognito-Benutzer wie Storage verwendet wird.

Abonnement im Frontend (Vues Web-App)

Aktualisieren Sie nach dem Einrichten von AppSync die Webanwendung weiter.

Fügen Sie eine graphql-Definitionsdatei hinzu

Erstellen Sie einen src / graphql-Ordner und fügen Sie drei Dateien hinzu.

src/graphql/schema.json Fügen Sie die heruntergeladene Datei unverändert zum Projekt hinzu.

src/graphql/queries.js


export const listSampleAppsyncTables = `query listSampleAppsyncTables($group: String) {
  listSampleAppsyncTables(
    limit: 1000000
    filter: {
      group: {eq:$group}
    }
  )
  {
    items 
    {
      group
      path
    }
  }
}
`;

Es ist eine Abfrage, um die Datensatzliste durch Angabe der Gruppe des Partitionsschlüssels abzurufen. Dies ist ein Rätsel, aber wenn Sie das Limit nicht angeben, können Sie die Daten nicht abrufen. Ich denke, es ist eine AppSync-Spezifikation, nicht graphql, aber was ist damit? Ich habe eine relativ große Anzahl von 1000000 angegeben, aber ehrlich gesagt ist es zu subtil. Wenn jemand eine bessere Art zu schreiben kennt, lass es mich wissen.

src/graphql/subscriptions.js


export const onCreateSampleAppsyncTable = `subscription OnCreateSampleAppsyncTable($group: String) {
    onCreateSampleAppsyncTable(group : $group) {
        group
        path
    }
}
`;

Es ist ein Abonnement, um die Gruppe des Partitionsschlüssels anzugeben und mit den Informationen benachrichtigt zu werden, wenn der Datensatz eingefügt wird. Ich habe "Wenn ein Datensatz eingefügt wird" geschrieben, aber es ist nicht möglich, einen Datensatz direkt in DynamoDB einzufügen. Er muss durch Erstellen in AppSync eingefügt werden.

AppSync-Informationen zu aws-exports.js hinzugefügt

Fügen Sie die für den Zugriff auf AppSync erforderlichen Informationen zu src / aws-exports.js hinzu.

src/aws-exports.js


const awsmobile = {
    "aws_project_region": "ap-northeast-1",
    "aws_cognito_identity_pool_id": "ap-northeast-1:********-****-****-****-************",
    "aws_cognito_region": "ap-northeast-1",
    "aws_user_pools_id": "ap-northeast-1_*********",
    "aws_user_pools_web_client_id": "**************************",
    "oauth": {},
    "aws_user_files_s3_bucket": "sample-vue-project-bucket-work",
    "aws_user_files_s3_bucket_region": "ap-northeast-1", 
    "aws_appsync_graphqlEndpoint": "https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql",
    "aws_appsync_region": "ap-northeast-1",
    "aws_appsync_authenticationType": "API_KEY",
    "aws_appsync_apiKey": "da2-**************************"
};
export default awsmobile;

Die in dieser Datei enthaltenen Informationen sind wichtig. Gehen Sie daher vorsichtig damit um, damit sie nicht auslaufen.

Implementierung der Web-App

Implementieren Sie nach Auswahl und Hochladen eines Bilds in Home eine Liste mit Informationen zum hochgeladenen Bild und zum monochrom verarbeiteten Bild am Seitenübergangsziel.

Fügen Sie eine Seite mit dem Namen Liste hinzu. Router-Einstellungen dafür.

src/router.js


import Vue from 'vue'; 
import Router from 'vue-router'; 
import Home from './views/Home.vue'; 
import About from './views/About.vue'; 
import List from './views/List.vue'; 
 
Vue.use(Router); 
 
export default new Router({ 
  routes: [ 
    { 
      path: '/', 
      name: 'home', 
      component: Home,
    }, 
    { 
      path: '/about', 
      name: 'about', 
      component: About, 
    }, 
    { 
      path: '/list', 
      name: 'list', 
      component: List, 
    }, 
  ] 
});

Listenseitenansicht

src/views/List.vue


<template> 
  <List /> 
</template> 
 
<script> 
  import List from '../components/List' 
  export default { 
    components: { 
      List 
    } 
  } 
</script> 

Seitenkomponenten auflisten

src/components/List.vue


<template>
  <v-container>
    <p>aufführen</p>
    <router-link to="/" >link to Home</router-link>
    <hr>
    
    <v-list>
      <v-list-item v-for="data in this.dataList" :key="data.path">
        <v-list-item-content>
          <a :href="data.image" target=”_blank”>
            <v-list-item-title v-text="data.path"></v-list-item-title>
          </a>
        </v-list-item-content>
        <v-list-item-avatar>
          <v-img :src="data.image"></v-img>
        </v-list-item-avatar>
      </v-list-item>
    </v-list>

  </v-container>
</template>

<script>
import { API, graphqlOperation, Storage } from 'aws-amplify';
import { listSampleAppsyncTables } from "../graphql/queries";
import { onCreateSampleAppsyncTable } from "../graphql/subscriptions";

const dataExpireSeconds = (30 * 60);
export default {
  name: 'List',
  data: () => ({
    group: null, 
    dataList: [], 
  }), 
  mounted: async function() {
    this.getList();
  }, 
  methods:{
    async getList() {
      this.group = this.$route.query.group;
      console.log("group : " + this.group);
      if(!this.group){
          return;
      }
      
      let apiResult = await API.graphql(graphqlOperation(listSampleAppsyncTables, { group : this.group }));
      let listAll = apiResult.data.listSampleAppsyncTables.items;
      for(let data of listAll) {
        let tmp = { path : data.path, image : "" };
        let list = [...this.dataList, tmp];
        this.dataList = list;
        console.log("path : " + data.path);
        Storage.get(data.path.replace('public/', ''), { expires: dataExpireSeconds }).then(result => {
          tmp.image = result;
          console.log("image : " + result);
        }).catch(err => console.log(err));
      }
      
      API.graphql(
          graphqlOperation(onCreateSampleAppsyncTable, { group : this.group } )
      ).subscribe({
          next: (eventData) => {
            let data = eventData.value.data.onCreateSampleAppsyncTable;
            let tmp = { path : data.path, image : "" };
            let list = [...this.dataList, tmp];
            this.dataList = list;
            console.log("path : " + data.path);
            Storage.get(data.path.replace('public/', ''), { expires: dataExpireSeconds }).then(result => {
              tmp.image = result;
              console.log("image : " + result);
            }).catch(err => console.log(err));
          }
      });
    }, 
  }
}
</script>

Rufen Sie die Gruppe im Abfrageparameter ab. In gemountet, bevor der Bildschirm angezeigt wird, werden die Datensatzdaten durch Angabe der Gruppe erfasst, oder die Datensatzdaten werden erfasst, wenn das Einfügeereignis empfangen wird. Die erfassten Datensatzdaten werden in einem Elementvariablenarray namens dataList gespeichert und nebeneinander in der V-Liste auf dem Bildschirm angezeigt. In der V-Liste werden der Pfad und das Bild der Datensatzdaten angezeigt. Der Zugriff auf das Bild erfolgt durch Abrufen der Adresse mit dem Ablaufdatum (30 Minuten) bei Strage.

src/components/Home.vue


<template>
  <v-container>
    <p>Zuhause</p>
    <router-link to="about" >link to About</router-link>
    <hr>
    <v-btn @click="selectFile">
      SELECT A FILE !!
    </v-btn>
    <input style="display: none" 
      ref="input" type="file" 
      @change="uploadSelectedFile()">
  </v-container>
</template>

<script>
import Vue from 'vue'
import { Auth, Storage } from 'aws-amplify';

export default {
  name: 'Home',
  data: () => ({
    loginid: "sample-vue-project-user", 
    loginpw: "sample-vue-project-user", 
  }), 
  mounted: async function() {
    this.login();
  }, 
  methods:{
    login() {
      console.log("login.");
      Auth.signIn(this.loginid, this.loginpw)
        .then((data) => {
          if(data.challengeName == "NEW_PASSWORD_REQUIRED"){
            console.log("new password required.");
            data.completeNewPasswordChallenge(this.loginpw, {}, 
              {
                onSuccess(result) {
                    console.log("onSuccess");
                    console.log(result);
                },
                onFailure(err) {
                    console.log("onFailure");
                    console.log(err);
                }
              }
            );
          }
          console.log("login successfully.");
        }).catch((err) => {
          console.log("login failed.");
          console.log(err);
        });
    },
    
    selectFile() {
      if(this.$refs.input != undefined){
        this.$refs.input.click();
      }
    }, 
    
    uploadSelectedFile() {
      let file = this.$refs.input.files[0];
      if(file == undefined){
        return;
      }
      console.log(file);

      let dt = new Date();
      let dirName = this.getDirString(dt);
      let filePath = dirName + "/" + file.name;      
      Storage.put(filePath, file).then(result => {
        console.log(result);
      }).catch(err => console.log(err));

      this.$router.push({ path: 'list', query: { group: dirName }});      
    }, 
    
    getDirString(date){
      let random = date.getTime() + Math.floor(100000 * Math.random());
      random = Math.random() * random;
      random = Math.floor(random).toString(16);
      return "" + 
        ("00" + date.getUTCFullYear()).slice(-2) + 
        ("00" + (date.getMonth() + 1)).slice(-2) + 
        ("00" + date.getUTCDate()).slice(-2) + 
        ("00" + date.getUTCHours()).slice(-2) + 
        ("00" + date.getUTCMinutes()).slice(-2) + 
        ("00" + date.getUTCSeconds()).slice(-2) + 
        "-" + random;
    }, 
  }
}
</script>

Verwenden Sie uploadSelectedFile, um nach dem Hochladen der Datei zur Listenseite zu wechseln. Zu diesem Zeitpunkt wird ein Abfrageparameter namens group angehängt.

Damit ist die Reparatur des Frontends (Webanwendung) abgeschlossen, die Funktionsprüfung wird jedoch nach Abschluss der Backend-Seite durchgeführt.

Hit aus dem Backend (Lambdas Python)

Implementiert das Einfügen eines Datensatzes einer Datei, die aus einer Webanwendung hochgeladen wurde, oder eines monochromen Bildpfads (S3-Schlüssel), der von Lambda über AppSync generiert und hochgeladen wurde.

Installieren Sie gql.

pip install gql -t .

Aktualisieren Sie lambda_function.py wie folgt:

lambda_function.py


# coding: UTF-8
import boto3
import os
from urllib.parse import unquote_plus
import numpy as np
import cv2
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
s3 = boto3.client("s3")

from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
ENDPOINT = "https://**************************.appsync-api.ap-northeast-1.amazonaws.com/graphql"
API_KEY = "da2-**************************"
_headers = {
    "Content-Type": "application/graphql",
    "x-api-key": API_KEY,
}
_transport = RequestsHTTPTransport(
    headers = _headers,
    url = ENDPOINT,
    use_json = True,
)
_client = Client(
    transport = _transport,
    fetch_schema_from_transport = True,
)

def lambda_handler(event, context):
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    logger.info("Function Start (deploy from S3) : Bucket={0}, Key={1}" .format(bucket, key))

    fileName = os.path.basename(key)
    dirPath = os.path.dirname(key)
    dirName = os.path.basename(dirPath)
    
    orgFilePath = u'/tmp/' + fileName
    processedFilePath = u'/tmp/processed-' + fileName

    if (key.startswith("public/processed/")):
        logger.info("not start with public")
        return
    
    apiCreateTable(dirName, key)

    keyOut = key.replace("public", "public/processed", 1)
    logger.info("Output local path = {0}".format(processedFilePath))

    try:
        s3.download_file(Bucket=bucket, Key=key, Filename=orgFilePath)

        orgImage = cv2.imread(orgFilePath)
        grayImage = cv2.cvtColor(orgImage, cv2.COLOR_RGB2GRAY)
        cv2.imwrite(processedFilePath, grayImage)

        s3.upload_file(Filename=processedFilePath, Bucket=bucket, Key=keyOut)
        apiCreateTable(dirName, keyOut)

        logger.info("Function Completed : processed key = {0}".format(keyOut))

    except Exception as e:
        print(e)
        raise e
        
    finally:
        if os.path.exists(orgFilePath):
            os.remove(orgFilePath)
        if os.path.exists(processedFilePath):
            os.remove(processedFilePath)

def apiCreateTable(group, path):
    logger.info("group={0}, path={1}".format(group, path))
    try:
        query = gql("""
            mutation create {{
                createSampleAppsyncTable(input:{{
                group: \"{0}\"
                path: \"{1}\"
              }}){{
                group path
              }}
            }}
            """.format(group, path))
        _client.execute(query)
    except Exception as e:
        print(e)

Informationen zu "ENDPOINT" und "API_KEY" finden Sie in den API-Einstellungen, die zuvor in AppSync erstellt wurden. Bitte komprimieren Sie es, laden Sie es in S3 hoch und stellen Sie es in Lambda bereit.

Funktionsprüfung

Wenn Sie die Web-App ausführen und ein Bild hochladen, drückt Lambda auf AppSync, erkennt es und listet es auf der Seite der Web-App auf. Selbst wenn ich die URL mit dem Abfrageparameter direkt drücke, erhalte ich die Liste von AppSync und liste sie auf. Screenshot 2020-01-03 at 09.19.47.pngScreenshot2020-01-03at09.20.39.png

Das Webanwendungsprojekt (Vue) sieht wie folgt aus. https://github.com/ww2or3ww/sample_vue_project/tree/work5

Das Lambda-Projekt ist unten. https://github.com/ww2or3ww/sample_lambda_py_project/tree/work3

Nachwort

Dies war das erste Thema, das ich laut gefragt habe, nachdem ich an JAWS UG Hamamatsu teilgenommen hatte. Ist es nicht möglich, den DynamoDB-Partitionsschlüssel oder den Sortierschlüssel in der Amplify-API anzugeben? Sie verwenden DynamoDB nicht oft ohne Schlüsseleinstellungen, oder? Ich kenne WebSocket nicht im Detail, aber ist es so etwas wie langes Abrufen? Ich erinnere mich, wie ich aufgeregt gesprochen habe.

Networking ist übrigens schwierig, nicht wahr? Ich respektiere diejenigen, die sich Netzwerktechniker nennen können. Um ehrlich zu sein, verstehe ich das nicht wirklich, selbst wenn es MQTT über WebSocket heißt. Bitte sagen Sie es mir auf leicht verständliche Weise.

AppSync-Beispiele werden häufig als Set mit Amplify CLI bezeichnet, sodass sie nur vom Front-End aus verwendet werden. Chat App, TODO App. DynamoDB ist auch ein vollständiger Scan. Ich denke, dass DynamoDB dazu neigt, die Anzahl der Datensätze zu erhöhen, oder es wird tendenziell für diesen Zweck verwendet, und in diesem Sinne ist das Scannen aller Datensätze nicht gut.

Recommended Posts

Verwenden Sie AppSync am vorderen und hinteren Ende
Starten und verwenden Sie das IPython-Notebook im Netzwerk
Sakura Verwenden von Python im Internet
Verwendung von Jupyter am Frontend von Spacon ITO
Verwenden Sie den Grove-Sensor mit Raspberry Pi
Rückblick auf 2016 in der Crystal-Sprache
Stellen Sie das in Python unter SQL Server erstellte Vorhersagemodell bereit und verwenden Sie es
Fügen Sie dem Bild Linien und Text hinzu
Verwenden Sie Python auf Raspberry Pi 3, um die LED zu beleuchten, wenn es dunkel wird!
Verwenden Sie die neueste Version von PyCharm unter Ubuntu
Ich habe versucht zu verstehen, wie Pandas und multiple Co-Linearität unter Verwendung des Affairs-Datensatzes als Thema verwendet werden.
Führen Sie die Kolben-App auf Cloud 9 und Apache Httpd aus
Verwendung des Befehls grep und häufiger Samples
Ubuntu 20.04 auf Himbeer-Pi 4 mit OpenCV und mit Python verwenden
Führen Sie einen Befehl auf dem Webserver aus und zeigen Sie das Ergebnis an
Wie man Argparse benutzt und den Unterschied zwischen Optparse
Installieren Sie django auf Python + Anaconda und starten Sie den Server
Verwenden Sie Python, um Windows und Mac zu überwachen und Informationen zu den Apps zu sammeln, an denen Sie arbeiten