• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenSRP / opensrp-client-child / #787

pending completion
#787

Pull #327

github-actions

web-flow
Merge 7e00a9d6d into c099d4f8b
Pull Request #327: use post request for old advanced search

5216 of 9039 relevant lines covered (57.71%)

0.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.89
opensrp-child/src/main/java/org/smartregister/child/interactor/ChildAdvancedSearchInteractor.java
1
package org.smartregister.child.interactor;
2

3
import androidx.annotation.VisibleForTesting;
4

5
import org.apache.commons.lang3.StringUtils;
6
import org.jetbrains.annotations.NotNull;
7
import org.jetbrains.annotations.Nullable;
8
import org.json.JSONArray;
9
import org.json.JSONException;
10
import org.json.JSONObject;
11
import org.smartregister.CoreLibrary;
12
import org.smartregister.DristhiConfiguration;
13
import org.smartregister.child.ChildLibrary;
14
import org.smartregister.child.contract.ChildAdvancedSearchContract;
15
import org.smartregister.child.util.AppExecutors;
16
import org.smartregister.child.util.ChildAppProperties;
17
import org.smartregister.child.util.Constants;
18
import org.smartregister.child.util.DBConstants;
19
import org.smartregister.domain.Response;
20
import org.smartregister.domain.ResponseStatus;
21
import org.smartregister.service.HTTPAgent;
22

23
import java.io.UnsupportedEncodingException;
24
import java.net.URLEncoder;
25
import java.util.Map;
26

27
import timber.log.Timber;
28

29
/**
30
 * Created by ndegwamartin on 11/04/2019.
31
 */
32
public class ChildAdvancedSearchInteractor implements ChildAdvancedSearchContract.Interactor {
1✔
33

34
    public static final String SEARCH_URL = "/rest/search/path";
35
    public static final String NEW_ADVANCE_SEARCH_URL = "/rest/client/search";
36
    private final AppExecutors appExecutors;
37
    private HTTPAgent httpAgent;
38
    private DristhiConfiguration dristhiConfiguration;
39
    private String motherGuardianNumber;
40

41
    public ChildAdvancedSearchInteractor() {
42
        this(new AppExecutors());
1✔
43
    }
1✔
44

45
    @VisibleForTesting
46
    ChildAdvancedSearchInteractor(AppExecutors appExecutors) {
1✔
47
        this.appExecutors = appExecutors;
1✔
48
        setMotherGuardianNumber(Constants.KEY.MOTHER_GUARDIAN_NUMBER);
1✔
49
    }
1✔
50

51
    @Override
52
    public void search(final Map<String, String> editMap, final ChildAdvancedSearchContract.InteractorCallBack callBack,
53
                       final String opensrpID) {
54
        Runnable runnable = () -> {
×
55

56
            final Response<String> response = globalSearch(editMap);
×
57
            appExecutors.mainThread().execute(() -> callBack.onResultsFound(response, opensrpID));
×
58
        };
×
59

60
        appExecutors.networkIO().execute(runnable);
×
61
    }
×
62

63
    private Response<String> globalSearch(Map<String, String> searchParameters) {
64
        if (ChildLibrary.getInstance().getProperties().isTrue(ChildAppProperties.KEY.USE_NEW_ADVANCE_SEARCH_APPROACH)) {
1✔
65
            return retrieveRemoteClients(searchParameters);
1✔
66
        }
67
        return searchUsingOldApproachWithPostRequest(searchParameters);
1✔
68
    }
69

70
    private Response<String> searchUsingOldApproachWithPostRequest(Map<String, String> searchParameters){
71
        if (!searchParameters.isEmpty()) {
1✔
72
            JSONObject jsonObject = new JSONObject();
1✔
73
            for (Map.Entry<String, String> entry : searchParameters.entrySet()) {
1✔
74
                String key = entry.getKey();
1✔
75
                String value = entry.getValue();
1✔
76

77
                if (DBConstants.KEY.MOTHER_GUARDIAN_PHONE_NUMBER.equals(key)) {
1✔
78
                    key = DBConstants.KEY.MOTHER_CONTACT_PHONE_NUMBER;
×
79
                }
80

81
                if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
1✔
82
                    try {
83
                        jsonObject.put(key, value);
1✔
84
                    } catch (JSONException e) {
×
85
                        e.printStackTrace();
×
86
                    }
1✔
87
                }
88

89
            }
1✔
90
            String uri = getDristhiConfiguration().dristhiBaseURL() + SEARCH_URL;
1✔
91
            Timber.i("Advance Search URI: %s ", uri);
1✔
92
            return getHttpAgent().post(uri, jsonObject.toString());
1✔
93
        }
94
        return new Response<>(ResponseStatus.failure, "[]");
×
95
    }
96

97
    /**
98
     * This method performs search using the endpoint rest/client/search. The query will search mother
99
     * and child separately and combine
100
     *
101
     * @param searchParameters Filter parameters
102
     * @return Payload string of the response of search
103
     */
104
    private Response<String> retrieveRemoteClients(Map<String, String> searchParameters) {
105
        SearchMother searchMother = null;
1✔
106
        try {
107
            searchMother = new SearchMother(searchParameters).invoke();
1✔
108
        } catch (JSONException e) {
×
109
            e.printStackTrace();
×
110
        }
1✔
111
        assert searchMother != null;
1✔
112
        Response<String> motherSearchResult = searchMother.getMotherSearchResult();
1✔
113

114
        JSONObject childSearchJSONObject = null;
1✔
115
        try {
116
            childSearchJSONObject = generateChildSearchParameters(searchParameters);
1✔
117
        } catch (JSONException e) {
×
118
            e.printStackTrace();
×
119
        }
1✔
120
        assert childSearchJSONObject != null;
1✔
121
        if (childSearchJSONObject.length()>0) {
1✔
122
            String searchEndpoint = getDristhiConfiguration().dristhiBaseURL() + NEW_ADVANCE_SEARCH_URL;
1✔
123
            Timber.i("Child Search URI: %s%s", searchEndpoint, childSearchJSONObject.toString());
1✔
124
            Response<String> childSearchResults = getHttpAgent().post(searchEndpoint, childSearchJSONObject.toString());
1✔
125
            if (childSearchResults.status() == ResponseStatus.success && motherSearchResult != null && motherSearchResult.status() == ResponseStatus.success) {
1✔
126
                try {
127
                    JSONArray mothersJsonArray = new JSONArray(motherSearchResult.payload());
1✔
128
                    JSONArray childrenJsonArray = new JSONArray(childSearchResults.payload());
1✔
129
                    for (int index = 0; index < mothersJsonArray.length(); index++) {
1✔
130
                        childrenJsonArray.put(mothersJsonArray.get(index));
1✔
131
                    }
132
                    return new Response<>(ResponseStatus.success, childrenJsonArray.toString());
1✔
133
                } catch (JSONException e) {
×
134
                    Timber.e(e);
×
135
                }
136
            }
137
            return childSearchResults;
1✔
138
        }
139
        return motherSearchResult != null ? motherSearchResult : new Response<>(ResponseStatus.failure, "[]");
1✔
140
    }
141

142
    public void setMotherGuardianNumber(String motherGuardianNumber) {
143
        this.motherGuardianNumber = motherGuardianNumber;
1✔
144
    }
1✔
145

146
    public String getMotherGuardianPhoneNumber() {
147
        return motherGuardianNumber;
1✔
148
    }
149

150
    private JSONObject generateChildSearchParameters(Map<String, String> searchParameters) throws JSONException {
151
        removeMotherSearchParameters(searchParameters);
1✔
152

153
        String identifier = searchParameters.remove(Constants.KEY.ZEIR_ID);
1✔
154
        //Search by ZEIR id and include mother relationship when identifier is provided
155
        JSONObject jsonObject = new JSONObject();
1✔
156
        if (StringUtils.isNotBlank(identifier)) {
1✔
157
            jsonObject.put("identifier", identifier);
1✔
158
            jsonObject.put("relationships", "mother");
1✔
159
            return jsonObject;
1✔
160
        }
161

162
        //Handle name param - use either firs/last name //TODO server does not support full name
163
        String name = searchParameters.remove(Constants.KEY.FIRST_NAME);
1✔
164
        String lastname = searchParameters.remove(Constants.KEY.LAST_NAME);
1✔
165
        name = StringUtils.isNotBlank(name) ? name : lastname;
1✔
166

167
        if (StringUtils.isNotBlank(name)) {
1✔
168
            jsonObject.put("name", name);
1✔
169
        }
170

171
        //Handle birth dates param
172
        String birthDate = getChildBirthDateParameter(searchParameters);
1✔
173

174
        if (StringUtils.isNotBlank(birthDate)) {
1✔
175
            jsonObject.put("birthdate", birthDate);
1✔
176
        }
177

178
        //Handle other client attributes
179
        String formattedAttributes = getChildClientAttributes(searchParameters);
1✔
180
        if (StringUtils.isNotBlank(formattedAttributes)) {
1✔
181
            jsonObject.put("attribute", formattedAttributes);
1✔
182
        }
183
        if (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(birthDate) || StringUtils.isNotBlank(formattedAttributes)) {
1✔
184
            jsonObject.put("relationships", "mother");
1✔
185
        }
186
        return jsonObject;
1✔
187
    }
188

189
    @NotNull
190
    private String getChildBirthDateParameter(Map<String, String> searchParameters) {
191
        String birthDate = "";
1✔
192
        String birthDatesString = searchParameters.remove(Constants.KEY.BIRTH_DATE);
1✔
193

194
        String[] birthDates = birthDatesString != null ? birthDatesString.split(":") : new String[]{};
1✔
195
        if (birthDates != null && birthDates.length == 2 ) {
1✔
196
            birthDate = String.format("%s:%s", birthDates[0], birthDates[1]);
1✔
197
        }
198
        return birthDate;
1✔
199
    }
200

201
    @Nullable
202
    private String getChildClientAttributes(Map<String, String> searchParameters) {
203
        StringBuilder clientAttributes = new StringBuilder();
1✔
204
        for (Map.Entry<String, String> entry : searchParameters.entrySet()) {
1✔
205
            String key = entry.getKey();
1✔
206
            String value = entry.getValue();
1✔
207
            if (key.equalsIgnoreCase(Constants.CHILD_STATUS.ACTIVE) && value.equalsIgnoreCase("true") ||
1✔
208
                    key.equalsIgnoreCase(Constants.CHILD_STATUS.LOST_TO_FOLLOW_UP) && value.equalsIgnoreCase("false") ||
1✔
209
                    key.equalsIgnoreCase(Constants.CHILD_STATUS.INACTIVE) && value.equalsIgnoreCase("false")) {
1✔
210
                continue;
×
211
            }
212
            clientAttributes.append(String.format("%s:%s,", key, value));
1✔
213
        }
1✔
214

215
        String formattedAttributes = null;
1✔
216
        if (StringUtils.isNotBlank(clientAttributes)) {
1✔
217
            formattedAttributes = clientAttributes.toString().replaceAll(",$", "").trim();
1✔
218
        }
219

220
        return formattedAttributes;
1✔
221
    }
222

223
    protected void removeMotherSearchParameters(Map<String, String> searchParameters) {
224
        searchParameters.remove(Constants.KEY.MOTHER_FIRST_NAME);
1✔
225
        searchParameters.remove(Constants.KEY.MOTHER_LAST_NAME);
1✔
226
        searchParameters.remove(getMotherGuardianPhoneNumber());
1✔
227
    }
1✔
228

229
    public DristhiConfiguration getDristhiConfiguration() {
230
        if (this.dristhiConfiguration == null) {
1✔
231
            this.dristhiConfiguration = CoreLibrary.getInstance().context().configuration();
1✔
232
        }
233
        return this.dristhiConfiguration;
1✔
234
    }
235

236
    public void setDristhiConfiguration(DristhiConfiguration dristhiConfiguration) {
237
        this.dristhiConfiguration = dristhiConfiguration;
×
238
    }
×
239

240
    private String urlEncode(String value) {
241
        try {
242
            return URLEncoder.encode(value, "UTF-8");
1✔
243
        } catch (UnsupportedEncodingException e) {
×
244
            return value;
×
245
        }
246
    }
247

248
    public HTTPAgent getHttpAgent() {
249
        if (this.httpAgent == null) {
1✔
250
            this.httpAgent = CoreLibrary.getInstance().context().getHttpAgent();
1✔
251
        }
252
        return this.httpAgent;
1✔
253

254
    }
255

256
    private class SearchMother {
257
        private final Map<String, String> searchParameters;
258
        private Response<String> motherSearchResult;
259

260
        public SearchMother(Map<String, String> searchParameters) {
1✔
261
            this.searchParameters = searchParameters;
1✔
262
        }
1✔
263

264
        public Response<String> getMotherSearchResult() {
265
            return motherSearchResult;
1✔
266
        }
267

268
        public SearchMother invoke() throws JSONException {
269
            JSONObject jsonObject = new JSONObject();
1✔
270
            String searchEndpoint = getDristhiConfiguration().dristhiBaseURL() + NEW_ADVANCE_SEARCH_URL;
1✔
271

272
            String name = searchParameters.remove(Constants.KEY.MOTHER_FIRST_NAME);
1✔
273
            String lastname = searchParameters.remove(Constants.KEY.MOTHER_LAST_NAME);
1✔
274
            name = StringUtils.isNotBlank(name) ? name : lastname;
1✔
275

276
            if (StringUtils.isNotBlank(name)) {
1✔
277
                jsonObject.put("name", name);
1✔
278
            }
279

280
            String phoneNumber = searchParameters.remove(getMotherGuardianPhoneNumber());
1✔
281
            if(StringUtils.isNotBlank(phoneNumber)){
1✔
282
                String attribute = String.format("%s:%s", getMotherGuardianPhoneNumber(), phoneNumber);
1✔
283
                jsonObject.put("attribute", attribute);
1✔
284
            }
285
            if(jsonObject.length() > 0){
1✔
286
                jsonObject.put("searchRelationship", Constants.KEY.MOTHER);
1✔
287
                motherSearchResult = getHttpAgent().post(searchEndpoint, jsonObject.toString());
1✔
288
                Timber.i("Mother Search URI: %s%s", searchEndpoint, jsonObject.toString());
1✔
289
            }
290

291
            return this;
1✔
292
        }
293
    }
294
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc