2525import java .nio .charset .StandardCharsets ;
2626import java .util .Collection ;
2727import java .util .Map ;
28+ import java .util .Optional ;
2829
2930import org .reactivestreams .Publisher ;
3031
32+ import com .google .common .base .CharMatcher ;
33+ import com .google .common .base .Preconditions ;
3134import com .google .common .collect .ImmutableMap ;
3235import com .google .common .io .ByteSource ;
3336import com .google .common .io .FileBackedOutputStream ;
3437
3538public interface BlobStoreDAO {
3639 record BlobMetadataName (String name ) {
37- // TODO validation (a-z,A-Z,0-9 -_) &length 128 char & non empty
40+ private static final CharMatcher CHAR_MATCHER = CharMatcher .inRange ('a' , 'z' )
41+ .and (CharMatcher .inRange ('A' , 'Z' ))
42+ .and (CharMatcher .inRange ('0' , '9' ))
43+ .and (CharMatcher .is ('-' ));
44+
45+ public BlobMetadataName {
46+ Preconditions .checkArgument (CHAR_MATCHER .matchesAllOf (name ), "Invalid char in metadata name. Must be a-z,A-Z,0-9 or - got " + name );
47+ Preconditions .checkArgument (name .length () < 128 , "Metadata name is too long. Size exceed 128 chars" );
48+ }
3849 }
50+
3951 record BlobMetadataValue (String value ) {
52+ public BlobMetadataValue {
53+ Preconditions .checkArgument (value .length () < 128 , "Metadata value is too long. Size exceed 128 chars" );
54+ }
55+ }
56+
57+ record ContentTransferEncoding (String value ) {
58+ public static BlobMetadataName NAME = new BlobMetadataName ("content-transfer-encoding" );
59+ public static ContentTransferEncoding ZSTD = new ContentTransferEncoding ("zstd" );
60+
61+ public static ContentTransferEncoding fromValue (BlobMetadataValue value ) {
62+ return new ContentTransferEncoding (value .value ());
63+ }
64+
65+ public ContentTransferEncoding {
66+ Preconditions .checkArgument (value .length () < 128 , "ContentTransferEncoding value is too long. Size exceed 128 chars" );
67+ }
68+
69+ public BlobMetadataValue asValue () {
70+ return new BlobMetadataValue (value );
71+ }
4072
4173 }
4274
75+ record BlobMetadata (Map <BlobMetadataName , BlobMetadataValue > metadata ) {
76+ public static BlobMetadata empty () {
77+ return new BlobMetadata (ImmutableMap .of ());
78+ }
79+
80+ public BlobMetadata withMetadata (BlobMetadataName name , BlobMetadataValue value ) {
81+ return new BlobMetadata (ImmutableMap .<BlobMetadataName , BlobMetadataValue >builder ()
82+ .putAll (metadata )
83+ .put (name , value )
84+ .build ());
85+ }
86+
87+ public Optional <ContentTransferEncoding > contentTransferEncoding () {
88+ return Optional .ofNullable (metadata .get (ContentTransferEncoding .NAME )).map (ContentTransferEncoding ::fromValue );
89+ }
90+
91+ public BlobMetadata withContentTransferEncoding (ContentTransferEncoding contentTransferEncoding ) {
92+ return withMetadata (ContentTransferEncoding .NAME , contentTransferEncoding .asValue ());
93+ }
94+ }
95+
96+
4397 sealed interface Blob {
44- Map < BlobMetadataName , BlobMetadataValue > metadata ();
98+ BlobMetadata metadata ();
4599
46100 // Have the POJOs encode some conversions ?
47101 InputStreamBlob asInputStream () throws IOException ;
@@ -51,16 +105,16 @@ sealed interface Blob {
51105 ByteSourceBlob asByteSource () throws IOException ;
52106 }
53107
54- record BytesBlob (byte [] payload , Map < BlobMetadataName , BlobMetadataValue > metadata ) implements Blob {
108+ record BytesBlob (byte [] payload , BlobMetadata metadata ) implements Blob {
55109 public static BytesBlob of (byte [] payload ) {
56- return of (payload , ImmutableMap . of ());
110+ return of (payload , BlobMetadata . empty ());
57111 }
58112
59113 public static BytesBlob of (String payload ) {
60- return of (payload .getBytes (StandardCharsets .UTF_8 ), ImmutableMap . of ());
114+ return of (payload .getBytes (StandardCharsets .UTF_8 ), BlobMetadata . empty ());
61115 }
62116
63- public static BytesBlob of (byte [] payload , Map < BlobMetadataName , BlobMetadataValue > metadata ) {
117+ public static BytesBlob of (byte [] payload , BlobMetadata metadata ) {
64118 return new BytesBlob (payload , metadata );
65119 }
66120
@@ -80,12 +134,12 @@ public ByteSourceBlob asByteSource() {
80134 }
81135 }
82136
83- record InputStreamBlob (InputStream payload , Map < BlobMetadataName , BlobMetadataValue > metadata ) implements Blob {
137+ record InputStreamBlob (InputStream payload , BlobMetadata metadata ) implements Blob {
84138 public static InputStreamBlob of (InputStream payload ) {
85- return of (payload , ImmutableMap . of ());
139+ return of (payload , BlobMetadata . empty ());
86140 }
87141
88- public static InputStreamBlob of (InputStream payload , Map < BlobMetadataName , BlobMetadataValue > metadata ) {
142+ public static InputStreamBlob of (InputStream payload , BlobMetadata metadata ) {
89143 return new InputStreamBlob (payload , metadata );
90144 }
91145
@@ -108,12 +162,12 @@ public ByteSourceBlob asByteSource() throws IOException {
108162 }
109163 }
110164
111- record ByteSourceBlob (ByteSource payload , Map < BlobMetadataName , BlobMetadataValue > metadata ) implements Blob {
165+ record ByteSourceBlob (ByteSource payload , BlobMetadata metadata ) implements Blob {
112166 public static ByteSourceBlob of (ByteSource payload ) {
113- return of (payload , ImmutableMap . of ());
167+ return of (payload , BlobMetadata . empty ());
114168 }
115169
116- public static ByteSourceBlob of (ByteSource payload , Map < BlobMetadataName , BlobMetadataValue > metadata ) {
170+ public static ByteSourceBlob of (ByteSource payload , BlobMetadata metadata ) {
117171 return new ByteSourceBlob (payload , metadata );
118172 }
119173
0 commit comments