Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lambda/admin_portal/portal_page/link_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ def _parse_request_data(cls, domain: str, _body: str) -> Optional[DelibirdLink]:
status=HTTPStatus(int(body["status"])),
disabled=bool(body["disabled"]),

memo=str(body["memo"]) if "memo" in body else "",
tag=set(body["tag"]) if "tag" in body else None,
expiration_date=as_jst(datetime.fromisoformat(str(body["expiration_date"]))) if "expiration_date" in body else None,
expired_origin=str(body["expired_origin"]) if "expired_origin" in body else None,
query_omit=bool(body["query_omit"]),
Expand Down Expand Up @@ -78,6 +80,8 @@ def perform(cls, domain: str, event: APIGatewayProxyEvent):
disabled=link_data.disabled,
uses=0,

memo=link_data.memo,
tag=link_data.tag,
expiration_date=link_data.expiration_date,
expired_origin=link_data.expired_origin,
query_omit=link_data.query_omit,
Expand Down
4 changes: 4 additions & 0 deletions lambda/admin_portal/portal_page/link_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def _parse_request_data(cls, domain: str, _body: str) -> Optional[DelibirdLink]:
status=HTTPStatus(int(body["status"])),
disabled=bool(body["disabled"]),

memo=str(body["memo"]) if "memo" in body else "",
tag=set(body["tag"]) if "tag" in body else None,
expiration_date=as_jst(datetime.fromisoformat(str(body["expiration_date"]))) if "expiration_date" in body else None,
expired_origin=str(body["expired_origin"]) if "expired_origin" in body else None,
query_omit=bool(body["query_omit"]),
Expand Down Expand Up @@ -71,6 +73,8 @@ def perform(cls, domain: str, event: APIGatewayProxyEvent):
DelibirdLinkTableModel.origin.set(link_data.link_origin),
DelibirdLinkTableModel.status.set(int(link_data.status)),
DelibirdLinkTableModel.disabled.set(link_data.disabled),
DelibirdLinkTableModel.memo.set(link_data.memo),
DelibirdLinkTableModel.tag.set(link_data.tag),
DelibirdLinkTableModel.expiration_date.set(link_data.expiration_date),
DelibirdLinkTableModel.expired_origin.set(link_data.expired_origin),
DelibirdLinkTableModel.query_omit.set(link_data.query_omit),
Expand Down
78 changes: 78 additions & 0 deletions lambda/admin_portal/static/links.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,20 @@ <h5 class="mb-0"><i class="bi bi-table"></i> Link Details</h5>
{{ link.link_slug }}
</a>
</strong>
{% if link.memo %}
<i class="bi bi-sticky-fill text-warning ms-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-html="true"
title="{{ link.memo|replace('"', '&quot;')|replace('\n', '<br>') }}"></i>
{% endif %}
{% if link.tag %}
<i class="bi bi-tags-fill text-info ms-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-html="true"
title="{{ link.tag|join(', ') }}"></i>
{% endif %}
</td>

<!-- Origin -->
Expand Down Expand Up @@ -189,6 +203,8 @@ <h5 class="mb-0"><i class="bi bi-table"></i> Link Details</h5>
data-expired-origin="{{ link.expired_origin or '' }}"
data-query-omit="{{ 'true' if link.query_omit else 'false' }}"
data-query-whitelist="{{ link.query_whitelist|join(',') if link.query_whitelist else '' }}"
data-memo="{{ link.memo or '' }}"
data-tag="{{ link.tag|join(',') if link.tag else '' }}"
data-bs-toggle="modal"
data-bs-target="#editLinkModal">
<i class="bi bi-pencil"></i>
Expand Down Expand Up @@ -303,6 +319,22 @@ <h5 class="modal-title" id="createLinkModalLabel">
placeholder="https://example.com/expired">
<div class="form-text">期限切れ時のリダイレクト先URL(オプション)</div>
</div>

<!-- Memo -->
<div class="mb-3">
<label for="memo" class="form-label">Memo</label>
<textarea class="form-control" id="memo" name="memo" rows="3"
placeholder="リンクに関するメモを入力"></textarea>
<div class="form-text">このリンクに関するメモ(オプション)</div>
</div>

<!-- Tag -->
<div class="mb-3">
<label for="tag" class="form-label">Tag</label>
<input type="text" class="form-control" id="tag" name="tag"
placeholder="tag1,tag2,tag3">
<div class="form-text">カンマ区切りでタグを入力(オプション)</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Expand Down Expand Up @@ -411,6 +443,22 @@ <h5 class="modal-title" id="editLinkModalLabel">
placeholder="https://example.com/expired" autocomplete="off">
<div class="form-text">期限切れ時のリダイレクト先URL(オプション)</div>
</div>

<!-- Memo -->
<div class="mb-3">
<label for="editMemo" class="form-label">Memo</label>
<textarea class="form-control" id="editMemo" name="memo" rows="3"
placeholder="リンクに関するメモを入力" autocomplete="off"></textarea>
<div class="form-text">このリンクに関するメモ(オプション)</div>
</div>

<!-- Tag -->
<div class="mb-3">
<label for="editTag" class="form-label">Tag</label>
<input type="text" class="form-control" id="editTag" name="tag"
placeholder="tag1,tag2,tag3" autocomplete="off">
<div class="form-text">カンマ区切りでタグを入力(オプション)</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Expand Down Expand Up @@ -438,6 +486,12 @@ <h5 class="modal-title" id="editLinkModalLabel">
<script{% if script_nonce %} nonce="{{ script_nonce }}"{% endif %}>
// Query Omit チェックボックスの制御
document.addEventListener('DOMContentLoaded', function() {
// Bootstrap ツールチップの初期化
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
const tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});

const queryOmitCheckbox = document.getElementById('queryOmit');
const queryWhitelistContainer = document.getElementById('queryWhitelistContainer');
const queryWhitelistInput = document.getElementById('queryWhitelist');
Expand Down Expand Up @@ -512,6 +566,16 @@ <h5 class="modal-title" id="editLinkModalLabel">
data.expired_origin = expiredOrigin;
}

const memo = document.getElementById('memo').value;
if (memo) {
data.memo = memo;
}

const tag = document.getElementById('tag').value;
if (tag) {
data.tag = tag.split(',').map(s => s.trim()).filter(s => s);
}

// query_omitがfalseの場合のみ、query_whitelistを送信
if (!queryOmitCheckbox.checked) {
const queryWhitelist = document.getElementById('queryWhitelist').value;
Expand Down Expand Up @@ -587,6 +651,8 @@ <h5 class="modal-title" id="editLinkModalLabel">
const expiredOrigin = this.getAttribute('data-expired-origin');
const queryOmit = this.getAttribute('data-query-omit') === 'true';
const queryWhitelist = this.getAttribute('data-query-whitelist');
const memo = this.getAttribute('data-memo');
const tag = this.getAttribute('data-tag');

// フォームに値を設定
document.getElementById('editLinkSlug').value = slug;
Expand All @@ -598,6 +664,8 @@ <h5 class="modal-title" id="editLinkModalLabel">
document.getElementById('editExpiredOrigin').value = expiredOrigin;
document.getElementById('editQueryOmit').checked = queryOmit;
document.getElementById('editQueryWhitelist').value = queryWhitelist;
document.getElementById('editMemo').value = memo;
document.getElementById('editTag').value = tag;

// Query Whitelist の状態を更新
updateEditQueryWhitelistState();
Expand Down Expand Up @@ -644,6 +712,16 @@ <h5 class="modal-title" id="editLinkModalLabel">
data.expired_origin = expiredOrigin;
}

const memo = document.getElementById('editMemo').value;
if (memo) {
data.memo = memo;
}

const tag = document.getElementById('editTag').value;
if (tag) {
data.tag = tag.split(',').map(s => s.trim()).filter(s => s);
}

// query_omitがfalseの場合のみ、query_whitelistを送信
if (!editQueryOmitCheckbox.checked) {
const queryWhitelist = editQueryWhitelistInput.value;
Expand Down
9 changes: 9 additions & 0 deletions lambda/layers/common/python/ddb/models/delibird_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class DelibirdLink:
disabled: bool = False
uses: int = 0

memo: str = ""
tag: set[str] = None

expiration_date: Optional[datetime] = None
expired_origin: Optional[str] = None

Expand All @@ -55,6 +58,8 @@ class DelibirdLink:
def __post_init__(self):
if self.query_whitelist is None:
self.query_whitelist = set()
if self.tag is None:
self.tag = set()
Comment on lines 58 to +62

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid writing empty tag sets to DynamoDB

When the request omits tags (the usual case for new links), _parse_request_data leaves tag as None, but DelibirdLink.__post_init__ turns that into an empty set and PortalLinkCreate/Update always write that value to DelibirdLinkTableModel.tag. DynamoDB/PynamoDB reject empty sets (UnicodeSetAttribute serialization raises a validation error), so creating or updating a link without tags will now fail. Preserve None or skip persisting when the tag list is empty so blank-tag submissions continue to succeed.

Useful? React with 👍 / 👎.


def check_active(self) -> tuple[bool, Optional[DelibirdLinkInactiveReason]]:
if self.disabled:
Expand Down Expand Up @@ -95,6 +100,8 @@ def from_model(model: "DelibirdLinkTableModel") -> "DelibirdLink":
status=HTTPStatus(model.status),
disabled=model.disabled,
uses=int(model.uses),
memo=model.memo,
tag=set(model.tag) if model.tag is not None else None,
expiration_date=as_jst(model.expiration_date) if model.expiration_date is not None else None,
expired_origin=model.expired_origin,
query_omit=model.query_omit,
Expand All @@ -116,6 +123,8 @@ class Meta:
status = NumberAttribute(null=False)
disabled = BooleanAttribute(null=False, default=False)
uses = NumberAttribute(null=False, default=0)
memo = UnicodeAttribute(null=False, default="")
tag = UnicodeSetAttribute(null=True)

expiration_date = DateTimeAttribute(null=True)
expired_origin = UnicodeAttribute(null=True)
Expand Down
2 changes: 2 additions & 0 deletions modules/aws_iam/lambda_admin_portal.tf
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ resource "aws_iam_role_policy" "lambda_admin_portal" {
"status",
"max_uses",
"disabled",
"memo",
"tag",
"expiration_date",
"expired_origin",
"query_omit",
Expand Down