diff options
author | Jesse Morgan <jesse@jesterpm.net> | 2014-09-21 15:01:40 -0700 |
---|---|---|
committer | Jesse Morgan <jesse@jesterpm.net> | 2014-09-21 15:01:40 -0700 |
commit | d01b3c8e49251c85cfa9c426064b841233c6c8e4 (patch) | |
tree | 755dbbf4ecc373b84f55f81d9de83e7d03454c49 /src/com | |
parent | c2feb363e513c0dea83d507eb9ba1918748d4e8e (diff) |
Adding Support for Assigning Attributes in F1.
Adding a new interface, F1API for F1 APIs which require a valid access
token. This is now used by AssessmentResultsPage to assign an attribute
each time someone completes the assessment.
Also adding an AttributeTool to list all attributes and assign
attributes to users.
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/p4square/f1oauth/Attribute.java | 87 | ||||
-rw-r--r-- | src/com/p4square/f1oauth/F1API.java | 44 | ||||
-rw-r--r-- | src/com/p4square/f1oauth/F1Access.java | 242 | ||||
-rw-r--r-- | src/com/p4square/f1oauth/F1Exception.java | 15 | ||||
-rw-r--r-- | src/com/p4square/f1oauth/SecondPartyVerifier.java | 23 | ||||
-rw-r--r-- | src/com/p4square/grow/GrowProcessComponent.java | 1 | ||||
-rw-r--r-- | src/com/p4square/grow/frontend/AssessmentResultsPage.java | 25 | ||||
-rw-r--r-- | src/com/p4square/grow/tools/AttributeTool.java | 143 |
8 files changed, 511 insertions, 69 deletions
diff --git a/src/com/p4square/f1oauth/Attribute.java b/src/com/p4square/f1oauth/Attribute.java new file mode 100644 index 0000000..fa46d90 --- /dev/null +++ b/src/com/p4square/f1oauth/Attribute.java @@ -0,0 +1,87 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import java.util.Date; + +/** + * F1 Attribute Data. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class Attribute { + /*Attribute + *{ + "attribute": { + "@id": "", + "@uri": "", + "person": { + "@id": "1636208", + "@uri": "https://demo.fellowshiponeapi.com/v1/People/1636208" + }, + "attributeGroup": { + "@id": "", + "@uri": "", + "name": null, + "attribute": { + "@id": "958", + "@uri": "", + "name": null + } + }, + "startDate": null, + "endDate": null, + "comment": null, + "createdDate": null, + "lastUpdatedDate": null + } + */ + + private Date mStartDate; + private Date mEndDate; + private String mComment; + + /** + * @return the start date for the attribute. + */ + public Date getStartDate() { + return mStartDate; + } + + /** + * Set the start date for the attribute. + */ + public void setStartDate(Date date) { + mStartDate = date; + } + + /** + * @return the end date for the attribute. + */ + public Date getEndDate() { + return mEndDate; + } + + /** + * Set the end date for the attribute. + */ + public void setEndDate(Date date) { + mEndDate = date; + } + + /** + * @return The comment on the Attribute. + */ + public String getComment() { + return mComment; + } + + /** + * Set the comment on the attribute. + */ + public void setComment(String comment) { + mComment = comment; + } +} diff --git a/src/com/p4square/f1oauth/F1API.java b/src/com/p4square/f1oauth/F1API.java new file mode 100644 index 0000000..88801db --- /dev/null +++ b/src/com/p4square/f1oauth/F1API.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +import java.io.IOException; +import java.util.Map; + +import com.p4square.restlet.oauth.OAuthException; +import com.p4square.restlet.oauth.OAuthUser; + +/** + * F1 API methods which require an authenticated user. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public interface F1API { + /** + * Fetch information about a user. + * + * @param user The user to fetch information about. + * @return An F1User object. + */ + F1User getF1User(OAuthUser user) throws OAuthException, IOException; + + /** + * Fetch a list of all attributes ids and names. + * + * @return A Map of attribute name to attribute id. + */ + Map<String, String> getAttributeList() throws F1Exception; + + /** + * Add an attribute to the user. + * + * @param user The user to add the attribute to. + * @param attributeName The attribute to add. + * @param attribute The attribute to add. + */ + boolean addAttribute(String userId, String attributeName, Attribute attribute) + throws F1Exception; + +} diff --git a/src/com/p4square/f1oauth/F1Access.java b/src/com/p4square/f1oauth/F1Access.java index 35957bf..32550c4 100644 --- a/src/com/p4square/f1oauth/F1Access.java +++ b/src/com/p4square/f1oauth/F1Access.java @@ -4,19 +4,26 @@ package com.p4square.f1oauth; +import java.io.IOException; import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.log4j.Logger; import org.restlet.Context; -import org.restlet.Response; import org.restlet.Request; +import org.restlet.Response; import org.restlet.data.ChallengeResponse; import org.restlet.data.ChallengeScheme; import org.restlet.data.MediaType; import org.restlet.data.Method; import org.restlet.data.Status; import org.restlet.engine.util.Base64; +import org.restlet.ext.jackson.JacksonRepresentation; +import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import com.p4square.restlet.oauth.OAuthException; @@ -42,11 +49,16 @@ public class F1Access { private static final String ACCESSTOKEN_URL= "Tokens/AccessToken"; private static final String TRUSTED_ACCESSTOKEN_URL = "/AccessToken"; + private static final SimpleDateFormat DATE_FORMAT = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + private final String mBaseUrl; private final String mMethod; private final OAuthHelper mOAuthHelper; + private final Map<String, String> mAttributeIdByName; + /** */ public F1Access(Context context, String consumerKey, String consumerSecret, @@ -91,6 +103,7 @@ public class F1Access { } }; + mAttributeIdByName = new HashMap<>(); } /** @@ -144,64 +157,205 @@ public class F1Access { } } - /* - public addAttribute(Attribute attribute, String comment) { - String baseUrl = getBaseUrl(); - Map newAttributeTemplate = null; - - // Get Attribute Template - Request request = new Request(Method.GET, - baseUrl + "People/" + getIdentifier() + "/Attributes/new.json"); - request.setChallengeResponse(getChallengeResponse()); - Response response = getContext().getClientDispatcher().handle(request); - - Representation representation = response.getEntity(); - try { - Status status = response.getStatus(); - if (status.isSuccess()) { - JacksonRepresentation<Map> entity = new JacksonRepresentation<Map>(response.getEntity(), Map.class); - newAttributeTemplate = entity.getObject(); - } + /** + * @return An F1API authenticated by the given user. + */ + public F1API getAuthenticatedApi(OAuthUser user) { + return new AuthenticatedApi(user); + } + + private class AuthenticatedApi implements F1API { + private final OAuthUser mUser; + + public AuthenticatedApi(OAuthUser user) { + mUser = user; + } - } finally { - if (representation != null) { - representation.release(); + /** + * Fetch information about a user. + * + * @param user The user to fetch information about. + * @return An F1User object. + */ + @Override + public F1User getF1User(OAuthUser user) throws OAuthException, IOException { + Request request = new Request(Method.GET, user.getLocation() + ".json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + Map data = entity.getObject(); + return new F1User(user, data); + + } else { + throw new OAuthException(status); + } + } finally { + if (response.getEntity() != null) { + response.release(); + } } } - if (newAttributeTemplate == null) { - LOG.error("Could not retrieve attribute template!"); - return; + @Override + public Map<String, String> getAttributeList() throws F1Exception { + // Note: this list is shared by all F1 users. + synchronized (mAttributeIdByName) { + if (mAttributeIdByName.size() == 0) { + // Reload attributes. Maybe it will be there now... + Request request = new Request(Method.GET, + mBaseUrl + "People/AttributeGroups.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + + Map attributeGroups = (Map) entity.getObject().get("attributeGroups"); + List<Map> groups = (List<Map>) attributeGroups.get("attributeGroup"); + + for (Map group : groups) { + List<Map> attributes = (List<Map>) group.get("attribute"); + if (attributes != null) { + for (Map attribute : attributes) { + String id = (String) attribute.get("@id"); + String name = ((String) attribute.get("name")); + mAttributeIdByName.put(name.toLowerCase(), id); + LOG.debug("Caching attribute '" + name + + "' with id '" + id + "'"); + } + } + } + } + + } catch (IOException e) { + throw new F1Exception("Could not parse AttributeGroups.", e); + + } finally { + if (representation != null) { + representation.release(); + } + } + } + + return mAttributeIdByName; + } } - // Populate Attribute Template + /** + * Add an attribute to the user. + * + * @param user The user to add the attribute to. + * @param attributeName The attribute to add. + * @param attribute The attribute to add. + */ + public boolean addAttribute(String userId, String attributeName, Attribute attribute) + throws F1Exception { + + // Get the attribute id. + String attributeId = getAttributeId(attributeName); + if (attributeId == null) { + throw new F1Exception("Could not find id for " + attributeName); + } + + // Get Attribute Template + Map attributeTemplate = null; + + { + Request request = new Request(Method.GET, + mBaseUrl + "People/" + userId + "/Attributes/new.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + Status status = response.getStatus(); + if (status.isSuccess()) { + JacksonRepresentation<Map> entity = + new JacksonRepresentation<Map>(response.getEntity(), Map.class); + attributeTemplate = entity.getObject(); + + } else { + throw new F1Exception("Failed to retrieve attribute template: " + + status); + } + + } catch (IOException e) { + throw new F1Exception("Could not parse attribute template.", e); + + } finally { + if (representation != null) { + representation.release(); + } + } + } + + if (attributeTemplate == null) { + throw new F1Exception("Could not retrieve attribute template."); + } + + // Populate Attribute Template + Map attributeMap = (Map) attributeTemplate.get("attribute"); + Map attributeGroup = (Map) attributeMap.get("attributeGroup"); + Map<String, String> attributeIdMap = new HashMap<>(); + attributeIdMap.put("@id", attributeId); + attributeGroup.put("attribute", attributeIdMap); - // POST new attribute - Request request = new Request(Method.POST, - baseUrl + "People/" + getIdentifier() + "/Attributes.json"); - request.setChallengeResponse(getChallengeResponse()); - Response response = getContext().getClientDispatcher().handle(request); + if (attribute.getStartDate() != null) { + attributeMap.put("startDate", DATE_FORMAT.format(attribute.getStartDate())); + } - Representation representation = response.getEntity(); - try { - Status status = response.getStatus(); - if (status.isSuccess()) { - JacksonRepresentation<Map> entity = new JacksonRepresentation<Map>(response.getEntity(), Map.class); - newAttributeTemplate = entity.getObject(); + if (attribute.getStartDate() != null) { + attributeMap.put("endDate", DATE_FORMAT.format(attribute.getStartDate())); } - } finally { - if (representation != null) { - representation.release(); + attributeMap.put("comment", attribute.getComment()); + + // POST new attribute + Status status; + { + Request request = new Request(Method.POST, + mBaseUrl + "People/" + userId + "/Attributes.json"); + request.setChallengeResponse(mUser.getChallengeResponse()); + request.setEntity(new JacksonRepresentation<Map>(attributeTemplate)); + Response response = mOAuthHelper.getResponse(request); + + Representation representation = response.getEntity(); + try { + status = response.getStatus(); + + if (status.isSuccess()) { + return true; + } + + } finally { + if (representation != null) { + representation.release(); + } + } } + + LOG.debug("addAttribute failed POST: " + status); + return false; } - if (newAttributeTemplate == null) { - LOG.error("Could retrieve attribute template!"); - return; + /** + * @return an attribute id for the given attribute name. + */ + private String getAttributeId(String attributeName) throws F1Exception { + Map<String, String> attributeMap = getAttributeList(); + + return attributeMap.get(attributeName.toLowerCase()); } } - */ } diff --git a/src/com/p4square/f1oauth/F1Exception.java b/src/com/p4square/f1oauth/F1Exception.java new file mode 100644 index 0000000..54c1a77 --- /dev/null +++ b/src/com/p4square/f1oauth/F1Exception.java @@ -0,0 +1,15 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.f1oauth; + +public class F1Exception extends Exception { + public F1Exception(String message) { + super(message); + } + + public F1Exception(String message, Exception cause) { + super(message, cause); + } +} diff --git a/src/com/p4square/f1oauth/SecondPartyVerifier.java b/src/com/p4square/f1oauth/SecondPartyVerifier.java index e2d6d00..882c7e7 100644 --- a/src/com/p4square/f1oauth/SecondPartyVerifier.java +++ b/src/com/p4square/f1oauth/SecondPartyVerifier.java @@ -54,7 +54,7 @@ public class SecondPartyVerifier implements Verifier { OAuthUser ouser = mHelper.getAccessToken(username, password); // Once we have a user, fetch the people record to get the user id. - F1User user = getF1User(ouser); + F1User user = mHelper.getAuthenticatedApi(ouser).getF1User(ouser); user.setEmail(username); // This seems like a hack... but it'll work @@ -69,25 +69,4 @@ public class SecondPartyVerifier implements Verifier { return RESULT_INVALID; // Invalid credentials } - private F1User getF1User(OAuthUser user) throws OAuthException, IOException { - Request request = new Request(Method.GET, user.getLocation() + ".json"); - request.setChallengeResponse(user.getChallengeResponse()); - Response response = mDispatcher.handle(request); - - try { - Status status = response.getStatus(); - if (status.isSuccess()) { - JacksonRepresentation<Map> entity = new JacksonRepresentation<Map>(response.getEntity(), Map.class); - Map data = entity.getObject(); - return new F1User(user, data); - - } else { - throw new OAuthException(status); - } - } finally { - if (response.getEntity() != null) { - response.release(); - } - } - } } diff --git a/src/com/p4square/grow/GrowProcessComponent.java b/src/com/p4square/grow/GrowProcessComponent.java index 18b3d5b..791f177 100644 --- a/src/com/p4square/grow/GrowProcessComponent.java +++ b/src/com/p4square/grow/GrowProcessComponent.java @@ -10,7 +10,6 @@ import java.io.IOException; import org.apache.log4j.Logger; import org.restlet.Application; -import org.restlet.Client; import org.restlet.Component; import org.restlet.Restlet; import org.restlet.data.ChallengeScheme; diff --git a/src/com/p4square/grow/frontend/AssessmentResultsPage.java b/src/com/p4square/grow/frontend/AssessmentResultsPage.java index c205503..ff1832c 100644 --- a/src/com/p4square/grow/frontend/AssessmentResultsPage.java +++ b/src/com/p4square/grow/frontend/AssessmentResultsPage.java @@ -4,6 +4,7 @@ package com.p4square.grow.frontend; +import java.util.Date; import java.util.Map; import freemarker.template.Template; @@ -22,9 +23,12 @@ import com.p4square.fmfacade.json.JsonRequestClient; import com.p4square.fmfacade.json.JsonResponse; import com.p4square.fmfacade.json.ClientException; +import com.p4square.f1oauth.Attribute; +import com.p4square.f1oauth.F1API; import com.p4square.f1oauth.F1User; import com.p4square.grow.config.Config; +import com.p4square.grow.provider.JsonEncodedProvider; /** * This page fetches the user's final score and displays the transitional page between @@ -105,7 +109,23 @@ public class AssessmentResultsPage extends FreeMarkerPageResource { F1User user = (F1User) getRequest().getClientInfo().getUser(); - // TODO: Update the attribute. + // Update the attribute. + String attributeName = "Assessment Complete - " + results.get("result"); + + try { + Attribute attribute = new Attribute(); + attribute.setStartDate(new Date()); + attribute.setComment(JsonEncodedProvider.MAPPER.writeValueAsString(results)); + + F1API f1 = mGrowFrontend.getF1Access().getAuthenticatedApi(user); + if (!f1.addAttribute(user.getIdentifier(), attributeName, attribute)) { + LOG.error("addAttribute failed for " + user.getIdentifier() + + " with attribute " + attributeName); + } + } catch (Exception e) { + LOG.error("addAttribute failed for " + user.getIdentifier() + + " with attribute " + attributeName, e); + } } /** @@ -124,7 +144,8 @@ public class AssessmentResultsPage extends FreeMarkerPageResource { final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); final Status status = response.getStatus(); if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); + LOG.warn("Error making backend request for '" + uri + "'. status = " + + response.getStatus().toString()); } return response; diff --git a/src/com/p4square/grow/tools/AttributeTool.java b/src/com/p4square/grow/tools/AttributeTool.java new file mode 100644 index 0000000..0775087 --- /dev/null +++ b/src/com/p4square/grow/tools/AttributeTool.java @@ -0,0 +1,143 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.grow.tools; + +import java.util.Arrays; +import java.util.Date; +import java.util.Map; + +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.data.Protocol; + +import com.p4square.grow.config.Config; +import com.p4square.f1oauth.Attribute; +import com.p4square.f1oauth.F1Access; +import com.p4square.f1oauth.F1API; +import com.p4square.f1oauth.F1Exception; +import com.p4square.restlet.oauth.OAuthUser; + +/** + * Tool for manipulating F1 Attributes. + * + * @author Jesse Morgan <jesse@jesterpm.net> + */ +public class AttributeTool { + + private static Config mConfig; + private static F1API mF1API; + + public static void usage() { + System.out.println("java com.p4square.grow.tools.AttributeTool <command>...\n"); + System.out.println("Commands:"); + System.out.println("\t--domain <domain> Set config domain"); + System.out.println("\t--dev Set config domain to dev"); + System.out.println("\t--config <file> Merge in config file"); + System.out.println("\t--list List all attributes"); + System.out.println("\t--assign <user> <attribute> <comment> Assign an attribute"); + } + + public static void main(String... args) { + if (args.length == 0) { + usage(); + System.exit(1); + } + + mConfig = new Config(); + + try { + mConfig.updateConfig(AttributeTool.class.getResourceAsStream("/grow.properties")); + + int offset = 0; + while (offset < args.length) { + if ("--domain".equals(args[offset])) { + mConfig.setDomain(args[offset + 1]); + mF1API = null; + offset += 2; + + } else if ("--dev".equals(args[offset])) { + mConfig.setDomain("dev"); + mF1API = null; + offset += 1; + + } else if ("--config".equals(args[offset])) { + mConfig.updateConfig(args[offset + 1]); + mF1API = null; + offset += 2; + + } else if ("--list".equals(args[offset])) { + offset = list(args, ++offset); + + } else if ("--assign".equals(args[offset])) { + offset = assign(args, ++offset); + + } else { + throw new IllegalArgumentException("Unknown command " + args[offset]); + } + } + } catch (Exception e) { + e.printStackTrace(); + System.exit(2); + } + } + + private static F1API getF1API() throws Exception { + if (mF1API == null) { + Context context = new Context(); + Client client = new Client(context, Arrays.asList(Protocol.HTTP, Protocol.HTTPS)); + context.setClientDispatcher(client); + + F1Access f1Access = new F1Access(context, + mConfig.getString("f1ConsumerKey"), + mConfig.getString("f1ConsumerSecret"), + mConfig.getString("f1BaseUrl"), + mConfig.getString("f1ChurchCode"), + F1Access.UserType.WEBLINK); + + // Gather Username and Password + String username = System.console().readLine("F1 Username: "); + char[] password = System.console().readPassword("F1 Password: "); + + OAuthUser user = f1Access.getAccessToken(username, new String(password)); + Arrays.fill(password, ' '); // Lost cause, but I'll still try. + + mF1API = f1Access.getAuthenticatedApi(user); + } + + return mF1API; + } + + private static int list(String[] args, int offset) throws Exception { + final F1API f1 = getF1API(); + + final Map<String, String> attributes = f1.getAttributeList(); + System.out.printf("%7s %s\n", "ID", "Name"); + for (Map.Entry<String, String> entry : attributes.entrySet()) { + System.out.printf("%7s %s\n", entry.getValue(), entry.getKey()); + } + + return offset; + } + + private static int assign(String[] args, int offset) throws Exception { + final String userId = args[offset++]; + final String attributeName = args[offset++]; + final String comment = args[offset++]; + + final F1API f1 = getF1API(); + + Attribute attribute = new Attribute(); + attribute.setStartDate(new Date()); + attribute.setComment(comment); + + if (f1.addAttribute(userId, attributeName, attribute)) { + System.out.println("Added attribute " + attributeName + " for " + userId); + } else { + System.out.println("Failed to add attribute " + attributeName + " for " + userId); + } + + return offset; + } +} |