Commit d68ca848 authored by Johnny's avatar Johnny

fix: delete unused attachments by using filter

parent 4b110d0d
...@@ -207,6 +207,17 @@ func (r *renderer) renderScalarComparison(field Field, op ComparisonOperator, ri ...@@ -207,6 +207,17 @@ func (r *renderer) renderScalarComparison(field Field, op ComparisonOperator, ri
} }
columnExpr := field.columnExpr(r.dialect) columnExpr := field.columnExpr(r.dialect)
if lit == nil {
switch op {
case CompareEq:
return renderResult{sql: fmt.Sprintf("%s IS NULL", columnExpr)}, nil
case CompareNeq:
return renderResult{sql: fmt.Sprintf("%s IS NOT NULL", columnExpr)}, nil
default:
return renderResult{}, errors.Errorf("operator %s not supported for null comparison", op)
}
}
placeholder := "" placeholder := ""
switch field.Type { switch field.Type {
case FieldTypeString: case FieldTypeString:
......
...@@ -297,7 +297,7 @@ func NewAttachmentSchema() Schema { ...@@ -297,7 +297,7 @@ func NewAttachmentSchema() Schema {
cel.Variable("filename", cel.StringType), cel.Variable("filename", cel.StringType),
cel.Variable("mime_type", cel.StringType), cel.Variable("mime_type", cel.StringType),
cel.Variable("create_time", cel.IntType), cel.Variable("create_time", cel.IntType),
cel.Variable("memo_id", cel.IntType), cel.Variable("memo_id", cel.AnyType),
nowFunction, nowFunction,
} }
......
...@@ -328,9 +328,18 @@ func TestAttachmentFilterNullMemoId(t *testing.T) { ...@@ -328,9 +328,18 @@ func TestAttachmentFilterNullMemoId(t *testing.T) {
tc.CreateAttachment(NewAttachmentBuilder(tc.CreatorID).Filename("with_memo.png").MimeType("image/png").MemoID(&memo.ID)) tc.CreateAttachment(NewAttachmentBuilder(tc.CreatorID).Filename("with_memo.png").MimeType("image/png").MemoID(&memo.ID))
tc.CreateAttachment(NewAttachmentBuilder(tc.CreatorID).Filename("no_memo.png").MimeType("image/png")) tc.CreateAttachment(NewAttachmentBuilder(tc.CreatorID).Filename("no_memo.png").MimeType("image/png"))
attachments := tc.ListWithFilter(`memo_id == ` + formatInt32(memo.ID)) // Test: memo_id == null
attachments := tc.ListWithFilter(`memo_id == null`)
require.Len(t, attachments, 1)
require.Equal(t, "no_memo.png", attachments[0].Filename)
require.Nil(t, attachments[0].MemoID)
// Test: memo_id != null
attachments = tc.ListWithFilter(`memo_id != null`)
require.Len(t, attachments, 1) require.Len(t, attachments, 1)
require.Equal(t, "with_memo.png", attachments[0].Filename) require.Equal(t, "with_memo.png", attachments[0].Filename)
require.NotNil(t, attachments[0].MemoID)
require.Equal(t, memo.ID, *attachments[0].MemoID)
} }
func TestAttachmentFilterEmptyFilename(t *testing.T) { func TestAttachmentFilterEmptyFilename(t *testing.T) {
......
...@@ -156,7 +156,19 @@ const Attachments = () => { ...@@ -156,7 +156,19 @@ const Attachments = () => {
// Delete all unused attachments // Delete all unused attachments
const handleDeleteUnusedAttachments = useCallback(async () => { const handleDeleteUnusedAttachments = useCallback(async () => {
try { try {
await Promise.all(unusedAttachments.map((attachment) => deleteAttachment(attachment.name))); let allUnusedAttachments: Attachment[] = [];
let nextPageToken = "";
do {
const response = await attachmentServiceClient.listAttachments({
pageSize: 1000,
pageToken: nextPageToken,
filter: "memo_id == null",
});
allUnusedAttachments = [...allUnusedAttachments, ...response.attachments];
nextPageToken = response.nextPageToken;
} while (nextPageToken);
await Promise.all(allUnusedAttachments.map((attachment) => deleteAttachment(attachment.name)));
toast.success(t("resource.delete-all-unused-success")); toast.success(t("resource.delete-all-unused-success"));
} catch (error) { } catch (error) {
handleError(error, toast.error, { handleError(error, toast.error, {
...@@ -166,7 +178,7 @@ const Attachments = () => { ...@@ -166,7 +178,7 @@ const Attachments = () => {
} finally { } finally {
await handleRefetch(); await handleRefetch();
} }
}, [unusedAttachments, t, handleRefetch, deleteAttachment]); }, [t, handleRefetch, deleteAttachment]);
// Handle search input change // Handle search input change
const handleSearchChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { const handleSearchChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment