summaryrefslogtreecommitdiff
path: root/src/main/java/com/p4square/groupsindexer/GroupsSearch.java
blob: 2cbc7e526f14e37433f462c084b30ebacbbfc409 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package com.p4square.groupsindexer;

import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.http.AWSRequestSigningApacheInterceptor;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.p4square.groupsindexer.model.ErrorResponse;
import com.p4square.groupsindexer.model.GroupSearchDocument;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequestInterceptor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * GroupsSearch is an API Gateway Proxy Lambda which executes a search and returns the results.
 *
 * Required (custom) environment variables:
 *
 * <ul>
 *  <li>ES_URL</li>
 *  <li>IMAGE_URL_PREFIX</li>
 * </ul>
 */
public class GroupsSearch implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    private static final AWSCredentialsProvider credentialsProvider = new DefaultAWSCredentialsProviderChain();

    private static final Logger LOG = LogManager.getLogger(GroupsSearch.class);
    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final RestHighLevelClient esClient;
    private final String imageUrlPrefix;

    public GroupsSearch() throws Exception {
        // Prefix to prepend to image urls.
        imageUrlPrefix = System.getenv("IMAGE_URL_PREFIX");

        // Setup ElasticSearch client
        final String ES_URL = System.getenv("ES_URL");
        AWS4Signer signer = new AWS4Signer();
        signer.setServiceName("es");
        signer.setRegionName(System.getenv("AWS_DEFAULT_REGION"));
        HttpRequestInterceptor interceptor = new AWSRequestSigningApacheInterceptor(signer.getServiceName(), signer, credentialsProvider);

        esClient = new RestHighLevelClient(RestClient
                .builder(HttpHost.create(ES_URL))
                .setHttpClientConfigCallback(hacb -> hacb.addInterceptorLast(interceptor)));
    }

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
        final APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();

        try {
            final Map<String, String> params = event.getQueryStringParameters();
            if (params == null) {
                response.setStatusCode(400);
                response.setBody(MAPPER.writeValueAsString(new ErrorResponse("Request must contain a query.")));
                return response;
            }

            final BoolQueryBuilder query = QueryBuilders.boolQuery();

            if (params.containsKey("q")) {
                query.must(QueryBuilders.simpleQueryStringQuery(params.get("q")));
            }

            if (params.containsKey("groupTypeId")) {
                query.filter(QueryBuilders.termQuery("groupType.id", params.get("groupTypeId")));
            }

            if (params.containsKey("campusId")) {
                query.filter(QueryBuilders.termQuery("campus.id", params.get("campusId")));
            }

            if (params.containsKey("meetingDayId")) {
                query.filter(QueryBuilders.termQuery("meetingDay.id", params.get("meetingDayId")));
            }

            if (params.containsKey("childcare")) {
                query.filter(QueryBuilders.termQuery("childcare", Boolean.parseBoolean(params.get("childcare"))));
            }

            params.entrySet()
                    .stream()
                    .filter(entry -> entry.getKey().startsWith("udf_"))
                    .map(entry -> QueryBuilders.termQuery("udf." + entry.getKey() + ".id", entry.getValue()))
                    .forEach(query::filter);

            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(query);
            searchSourceBuilder.size(20);

            SearchRequest searchRequest = new SearchRequest("groups");
            searchRequest.types("group");
            searchRequest.source(searchSourceBuilder);

            SearchResponse searchResponse = esClient.search(searchRequest);

            List<GroupSearchDocument> docs = new ArrayList<>();
            for (final SearchHit hit : searchResponse.getHits().getHits()) {
                GroupSearchDocument doc = MAPPER.readValue(hit.getSourceAsString(), GroupSearchDocument.class);
                // Sanitize the output
                doc.setLeaderEmail(null);
                if (doc.getImageUrl() != null) {
                    doc.setImageUrl(imageUrlPrefix + "/" + doc.getImageUrl());
                }
                docs.add(doc);
            }

            response.setStatusCode(200);
            response.setBody(MAPPER.writeValueAsString(docs));

        } catch (Exception e) {
            LOG.error(e.getMessage());
            response.setStatusCode(500);
            try {
                response.setBody(MAPPER.writeValueAsString(new ErrorResponse(e.getMessage())));
            } catch (JsonProcessingException _) {
                // Unexpected.
            }
        }

        return response;
    }
}