/**
* Copyright (c) 2016 Couchbase, Inc. All rights reserved.
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package com.couchbase.lite.mockserver;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
import okio.Buffer;
/*
Generate a _bulk_get response, eg
Request:
--------
{"docs":[
{"atts_since":null,"id":"48c2868b-ec8b","rev":"1-dd9df9"},
{"atts_since":null,"id":"53a11c38-d729","rev":"1-dd9df9"},
..
]}
Response:
---------
Headers:
Content Type: multipart/mixed; boundary="77d6fa0c04b501832f5dcf93d515cb73f39fa74104b01bc5a5b24dd57f4a"
Body:
--77d6fa0c04b501832f5dcf93d515cb73f39fa74104b01bc5a5b24dd57f4a
Content-Type: application/json
{"_deleted":true,"_id":"6f0ac441-9092-45e5-82db-c76be5bd0e48","_rev":"4-4e06228ca6233e67c363a6a2c48e10bd","_revisions":{"ids":["4e06228ca6233e67c363a6a2c48e10bd","3a6249a96cd3fbceaff91d8a3b182a0f","c709c454aaf733264d332d4fd5132af5","c4103f122d27677c9db144cae1394a66"],"start":4}}
--77d6fa0c04b501832f5dcf93d515cb73f39fa74104b01bc5a5b24dd57f4a
X-Doc-Id: doc1-1407952481755
X-Rev-Id: 76-8fbee7ae13afadf9fe81d1d0831930cc
Content-Type: multipart/related; boundary="aff16d5e5921568ad8fab41b9ec3c2dc86390da4f9dbbc2cec7bf5e2655f"
--aff16d5e5921568ad8fab41b9ec3c2dc86390da4f9dbbc2cec7bf5e2655f
Content-Type: application/json
{"_attachments":{"attachment.png":{"content_type":"image/png","digest":"sha1-LmsoqJJ6LOn4YS60pYnvrKbBd64=","follows":true,"length":519173,"revpos":1}},"_id":"doc1-1407952481755","_rev":"76-8fbee7ae13afadf9fe81d1d0831930cc","_revisions":{"ids":["8fbee7ae13afadf9fe81d1d0831930cc"],"start":76},"checked":false,"created_at":"","list_id":"","ocr_decoded":" \n\n","title":"","type":""}
--aff16d5e5921568ad8fab41b9ec3c2dc86390da4f9dbbc2cec7bf5e2655f
Content-Type: image/png
Content-Disposition: attachment; filename="attachment.png"
PNG
--aff16d5e5921568ad8fab41b9ec3c2dc86390da4f9dbbc2cec7bf5e2655f--
--77d6fa0c04b501832f5dcf93d515cb73f39fa74104b01bc5a5b24dd57f4a--
...
*/
public class MockDocumentBulkGet implements SmartMockResponse {
private Map<String, MockDocumentGet.MockDocument> documents;
private boolean isSticky;
private long delayMs;
private boolean is404 = false;
public MockDocumentBulkGet() {
this.documents = new HashMap<String, MockDocumentGet.MockDocument>();
}
@Override
public MockResponse generateMockResponse(RecordedRequest request) {
if (is404) {
MockResponse mockResponse = new MockResponse();
MockHelper.set404NotFoundJson(mockResponse);
return mockResponse;
}
try {
byte[] body = MockHelper.getUncompressedBody(request);
Map<String, Object> jsonMap = MockHelper.getJsonMapFromRequest(body);
List docs = (List) jsonMap.get("docs");
MockResponse mockResponse = new MockResponse();
String boundary = "77d6fa0c04b501832f5dcf93d515cb73f39fa74104b01bc5a5b24dd57f4a";
String contentType = String.format(Locale.ENGLISH, "multipart/mixed; boundary=\"%s\"", boundary);
mockResponse.setHeader("Content-Type", contentType);
ByteArrayOutputStream o = new ByteArrayOutputStream();
/*
* byte[] mime = new String("--BOUNDARY\r\nFoo: Bar\r\n Header : Val ue \r\n\r\npart the first\r\n--BOUNDARY \r\n\r\n2nd part\r\n--BOUNDARY--").getBytes(utf8);
*/
int i = 0;
for (Object doc : docs) {
Map<String, Object> documentMap = (Map) doc;
String docId = (String) documentMap.get("id");
MockDocumentGet.MockDocument mockDocument = documents.get(docId);
if (mockDocument == null)
continue;
boolean addEmptyLine = false;
if (i > 0) {
addEmptyLine = true;
}
addMockDocument(o, mockDocument, boundary, addEmptyLine);
i += 1;
}
// final boundary to mark end
append(o, "\r\n").append(o, "--").append(o, boundary).append(o, "--");
byte[] byteArray = o.toByteArray();
Buffer buffer = new Buffer();
buffer.write(byteArray, 0, byteArray.length);
mockResponse.setBody(buffer);
mockResponse.setStatus("HTTP/1.1 200 OK");
return mockResponse;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private void addMockDocument(ByteArrayOutputStream o, MockDocumentGet.MockDocument mockDocument, String boundary, boolean addEmptyLine) throws Exception {
boolean attachmentFollows = true;
if (addEmptyLine) {
append(o, "\r\n");
}
append(o, "--").append(o, boundary).append(o, "\r\n");
if (mockDocument.isMissing()) {
// missing revision
append(o, "Content-Type: application/json; error=\"true\"");
append(o, "\r\n\r\n");
append(o, mockDocument.generateDocumentBody(attachmentFollows));
append(o, "\r\n");
} else if (!mockDocument.hasAttachment()) {
// no attachment, add json part
append(o, "Content-Type: application/json");
append(o, "\r\n\r\n");
append(o, mockDocument.generateDocumentBody(attachmentFollows));
append(o, "\r\n");
} else {
// has attachment
// headers
append(o, "X-Doc-Id: ").append(o, mockDocument.getDocId()).append(o, "\r\n");
append(o, "X-Rev-Id: ").append(o, mockDocument.getDocRev()).append(o, "\r\n");
String innerBoundary = "aff16d5e5921568ad8fab41b9ec3c2dc86390da4f9dbbc2cec7bf5e2655f";
String innerContentType = String.format(Locale.ENGLISH, "multipart/related; boundary=\"%s\"", innerBoundary);
append(o, "Content-Type: ").append(o, innerContentType);
append(o, "\r\n\r\n");
// boundary
append(o, "--").append(o, innerBoundary).append(o, "\r\n");
// json part
append(o, "Content-Type: application/json");
append(o, "\r\n\r\n");
append(o, mockDocument.generateDocumentBody(attachmentFollows));
append(o, "\r\n");
// boundary
append(o, "--").append(o, innerBoundary).append(o, "\r\n");
// image part
append(o, "Content-Type: image/png");
append(o, "\r\n");
String contentDisposition = String.format(Locale.ENGLISH, "Content-Disposition: attachment; filename=\"%s\"", mockDocument.getAttachmentName());
append(o, contentDisposition);
append(o, "\r\n\r\n");
// image data
byte[] attachmentBytes = MockDocumentGet.getAssetByteArray(mockDocument.getAttachmentName());
o.write(attachmentBytes);
// final boundary to mark end of part
append(o, "\r\n").append(o, "--").append(o, innerBoundary).append(o, "--");
}
}
private MockDocumentBulkGet append(ByteArrayOutputStream baos, String string) throws Exception {
baos.write(string.getBytes("UTF-8"));
return this;
}
public void addDocument(MockDocumentGet.MockDocument mockDocument) {
this.documents.put(mockDocument.getDocId(), mockDocument);
}
@Override
public boolean isSticky() {
return this.isSticky;
}
@Override
public long delayMs() {
return delayMs;
}
public void setSticky(boolean isSticky) {
this.isSticky = isSticky;
}
public long getDelayMs() {
return delayMs;
}
public void setDelayMs(long delayMs) {
this.delayMs = delayMs;
}
public void set404(boolean is404) {
this.is404 = is404;
}
}