[PYTHON] Utilisez AppSync à l'avant et à l'arrière

Utilisez AppSync à l'avant et à l'arrière

Cet article est un article pratique pour passer en revue et établir les connaissances acquises en développant Serverless Web App Mosaic C'est l'un des w2or3w / items / 87b57dfdbcf218de91e2).

Ce serait bien de lire cet article après avoir regardé ce qui suit.

introduction

Une API appelée AppSync est utilisée pour la gestion des données des images téléchargées et des images traitées, ainsi que pour le transfert de données côté client. DynamoDB est utilisé comme source de données pour AppSync. AppSync peut également être configuré avec la CLI Amplify, mais je ne semblais pas être en mesure de spécifier la clé de partition DynanoDB ou la clé de tri, donc je n'utilise pas la CLI Amplify. Créez DynamoDB et AppSync dans la console AWS. Requête de Vue sur le front-end en utilisant Amplify, et de Lambda (Python) sur le back-end par HTTP.

contenu

Configuration d'AppSync

Je peux configurer AppSync avec la CLI Amplify, mais il semble que je ne puisse pas spécifier la clé de partition et la clé de tri de DynamoDB. Je pourrais peut-être le faire, mais je ne savais pas comment le faire. Si quelqu'un sait, faites-le moi savoir. Donc, c'est un peu plus gênant que la ligne de commande, mais faisons-le avec la console AWS.

Créer une table DynamoDB

Créez d'abord DynamoDB comme source de données pour AppSync. AWS Console> DynamoDB> Créer une table Créez un tableau avec les paramètres suivants. Nom de la table: sample_appsync_table Clé de partition: groupe (chaîne de caractères) Clé de tri: chemin (chaîne de caractères) Screenshot 2020-01-01 at 15.25.19.png

Création de l'API AppSync

Une fois DynamoDB créé, il est temps de créer AppSync. AWS Console> AppSync> Créer une API

Étape 1: Premiers pas Sélectionnez "Importer la table DynamoDB" et appuyez sur le bouton "Démarrer". ![Capture d'écran 2020-01-02 à 23.36.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/0d34eb39-937e-1b41-215e- 5216cc8228dc.png) Étape 2: créer un modèle Sélectionnez la table que vous avez créée précédemment (sample_appsync_table). Sélectionnez "Nouveau rôle" dans "Créer ou utiliser un rôle existant". Appuyez sur le bouton "Importer". Screenshot 2020-01-02 at 23.36.23.png Appuyez simplement sur le bouton "Créer". Screenshot 2020-01-02 at 23.40.08.png Étape 3: créer une ressource Définissez le nom de l'API et appuyez sur le bouton "Créer". Screenshot 2020-01-02 at 23.42.14.png

Obtenez schema.json

AWS Console> AppSync> sample_appsync_table> Dans le menu Schéma Téléchargez schema.json. Ce fichier est utilisé dans l'application Web. Screenshot 2020-01-01 at 16.48.25.png

Obtention des informations d'identification

AWS Console> AppSync> sample_appsync_table> Dans le menu des paramètres Vérifiez les informations de ʻAPI URL et ʻAPI KEY dans API Detail. Ces informations seront utilisées dans l'application. ink.png

Laissez le mode d'authentification comme "clé API". La date d'expiration de la clé API est de 7 jours par défaut, mais vous pouvez la prolonger jusqu'à 365 jours en la modifiant. J'écrirai un autre article sur la façon d'utiliser le mode d'authentification en tant qu'utilisateur Cognito comme Storage.

Abonnement sur le front end (application web de Vue)

Après avoir configuré AppSync, continuez à mettre à jour l'application Web.

Ajouter un fichier de définition graphql

Créez un dossier src / graphql et ajoutez trois fichiers.

src/graphql/schema.json Ajoutez le fichier téléchargé au projet tel quel.

src/graphql/queries.js


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

Il s'agit d'une requête pour obtenir la liste des enregistrements en spécifiant le groupe de la clé de partition. C'est un mystère, mais si vous ne spécifiez pas la limite, vous ne pourrez pas obtenir les données. Je pense que c'est une spécification AppSync, pas graphql, mais qu'en est-il? J'ai spécifié un nombre raisonnablement élevé de 1000000, mais honnêtement, c'est trop subtil. Si quelqu'un connaît une meilleure façon d'écrire, faites-le moi savoir.

src/graphql/subscriptions.js


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

Il s'agit d'un abonnement pour spécifier le groupe de la clé de partition et pour être notifié avec les informations lorsque l'enregistrement est inséré. J'ai écrit "Lorsqu'un enregistrement est inséré", mais il n'est pas possible d'insérer un enregistrement directement dans DynamoDB, et il doit être inséré par create dans AppSync.

Ajout d'informations AppSync à aws-exports.js

Ajoutez les informations requises pour accéder à AppSync dans src / aws-exports.js.

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;

Les informations contenues dans ce fichier sont importantes, veuillez donc les manipuler avec soin afin de ne pas les fuir.

Implémentation de l'application Web

Après avoir sélectionné une image dans Accueil et l'avoir téléchargée, implémentez une liste d'informations sur l'image téléchargée et l'image traitée monochrome à la destination de transition de page.

Ajoutez une page appelée Liste. Paramètres du routeur pour cela.

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, 
    }, 
  ] 
});

Vue de la page de liste

src/views/List.vue


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

Liste des composants de la page

src/components/List.vue


<template>
  <v-container>
    <p>liste</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>

Obtenez le groupe dans le paramètre de requête. Lorsqu'elles sont montées avant l'affichage de l'écran, les données d'enregistrement sont acquises en spécifiant le groupe, ou les données d'enregistrement sont acquises lorsque l'événement d'insertion est reçu. Les données d'enregistrement acquises sont conservées dans un tableau de variables membres appelé dataList et affichées côte à côte dans v-list à l'écran. Dans v-list, le chemin et l'image des données d'enregistrement sont affichés. On accède à l'image en obtenant l'adresse avec la date d'expiration (30 minutes) avec Strage.

src/components/Home.vue


<template>
  <v-container>
    <p>domicile</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>

Utilisez uploadSelectedFile pour accéder à la page Liste après le téléchargement du fichier. À ce moment-là, un paramètre de requête appelé groupe est attaché.

Ceci termine la réparation du serveur frontal (application Web), mais le contrôle du fonctionnement est effectué une fois le côté arrière terminé.

Frappé depuis le backend (Python de Lambda)

Mettre en œuvre pour insérer un enregistrement d'un fichier téléchargé depuis une application Web ou un chemin d'image monochrome (clé S3) généré et téléchargé par Lambda via AppSync.

Installez gql.

pip install gql -t .

Mettez à jour lambda_function.py comme suit:

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)

Pour ʻENDPOINT et ʻAPI_KEY, reportez-vous aux paramètres d'API créés précédemment dans AppSync. Veuillez le compresser, le télécharger sur S3 et le déployer sur Lambda.

Contrôle de fonctionnement

Lorsque vous exécutez l'application Web et téléchargez une image, Lambda accède à AppSync, la détecte et la répertorie du côté de l'application Web. Même si je frappe directement l'URL avec le paramètre de requête, j'obtiens la liste d'AppSync et je la liste. Screenshot 2020-01-03 at 09.19.47.pngScreenshot2020-01-03at09.20.39.png

Le projet d'application Web (Vue) est le suivant. https://github.com/ww2or3ww/sample_vue_project/tree/work5

Le projet Lambda est ci-dessous. https://github.com/ww2or3ww/sample_lambda_py_project/tree/work3

Épilogue

C'était le premier sujet que j'ai posé à haute voix après avoir participé à JAWS UG Hamamatsu. N'est-il pas possible de spécifier la clé de partition DynamoDB ou la clé de tri dans l'API d'Amplify? Vous n'utilisez pas souvent DynamoDB sans paramètres clés, non? Je ne connais pas WebSocket en détail, mais est-ce quelque chose comme un long sondage? Je me souviens avoir parlé avec excitation.

Au fait, le réseautage est difficile, n'est-ce pas? Je respecte ceux qui peuvent se dire ingénieurs réseau. Pour être honnête, je ne sais pas si cela s'appelle MQTT sur WebSocket. Veuillez me le dire d'une manière facile à comprendre.

Les échantillons AppSync sont souvent considérés comme un ensemble avec Amplify CLI, ils ne sont donc utilisés que depuis le frontal. Application de chat, application TODO. DynamoDB est également une analyse complète. Je pense que DynamoDB a tendance à augmenter le nombre d'enregistrements, ou il a tendance à être utilisé à cette fin, et dans ce sens, l'analyse de tous les enregistrements n'est pas bonne.

Recommended Posts

Utilisez AppSync à l'avant et à l'arrière
Lancer et utiliser le notebook IPython sur le réseau
Sakura utilisant Python sur Internet
Comment utiliser Jupyter sur le frontal de Spacon ITO
Utiliser le capteur Grove avec Raspberry Pi
Retour sur 2016 dans le langage Crystal
Déployer et utiliser le modèle de prédiction créé en Python sur SQL Server
Ajouter des lignes et du texte sur l'image
Utilisez python sur Raspberry Pi 3 pour éclairer la LED quand il fait noir!
Utilisez la dernière version de PyCharm sur Ubuntu
J'ai essayé de comprendre comment utiliser les pandas et la colinéarité multiple en utilisant l'ensemble de données Affaires comme thème.
Exécutez l'application flask sur Cloud 9 et Apache Httpd
Comment utiliser la commande grep et des exemples fréquents
Ubuntu 20.04 sur raspberry pi 4 avec OpenCV et utilisation avec python
Exécuter une commande sur le serveur Web et afficher le résultat
Comment utiliser argparse et la différence entre optparse
Installez django sur python + anaconda et démarrez le serveur
Utilisez Python pour surveiller Windows et Mac et collecter des informations sur les applications sur lesquelles vous travaillez