forked from scratchfoundation/scratch-gui
-
-
Notifications
You must be signed in to change notification settings - Fork 16
Closed
Description
目的
グループの作成、参加、検索機能を実装します(createGroup, joinGroup, listGroupsByDomain)。Domain概念に対応し、グローバルIPベースのスコープ管理を実現します。
タスク
listGroupsByDomain Queryリゾルバー(スキャン機能)
- DynamoDB DataSourceの定義
- リクエストハンドラー (JS) の実装
- Domain自動取得:
ctx.identity.sourceIpまたはctx.args.domain - DynamoDB Query:
PK = DOMAIN#{domain},SK begins_with GROUP#
- Domain自動取得:
- レスポンスハンドラーの実装
- Group型の配列を返却
- Resolverの登録
createGroup Mutationリゾルバー
- リクエストハンドラー (JS) の実装
- Domain自動取得:
ctx.args.domainまたはctx.identity.sourceIp - グループID生成:
util.autoId() - fullId生成:
{group_id}@{domain} - Groupメタデータの作成
PK: DOMAIN#{domain},SK: GROUP#{group_id}#METADATA- 属性:
id,domain,fullId,name,hostId,createdAt
- Domain自動取得:
- レスポンスハンドラーの実装
- Group型を返却
- Resolverの登録
joinGroup Mutationリゾルバー
- リクエストハンドラー (JS) の実装
- Nodeの追加
PK: DOMAIN#{domain},SK: GROUP#{group_id}#NODE#{node_id}
- Node所属情報の作成
PK: NODE#{node_id},SK: METADATA- 属性:
nodeId,groupId,domain
- TransactWriteItemsでアトミックに実行
- Nodeの追加
- レスポンスハンドラーの実装
- Node型を返却
- Resolverの登録
テスト
- listGroupsByDomain動作確認
- Domain指定あり
- Domain指定なし(グローバルIP自動取得)
- createGroup動作確認
- fullIdの正しい生成
- joinGroup動作確認
- エラーハンドリング確認
成果物
- JSリゾルバーファイル
js/resolvers/Query.listGroupsByDomain.req.jsjs/resolvers/Query.listGroupsByDomain.res.jsjs/resolvers/Mutation.createGroup.req.jsjs/resolvers/Mutation.createGroup.res.jsjs/resolvers/Mutation.joinGroup.req.jsjs/resolvers/Mutation.joinGroup.res.js
- CDK Resolver定義コード
リゾルバー実装例
listGroupsByDomain リクエスト
// js/resolvers/Query.listGroupsByDomain.req.js
function request(ctx) {
// Domain決定: 引数 > ソースIP
const sourceIp = ctx.identity.sourceIp[0];
const domain = ctx.args.domain || sourceIp;
return {
operation: 'Query',
query: {
expression: 'pk = :pk AND begins_with(sk, :sk_prefix)',
expressionValues: {
':pk': { S: `DOMAIN#${domain}` },
':sk_prefix': { S: 'GROUP#' }
}
}
};
}listGroupsByDomain レスポンス
// js/resolvers/Query.listGroupsByDomain.res.js
function response(ctx) {
if (ctx.error) {
util.error(ctx.error.message, ctx.error.type);
}
// DynamoDBアイテムをGroup型に変換
return ctx.result.items
.filter(item => item.sk.S.endsWith('#METADATA'))
.map(item => ({
id: item.id.S,
domain: item.domain.S,
fullId: item.fullId.S,
name: item.name.S,
hostId: item.hostId.S,
createdAt: item.createdAt.S
}));
}createGroup リクエスト
// js/resolvers/Mutation.createGroup.req.js
function request(ctx) {
const { name, hostId, domain } = ctx.args;
// Domain決定: 引数 > ソースIP
const sourceIp = ctx.identity.sourceIp[0];
const actualDomain = domain || sourceIp;
// Domain文字列のバリデーション(最大256文字)
if (actualDomain.length > 256) {
util.error('Domain must be 256 characters or less', 'ValidationError');
}
// グループID生成
const groupId = util.autoId();
const fullId = `${groupId}@${actualDomain}`;
const now = util.time.nowISO8601();
return {
operation: 'PutItem',
key: {
pk: { S: `DOMAIN#${actualDomain}` },
sk: { S: `GROUP#${groupId}#METADATA` }
},
attributeValues: {
id: { S: groupId },
domain: { S: actualDomain },
fullId: { S: fullId },
name: { S: name },
hostId: { S: hostId },
createdAt: { S: now },
// GSI用
gsi_pk: { S: `GROUP#${groupId}` },
gsi_sk: { S: `DOMAIN#${actualDomain}` }
}
};
}createGroup レスポンス
// js/resolvers/Mutation.createGroup.res.js
function response(ctx) {
if (ctx.error) {
util.error(ctx.error.message, ctx.error.type);
}
// 入力引数とソースIPからGroup型を構築
const sourceIp = ctx.identity.sourceIp[0];
const domain = ctx.args.domain || sourceIp;
const groupId = ctx.result.Attributes.id.S;
return {
id: groupId,
domain: domain,
fullId: `${groupId}@${domain}`,
name: ctx.args.name,
hostId: ctx.args.hostId,
createdAt: ctx.result.Attributes.createdAt.S
};
}joinGroup リクエスト(TransactWriteItems)
// js/resolvers/Mutation.joinGroup.req.js
function request(ctx) {
const { groupId, domain, nodeId } = ctx.args;
const now = util.time.nowISO8601();
return {
operation: 'TransactWriteItems',
transactItems: [
// Node情報の追加
{
table: 'MeshV2Table',
operation: 'PutItem',
key: {
pk: { S: `DOMAIN#${domain}` },
sk: { S: `GROUP#${groupId}#NODE#${nodeId}` }
},
attributeValues: {
nodeId: { S: nodeId },
groupId: { S: groupId },
domain: { S: domain },
name: { S: `Node ${nodeId}` },
data: { L: [] },
timestamp: { S: now }
}
},
// Node所属情報の作成
{
table: 'MeshV2Table',
operation: 'PutItem',
key: {
pk: { S: `NODE#${nodeId}` },
sk: { S: 'METADATA' }
},
attributeValues: {
nodeId: { S: nodeId },
groupId: { S: groupId },
domain: { S: domain }
}
}
]
};
}関連
- EPIC Issue: EPIC: Mesh v2 拡張機能の実装 #444
- Phase: 2 (バックエンドロジック)
- 依存: Phase 1-3: AppSync GraphQL APIとスキーマの実装 #448 (Phase 1-3)
- 更新: Domain概念の導入、listGroupsByDomain追加(Phase 0レビューフィードバック)
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Type
Projects
Status
Done