summaryrefslogtreecommitdiff
path: root/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com')
-rw-r--r--src/main/java/com/p4square/groupsindexer/GroupsSearch.java144
-rw-r--r--src/main/java/com/p4square/groupsindexer/SearchFields.java153
-rw-r--r--src/main/java/com/p4square/groupsindexer/UpdateIndexes.java150
-rw-r--r--src/main/java/com/p4square/groupsindexer/model/ErrorResponse.java22
-rw-r--r--src/main/java/com/p4square/groupsindexer/model/GroupSearchDocument.java232
-rw-r--r--src/main/java/com/p4square/groupsindexer/model/GroupSearchDocumentAdapter.java53
-rw-r--r--src/main/java/com/p4square/groupsindexer/model/Reference.java28
-rw-r--r--src/main/java/com/p4square/groupsindexer/model/SearchField.java52
-rw-r--r--src/main/java/com/p4square/groupsindexer/model/StringPair.java32
9 files changed, 866 insertions, 0 deletions
diff --git a/src/main/java/com/p4square/groupsindexer/GroupsSearch.java b/src/main/java/com/p4square/groupsindexer/GroupsSearch.java
new file mode 100644
index 0000000..2cbc7e5
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/GroupsSearch.java
@@ -0,0 +1,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;
+ }
+}
diff --git a/src/main/java/com/p4square/groupsindexer/SearchFields.java b/src/main/java/com/p4square/groupsindexer/SearchFields.java
new file mode 100644
index 0000000..8c21449
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/SearchFields.java
@@ -0,0 +1,153 @@
+package com.p4square.groupsindexer;
+
+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.ccbapi.CCBAPI;
+import com.p4square.ccbapi.CCBAPIClient;
+import com.p4square.ccbapi.model.*;
+import com.p4square.groupsindexer.model.ErrorResponse;
+import com.p4square.groupsindexer.model.SearchField;
+import com.p4square.groupsindexer.model.StringPair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * SearchFields is an API Gateway Proxy which returns the searchable dropdown fields and their choices.
+ *
+ * Required (custom) environment variables:
+ * <ul>
+ * <li>CCBAPIURL</li>
+ * <li>CCBAPIUser</li>
+ * <li>CCBAPIPassword</li>
+ * </ul>
+ *
+ */
+public class SearchFields implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
+
+ private static final long REFRESH_INTERVAL_MS = 15 * 60 * 1000;
+
+ private static final Logger LOG = LogManager.getLogger(GroupsSearch.class);
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private final CCBAPI ccbClient;
+
+ private List<SearchField> cachedFields;
+ private long lastRefresh;
+
+ public SearchFields() throws Exception {
+ // Setup CCB Client
+ final String CCBAPIURL = System.getenv("CCBAPIURL");
+ final String CCBAPIUser = System.getenv("CCBAPIUser");
+ final String CCBAPIPassword = System.getenv("CCBAPIPassword");
+ ccbClient = new CCBAPIClient(new URI(CCBAPIURL), CCBAPIUser, CCBAPIPassword);
+ }
+
+ @Override
+ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
+ final APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
+ try {
+ final List<SearchField> fields = getFields();
+ if (fields == null) {
+ response.setStatusCode(500);
+ response.setBody(MAPPER.writeValueAsString(new ErrorResponse("Unable to get search fields.")));
+ return response;
+ }
+
+ response.setStatusCode(200);
+ response.setBody(MAPPER.writeValueAsString(fields));
+
+ } catch (Exception e) {
+ LOG.error(e.getMessage());
+ response.setStatusCode(500);
+ try {
+ response.setBody(MAPPER.writeValueAsString(new ErrorResponse(e.getMessage())));
+ } catch (JsonProcessingException _) {
+ // Unexpected.
+ }
+ }
+
+ return response;
+ }
+
+ private synchronized List<SearchField> getFields() {
+ if (System.currentTimeMillis() - lastRefresh < REFRESH_INTERVAL_MS) {
+ LOG.debug("Using cached CCB fields");
+ return cachedFields;
+ }
+
+ try {
+ LOG.info("Fetching fields from CCB");
+
+ cachedFields = new ArrayList<>();
+ final GetCustomFieldLabelsResponse labels = ccbClient.getCustomFieldLabels();
+
+ cachedFields.add(new SearchField("groupTypeId", "Group Type", getValues(LookupTableType.GROUP_TYPE)));
+ // TODO fields.add(new SearchField("campusId", "Campus", ...));
+ cachedFields.add(new SearchField("meetingDayId", "Day", getValues(LookupTableType.MEET_DAY)));
+
+
+ for (final CustomField field : labels.getCustomFields()) {
+ final LookupTableType type = getTypeFromString(field.getName());
+ if (type != null) {
+ cachedFields.add(new SearchField(getSearchFieldIdForType(type), field.getLabel(), getValues(type)));
+ }
+ }
+
+ cachedFields.add(new SearchField("childcare", "Childcare",
+ Arrays.asList(StringPair.of("true", "Yes"), StringPair.of("false", "No"))));
+
+ lastRefresh = System.currentTimeMillis();
+ return cachedFields;
+
+ } catch (Exception e) {
+ LOG.error(e.getMessage());
+ return null;
+ }
+ }
+
+ private LookupTableType getTypeFromString(String name) {
+ switch (name) {
+ case "udf_grp_pulldown_1":
+ return LookupTableType.UDF_GRP_PULLDOWN_1;
+ case "udf_grp_pulldown_2":
+ return LookupTableType.UDF_GRP_PULLDOWN_2;
+ case "udf_grp_pulldown_3":
+ return LookupTableType.UDF_GRP_PULLDOWN_3;
+ default:
+ return null;
+ }
+ }
+
+ private String getSearchFieldIdForType(LookupTableType type) {
+ switch (type) {
+ case UDF_GRP_PULLDOWN_1:
+ return "udf_1";
+ case UDF_GRP_PULLDOWN_2:
+ return "udf_2";
+ case UDF_GRP_PULLDOWN_3:
+ return "udf_3";
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private List<StringPair> getValues(LookupTableType type) throws IOException {
+ final GetLookupTableRequest lookupTableRequest = new GetLookupTableRequest().withType(type);
+ final GetLookupTableResponse lookupTable = ccbClient.getLookupTable(lookupTableRequest);
+
+ return lookupTable.getItems()
+ .stream()
+ .map(entry -> StringPair.of(String.valueOf(entry.getId()), entry.getName()))
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/src/main/java/com/p4square/groupsindexer/UpdateIndexes.java b/src/main/java/com/p4square/groupsindexer/UpdateIndexes.java
new file mode 100644
index 0000000..e4937e1
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/UpdateIndexes.java
@@ -0,0 +1,150 @@
+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.ScheduledEvent;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3ClientBuilder;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.p4square.ccbapi.CCBAPI;
+import com.p4square.ccbapi.CCBAPIClient;
+import com.p4square.ccbapi.model.*;
+import com.p4square.groupsindexer.model.GroupSearchDocument;
+import com.p4square.groupsindexer.model.GroupSearchDocumentAdapter;
+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.bulk.BulkRequest;
+import org.elasticsearch.action.bulk.BulkResponse;
+import org.elasticsearch.client.Requests;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.xcontent.XContentType;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * UpdateIndexes is a scheduled lambda which populates the groups search index.
+ *
+ * Required (custom) environment variables:
+ * <ul>
+ * <li>CCBAPIURL</li>
+ * <li>CCBAPIUser</li>
+ * <li>CCBAPIPassword</li>
+ * <li>ES_URL</li>
+ * <li>IMAGE_BUCKET</li>
+ * </ul>
+ *
+ */
+public class UpdateIndexes implements RequestHandler<ScheduledEvent, String> {
+
+ private static final AWSCredentialsProvider credentialsProvider = new DefaultAWSCredentialsProviderChain();
+
+ private static final Logger LOG = LogManager.getLogger(UpdateIndexes.class);
+ private static final GroupSearchDocumentAdapter ADAPTER = new GroupSearchDocumentAdapter();
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private final String imageBucket;
+
+ private final CCBAPI ccbClient;
+ private final RestHighLevelClient esClient;
+ private final AmazonS3 s3Client;
+
+ public UpdateIndexes() throws Exception {
+ // Setup CCB Client
+ final String CCBAPIURL = System.getenv("CCBAPIURL");
+ final String CCBAPIUser = System.getenv("CCBAPIUser");
+ final String CCBAPIPassword = System.getenv("CCBAPIPassword");
+ ccbClient = new CCBAPIClient(new URI(CCBAPIURL), CCBAPIUser, CCBAPIPassword);
+
+ // 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)));
+
+ // Setup S3 Client
+ imageBucket = System.getenv("IMAGE_BUCKET");
+ s3Client = AmazonS3ClientBuilder.defaultClient();
+ }
+
+ @Override
+ public String handleRequest(ScheduledEvent s3Event, Context context) {
+ try {
+ GetGroupProfilesResponse response = ccbClient.getGroupProfiles(
+ new GetGroupProfilesRequest()
+ .withIncludeImageUrl(true)
+ .withIncludeParticipants(false));
+
+ final BulkRequest indexRequest = new BulkRequest();
+
+ for (GroupProfile profile : response.getGroups()) {
+ if (!profile.isActive() ||
+ !profile.isPublicSearchListed() ||
+ profile.getInteractionType() != InteractionType.MEMBERS_INTERACT) {
+ LOG.info("Skipping inactive/unlisted group " + profile.getName());
+ continue;
+ }
+
+ // Transform GroupProfile to Search Document.
+ final GroupSearchDocument document = ADAPTER.apply(profile);
+
+ // Save GroupProfile image.
+ document.setImageUrl(null);
+ if (profile.getImageUrl() != null && !profile.getImageUrl().isEmpty()) {
+ final String imageKey = "group-images/group-" + profile.getId();
+ InputStream in = null;
+ try {
+ final URL imageUrl = new URL(profile.getImageUrl());
+ in = imageUrl.openStream();
+ s3Client.putObject(imageBucket, imageKey, in, null);
+ document.setImageUrl(imageKey);
+ } catch (Exception e) {
+ LOG.error("Failed to upload image for group " + profile.getId(), e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ // Add request to batch.
+ indexRequest.add(Requests
+ .indexRequest("groups")
+ .type("group")
+ .id(String.valueOf(document.getId()))
+ .source(MAPPER.writeValueAsString(document), XContentType.JSON));
+ }
+
+ BulkResponse esResponse = esClient.bulk(indexRequest);
+
+ if (esResponse.hasFailures()) {
+ throw new RuntimeException(esResponse.buildFailureMessage());
+ }
+
+ LOG.info("Updated search index. Found " + response.getGroups().size() + " groups.");
+ return "ok";
+
+ } catch (IOException e) {
+ LOG.error("Unexpected Exception: " + e.getMessage(), e);
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/com/p4square/groupsindexer/model/ErrorResponse.java b/src/main/java/com/p4square/groupsindexer/model/ErrorResponse.java
new file mode 100644
index 0000000..186bd27
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/model/ErrorResponse.java
@@ -0,0 +1,22 @@
+package com.p4square.groupsindexer.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class ErrorResponse {
+ @JsonProperty("error")
+ private String error;
+
+ public ErrorResponse() {}
+
+ public ErrorResponse(String error) {
+ this.error = error;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+}
diff --git a/src/main/java/com/p4square/groupsindexer/model/GroupSearchDocument.java b/src/main/java/com/p4square/groupsindexer/model/GroupSearchDocument.java
new file mode 100644
index 0000000..e336cb8
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/model/GroupSearchDocument.java
@@ -0,0 +1,232 @@
+package com.p4square.groupsindexer.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A group description in ElasticSearch.
+ */
+public class GroupSearchDocument {
+
+ @JsonProperty("id")
+ private int id;
+
+ @JsonProperty("name")
+ private String name;
+
+ @JsonProperty("description")
+ private String description;
+
+ @JsonProperty("image-url")
+ private String imageUrl;
+
+ @JsonProperty("leader-id")
+ private int leaderId;
+
+ @JsonProperty("leader-name")
+ private String leaderName;
+
+ @JsonProperty("leader-email")
+ private String leaderEmail;
+
+ @JsonProperty("member-count")
+ private int currentMembers;
+
+ @JsonProperty("group-capacity")
+ private Integer groupCapacity;
+
+ @JsonProperty("childcare")
+ private boolean childcareProvided;
+
+ @JsonProperty("listed")
+ private boolean listed;
+
+ @JsonProperty("public")
+ private boolean publicSearchListed;
+
+ @JsonProperty("active")
+ private boolean active;
+
+ @JsonProperty("udf")
+ private Map<String, Reference> customFields;
+
+ private Reference campus;
+ private Reference groupType;
+ private Reference department;
+ private Reference area;
+ private Reference meetingDay;
+ private Reference meetingTime;
+
+ public GroupSearchDocument() {
+ customFields = new HashMap<>();
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public int getLeaderId() {
+ return leaderId;
+ }
+
+ public void setLeaderId(int leaderId) {
+ this.leaderId = leaderId;
+ }
+
+ public String getLeaderName() {
+ return leaderName;
+ }
+
+ public void setLeaderName(String leaderName) {
+ this.leaderName = leaderName;
+ }
+
+ public String getLeaderEmail() {
+ return leaderEmail;
+ }
+
+ public void setLeaderEmail(String leaderEmail) {
+ this.leaderEmail = leaderEmail;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
+ public int getCurrentMembers() {
+ return currentMembers;
+ }
+
+ public void setCurrentMembers(int currentMembers) {
+ this.currentMembers = currentMembers;
+ }
+
+ public Integer getGroupCapacity() {
+ return this.groupCapacity;
+ }
+
+ @JsonIgnore
+ public boolean isGroupCapacityUnlimited() {
+ return this.groupCapacity == null;
+ }
+
+ public void setGroupCapacity(Integer groupCapacity) {
+ this.groupCapacity = groupCapacity;
+ }
+
+ public boolean isChildcareProvided() {
+ return childcareProvided;
+ }
+
+ public void setChildcareProvided(boolean childcareProvided) {
+ this.childcareProvided = childcareProvided;
+ }
+
+ public boolean isListed() {
+ return listed;
+ }
+
+ public void setListed(boolean listed) {
+ this.listed = listed;
+ }
+
+ public boolean isPublicSearchListed() {
+ return publicSearchListed;
+ }
+
+ public void setPublicSearchListed(boolean publicSearchListed) {
+ this.publicSearchListed = publicSearchListed;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public Reference getCampus() {
+ return campus;
+ }
+
+ public void setCampus(Reference campus) {
+ this.campus = campus;
+ }
+
+ public Reference getGroupType() {
+ return groupType;
+ }
+
+ public void setGroupType(Reference groupType) {
+ this.groupType = groupType;
+ }
+
+ public Reference getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(Reference department) {
+ this.department = department;
+ }
+
+ public Reference getArea() {
+ return area;
+ }
+
+ public void setArea(Reference area) {
+ this.area = area;
+ }
+
+ public Reference getMeetingDay() {
+ return meetingDay;
+ }
+
+ public void setMeetingDay(Reference meetingDay) {
+ this.meetingDay = meetingDay;
+ }
+
+ public Reference getMeetingTime() {
+ return meetingTime;
+ }
+
+ public void setMeetingTime(Reference meetingTime) {
+ this.meetingTime = meetingTime;
+ }
+
+ public Map<String, Reference> getCustomFields() {
+ return customFields;
+ }
+
+ public void setCustomFields(Map<String, Reference> customFields) {
+ this.customFields = customFields;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/p4square/groupsindexer/model/GroupSearchDocumentAdapter.java b/src/main/java/com/p4square/groupsindexer/model/GroupSearchDocumentAdapter.java
new file mode 100644
index 0000000..ebe852d
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/model/GroupSearchDocumentAdapter.java
@@ -0,0 +1,53 @@
+package com.p4square.groupsindexer.model;
+
+import com.p4square.ccbapi.model.CustomPulldownFieldValue;
+import com.p4square.ccbapi.model.GroupProfile;
+
+import java.util.function.Function;
+
+/**
+ * GroupSearchDocumentAdapter is a function which converts a CCB {@link GroupProfile} to a {@link GroupSearchDocument}.
+ */
+public class GroupSearchDocumentAdapter implements Function<GroupProfile, GroupSearchDocument> {
+ @Override
+ public GroupSearchDocument apply(GroupProfile groupProfile) {
+ final GroupSearchDocument doc = new GroupSearchDocument();
+
+ doc.setId(groupProfile.getId());
+ doc.setName(groupProfile.getName());
+ doc.setDescription(groupProfile.getDescription());
+ doc.setImageUrl(groupProfile.getImageUrl());
+ doc.setLeaderId(groupProfile.getMainLeader().getId());
+ doc.setLeaderName(groupProfile.getMainLeader().getFullName());
+ doc.setLeaderEmail(groupProfile.getMainLeader().getEmail());
+ doc.setCurrentMembers(groupProfile.getCurrentMembers());
+ doc.setGroupCapacity(groupProfile.getGroupCapacity());
+ doc.setChildcareProvided(groupProfile.isChildcareProvided());
+ doc.setListed(groupProfile.isListed());
+ doc.setPublicSearchListed(groupProfile.isPublicSearchListed());
+ doc.setActive(groupProfile.isActive());
+ doc.setCampus(adaptReference(groupProfile.getCampus()));
+ doc.setGroupType(adaptReference(groupProfile.getGroupType()));
+ doc.setDepartment(adaptReference(groupProfile.getDepartment()));
+ doc.setArea(adaptReference(groupProfile.getArea()));
+ doc.setMeetingDay(adaptReference(groupProfile.getMeetingDay()));
+ doc.setMeetingTime(adaptReference(groupProfile.getMeetingTime()));
+
+ for (final CustomPulldownFieldValue field : groupProfile.getCustomPulldownFields()) {
+ final Reference ref = new Reference();
+ ref.setId(String.valueOf(field.getSelection().getId()));
+ ref.setLabel(field.getSelection().getLabel());
+ doc.getCustomFields().put(field.getName(), ref);
+ }
+
+
+ return doc;
+ }
+
+ private Reference adaptReference(com.p4square.ccbapi.model.Reference r) {
+ final Reference ref = new Reference();
+ ref.setId(String.valueOf(r.getId()));
+ ref.setLabel(r.getName());
+ return ref;
+ }
+}
diff --git a/src/main/java/com/p4square/groupsindexer/model/Reference.java b/src/main/java/com/p4square/groupsindexer/model/Reference.java
new file mode 100644
index 0000000..84dcc15
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/model/Reference.java
@@ -0,0 +1,28 @@
+package com.p4square.groupsindexer.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Reference {
+
+ @JsonProperty("id")
+ private String id;
+
+ @JsonProperty("label")
+ private String label;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+}
diff --git a/src/main/java/com/p4square/groupsindexer/model/SearchField.java b/src/main/java/com/p4square/groupsindexer/model/SearchField.java
new file mode 100644
index 0000000..454b082
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/model/SearchField.java
@@ -0,0 +1,52 @@
+package com.p4square.groupsindexer.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+/**
+ * SearchField describes a pulldown field and its options.
+ */
+public class SearchField {
+
+ @JsonProperty("id")
+ private String id;
+
+ @JsonProperty("label")
+ private String label;
+
+ @JsonProperty("values")
+ private List<StringPair> values;
+
+ public SearchField() { }
+
+ public SearchField(String id, String label, List<StringPair> values) {
+ this.id = id;
+ this.label = label;
+ this.values = values;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public List<StringPair> getValues() {
+ return values;
+ }
+
+ public void setValues(List<StringPair> values) {
+ this.values = values;
+ }
+}
diff --git a/src/main/java/com/p4square/groupsindexer/model/StringPair.java b/src/main/java/com/p4square/groupsindexer/model/StringPair.java
new file mode 100644
index 0000000..a16040a
--- /dev/null
+++ b/src/main/java/com/p4square/groupsindexer/model/StringPair.java
@@ -0,0 +1,32 @@
+package com.p4square.groupsindexer.model;
+
+/**
+ * A key/value pair of Strings.
+ */
+public class StringPair {
+ private String key;
+ private String value;
+
+ public static StringPair of(String key, String value) {
+ final StringPair pair = new StringPair();
+ pair.setKey(key);
+ pair.setValue(value);
+ return pair;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}