@@ -19,10 +19,16 @@ import com.adobe.testing.s3mock.dto.AccessControlPolicy
1919import com.adobe.testing.s3mock.dto.Bucket
2020import com.adobe.testing.s3mock.dto.CanonicalUser
2121import com.adobe.testing.s3mock.dto.ChecksumType
22+ import com.adobe.testing.s3mock.dto.Delete
23+ import com.adobe.testing.s3mock.dto.DeleteResult
24+ import com.adobe.testing.s3mock.dto.DeletedS3Object
25+ import com.adobe.testing.s3mock.dto.GetObjectAttributesOutput
2226import com.adobe.testing.s3mock.dto.Grant
27+ import com.adobe.testing.s3mock.dto.LegalHold
2328import com.adobe.testing.s3mock.dto.Mode
2429import com.adobe.testing.s3mock.dto.Owner
2530import com.adobe.testing.s3mock.dto.Retention
31+ import com.adobe.testing.s3mock.dto.S3ObjectIdentifier
2632import com.adobe.testing.s3mock.dto.StorageClass
2733import com.adobe.testing.s3mock.dto.Tag
2834import com.adobe.testing.s3mock.dto.TagSet
@@ -469,8 +475,9 @@ internal class ObjectControllerTest : BaseControllerTest() {
469475 )
470476 )
471477 val s3ObjectMetadata = s3ObjectMetadata(
472- key, UUID .randomUUID().toString(),
473- null , null , null , tagging.tagSet.tags
478+ key,
479+ UUID .randomUUID().toString(),
480+ tags = tagging.tagSet.tags
474481 )
475482 whenever(objectService.verifyObjectExists(" test-bucket" , key, null ))
476483 .thenReturn(s3ObjectMetadata)
@@ -538,8 +545,9 @@ internal class ObjectControllerTest : BaseControllerTest() {
538545 val instant = Instant .ofEpochMilli(1514477008120L )
539546 val retention = Retention (Mode .COMPLIANCE , instant)
540547 val s3ObjectMetadata = s3ObjectMetadata(
541- key, UUID .randomUUID().toString(),
542- null , null , retention, null
548+ key,
549+ UUID .randomUUID().toString(),
550+ retention = retention,
543551 )
544552 whenever(objectService.verifyObjectLockConfiguration(" test-bucket" , key, null ))
545553 .thenReturn(s3ObjectMetadata)
@@ -622,6 +630,172 @@ internal class ObjectControllerTest : BaseControllerTest() {
622630 assertThat(response.headers.eTag).isEqualTo(" \" $digest \" " )
623631 }
624632
633+ @Test
634+ fun testDeleteObjectTagging_NoContent () {
635+ givenBucket()
636+ val key = " name"
637+ val s3ObjectMetadata = s3ObjectMetadata(key, UUID .randomUUID().toString())
638+ whenever(objectService.verifyObjectExists(" test-bucket" , key, null )).thenReturn(s3ObjectMetadata)
639+
640+ val headers = HttpHeaders ().apply {
641+ this .accept = listOf (MediaType .APPLICATION_XML )
642+ }
643+ val uri = UriComponentsBuilder
644+ .fromUriString(" /test-bucket/$key " )
645+ .queryParam(AwsHttpParameters .TAGGING , " ignored" )
646+ .build()
647+ .toString()
648+
649+ val response = restTemplate.exchange(
650+ uri,
651+ HttpMethod .DELETE ,
652+ HttpEntity <Any >(headers),
653+ String ::class .java
654+ )
655+
656+ assertThat(response.statusCode).isEqualTo(HttpStatus .NO_CONTENT )
657+ verify(objectService).setObjectTags(" test-bucket" , key, null , null )
658+ }
659+
660+ @Test
661+ fun testGetLegalHold_Ok () {
662+ givenBucket()
663+ val key = " locked"
664+ val legalHold = LegalHold (LegalHold .Status .ON )
665+ val metadata = s3ObjectMetadata(
666+ key,
667+ UUID .randomUUID().toString(),
668+ legalHold = legalHold
669+ )
670+ whenever(objectService.verifyObjectExists(" test-bucket" , key, null )).thenReturn(metadata)
671+ whenever(objectService.verifyObjectLockConfiguration(" test-bucket" , key, null )).thenReturn(metadata)
672+
673+ val headers = HttpHeaders ().apply {
674+ this .accept = listOf (MediaType .APPLICATION_XML )
675+ this .contentType = MediaType .APPLICATION_XML
676+ }
677+ val uri = UriComponentsBuilder
678+ .fromUriString(" /test-bucket/$key " )
679+ .queryParam(AwsHttpParameters .LEGAL_HOLD , " ignored" )
680+ .build()
681+ .toString()
682+
683+ val response = restTemplate.exchange(
684+ uri,
685+ HttpMethod .GET ,
686+ HttpEntity <Any >(headers),
687+ String ::class .java
688+ )
689+
690+ assertThat(response.statusCode).isEqualTo(HttpStatus .OK )
691+ assertThat(response.body).isEqualTo(MAPPER .writeValueAsString(legalHold))
692+ }
693+
694+ @Test
695+ fun testPutLegalHold_Ok () {
696+ givenBucket()
697+ val key = " locked"
698+ val legalHold = LegalHold (LegalHold .Status .OFF )
699+
700+ val headers = HttpHeaders ().apply {
701+ this .accept = listOf (MediaType .APPLICATION_XML )
702+ this .contentType = MediaType .APPLICATION_XML
703+ }
704+ val uri = UriComponentsBuilder
705+ .fromUriString(" /test-bucket/$key " )
706+ .queryParam(AwsHttpParameters .LEGAL_HOLD , " ignored" )
707+ .build()
708+ .toString()
709+
710+ val response = restTemplate.exchange(
711+ uri,
712+ HttpMethod .PUT ,
713+ HttpEntity (MAPPER .writeValueAsString(legalHold), headers),
714+ String ::class .java
715+ )
716+
717+ assertThat(response.statusCode).isEqualTo(HttpStatus .OK )
718+ verify(objectService).setLegalHold(" test-bucket" , key, null , legalHold)
719+ }
720+
721+ @Test
722+ fun testGetObjectAttributes_Ok () {
723+ givenBucket()
724+ val key = " attrs.txt"
725+ val testFile = File (UPLOAD_FILE_NAME )
726+ val hex = DigestUtil .hexDigest(Files .newInputStream(testFile.toPath()))
727+ val metadata = s3ObjectMetadata(key, hex)
728+ whenever(objectService.verifyObjectExists(" test-bucket" , key, null )).thenReturn(metadata)
729+
730+ val headers = HttpHeaders ().apply {
731+ this .accept = listOf (MediaType .APPLICATION_XML )
732+ this .contentType = MediaType .APPLICATION_XML
733+ this .add(AwsHttpHeaders .X_AMZ_OBJECT_ATTRIBUTES , " ETag,Checksum,ObjectSize,StorageClass" )
734+ }
735+ val uri = UriComponentsBuilder
736+ .fromUriString(" /test-bucket/$key " )
737+ .queryParam(AwsHttpParameters .ATTRIBUTES , " ignored" )
738+ .build()
739+ .toString()
740+
741+ val response = restTemplate.exchange(
742+ uri,
743+ HttpMethod .GET ,
744+ HttpEntity <Any >(headers),
745+ String ::class .java
746+ )
747+
748+ val expected = GetObjectAttributesOutput (
749+ null ,
750+ hex,
751+ null ,
752+ testFile.length(),
753+ StorageClass .STANDARD
754+ )
755+ assertThat(response.statusCode).isEqualTo(HttpStatus .OK )
756+ assertThat(response.body).isEqualTo(MAPPER .writeValueAsString(expected))
757+ }
758+
759+ @Test
760+ fun testDeleteObjects_Ok () {
761+ givenBucket()
762+ val body = Delete (
763+ listOf (
764+ S3ObjectIdentifier (" a" , " etag" , " 0" , " 1" , " v1" ),
765+ S3ObjectIdentifier (" b" , " etag2" , " 0" , " 2" , " v2" )
766+ ),
767+ false
768+ )
769+ val expected = DeleteResult (
770+ emptyList(),
771+ listOf (
772+ DeletedS3Object (null , null , " a" , " v1" ),
773+ DeletedS3Object (null , null , " b" , " v2" )
774+ )
775+ )
776+ whenever(objectService.deleteObjects(" test-bucket" , body)).thenReturn(expected)
777+
778+ val headers = HttpHeaders ().apply {
779+ this .accept = listOf (MediaType .APPLICATION_XML )
780+ this .contentType = MediaType .APPLICATION_XML
781+ }
782+ val uri = UriComponentsBuilder
783+ .fromUriString(" /test-bucket" )
784+ .queryParam(AwsHttpParameters .DELETE , " ignored" )
785+ .build()
786+ .toString()
787+
788+ val response = restTemplate.exchange(
789+ uri,
790+ HttpMethod .POST ,
791+ HttpEntity (MAPPER .writeValueAsString(body), headers),
792+ String ::class .java
793+ )
794+
795+ assertThat(response.statusCode).isEqualTo(HttpStatus .OK )
796+ assertThat(response.body).isEqualTo(MAPPER .writeValueAsString(expected))
797+ }
798+
625799 private fun givenBucket () {
626800 whenever(bucketService.getBucket(TEST_BUCKET_NAME )).thenReturn(TEST_BUCKET )
627801 whenever(bucketService.doesBucketExist(TEST_BUCKET_NAME )).thenReturn(true )
@@ -649,7 +823,7 @@ internal class ObjectControllerTest : BaseControllerTest() {
649823 id : String , digest : String , encryption : String? , encryptionKey : String?
650824 ): S3ObjectMetadata {
651825 return s3ObjectMetadata(
652- id, digest, encryption, encryptionKey, null , null
826+ id, digest, encryption, encryptionKey,
653827 )
654828 }
655829
@@ -660,7 +834,8 @@ internal class ObjectControllerTest : BaseControllerTest() {
660834 encryption : String? = null,
661835 encryptionKey : String? = null,
662836 retention : Retention ? = null,
663- tags : List <Tag >? = null
837+ tags : List <Tag >? = null,
838+ legalHold : LegalHold ? = null
664839 ): S3ObjectMetadata {
665840 return S3ObjectMetadata (
666841 UUID .randomUUID(),
@@ -673,7 +848,7 @@ internal class ObjectControllerTest : BaseControllerTest() {
673848 Path .of(UPLOAD_FILE_NAME ),
674849 null ,
675850 tags,
676- null ,
851+ legalHold ,
677852 retention,
678853 Owner .DEFAULT_OWNER ,
679854 null ,
0 commit comments