はじめに
Amazon Chime SDKで会議アプリの試用・検証する機会があったので、メモ書きです。
前提
node.js v22.13.0がインストールされていること
AWS CLIが実行できる環境になっていること
手順参考(英語サイト)
ReactとChime SDK関連のインストール
npx create-react-app react-sample --template typescript
を実行して、「y」で基本的なアプリの設定を実施する。
npm を動かすのが久しぶりだったので、最新Verが存在するよ!と言われたのでバージョンアップを実施しました。
cd react-sample
npm install
脆弱体の問題に対する改修版があるというので、指示どおりに当てる。
npm audit fix --force
npm audit
次に、Amazon Chime SDK React コンポーネントライブラリとピア依存関係を React アプリケーションのルートディレクトリにインストールするのですが、、、公開されてから、結構期間が経ってるようでバージョンの依存関係が成立しなくなっているようです。
npm install --save amazon-chime-sdk-component-library-react amazon-chime-sdk-js styled-components styled-system aws-amplify && npm install --save-dev @types/styled-components
「--legacy-peer-deps」のオプションをつけて無視しつつインストールしてみます。
また公式サイトの方を見ると、Ver3まで上がっている「@3付き」ので、最新にしてインストールする。
npm install --save --legacy-peer-deps amazon-chime-sdk-component-library-react@3 amazon-chime-sdk-js@3 styled-components styled-system aws-amplify && npm install --save-dev --legacy-peer-deps @types/styled-components
Amplifyのセットアップ
npm install -g @aws-amplify/cli
「プロジェクトをAmplifyプロジェクトとして初期化」で問題発生。。。
実行環境では、AWS SSOでCLIを実行しているのだけど、IAMユーザを使って固定のアクセスキーを払い出さないとセットアップをさせてくれない模様。。。
仕方ないので一時的にIAMユーザとアクセスキーを払い出して対応する。
海外ブログの手順になった設問だけメモ書き
⚠️ For new projects, we recommend starting with AWS Amplify Gen 2, our new code-first developer experience. Get started at https://docs.amplify.aws/react/start/quickstart/
新しいバージョンのAmplify(Gen2)があるのにGen1を使いますか?ってことで、「Yes」
Amplify(Gen2)は初期化の手順や思想が異なり、海外ブログの手順が全く役に立たなくなるほどの乖離があるため、Gen1で続行する。
√ Why would you like to use Amplify Gen 1? · I am a current Gen 1 user
選択肢は複数の中から選択できましたが、Gen1の既存ユーザだから。と無難な選択をする。
React関連のソースを手順書に従って置き換えていく。
- \public\index.html
- \src\components\Meeting.tsx
- \src\components\MeetingForm.tsx
- \src\index.tsx
- \src\aws-exports.js (ファイルを残すだけ)
Amplifyのバックグラウンドの登録
- Lambdaの追加の手順は、手順通りに、、、
- GraphQLの追加で、問題発生。。。
どうもスキーマ定義がV1で書かれているが、現状はV2に書き直さないとダメな模様。
手順書のGraphQLのスキーマ定義
type Meeting @model(mutations: {create: "createMeetingGraphQL", delete: "deleteMeetingGraphQL"}, subscriptions: null) @key(fields: ["title"]){
meetingId: String!
title: String!
data: String!
}
type Attendee @model(mutations: {create: "createAttendeeGraphQL", delete: "deleteAttendeeGraphQL"}, subscriptions: null) @key(fields: ["attendeeId"]){
attendeeId: String!
name: String!
}
type Query {
createChimeMeeting(title: String, name: String, region: String): Response @function(name: "reactSampleLambda-${env}")
joinChimeMeeting(meetingId: String, name: String): Response @function(name: "reactSampleLambda-${env}")
endChimeMeeting(meetingId: String): Response @function(name: "reactSampleLambda-${env}")
}
type Response {
statusCode: String!
headers: String
body: String
isBase64Encoded: String
}
V1→V2へのマイグレーション方法
@key の廃止と、@authの追加に対応しないといけないと。。。
type Meeting @model(mutations: {create: "createMeetingGraphQL", delete: "deleteMeetingGraphQL"}, subscriptions: null)
@auth(rules: [{allow: owner}, {allow: private, operations:[read, create, update, delete]}])
{
meetingId: String!
title: String! @primaryKey
data: String!
}
type Attendee @model(mutations: {create: "createAttendeeGraphQL", delete: "deleteAttendeeGraphQL"}, subscriptions: null)
@auth(rules: [{allow: owner}, {allow: private, operations:[read, create, update, delete]}])
{
attendeeId: String! @primaryKey
name: String!
}
type Query {
createChimeMeeting(title: String, name: String, region: String): Response @function(name: "reactSampleLambda-${env}")
joinChimeMeeting(meetingId: String, name: String): Response @function(name: "reactSampleLambda-${env}")
endChimeMeeting(meetingId: String): Response @function(name: "reactSampleLambda-${env}")
}
type Response {
statusCode: String!
headers: String
body: String
isBase64Encoded: String
}
上記のような形に書き直して、
amplify push
を実行した所、デプロイできた模様。
手順通りに、起動しようとしたところ、まだ依存関係問題が発生した。
npm install && npm run build && npm run start
ので、「 --legacy-peer-deps」のオプションを付けて実行する。
npm install && npm run build && npm run start
と、
'react-scripts' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。
'react-scripts'が一緒に入らなかった模様なので追加でインストールする。
npm install --legacy-peer-deps react-scripts
で、「--legacy-peer-deps」を忘れずに。。。
ここまできて、どうにも起動が、上手くいかないのでバージョン依存関係の解消に、本腰を入れて手を付ける。
npmのバージョンが新しすぎてダメなようなので10まで落とす。
npm install npm@10.0.0 -g
Reactのバージョンが新しすぎるので、こちらもバージョンダウンする。
npm install --save react@18.0.0 react-dom@18.0.0
どうも
npm audit fix --force
npm audit
を実施すると、'react-scripts' の実行バイナリが消えてしまう。
結果
'react-scripts' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。
が発生してしまう模様。。。
package.json
に、
"react-scripts": "^5.0.1",
を記載して、
npm install react-scripts --save
をし直す事で、コマンドを認識するようになった。
次は、
npm run build
が成功しない。。。
の事象が発生している模様。
- \src\index.tsx
のimport分を変更する。
import { Amplify } from 'aws-amplify';
- \amplify\backend\api\amplifydemo\schema.graphql
サンプルはV1で、最新だとV2で動作しようとするのでマイグレーションが必要だった
type Meeting @model(mutations: {create: "createMeetingGraphQL", delete: "deleteMeetingGraphQL"}, subscriptions: null)
@auth(rules: [{allow: owner}, {allow: private, operations:[read, create, update, delete]}])
{
meetingId: String!
title: String! @primaryKey
data: String!
}
type Attendee @model(mutations: {create: "createAttendeeGraphQL", delete: "deleteAttendeeGraphQL"}, subscriptions: null)
@auth(rules: [{allow: owner}, {allow: private, operations:[read, create, update, delete]}])
{
attendeeId: String! @primaryKey
name: String!
}
type Query {
createChimeMeeting(title: String, name: String, region: String): Response @function(name: "reactSampleLambda-${env}")
joinChimeMeeting(meetingId: String, name: String): Response @function(name: "reactSampleLambda-${env}")
endChimeMeeting(meetingId: String): Response @function(name: "reactSampleLambda-${env}")
}
type Response {
statusCode: String!
headers: String
body: String
isBase64Encoded: String
}
- \src\uril\api.ts
amplify V5のサンプル例となっており、現行はV6で動作しようとする。
上記のサイトを参考に、V6用に書き換える。
import { generateClient } from 'aws-amplify/api';
import { createAttendeeGraphQL, createMeetingGraphQL, deleteMeetingGraphQL } from '../graphql/mutations';
import { createChimeMeeting, getAttendee, endChimeMeeting, getMeeting, joinChimeMeeting } from '../graphql/queries';
export async function createMeeting(title: string, attendeeName: string, region: string) {
const client = generateClient();
const joinInfo: any = await client.graphql({query: createChimeMeeting, variables:{title: title, name: attendeeName, region: region }});
const joinInfoJson = joinInfo.data.createChimeMeeting;
const joinInfoJsonParse = JSON.parse(joinInfoJson.body);
return joinInfoJsonParse;
}
export async function joinMeeting(meetingId: string, name: string) {
const client = generateClient();
const joinInfo: any = await client.graphql({query: joinChimeMeeting, variables:{meetingId: meetingId, name: name}});
const joinInfoJson = joinInfo.data.joinChimeMeeting;
const joinInfoJsonParse = JSON.parse(joinInfoJson.body);
return joinInfoJsonParse;
}
export async function endMeeting(meetingId: string) {
const client = generateClient();
const endInfo: any = await client.graphql({query: endChimeMeeting, variables:{meetingId: meetingId}});
const endInfoJson = endInfo.data.endChimeMeeting;
await client.graphql({query: deleteMeetingGraphQL, variables:{title: meetingId}});
return endInfoJson;
}
export async function addMeetingToDB(title: string, meetingId: string, meetingData: string) {
const client = generateClient();
await client.graphql({query:createMeetingGraphQL , variables:{input: {title: title, meetingId: meetingId, data: meetingData, }}});
}
export async function addAttendeeToDB(attendeeID: string, attendeeName: string) {
const client = generateClient();
await client.graphql({query:createAttendeeGraphQL , variables:{input: {attendeeId: attendeeID, name: attendeeName }}});
}
export async function getMeetingFromDB(title: string) {
const client = generateClient();
const meetingInfo = await client.graphql({query:getMeeting , variables:{title: title } });
return meetingInfo;
}
export async function getAttendeeFromDB(attendeeId: string) {
const client = generateClient();
const attendeeInfo = await client.graphql({ query:getAttendee , variables:{attendeeId: attendeeId } });
return attendeeInfo;
}
サンプルは、V1で書かれているので、V3に対応する必要がある。(メモ書き)
import React, { ChangeEvent, FC, FormEvent, useState } from 'react';
import {
Flex,
FormField,
Input,
PrimaryButton,
useMeetingManager,
} from 'amazon-chime-sdk-component-library-react';
import { MeetingSessionConfiguration } from 'amazon-chime-sdk-js';
import { addAttendeeToDB, addMeetingToDB, createMeeting, getAttendeeFromDB, getMeetingFromDB, joinMeeting } from '../utils/api';
const MeetingForm: FC = () => {
const meetingManager = useMeetingManager();
const [meetingTitle, setMeetingTitle] = useState('');
const [attendeeName, setName] = useState('');
function getAttendeeCallback() {
return async (chimeAttendeeId: string, externalUserId?: string) => {
const attendeeInfo: any = await getAttendeeFromDB(chimeAttendeeId);
const attendeeData = attendeeInfo.data.getAttendee;
return {
name: attendeeData.name
};
}
}
const clickedJoinMeeting = async (event: FormEvent) => {
event.preventDefault();
meetingManager.getAttendee = getAttendeeCallback();
const title = meetingTitle.trim().toLocaleLowerCase();
const name = attendeeName.trim();
// Fetch the Meeting via AWS AppSync - if it exists, then the meeting has already
// been created, and you just need to join it - you don't need to create a new meeting
const meetingResponse: any = await getMeetingFromDB(title);
const meetingJson = meetingResponse.data.getMeeting;
try {
if (meetingJson) {
const meetingData = JSON.parse(meetingJson.data);
const joinInfo = await joinMeeting(meetingData.MeetingId, name);
await addAttendeeToDB(joinInfo.Attendee.AttendeeId, name);
const meetingSessionConfiguration = new MeetingSessionConfiguration(
meetingData,
joinInfo.Attendee
);
await meetingManager.join(meetingSessionConfiguration);
} else {
const joinInfo = await createMeeting(title, name, 'us-east-1');
await addMeetingToDB(title, joinInfo.Meeting.MeetingId, JSON.stringify(joinInfo.Meeting)); await addAttendeeToDB(joinInfo.Attendee.AttendeeId, name);
const meetingSessionConfiguration = new MeetingSessionConfiguration(
joinInfo.Meeting,
joinInfo.Attendee
);
await meetingManager.join(meetingSessionConfiguration);
}
} catch (error) {
console.log(error);
}
// At this point you can let users setup their devices, or start the session immediately
await meetingManager.start();
};
return (
<form>
<FormField
field={Input}
label='Meeting Id'
value={meetingTitle}
fieldProps={{
name: 'Meeting Id',
placeholder: 'Enter a Meeting ID',
}}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setMeetingTitle(e.target.value);
}}
/>
<FormField
field={Input}
label="Name"
value={attendeeName}
fieldProps={{
name: 'Name',
placeholder: 'Enter your Attendee Name'
}}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
}}
/>
<Flex
container
layout="fill-space-centered"
style={{ marginTop: '2.5rem' }}
>
<PrimaryButton label="Join Meeting" onClick={clickedJoinMeeting} />
</Flex>
</form>
);
};
export default MeetingForm;
MeetingSessionConfiguration 関連の取り扱いが変わったらしく、V3対応に書き換える。
これでも、まだビルドが通らない。。。
index.tsx
Reactの最新版では、react-domからrenderが無くなってると。。。
そしてtypescriptのチェックが強固にかかりすぎてるので、
で、Nullチェックを回避する。
import { createRoot } from 'react-dom/client';
import { ThemeProvider } from 'styled-components';
import {
MeetingProvider,
lightTheme
} from 'amazon-chime-sdk-component-library-react';
import Meeting from './components/Meeting';
import MeetingForm from './components/MeetingForm';
import { Amplify } from 'aws-amplify';
import awsconfig from './aws-exports';
Amplify.configure(awsconfig);
const container = document.getElementById('root')!;
const root = createRoot(container);
window.addEventListener('load', () => {
root.render(
<ThemeProvider theme={lightTheme}>
<MeetingProvider>
<MeetingForm />
<Meeting/>
</MeetingProvider>
</ThemeProvider>
);
});
と、ここまでやって、最新版に対応したソースは、
配下にあった。諸々、手修正したものの、上記から最新版を適用することで、無事に動いた。
リージョン情報はハードコードされているソースが何個かあるので、動作させるリージョンに修正した。(メモ書き)