/** * Copyright (c) 2016 Couchbase, Inc. All rights reserved. * * 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.replicator; import com.couchbase.lite.Database; import com.couchbase.lite.Manager; import com.couchbase.lite.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.Protocol; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; /** * Created by hideki on 5/16/16. */ public class CustomizableMockInterceptor implements Interceptor { interface Responder { Response execute(Request request) throws IOException; } public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); private List<Request> capturedRequests = new CopyOnWriteArrayList<Request>(); //private Map<String, ResponseData> responseData = new HashMap<String, ResponseData>(); private Map<String, Responder> responders = new HashMap<String, Responder>(); // if this is set, it will delay responses by this number of milliseconds private long responseDelayMilliseconds; public CustomizableMockInterceptor() { addDefaultResponders(); } @Override public Response intercept(Chain chain) throws IOException { capturedRequests.add(chain.request()); return process(chain.request()); } protected Response process(Request request) throws IOException { delayResponseIfNeeded(); for (String key : responders.keySet()) { if (key.equals("*") || request.url().pathSegments().contains(key)) { return responders.get(key).execute(request); } } throw new RuntimeException("No responders matched for url pattern: " + request.url().pathSegments()); } public void setResponseDelayMilliseconds(long responseDelayMilliseconds) { this.responseDelayMilliseconds = responseDelayMilliseconds; } private void delayResponseIfNeeded() { if (responseDelayMilliseconds > 0) { try { Thread.sleep(responseDelayMilliseconds); } catch (InterruptedException e) { e.printStackTrace(); } } } public List<Request> getCapturedRequests() { return capturedRequests; } public void clearCapturedRequests() { capturedRequests.clear(); } public void clearResponders() { responders.clear(); } public void setResponder(String urlPattern, Responder responder) { responders.put(urlPattern, responder); } public void addDefaultResponders() { addResponderRevDiffsAllMissing(); addResponderFakeBulkDocs(); //addResponderFakeLocalDocumentUpdateIOException(); } public void addResponseReturnEmptyChangesFeed() { setResponder("_changes", new Responder() { @Override public Response execute(Request request) throws IOException { String json = "{\"results\":[]}"; return new Response.Builder() .request(request) .code(200) .protocol(Protocol.HTTP_1_1) .body(ResponseBody.create(JSON, json)).build(); } }); } public void addResponderThrowExceptionAllRequests() { setResponder("*", new Responder() { @Override public Response execute(Request request) throws IOException { throw new IOException("Test IOException"); } }); } public void addResponderReturnInvalidChangesFeedJson() { setResponder("_changes", new Responder() { @Override public Response execute(Request request) throws IOException { String json = "{\"results\":["; return new Response.Builder() .request(request) .code(200) .protocol(Protocol.HTTP_1_1) .body(ResponseBody.create(JSON, json)).build(); } }); } public void addResponderFakeLocalDocumentUpdate404() { setResponder("_local", new Responder() { @Override public Response execute(Request request) throws IOException { String json = "{\"error\":\"not_found\",\"reason\":\"missing\"}"; return new Response.Builder() .request(request) .code(404) .protocol(Protocol.HTTP_1_1) .body(ResponseBody.create(JSON, json)).build(); } }); } public void addResponderRevDiffsAllMissing() { setResponder("_revs_diff", new Responder() { @Override public Response execute(Request request) throws IOException { return fakeRevsDiff(request); } }); } public void addResponderFakeBulkDocs() { setResponder("_bulk_docs", new Responder() { @Override public Response execute(Request request) throws IOException { return fakeBulkDocs(request); } }); } /* - /{db}/_bulk_docs Request JSON: { "new_edits": false, "docs": [{ "_rev": "2-e776a593-6b61-44ee-b51a-0bdf205c9e13", "foo": 1, "_id": "doc1-1384988871931", "_revisions": { "start": 2, "ids": ["e776a593-6b61-44ee-b51a-0bdf205c9e13", "38af0cab-d397-4b68-a59e-67c341f98dc4"] } }, { ... } } Response JSON: [{ "id": "doc1-1384988871931", "rev": "2-e776a593-6b61-44ee-b51a-0bdf205c9e13" }, { ... }] */ public static Response fakeBulkDocs(Request request) throws IOException { Map<String, Object> jsonMap = OkHttpUtils.getJsonMapFromRequest(request); List<Map<String, Object>> responseList = new ArrayList<Map<String, Object>>(); ArrayList<Map<String, Object>> docs = (ArrayList) jsonMap.get("docs"); for (Map<String, Object> doc : docs) { Map<String, Object> responseListItem = new HashMap<String, Object>(); responseListItem.put("id", doc.get("_id")); responseListItem.put("rev", doc.get("_rev")); Log.d(Database.TAG, "id: " + doc.get("_id")); Log.d(Database.TAG, "rev: " + doc.get("_rev")); responseList.add(responseListItem); } String json = Manager.getObjectMapper().writeValueAsString(responseList); return new Response.Builder() .request(request) .code(200) .protocol(Protocol.HTTP_1_1) .body(ResponseBody.create(JSON, json)).build(); } /* - /{db}/_rev_diff Request JSON: { "doc2-1384988871931": ["1-b52e6d59-4151-4802-92fb-7e34ceff1e92"], "doc1-1384988871931": ["2-e776a593-6b61-44ee-b51a-0bdf205c9e13"] } Response JSON: { "doc1-1384988871931": { "missing": ["2-e776a593-6b61-44ee-b51a-0bdf205c9e13"] }, "doc2-1384988871931": { "missing": ["1-b52e6d59-4151-4802-92fb-7e34ceff1e92"] } } */ public static Response fakeRevsDiff(Request request) throws IOException { Map<String, Object> jsonMap = OkHttpUtils.getJsonMapFromRequest(request); Map<String, Object> responseMap = new HashMap<String, Object>(); for (String key : jsonMap.keySet()) { ArrayList value = (ArrayList) jsonMap.get(key); Map<String, Object> missingMap = new HashMap<String, Object>(); missingMap.put("missing", value); responseMap.put(key, missingMap); } String json = Manager.getObjectMapper().writeValueAsString(responseMap); return new Response.Builder() .request(request) .code(200) .protocol(Protocol.HTTP_1_1) .body(ResponseBody.create(JSON, json)).build(); } }