📑

S3をトリガーにしてDynamo DBにデータを格納するLambdaを作くってみた(テストも)

2023/08/01に公開

はじめに

この記事では、AWS Lambdaを使用して、S3から取得したTSV(Tab Separated Values)ファイルの内容をDynamoDBに保存する方法を解説します。
この実装を行うことで、S3にアップロードされたTSVファイルを自動的にDynamoDBに登録するシステムを構築できます。

前提条件

AWSアカウントを持っていること。
AWS CLIがインストールされ、AWSアカウントにアクセスできること。
Node.jsがインストールされていること。
LambdaStackの内容
あくまでLambdaだけ共有します。

Lambdaの実装

import {S3Event} from 'aws-lambda'
import AWS from 'aws-sdk'
import csvParser from 'csv-parser'

export const handler = async (event:S3Event) => {
 const s3 = new AWS.S#()
 const dynamoDB = new AWS.DynamoDB.DocumentClient()
 const tableName = 'id_catalog_develop'
 
 try{
   const bucketName = event.Records[0].s3.bucket.name
   const objectKey = event.Records[0].s3.object.key
  
   if(!objectKey.includes('.tsv')){
   //このLambdaはtsvの拡張子しか受け入れないような仕様にしているため
    throw new Error('objectKey is not a .tsv file')
   }
  
   const s3stream = s3.getObject({Bucket:bucketName,Key:objectKey})
   const tsvParser = s3stream.pipe(csvParser({separator:'\t'}))
   
   const sequenceParam = {
    TableName:'sequence'
    Key:{
     //DynamoDBのパーティションキー↓
     seq_name:'tableName'
    }
   }
   for await(const row of tsvParser){
    //↓擬似的なauto incrementを作成するためのテーブル
    const data = await dynamoDB.get(sequenceParam).promise()
    let incrementNumber = Number(data.Item?.current_number)
    if(isNaN(incrementNumber)){
     //デフォルトの値を0とする
     incrementNumber = 0
    }
    
    const param = {
     TableName:tableName,
     Key:{
      sid:incrementNumber.toString()
     }
    }
    
    const item = {
     ...row,
     sid:incrementNumber.toString()
     }
     
     await dynamoDB.put({ TableName:tableName ,Item: item }).promise()
   }
   incrementNumber++
   const sequenceItem = {
    seq_name:tableName,
    current_number:incrementNumber
   }
   await dyanmoDB.put({ TableName:sequence ,Item: sequenceItem })
 }catch(error){
  return error
 }
}

Lambdaのテスト

import {S3Event} from `aws-lambda`
import * as lambda from `テストをしたいファイルのPATH`
import AWSMock from `aws-sdk-mock`

const bucketName = `bucket-name`
const objectKey = `object-key.tsv`

const dummyData = Column1\tColumn2\tColumn3
Data1\tValue1\t100
Data2\tValue2\t200
Data3\tValue3\t300
Data4\tValue4\t400
Data5\tValue5\t500

describe(
 it(`正常系`,async() => {
   //S3のgetObjectを使用されたときにはこのMockを返却しますよって感じ
  AWSMock.mock(`s3`,`getObject`,Buffer.from(dummyData))
  
  const putMock = jest.fn().mockReturnValue((promise:() => Promise.resolve))
  AWSMock.mock(`DynamoDB.DocumentClient`,`put`,putMock)
  
  //このeventは調べたらすぐ出てきます
  const res = await lambda.handler(event)
  
  expext(res).toBe(undefind)
  expect(putMock.mock.calls[0][0].TableName).toBe(`id_catalog_develop`)
  expect(putMock.mock.calls[0][0].Item).toEqual({
   
  })
 })
)

あとで書きます

Discussion