package org.karmaexchange.resources; import static com.google.common.base.Preconditions.checkState; import static org.karmaexchange.util.OfyService.ofy; import static org.karmaexchange.util.UserService.getCurrentUserKey; import java.net.URI; import java.util.Collections; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.karmaexchange.dao.Leaderboard; import org.karmaexchange.dao.Leaderboard.LeaderboardType; import org.karmaexchange.dao.Organization; import org.karmaexchange.dao.RequestStatus; import org.karmaexchange.dao.User; import org.karmaexchange.dao.Waiver; import org.karmaexchange.resources.msg.ErrorResponseMsg; import org.karmaexchange.resources.msg.ListResponseMsg; import org.karmaexchange.resources.msg.OrganizationMemberView; import org.karmaexchange.resources.msg.ErrorResponseMsg.ErrorInfo; import org.karmaexchange.resources.msg.WaiverSummaryView; import org.karmaexchange.util.OfyUtil; import org.karmaexchange.util.PaginatedQuery; import org.karmaexchange.util.PaginatedQuery.ConditionFilter; import org.karmaexchange.util.PaginatedQuery.StartsWithFilter; import com.google.common.collect.Lists; import com.googlecode.objectify.Key; @Path("/org") public class OrganizationResource extends ViewlessBaseDaoResource<Organization> { public static final String NAME_PREFIX_PARAM = "name_prefix"; public static final String ROLE_PARAM = "role"; public static final String MIN_ROLE_PARAM = "min_role"; public static final String MEMBERSHIP_STATUS_PARAM = "membership_status"; public static final String LEADERBOARD_TYPE_PARAM = "type"; public static final String INCLUDE_PARENT_ORGS_PARAM = "include_parent_orgs"; @Override protected void preProcessUpsert(Organization org) { if (!org.isKeyComplete()) { org.initFromPage(); } } @Override protected Key<Organization> keyFromId(String id) { return Organization.createKey(id); } @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getResources() { MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters(); String namePrefix = queryParams.getFirst(NAME_PREFIX_PARAM); PaginatedQuery.Builder<Organization> queryBuilder = PaginatedQuery.Builder.create(Organization.class, uriInfo, DEFAULT_NUM_SEARCH_RESULTS) .setOrder("searchableOrgName"); if (namePrefix != null) { queryBuilder.addFilter(new StartsWithFilter("searchableOrgName", namePrefix.toLowerCase())); } ListResponseMsg<Organization> orgs = ListResponseMsg.create(queryBuilder.build().execute()); return Response.ok(new GenericEntity<ListResponseMsg<Organization>>(orgs) {}).build(); } @Path("{org}/children") @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public ListResponseMsg<Organization> getChildren( @PathParam("org") String orgKeyStr) { Key<Organization> orgKey = OfyUtil.<Organization>createKey(orgKeyStr); List<Organization> childOrgs = ofy().load().type(Organization.class).filter("parentOrg.key", orgKey).list(); Collections.sort(childOrgs, Organization.OrgNameComparator.INSTANCE); return ListResponseMsg.create(childOrgs); } @Path("{org}/member") @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public ListResponseMsg<OrganizationMemberView> getMembers( @PathParam("org") String orgKeyStr, @QueryParam(ROLE_PARAM) Organization.Role role, @QueryParam(MEMBERSHIP_STATUS_PARAM) RequestStatus membershipStatus) { Key<Organization> orgKey = OfyUtil.<Organization>createKey(orgKeyStr); String membershipCondition; if (membershipStatus == RequestStatus.PENDING) { if (role != null) { throw ErrorResponseMsg.createException( "role can not be specified for PENDING membership requests", ErrorInfo.Type.BAD_REQUEST); } membershipCondition = "organizationMemberships.organizationPendingMembershipRequest.key"; } else { if (role == null) { role = Organization.Role.MEMBER; } if (role == Organization.Role.MEMBER) { membershipCondition = "organizationMemberships.organizationMember.key"; } else if (role == Organization.Role.ADMIN) { membershipCondition = "organizationMemberships.organizationMemberWithAdminRole.key"; } else { checkState(role == Organization.Role.ORGANIZER); membershipCondition = "organizationMemberships.organizationMemberWithOrganizerRole.key"; } } PaginatedQuery.Builder<User> queryBuilder = PaginatedQuery.Builder.create(User.class, uriInfo, DEFAULT_NUM_SEARCH_RESULTS) .addFilter(new ConditionFilter(membershipCondition, orgKey)) .setOrder("searchableFullName"); PaginatedQuery.Result<User> queryResult = queryBuilder.build().execute(); return ListResponseMsg.create( OrganizationMemberView.create(queryResult.getSearchResults(), orgKey), queryResult.getPagingInfo()); } @Path("{org}/member") @POST @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public void upsertMember( @PathParam("org") String orgKeyStr, @QueryParam("user") String userKeyStr, @QueryParam(ROLE_PARAM) Organization.Role role) { Key<Organization> orgKey = OfyUtil.<Organization>createKey(orgKeyStr); Key<User> userKey = (userKeyStr == null) ? getCurrentUserKey() : OfyUtil.<User>createKey(userKeyStr); if (role == null) { role = Organization.Role.MEMBER; } User.updateMembership(userKey, orgKey, role); } @Path("{org}/member") @DELETE public void deleteMember( @PathParam("org") String orgKeyStr, @QueryParam("user") String userKeyStr) { Key<Organization> orgKey = OfyUtil.<Organization>createKey(orgKeyStr); Key<User> userKey = (userKeyStr == null) ? getCurrentUserKey() : OfyUtil.<User>createKey(userKeyStr); User.updateMembership(userKey, orgKey, null); } @Path("{org}/leaderboard") @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Leaderboard getLeaderboard( @PathParam("org") String orgKeyStr, @QueryParam(LEADERBOARD_TYPE_PARAM) LeaderboardType type) { if (type == null) { type = LeaderboardType.ALL_TIME; } Key<Organization> orgKey = OfyUtil.<Organization>createKey(orgKeyStr); return ofy().load().key(Leaderboard.createKey(orgKey, type)).now(); } @Path("{org_key}/waiver") @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public ListResponseMsg<WaiverSummaryView> getWaivers( @PathParam("org_key") String orgKeyStr, @DefaultValue("false") @QueryParam(INCLUDE_PARENT_ORGS_PARAM) boolean includeParentOrgs) { Key<Organization> rootOrgKey = OfyUtil.<Organization>createKey(orgKeyStr); List<Organization> orgs; if (includeParentOrgs) { orgs = Organization.getOrgAndAncestorOrgs(rootOrgKey); } else { orgs = Lists.newArrayList(ofy().load().key(rootOrgKey).now()); } // Asynchronously launch the queries to fetch the waivers. List<Pair<Organization, ? extends Iterable<Waiver>>> orgsAndWaivers = Lists.newArrayList(); for (Organization org : orgs) { orgsAndWaivers.add(ImmutablePair.of(org, ofy().load().type(Waiver.class).ancestor(Key.create(org)).iterable())); } // Create the waiver summaries. List<WaiverSummaryView> waiverSummaries = Lists.newArrayList(); for (Pair<Organization, ? extends Iterable<Waiver>> orgAndWaiver : orgsAndWaivers) { waiverSummaries.addAll( WaiverSummaryView.create(orgAndWaiver.getLeft(), orgAndWaiver.getRight())); } Collections.sort(waiverSummaries, WaiverSummaryView.OrgAndDescriptionComparator.INSTANCE); return ListResponseMsg.create(waiverSummaries); } @Path("{org_key}/waiver") @POST @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response upsertWaiver( @PathParam("org_key") String orgKeyStr, Waiver waiver) { Key<Organization> orgKey = OfyUtil.<Organization>createKey(orgKeyStr); if (waiver == null) { throw ErrorResponseMsg.createException("waiver argument not specified", ErrorInfo.Type.BAD_REQUEST); } Waiver.insert(orgKey, waiver); URI uri = uriInfo.getBaseUriBuilder().path(WaiverResource.PATH).path(waiver.getKey()).build(); return Response.created(uri).build(); } }