LambdaでS3にある画像をDropbox Businessにアップロードする

LambdaでS3にある画像をDropbox Businessにアップロードしたときの個人メモ

---

# 前提
- [LambdaでS3にある画像をDropboxにアップロードする](https://qiita.com/nkmk1215/items/da11594807552db64e93)ができていること
- [Dropbox Business APIの基礎知識](https://qiita.com/nkmk1215/items/7b1427a74a74b473266c)を把握していること
- アクセストークンを発行できること
    - ここでは[チーム管理者](https://help.dropbox.com/ja-jp/teams-admins/admin/change-admin-rights)が付与されている状態
    - 権限がない場合、アクセストークンはチーム管理者に必要に応じて  
      生成してもらう必要がある
    - 権限の有無はアプリのコンソール画面からアクセストークンの生成が  
    - できるかどうかで確認できる
![030.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/06b84673-4450-dce0-ff6a-7b4bb9fc63a9.jpeg)


# 実行準備
- チームスペースの共有フォルダにアップロードする場合は  
  ルートフォルダを指定する必要がある
- ルートフォルダのIDは事前に取得が必要

## アプリの作成
- Dropbox Business用にアプリ作成が必要
- 「Full Dropbox」で作成
    - 「App folder」の場合、自動的にアプリ専用のフォルダが生成されるが、  
      アプリを作成した個人のフォルダ内に作成される

![028.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/619526a8-6f4f-a348-81e7-76d0f08b331d.jpeg)


## root_namespace_idの取得
- [get_current_account](https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account)を利用してroot_namespace_idを確認する
- API実行に必要な「account_info.read」はデフォルトで付与されているため  
  アプリの権限変更はここでは不要

![029.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/97d04184-104c-4703-fe6a-551ef88b8140.jpeg)

- CloudShellにて実行

```
curl -X POST https://api.dropboxapi.com/2/users/get_current_account \
    --header "Authorization: Bearer <get access token>"
```

- 実行結果から「root_namespace_id」を見つける  
    - Dropbox Businessの場合、「home_namespace_id」と  
      「root_namespace_id」が一致しない
    - ユーザー版と同様にuploadした場合、ルートフォルダは  
      「home_namespace_id」をデフォルト指定することになる

```
{
	 "account_id":"dbid:xxxx"
	,"name":{
		 "given_name":"xxxx"
		,"surname":"xxxx"
		,"familiar_name":"xxxx"
		,"display_name":"xxxx"
		,"abbreviated_name":"xxxx"
	}
	,"email":"xxxx@gmail.com"
	,"email_verified":true
	,"disabled":false
	,"country":"JP"
	,"locale":"ja"
	,"referral_link":"https://www.dropbox.com/referrals/xxxx"
	,"team":{
		 "id":"dbtid:xxxx"
		,"name":"xxxx"
		,"sharing_policies":{
			 "shared_folder_member_policy":{
				 ".tag":"anyone"
			}
			,"shared_folder_join_policy":{
				 ".tag":"from_anyone"
			}
			,"shared_link_create_policy":{
				 ".tag":"default_public"
			}
		}
		,"office_addin_policy":{
			 ".tag":"enabled"
		}
	}
	,"team_member_id":"dbmid:xxxx"
	,"is_paired":true
	,"account_type":{
		 ".tag":"business"
	}
	,"root_info":{
		 ".tag":"team"
		,"root_namespace_id":"123456789"
		,"home_namespace_id":"xxxx"
		,"home_path":"/xxxx@gmail.com"
	}
}
```

## ソース修正
- [LambdaでS3にある画像をDropboxにアップロードする](https://qiita.com/nkmk1215/items/da11594807552db64e93#lambda)で作成したLambda関数にて  
ヘッダー「Dropbox-API-Path-Root」を追加

```lambda_function.py
import boto3
import base64
import requests
import json

ACCESS_TOKEN = '（取得したアクセストークン）'

#ルートフォルダを指定
NAMESPACE_ID = '123456789'

#ルートフォルダ配下の配置場所
FOLDER_PATH = "/image/"
#ファイル名
FILE_NAME = 'escle.png'

ARG_TMP = '{"path": "' + FOLDER_PATH + FILE_NAME + '","mode": "add","autorename": true,"mute": false,"strict_conflict": false}'
ARG = ARG_TMP.encode("utf_8")

# S3から指定したバケット、フォルダ、ファイル名の画像を取得する
# 取得できる画像サイズはLambdaのメモリーに依存
# return:画像のバイナリデータ
def get_img_from_s3():
    s3 = boto3.client('s3')
    bucket_name = 'xxxx'
    file_path = 'sample/' + FILE_NAME
    response = s3.get_object(Bucket=bucket_name, Key=file_path)
    body = response['Body'].read()
    return body

# 画像をDropboxにアップロードする
# img:画像のバイナリデータ
# return:アップロード結果
def upload_to_dropbox(img):
    headers = {
        'Authorization': 'Bearer ' + ACCESS_TOKEN,
        'Dropbox-API-Arg': ARG,
        'Content-Type': 'application/octet-stream',
        'Dropbox-API-Path-Root': '{".tag": "namespace_id", "namespace_id": "' + NAMESPACE_ID + '"}',
    }

    response = requests.post('https://content.dropboxapi.com/2/files/upload', headers=headers, data=img)
    return json.dumps(response,default=str)


def lambda_handler(event, context):
    img = get_img_from_s3()
    result = upload_to_dropbox(img)

    return result
```

# 実行手順
- ユーザー版と同様に実行できる
- 以下のルートフォルダ直下のimageフォルダ内に画像ファイルがアップロードされる
    - ルートフォルダを指定しない場合はユーザーフォルダ「xxxx@gmail.com」内に  
      imageフォルダが作成され、その中に画像ファイルがアップロードされる
    - 「App folder」のAPIの場合、「xxxx@gmail.com」内にアプリ名のフォルダが  
      作成され、その中にimageフォルダが作成され、画像ファイルがアップロードされる

![032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/13f6c3e6-31c2-3a7b-3e56-cf85421843bb.png)

# 感想
- Dropbox Businessだとルートフォルダが異なることに気づくまで時間がかかった
    - 安全な仕様だが、ユーザー版と比較して挙動が変わったように見えるのでとまどった
    - uploadのリファレンスからヘッダー「Dropbox-API-Path-Root」にたどりつけなかった
- エラー時のレスポンスにステータスコードしか含まれておらず、調査が難航した
    - うまくメッセージを取得する方法はあるかもしれないが
- 詰まったらリファレンス以外の公式ドキュメントを漁るくせをつけたい