Skip to content

Commit dbda260

Browse files
author
pkryshtop
committed
feat: pass correct filename on attachment upload
1 parent ed690f2 commit dbda260

File tree

8 files changed

+154
-4
lines changed

8 files changed

+154
-4
lines changed

smartling-api-commons/src/main/java/com/smartling/resteasy/ext/ExtendedMultipartFormWriter.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.smartling.resteasy.ext;
22

3+
import org.apache.commons.lang3.StringUtils;
34
import org.jboss.resteasy.annotations.providers.multipart.PartType;
45
import org.jboss.resteasy.plugins.providers.multipart.FieldEnablerPrivilegedAction;
56
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormAnnotationWriter;
@@ -33,7 +34,8 @@ public class ExtendedMultipartFormWriter extends MultipartFormAnnotationWriter
3334
@Override
3435
protected void getFields(Class<?> type, MultipartFormDataOutput output, Object obj) throws IOException
3536
{
36-
for (Field field : type.getDeclaredFields())
37+
Field[] declaredFields = type.getDeclaredFields();
38+
for (Field field : declaredFields)
3739
{
3840
if (field.isAnnotationPresent(DynamicFormParam.class) && field.isAnnotationPresent(PartType.class))
3941
{
@@ -84,11 +86,42 @@ protected void getFields(Class<?> type, MultipartFormDataOutput output, Object o
8486
}
8587
}
8688
}
89+
if (field.isAnnotationPresent(FileFormParam.class) && field.isAnnotationPresent(PartType.class))
90+
{
91+
AccessController.doPrivileged(new FieldEnablerPrivilegedAction(field));
92+
93+
FileFormParam fileFormParam = field.getAnnotation(FileFormParam.class);
94+
String name = fileFormParam.value();
95+
Object value = safeExtractValue(obj, field);
96+
String mediaType = field.getAnnotation(PartType.class).value();
97+
String filename = getFilename(obj, declaredFields, field);
98+
99+
output.addFormData(name, value, field.getType(), field.getGenericType(), MediaType.valueOf(mediaType), filename);
100+
}
87101
}
88102

89103
super.getFields(type, output, obj);
90104
}
91105

106+
private String getFilename(Object obj, Field[] declaredFields, Field field)
107+
{
108+
String filenameField = field.getAnnotation(FileFormParam.class).filenameField();
109+
if (StringUtils.isNotEmpty(filenameField))
110+
{
111+
for (Field declaredField : declaredFields)
112+
{
113+
if (declaredField.getName().equals(filenameField))
114+
{
115+
AccessController.doPrivileged(new FieldEnablerPrivilegedAction(declaredField));
116+
117+
return (String) safeExtractValue(obj, declaredField);
118+
}
119+
}
120+
}
121+
122+
return getFilename(field);
123+
}
124+
92125
private Object safeExtractValue(Object obj, Field field)
93126
{
94127
Object value;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.smartling.resteasy.ext;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Must be used in conjunction with {@link org.jboss.resteasy.annotations.providers.multipart.PartType}.
10+
* It defines file form data with filename.
11+
* <p>
12+
* It is a replacement for {@link javax.ws.rs.FormParam} and {@link org.jboss.resteasy.annotations.jaxrs.FormParam}
13+
* and should not be used together on the same field.
14+
* <p>
15+
* Be careful, it may not fully replace FormParam functionality.
16+
*/
17+
@Target(ElementType.FIELD)
18+
@Retention(RetentionPolicy.RUNTIME)
19+
public @interface FileFormParam
20+
{
21+
String value();
22+
String filenameField() default "";
23+
}

smartling-api-commons/src/test/java/com/smartling/resteasy/ext/ExtendedMultipartFormWriterTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
package com.smartling.resteasy.ext;
22

33
import com.smartling.resteasy.ext.data.FormWithDynamicParams;
4+
import com.smartling.resteasy.ext.data.FormWithFileFormParams;
45
import com.smartling.resteasy.ext.data.FormWithListParams;
56
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
67
import org.junit.Before;
78
import org.junit.Test;
89

910
import java.io.ByteArrayInputStream;
11+
import java.io.InputStream;
1012
import java.util.ArrayList;
1113
import java.util.HashMap;
1214
import java.util.List;
1315
import java.util.Map;
1416

1517
import static java.lang.Boolean.TRUE;
18+
import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
1619
import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
1720
import static org.mockito.ArgumentMatchers.any;
1821
import static org.mockito.ArgumentMatchers.eq;
@@ -85,4 +88,26 @@ public void shouldExtractListFormParams() throws Exception
8588
verifyNoMoreInteractions(output);
8689
}
8790

91+
@Test
92+
public void shouldExtractFileFormParams() throws Exception
93+
{
94+
MultipartFormDataOutput output = mock(MultipartFormDataOutput.class);
95+
InputStream ins1 = mock(InputStream.class);
96+
InputStream ins2 = mock(InputStream.class);
97+
InputStream ins3 = mock(InputStream.class);
98+
99+
FormWithFileFormParams form = new FormWithFileFormParams();
100+
form.setFile1(ins1);
101+
form.setFile1Name("file1.txt");
102+
form.setFile2(ins2);
103+
form.setFile3(ins3);
104+
105+
testedInstance.getFields(FormWithFileFormParams.class, output, form);
106+
107+
verify(output).addFormData(eq("file1"), eq(ins1), eq(InputStream.class), eq(InputStream.class), eq(APPLICATION_OCTET_STREAM_TYPE), eq("file1.txt"));
108+
verify(output).addFormData(eq("file2"), eq(ins2), eq(InputStream.class), eq(InputStream.class), eq(APPLICATION_OCTET_STREAM_TYPE), eq("file2"));
109+
verify(output).addFormData(eq("file3"), eq(ins3), eq(InputStream.class), eq(InputStream.class), eq(APPLICATION_OCTET_STREAM_TYPE), isNull());
110+
verify(output).addFormData(eq("file1Name"), eq("file1.txt"), eq(String.class), eq(String.class), eq(TEXT_PLAIN_TYPE), isNull());
111+
verifyNoMoreInteractions(output);
112+
}
88113
}

smartling-api-commons/src/test/java/com/smartling/resteasy/ext/data/DummyMultipartApi.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,9 @@ public interface DummyMultipartApi
2121
@Consumes(MULTIPART_FORM_DATA)
2222
@Path(DUMMY_MULTIPART_API + "/list-fields")
2323
void postListParams(@MultipartForm FormWithListParams form);
24+
25+
@POST
26+
@Consumes(MULTIPART_FORM_DATA)
27+
@Path(DUMMY_MULTIPART_API + "/file-fields")
28+
void postFileFormParams(@MultipartForm FormWithFileFormParams form);
2429
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.smartling.resteasy.ext.data;
2+
3+
import com.smartling.resteasy.ext.FileFormParam;
4+
import lombok.Data;
5+
import org.jboss.resteasy.annotations.providers.multipart.PartFilename;
6+
import org.jboss.resteasy.annotations.providers.multipart.PartType;
7+
8+
import javax.ws.rs.FormParam;
9+
import java.io.InputStream;
10+
11+
@Data
12+
public class FormWithFileFormParams
13+
{
14+
@FileFormParam(value = "file1", filenameField = "file1Name")
15+
@PartType("application/octet-stream")
16+
private InputStream file1;
17+
18+
@FileFormParam("file2")
19+
@PartFilename("file2")
20+
@PartType("application/octet-stream")
21+
private InputStream file2;
22+
23+
@FileFormParam("file3")
24+
@PartType("application/octet-stream")
25+
private InputStream file3;
26+
27+
@FormParam("file1Name")
28+
@PartType("text/plain")
29+
private String file1Name;
30+
}

smartling-api-commons/src/test/java/com/smartling/resteasy/ext/integration/ExtendedMultipartFormWriterIntegrationTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.smartling.resteasy.ext.ExtendedMultipartFormWriter;
55
import com.smartling.resteasy.ext.data.DummyMultipartApi;
66
import com.smartling.resteasy.ext.data.FormWithDynamicParams;
7+
import com.smartling.resteasy.ext.data.FormWithFileFormParams;
78
import com.smartling.resteasy.ext.data.FormWithListParams;
89
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
910
import org.junit.Before;
@@ -12,6 +13,7 @@
1213

1314
import javax.ws.rs.client.ClientBuilder;
1415
import java.io.ByteArrayInputStream;
16+
import java.nio.charset.StandardCharsets;
1517
import java.util.ArrayList;
1618
import java.util.HashMap;
1719
import java.util.List;
@@ -99,4 +101,36 @@ public void shouldExtractListFormParams() throws Exception
99101
.build())
100102
);
101103
}
104+
105+
@Test
106+
public void shouldExtractFileFormParams() throws Exception
107+
{
108+
FormWithFileFormParams form = new FormWithFileFormParams();
109+
form.setFile1(new ByteArrayInputStream("file1 data".getBytes(StandardCharsets.UTF_8)));
110+
form.setFile1Name("file1.txt");
111+
form.setFile2(new ByteArrayInputStream("file2 data".getBytes(StandardCharsets.UTF_8)));
112+
form.setFile3(new ByteArrayInputStream("file3 data".getBytes(StandardCharsets.UTF_8)));
113+
114+
115+
dummyApi().postFileFormParams(form);
116+
117+
dummyApiEndpoint.verify(postRequestedFor(urlEqualTo(DUMMY_MULTIPART_API + "/file-fields"))
118+
.withRequestBodyPart(aMultipart()
119+
.withHeader("Content-Disposition", equalTo("form-data; name=\"file1\"; filename=\"file1.txt\""))
120+
.withBody(equalTo("file1 data"))
121+
.build())
122+
.withRequestBodyPart(aMultipart()
123+
.withHeader("Content-Disposition", equalTo("form-data; name=\"file2\"; filename=\"file2\""))
124+
.withBody(equalTo("file2 data"))
125+
.build())
126+
.withRequestBodyPart(aMultipart()
127+
.withHeader("Content-Disposition", equalTo("form-data; name=\"file3\""))
128+
.withBody(equalTo("file3 data"))
129+
.build())
130+
.withRequestBodyPart(aMultipart()
131+
.withName("file1Name")
132+
.withBody(equalTo("file1.txt"))
133+
.build())
134+
);
135+
}
102136
}

smartling-attachments-api/src/main/java/com/smartling/api/attachments/v2/pto/AttachmentUploadPTO.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.smartling.api.attachments.v2.pto;
22

3+
import com.smartling.resteasy.ext.FileFormParam;
34
import com.smartling.resteasy.ext.ListFormParam;
45
import lombok.AllArgsConstructor;
56
import lombok.Data;
@@ -16,8 +17,7 @@
1617
@AllArgsConstructor
1718
public class AttachmentUploadPTO
1819
{
19-
@FormParam("file")
20-
@PartFilename("file")
20+
@FileFormParam(value = "file", filenameField = "name")
2121
@PartType("application/octet-stream")
2222
private InputStream file;
2323

smartling-attachments-api/src/test/java/com/smartling/api/attachments/v2/AttachmentsApiTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public void testUploadAttachment() throws Exception
178178
String partSeparator = requestBody.substring(0, 38);
179179

180180
assertEquals(requestBody, partSeparator + "\r\n" +
181-
"Content-Disposition: form-data; name=\"file\"; filename=\"file\"\r\n" +
181+
"Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" +
182182
"Content-Type: application/octet-stream\r\n" +
183183
"\r\n" +
184184
"content\r\n" +

0 commit comments

Comments
 (0)