From fafa1140f3e8eedcb0f00b25c7891093df5fbf43 Mon Sep 17 00:00:00 2001 From: Jesse Morgan Date: Sat, 21 Jul 2018 22:42:50 -0700 Subject: Initial commit of groups search lambdas --- pom.xml | 136 ++++++++++++ .../com/p4square/groupsindexer/GroupsSearch.java | 144 +++++++++++++ .../com/p4square/groupsindexer/SearchFields.java | 153 ++++++++++++++ .../com/p4square/groupsindexer/UpdateIndexes.java | 150 +++++++++++++ .../groupsindexer/model/ErrorResponse.java | 22 ++ .../groupsindexer/model/GroupSearchDocument.java | 232 +++++++++++++++++++++ .../model/GroupSearchDocumentAdapter.java | 53 +++++ .../p4square/groupsindexer/model/Reference.java | 28 +++ .../p4square/groupsindexer/model/SearchField.java | 52 +++++ .../p4square/groupsindexer/model/StringPair.java | 32 +++ src/main/resources/log4j2.xml | 15 ++ 11 files changed, 1017 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/p4square/groupsindexer/GroupsSearch.java create mode 100644 src/main/java/com/p4square/groupsindexer/SearchFields.java create mode 100644 src/main/java/com/p4square/groupsindexer/UpdateIndexes.java create mode 100644 src/main/java/com/p4square/groupsindexer/model/ErrorResponse.java create mode 100644 src/main/java/com/p4square/groupsindexer/model/GroupSearchDocument.java create mode 100644 src/main/java/com/p4square/groupsindexer/model/GroupSearchDocumentAdapter.java create mode 100644 src/main/java/com/p4square/groupsindexer/model/Reference.java create mode 100644 src/main/java/com/p4square/groupsindexer/model/SearchField.java create mode 100644 src/main/java/com/p4square/groupsindexer/model/StringPair.java create mode 100644 src/main/resources/log4j2.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..31e4e13 --- /dev/null +++ b/pom.xml @@ -0,0 +1,136 @@ + + + 4.0.0 + + com.p4square + groups-search + 1.0-SNAPSHOT + + Foursquare Groups Search API + https://github.com/PuyallupFoursquare/ccb-groups-search + + + MIT License + http://www.opensource.org/licenses/mit-license.php + + + + + + Jesse Morgan + jesse@jesterpm.net + + + + + + jitpack.io + https://jitpack.io + + + + + + com.amazonaws + aws-lambda-java-core + 1.2.0 + compile + + + com.amazonaws + aws-lambda-java-events + 2.2.2 + compile + + + com.amazonaws + aws-lambda-java-log4j2 + 1.1.0 + compile + + + com.amazonaws + aws-java-sdk-elasticsearch + [1.11,1.12) + compile + + + com.amazonaws + aws-java-sdk-s3 + [1.11,1.12) + compile + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + [6.3,6.4) + compile + + + org.apache.httpcomponents + httpclient + 4.5.5 + + + com.github.awslabs + aws-request-signing-apache-interceptor + b3772780da33a9b50d806636dc90f8fc6b74b8dc + compile + + + com.p4square + ccbapi + [1.3,) + compile + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + UTF-8 + + + + org.apache.maven.plugins + maven-shade-plugin + 2.4.3 + + + package + + shade + + + + + + + + + + + + com.github.edwgiz + maven-shade-plugin.log4j2-cachefile-transformer + 2.8.1 + + + + + + + + scm:git:git@github.com:PuyallupFoursquare/ccb-groups-search + scm:git:git@github.com:PuyallupFoursquare/ccb-groups-search.git + scm:git:git@github.com:PuyallupFoursquare/ccb-groups-search.git + + 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: + * + * + */ +public class GroupsSearch implements RequestHandler { + + 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 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 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: + *
    + *
  • CCBAPIURL
  • + *
  • CCBAPIUser
  • + *
  • CCBAPIPassword
  • + *
+ * + */ +public class SearchFields implements RequestHandler { + + 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 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 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 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 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: + *
    + *
  • CCBAPIURL
  • + *
  • CCBAPIUser
  • + *
  • CCBAPIPassword
  • + *
  • ES_URL
  • + *
  • IMAGE_BUCKET
  • + *
+ * + */ +public class UpdateIndexes implements RequestHandler { + + 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 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 getCustomFields() { + return customFields; + } + + public void setCustomFields(Map 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 { + @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 values; + + public SearchField() { } + + public SearchField(String id, String label, List 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 getValues() { + return values; + } + + public void setValues(List 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; + } +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..c49d424 --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,15 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1}:%L - %m%n + + + + + + + + + \ No newline at end of file -- cgit v1.2.3