@@ -11,6 +11,7 @@ import (
1111
1212 "github.com/aws/aws-sdk-go-v2/aws"
1313 "github.com/aws/aws-sdk-go-v2/service/s3"
14+ "github.com/aws/aws-sdk-go-v2/service/s3/types"
1415 "github.com/google/uuid"
1516 "github.com/indeedeng/iwf/config"
1617 "github.com/indeedeng/iwf/service/common/log"
@@ -81,7 +82,7 @@ func (b *blobStoreImpl) WriteObject(ctx context.Context, workflowId, data string
8182 storeId = b .activeStorage .StorageId
8283 randomUuid := uuid .New ().String ()
8384 yyyymmdd := time .Now ().Format ("20060102" )
84- // yymmdd $workflowId/uuid
85+ // yyyymmdd $workflowId/uuid
8586 // Note: using $ here so that the listing can be much easier to implement for pagination
8687 path = fmt .Sprintf ("%s$%s/%s" , yyyymmdd , workflowId , randomUuid )
8788
@@ -155,12 +156,122 @@ func (b *blobStoreImpl) CountWorkflowObjectsForTesting(ctx context.Context, work
155156 return int64 (len (result .Contents )), nil
156157}
157158
158- func (b * blobStoreImpl ) DeleteObjectPath (ctx context.Context , storeId , path string ) error {
159- //TODO implement me
160- panic ("implement me" )
159+ func (b * blobStoreImpl ) DeleteWorkflowObjects (ctx context.Context , storeId , workflowPath string ) error {
160+ storeConfig , ok := b .supportedStore [storeId ]
161+ if ! ok {
162+ return errors .New ("store not found for " + storeId )
163+ }
164+
165+ // Construct the prefix for all objects of this workflow
166+ prefix := fmt .Sprintf ("%s%s/" , b .pathPrefix , workflowPath )
167+
168+ // Paginate through all objects and delete them in batches
169+ var continuationToken * string
170+ for {
171+ listInput := & s3.ListObjectsV2Input {
172+ Bucket : aws .String (storeConfig .S3Bucket ),
173+ Prefix : aws .String (prefix ),
174+ }
175+
176+ if continuationToken != nil {
177+ listInput .ContinuationToken = continuationToken
178+ }
179+
180+ listResult , err := b .s3Client .ListObjectsV2 (ctx , listInput )
181+ if err != nil {
182+ return fmt .Errorf ("failed to list objects for deletion: %w" , err )
183+ }
184+
185+ // If no objects found, we're done
186+ if len (listResult .Contents ) == 0 {
187+ break
188+ }
189+
190+ // Prepare objects for batch deletion
191+ var objectsToDelete []types.ObjectIdentifier
192+ for _ , obj := range listResult .Contents {
193+ if obj .Key != nil {
194+ objectsToDelete = append (objectsToDelete , types.ObjectIdentifier {
195+ Key : obj .Key ,
196+ })
197+ }
198+ }
199+
200+ // Delete objects in batch
201+ if len (objectsToDelete ) > 0 {
202+ deleteResult , err := b .s3Client .DeleteObjects (ctx , & s3.DeleteObjectsInput {
203+ Bucket : aws .String (storeConfig .S3Bucket ),
204+ Delete : & types.Delete {
205+ Objects : objectsToDelete ,
206+ Quiet : aws .Bool (true ), // Don't return successful deletions
207+ },
208+ })
209+ if err != nil {
210+ return fmt .Errorf ("failed to delete objects: %w" , err )
211+ }
212+
213+ // Check for any delete errors
214+ if len (deleteResult .Errors ) > 0 {
215+ var errorMsgs []string
216+ for _ , delErr := range deleteResult .Errors {
217+ if delErr .Key != nil && delErr .Code != nil && delErr .Message != nil {
218+ errorMsgs = append (errorMsgs , fmt .Sprintf ("key=%s, code=%s, message=%s" ,
219+ * delErr .Key , * delErr .Code , * delErr .Message ))
220+ }
221+ }
222+ return fmt .Errorf ("some objects failed to delete: %s" , strings .Join (errorMsgs , "; " ))
223+ }
224+ }
225+
226+ // Check if there are more objects to process
227+ if listResult .IsTruncated == nil || ! * listResult .IsTruncated {
228+ break
229+ }
230+ continuationToken = listResult .NextContinuationToken
231+ }
232+
233+ return nil
161234}
162235
163- func (b * blobStoreImpl ) ListObjectPaths (ctx context.Context , input ListObjectPathsInput ) (* ListObjectPathsOutput , error ) {
164- //TODO implement me
165- panic ("implement me" )
236+ func (b * blobStoreImpl ) ListWorkflowPaths (ctx context.Context , input ListObjectPathsInput ) (* ListObjectPathsOutput , error ) {
237+ storeConfig , ok := b .supportedStore [input .StoreId ]
238+ if ! ok {
239+ return nil , errors .New ("store not found for " + input .StoreId )
240+ }
241+
242+ listInput := & s3.ListObjectsV2Input {
243+ Bucket : aws .String (storeConfig .S3Bucket ),
244+ Prefix : aws .String (b .pathPrefix ),
245+ Delimiter : aws .String ("/" ),
246+ }
247+
248+ // Set continuation token if provided
249+ if input .ContinuationToken != nil {
250+ listInput .ContinuationToken = input .ContinuationToken
251+ }
252+
253+ result , err := b .s3Client .ListObjectsV2 (ctx , listInput )
254+ if err != nil {
255+ return nil , err
256+ }
257+
258+ // Extract workflow paths from common prefixes
259+ workflowPaths := make ([]string , 0 , len (result .CommonPrefixes ))
260+ for _ , commonPrefix := range result .CommonPrefixes {
261+ if commonPrefix .Prefix != nil {
262+ // Remove the pathPrefix to get the workflow path (yyyymmdd$workflowId)
263+ prefixStr := * commonPrefix .Prefix
264+ if strings .HasPrefix (prefixStr , b .pathPrefix ) {
265+ workflowPath := strings .TrimPrefix (prefixStr , b .pathPrefix )
266+ // Remove trailing "/" if present
267+ workflowPath = strings .TrimSuffix (workflowPath , "/" )
268+ workflowPaths = append (workflowPaths , workflowPath )
269+ }
270+ }
271+ }
272+
273+ return & ListObjectPathsOutput {
274+ ContinuationToken : result .NextContinuationToken ,
275+ WorkflowPaths : workflowPaths ,
276+ }, nil
166277}
0 commit comments