From a7e6d6198af37083c78acf35d0367cc328c67a33 Mon Sep 17 00:00:00 2001 From: Adam Oren Date: Thu, 13 Dec 2012 17:00:46 -0500 Subject: [PATCH 1/2] merging from 1.0.65 to github --- NOTICE.txt | 152 ++++- client/pom.xml | 7 + .../java/org/slc/sli/api/client/Entity.java | 45 +- .../org/slc/sli/api/client/RESTClient.java | 231 +++++++ .../org/slc/sli/api/client/SLIClient.java | 204 ++----- .../sli/api/client/SLIClientException.java | 22 +- .../slc/sli/api/client/SLIClientFactory.java | 48 ++ .../sli/api/client/constants/EntityNames.java | 5 +- .../api/client/constants/ResourceNames.java | 26 +- .../constants/v1/ParameterConstants.java | 3 +- .../client/constants/v1/PathConstants.java | 10 +- .../slc/sli/api/client/impl/BasicClient.java | 200 +++---- .../api/client/impl/BasicClientFactory.java | 64 ++ .../sli/api/client/impl/BasicRESTClient.java | 353 +++++++++++ .../sli/api/client/impl/GenericEntity.java | 82 ++- .../slc/sli/api/client/impl/RESTClient.java | 562 ------------------ .../slc/sli/api/client/security/SliApi.java | 2 +- .../slc/sli/api/client/util/URLBuilder.java | 37 +- .../slc/sli/api/client/URLBuilderTest.java | 10 + .../sli/api/client/impl/BasicClientTest.java | 144 +++++ sample/sample.properties | 2 +- .../org/slc/sli/sample/oauth/AuthFilter.java | 99 +-- .../sample/oauth/TestRESTClientServlet.java | 330 ++++++++++ .../slc/sli/sample/oauth/TestSDKServlet.java | 63 +- .../slc/sli/sample/oauth/model/Cohorts.java | 17 +- .../oauth/model/DisciplineIncidents.java | 18 +- .../slc/sli/sample/oauth/model/Programs.java | 18 +- .../slc/sli/sample/oauth/model/Students.java | 17 +- .../slc/sli/sample/oauth/model/Teachers.java | 50 +- .../src/main/webapp/WEB-INF/restsdktest.jsp | 14 + sample/src/main/webapp/WEB-INF/web.xml | 11 + 31 files changed, 1815 insertions(+), 1031 deletions(-) create mode 100644 client/src/main/java/org/slc/sli/api/client/RESTClient.java create mode 100644 client/src/main/java/org/slc/sli/api/client/SLIClientFactory.java create mode 100644 client/src/main/java/org/slc/sli/api/client/impl/BasicClientFactory.java create mode 100644 client/src/main/java/org/slc/sli/api/client/impl/BasicRESTClient.java delete mode 100644 client/src/main/java/org/slc/sli/api/client/impl/RESTClient.java create mode 100644 client/src/test/java/org/slc/sli/api/client/impl/BasicClientTest.java create mode 100644 sample/src/main/java/org/slc/sli/sample/oauth/TestRESTClientServlet.java create mode 100644 sample/src/main/webapp/WEB-INF/restsdktest.jsp diff --git a/NOTICE.txt b/NOTICE.txt index ad09002..bba2fdc 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -4,8 +4,132 @@ Copyright 2012 Shared Learning Collaborative, LLC This product includes software developed by SLC (http://www.slcedu.org/). +%% The following software may be included in this product: JAX-RS + Use of any of this software is governed by the terms of the license below: + +Copyright (c) YYYY Oracle and/or its affiliates. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 2 only ("GPL") or the Common Development +and Distribution License("CDDL") (collectively, the "License"). You +may not use this file except in compliance with the License. You can +obtain a copy of the License at +http://glassfish.java.net/public/CDDL+GPL_1_1.html +or packager/legal/LICENSE.txt. See the License for the specific +language governing permissions and limitations under the License. + +When distributing the software, include this License Header Notice in each +file and include the License file at packager/legal/LICENSE.txt. + +GPL Classpath Exception: +Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the GPL Version 2 section of the License +file that accompanied this code. + +Modifications: +If applicable, add the following below the License Header, with the fields +enclosed by brackets [] replaced by your own identifying information: +"Portions Copyright [year] [name of copyright owner]" + +Contributor(s): +If you wish your version of this file to be governed by only the CDDL or +only the GPL Version 2, indicate your decision by adding "[Contributor] +elects to include this software in this distribution under the [CDDL or GPL +Version 2] license." If you don't indicate a single choice of license, a +recipient has the option to distribute your version of this file under +either the CDDL, the GPL Version 2 or to extend the choice of license to +its licensees as provided above. However, if you add GPL Version 2 code +and therefore, elected the GPL Version 2 license, then the option applies +only if the new code is made subject to such option by the copyright +holder. + +%% The following software may be included in this product: Jersey-Client + Use of any of this software is governed by the terms of the license below: + +Copyright (c) YYYY Oracle and/or its affiliates. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 2 only ("GPL") or the Common Development +and Distribution License("CDDL") (collectively, the "License"). You +may not use this file except in compliance with the License. You can +obtain a copy of the License at +http://glassfish.java.net/public/CDDL+GPL_1_1.html +or packager/legal/LICENSE.txt. See the License for the specific +language governing permissions and limitations under the License. + +When distributing the software, include this License Header Notice in each +file and include the License file at packager/legal/LICENSE.txt. + +GPL Classpath Exception: +Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the GPL Version 2 section of the License +file that accompanied this code. + +Modifications: +If applicable, add the following below the License Header, with the fields +enclosed by brackets [] replaced by your own identifying information: +"Portions Copyright [year] [name of copyright owner]" + +Contributor(s): +If you wish your version of this file to be governed by only the CDDL or +only the GPL Version 2, indicate your decision by adding "[Contributor] +elects to include this software in this distribution under the [CDDL or GPL +Version 2] license." If you don't indicate a single choice of license, a +recipient has the option to distribute your version of this file under +either the CDDL, the GPL Version 2 or to extend the choice of license to +its licensees as provided above. However, if you add GPL Version 2 code +and therefore, elected the GPL Version 2 license, then the option applies +only if the new code is made subject to such option by the copyright +holder. + + +%% The following software may be included in this product: Apache Commons IO + Use of any of this software is governed by the terms of the license below: + +Copyright 2002-2012 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +%% The following software may be included in this product: Apache Commons CLI + Use of any of this software is governed by the terms of the license below: + +Copyright 2002-2012 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +%% The following software may be included in this product: Jackson + Use of any of this software is governed by the terms of the license below: + +Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi + Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this software except in compliance with the License. +you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 @@ -16,3 +140,29 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +%% The following software may be included in this product: Scribe + Use of any of this software is governed by the terms of the license below: + +The MIT License + +Copyright (c) 2010 Pablo Fernandez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + diff --git a/client/pom.xml b/client/pom.xml index 3cee20c..5239e69 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -60,6 +60,13 @@ ${junit.version} test + + + org.mockito + mockito-all + 1.8.5 + test + diff --git a/client/src/main/java/org/slc/sli/api/client/Entity.java b/client/src/main/java/org/slc/sli/api/client/Entity.java index 6db76c1..b0f7c3a 100644 --- a/client/src/main/java/org/slc/sli/api/client/Entity.java +++ b/client/src/main/java/org/slc/sli/api/client/Entity.java @@ -17,6 +17,7 @@ package org.slc.sli.api.client; +import java.net.URL; import java.util.List; import java.util.Map; @@ -24,43 +25,43 @@ * Generic entity returned by the SLI API ReSTful service. Each entity has a unique * identifier, a data collection, and a collection of resource links. This interface provides * the most stripped down interface required by all entities. - * + * * Each entity returned by the SLI API contains a type string. This type identifies * the SLI data model type returned by the API. When creating or updating an entity, the * entity type must be provided. The API will reject entities that are not typed. - * + * * Data is represented as a map of maps. Value types are defined by the SLI data model. * The API validates all incoming entities and rejects entities that are missing required * fields or contain invalid values. - * + * * The API does entity validation against the SLI XML Schema. Attempts to create or update * an entity using data that does not conform to this schema are rejected by the API. - * + * * Each Entity returned by the API contains one or more links to associated resources. * These resources are context-sensitive. These links are read-only and cannot be altered * by client applications. - * + * * The fields returned by the API and the available resource links are context-sensitive based * on the role(s) assigned by an identity provider and associations between the user and * the resource, if any. Responses contain only the information available to the user * based on these roles and associations. Resources that are not associated with the * current user are not returned by the API. - * + * * @author asaarela */ public interface Entity { - + /** Key to locate 'links' field of the Entity. */ static final String LINKS_KEY = "links"; - + /** Key to locate the Entity's id field */ static final String ENTITY_ID_KEY = "id"; - + /** * Get the data associated with this entity. If the entity has no data, returns * an empty map. The key into this map is the property name. The values of this * map can one of the following JSON types: - * + * * - * + * * @return Map of data. */ Map getData(); - + /** * Get the type name for this entity. - * + * * @return EntityType for this entity - * + * * @see org.slc.sli.api.client.constants.EntityNames for a list of available names. */ String getEntityType(); - + /** * Get the ID for the entity. Each entity in the system has a unique identifier * assigned to it. - * + * * @return id String */ String getId(); - + /** * Get a list of links for this entity. If the entity has no links, returns an empty list. - * + * * @return a List of links. */ + @Deprecated List getLinks(); + + /** + * Get a map of resource name to link. + * + * @return a Map of resource name to link + */ + Map getLinkMap(); } diff --git a/client/src/main/java/org/slc/sli/api/client/RESTClient.java b/client/src/main/java/org/slc/sli/api/client/RESTClient.java new file mode 100644 index 0000000..1e9ca62 --- /dev/null +++ b/client/src/main/java/org/slc/sli/api/client/RESTClient.java @@ -0,0 +1,231 @@ +/* + * Copyright 2012 Shared Learning Collaborative, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.slc.sli.api.client; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.scribe.exceptions.OAuthException; + +/** + * Generic REST client. Provides the ability to connect to a ReSTful web service and make + * requests. + * + */ +public interface RESTClient { + + /** + * Retrieve the resource URL to the identity provider (IDP) used by application users + * to authenticate. The client application is responsible for redirecting the user + * to this URL. The response from this URL will contain the authorization token + * required to connect to the API. + * + * @return A URL that directs the user to authenticate with the appropriate IDP. On + * successful login, the IDP sends an authorization token to the callbackURL. + */ + public abstract URL getLoginURL(); + + + + + /** + * Connect to the SLI ReSTful API web service passing the authentication token provided by + * the IDP. The IDP will redirect successful login attempts to the callbackURL and include + * an authorization token in the response. You must then pass the authorization token to + * this call. + * + * If the code is invalid, an exception is thrown. + * + * @requestCode Code provided to the callbackURL by the IDP. + * @param authorizationCode + * Authorization request code returned by oauth to the callbackURL. + * @return HTTP Response to the request. + * @throws OAuthException + * @throws MalformedURLException + * @throws URISyntaxException + */ + public abstract Response connect(final String authorizationCode) + throws OAuthException, MalformedURLException, URISyntaxException; + + /** + * Connect to the API with a pre authorized token + * + * @param sessionToken authorized session token + */ + public void connectWithToken(final String sessionToken); + + /** + * Disconnect from the IDP. + */ + public abstract void disconnect(); + + /** + * Call the session/check API. If the SAML token is invalid or null, this will redirect + * to the realm selector page. + * + * @param token + * SAML token or null. + * @return String containing the authentication token. + * @throws URISyntaxException + * @throws IOException + */ + public abstract String sessionCheck(final String token) throws URISyntaxException, IOException; + + /** + * Make a synchronous GET request to a REST service. + * + * @param url + * full URL to the request. + * @return ClientResponse containing the status code and return values. + * @throws MalformedURLException + * @throws URISyntaxException + */ + public abstract Response getRequest(final URL url) throws MalformedURLException, URISyntaxException; + + /** + * Make a synchronous GET request to a REST service. The request includes additional header + * information. + * + * @param url + * Fully qualified URL to the ReSTful resource. + * @param headers + * key / value pairs of the headers to attach to the request. + * @return ClientResponse containing the status code and return value(s). + * @throws URISyntaxException + */ + public abstract Response getRequestWithHeaders(final URL url, final Map headers) + throws URISyntaxException; + + + /** + * Synchronously post a new entity to the REST service. This corresponds to a create operation. + * + * @param url + * Fully qualified URL to the ReSTful resource. + * @param json + * Json entity to post. + * @return ClientResponse containing the status code and return value(s). + * @throws URISyntaxException + * @throws MalformedURLException + */ + public abstract Response postRequest(final URL url, final String json) throws URISyntaxException, + MalformedURLException; + + /** + * Synchronously post a new entity to the REST service. This request includes additional header + * information. + * + * @param url + * Fully qualified URL to the ReSTful resource. + * @param json + * JSON to post. + * @param headers + * key / value pairs of the headers to attach to the request. A key can map + * to multiple values. + * @return ClientResponse containing the status code and return value(s). + * @throws URISyntaxException + * @throws MalformedURLException + */ + public abstract Response postRequestWithHeaders(final URL url, final String json, final Map headers) + throws URISyntaxException, MalformedURLException; + + /** + * Synchronous Put request to the REST service. This corresponds to an update operation. + * + * @param url + * Fully qualified URL to the ReSTful resource. + * @param json + * JSON of the entity to PUT. + * @return ClientResponse containing the status code and return value(s). + * @throws MalformedURLException + * @throws URISyntaxException + */ + public abstract Response putRequest(final URL url, final String json) throws MalformedURLException, + URISyntaxException; + + /** + * Synchronous Put request to the REST service. This corresponds to an update operation. + * This request includes additional header information. + * + * @param url + * Fully qualified URL to the ReSTful resource. + * @param json + * JSON of the entity to PUT. + * @param headers + * key / value pairs of the headers to attach to the request. A key can map + * to multiple values. + * @return ClientResponse containing the status code and return value(s). + * @throws MalformedURLException + * @throws URISyntaxException + */ + public abstract Response putRequestWithHeaders(final URL url, final String json, final Map headers) + throws MalformedURLException, URISyntaxException; + + /** + * Synchronously delete an existing entity using the REST service. + * + * @param url + * Fully qualified URL to the ReSTful resource. + * @return ClientResponse containing the status code and return value(s). + * @throws MalformedURLException + * @throws URISyntaxException + */ + public abstract Response deleteRequest(final URL url) throws MalformedURLException, URISyntaxException; + + /** + * Synchronously delete an existing entity using the REST service. This request includes + * additional header + * information. + * + * @param url + * Fully qualified URL to the ReSTful resource. + * @param headers + * key / value pairs of the headers to attach to the request. A key can map + * to multiple values. + * @return ClientResponse containing the status code and return value(s). + * @throws MalformedURLException + * @throws URISyntaxException + */ + public abstract Response deleteRequestWithHeaders(final URL url, final Map headers) + throws MalformedURLException, URISyntaxException; + + /** + * Get the base URL for all SLI API ReSTful service calls. + * + * @return Server URL string. + */ + public abstract String getBaseURL(); + + /** + * Set the sessionToken for all SLI API ReSTful service calls. + * + * @param sessionToken + */ + public abstract void setSessionToken(String sessionToken); + + /** + * Get the sessionToken for all SLI API ReSTful service calls. + * + * @return sessionToken + */ + public abstract String getSessionToken(); + +} diff --git a/client/src/main/java/org/slc/sli/api/client/SLIClient.java b/client/src/main/java/org/slc/sli/api/client/SLIClient.java index 1fbf7ea..3d439d1 100644 --- a/client/src/main/java/org/slc/sli/api/client/SLIClient.java +++ b/client/src/main/java/org/slc/sli/api/client/SLIClient.java @@ -14,20 +14,16 @@ * limitations under the License. */ - package org.slc.sli.api.client; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; -import java.net.URL; import java.util.List; import javax.ws.rs.MessageProcessingException; import javax.ws.rs.core.Response; -import org.scribe.exceptions.OAuthException; - import org.slc.sli.api.client.util.Query; /** @@ -49,66 +45,36 @@ */ public interface SLIClient { - /** - * Retrieve the resource URL to the identity provider (IDP) used by application users - * to authenticate. The client application is responsible for redirecting the user - * to this URL. The response from this URL will contain the authorization token - * required to connect to the API. - * - * @return A URL that directs the user to authenticate with the appropriate IDP. On - * successful login, the IDP sends an authorization token to the callbackURL. - */ - public abstract URL getLoginURL(); - - /** - * Connect to the SLI ReSTful API web service passing the authentication token provided by - * the IDP. The IDP will redirect successful login attempts to the callbackURL and include - * an authorization token in the response. You must then pass the authorization token to - * this call. - * - * If the code is invalid, an exception is thrown. - * - * @requestCode Code provided to the callbackURL by the IDP. - * @param authorizationToken - * Authorization token for the authenticated user, or null if - * authentication fails - * @return HTTP Response to the request. - * @throws OAuthException - */ - public abstract Response connect(final String requestCode, String authorizationToken) throws OAuthException; - - /** - * Logout and invalidate the session. - */ - public abstract void logout(); - /** * Create operation * * @param e * Entity to create - * @return Response to the update request. + * @return The id of the created entity * * @throws URISyntaxException * @throws IOException + * @throws SLIClientException + * thrown if the back end API was not able to process the create */ public abstract String create(final Entity e) throws IOException, URISyntaxException, SLIClientException; /** * Create operation * - * @param sessionToken - * Session token. - * @param resourceUrl - * The ReST resource url suffix * @param e * Entity to create - * @return Response to the create request. - * @throws MalformedURLException + * @param resourceUrl + * the url to post on + * @return The id of the created entity + * * @throws URISyntaxException + * @throws IOException + * @throws SLIClientException + * thrown if the back end API was not able to process the create */ - public abstract Response create(final String sessionToken, final String resourceUrl, final Entity e) - throws IOException, URISyntaxException; + public abstract String create(final Entity e, String resourceUrl) throws IOException, URISyntaxException, + SLIClientException; /** * Read operation by ID. @@ -121,15 +87,15 @@ public abstract Response create(final String sessionToken, final String resource * The ID of the entity to read. * @param query * Query parameters. - * @return ClientResponse from the ReST call. * * @throws URISyntaxException * @throws IOException * @throws MessageProcessingException * @throws SLIClientException + * thrown if the back end API was not able to process the read */ - public abstract Response read(List entities, final String type, final String id, final Query query) - throws URISyntaxException, MessageProcessingException, IOException; + public abstract void read(List entities, final String type, final String id, final Query query) + throws URISyntaxException, MessageProcessingException, IOException, SLIClientException; /** * Read operation @@ -140,129 +106,97 @@ public abstract Response read(List entities, final String type, final St * The type of entity * @param query * Query parameters. - * @return ClientResponse from the ReST call. * * @throws URISyntaxException * @throws IOException * @throws MessageProcessingException + * @throws SLIClientException + * thrown if the back end API was not able to process the read */ - public abstract Response read(List entities, final String type, final Query query) - throws URISyntaxException, MessageProcessingException, IOException; + public abstract void read(List entities, final String type, final Query query) throws URISyntaxException, + MessageProcessingException, IOException, SLIClientException; /** * Read operation * - * @param sessionToken - * Session token. - * @param entities - * Entities returned by the API. * @param resourceUrl - * The ReST resource url suffix - * @param entityClass - * Entity class. - * @return ClientResponse from the ReST call. + * The ReST resource url or url suffix + * @return The entities that were read * @throws MalformedURLException * @throws URISyntaxException + * @throws SLIClientException + * thrown if the back end API was not able to process the read */ - public abstract Response read(final String sessionToken, List entities, final String resourceUrl, Class entityClass) - throws URISyntaxException, MessageProcessingException, IOException; + public abstract List read(final String resourceUrl) throws URISyntaxException, MessageProcessingException, + IOException, SLIClientException; + /** + * Read operation + * + * @param resourceUrl + * The ReST resource url or url suffix + * @param query + * Query parameters. + * @return The entities that were read + * @throws MalformedURLException + * @throws URISyntaxException + * @throws SLIClientException + * thrown if the back end API was not able to process the read + */ + public abstract List read(final String resourceUrl, Query query) throws URISyntaxException, + MessageProcessingException, IOException, SLIClientException; /** * Update operation * * @param e * Entity to update. - * @return Response to the update request. * * @throws URISyntaxException * @throws IOException * @throws MessageProcessingException + * @throws SLIClientException + * thrown if the back end API was not able to process the update */ - public abstract Response update(final Entity e) throws URISyntaxException, MessageProcessingException, IOException; + public abstract void update(final Entity e) throws URISyntaxException, MessageProcessingException, IOException, + SLIClientException; /** - * Update operation + * Delete operation * - * @param sessionToken - * Session token. - * @param resourceUrl - * The ReST resource url suffix - * @param e - * Entity to update. - * @return Response to the update request. + * @param entityType + * the type of the entity + * @param entityId + * the id of the entity * @throws MalformedURLException * @throws URISyntaxException + * @throws SLIClientException + * thrown if the back end API was not able to process the update */ - public abstract Response update(final String sessionToken, final String resourceUrl, final Entity e) - throws IOException, URISyntaxException; - + public abstract void delete(final String entityType, final String entityId) throws MalformedURLException, + URISyntaxException, SLIClientException; /** * Delete operation * * @param e * Entity to delete - * @return Response to the delete request. - * - * @throws MalformedURLException - * @throws URISyntaxException - */ - public abstract void delete(final String entityType, final String entityId) throws MalformedURLException, URISyntaxException, SLIClientException; - - /** - * Delete operation * - * @param sessionToken - * Session token. - * @param resourceUrl - * The ReST resource url suffix - * @return Response to the delete request. * @throws MalformedURLException * @throws URISyntaxException + * @throws SLIClientException + * thrown if the back end API was not able to process the update */ - public abstract Response deleteByToken(final String sessionToken, final String resourceUrl) throws MalformedURLException, - URISyntaxException; - - /** - * Perform a get operation against a generic resource. This is useful when following links - * returned by other resources, for example. - * - * @param entities - * Entities returned by the API in response to this request. - * @param resourceURL - * URL to get - * @param query - * Query to append to the resource. - * @return ClientResponse from the ReST call. - * - * @throws URISyntaxException - * @throws IOException - * @throws MessageProcessingException - */ - public abstract Response getResource(List entities, URL resourceURL, Query query) - throws URISyntaxException, MessageProcessingException, IOException; + public abstract void delete(Entity e) throws MalformedURLException, URISyntaxException, SLIClientException; /** - * Perform a get operation against a generic resource. This is useful when following links - * returned by other resources, for example. + * Get a RESTClient for more low level operations * - * @param sessionToken - * Session token. - * @param entities - * Entities returned by the API in response to this request. - * @param restURL - * ReST URL to get - * @param entityClass - * Entity class. - * @return ClientResponse from the ReST call. - * @throws MalformedURLException - * @throws URISyntaxException + * @return */ - public abstract Response getResource(final String sessionToken, List entities, final URL restURL, Class entityClass) - throws URISyntaxException, MessageProcessingException, IOException; - + public abstract RESTClient getRESTClient(); + // Deprecated /** * Get the home resource for the authenticated user. * @@ -275,22 +209,4 @@ public abstract Response getResource(final String sessionToken, List entities, f public abstract Response getHomeResource(Entity home) throws URISyntaxException, MessageProcessingException, IOException; - /** - * Set access token - * - * @param sessionToken - * Session token - */ - public abstract void setToken(String sessionToken); - - /** - * Get access token - * - * @return sessionToken - * Session token - */ - public abstract String getToken(); - - - } diff --git a/client/src/main/java/org/slc/sli/api/client/SLIClientException.java b/client/src/main/java/org/slc/sli/api/client/SLIClientException.java index 7a2394f..1aa789d 100644 --- a/client/src/main/java/org/slc/sli/api/client/SLIClientException.java +++ b/client/src/main/java/org/slc/sli/api/client/SLIClientException.java @@ -13,15 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.slc.sli.api.client; -package org.slc.sli.api.client; - +/** + * Generic exception used primarily by the SLIClient implementation. + * + * @author Stephan Altmueller + * + */ +@SuppressWarnings("serial") public class SLIClientException extends Exception { - public SLIClientException() { - super(); - } + public SLIClientException() { + super(); + } - public SLIClientException(String msg) { - super(msg); - } + public SLIClientException(String msg) { + super(msg); + } } diff --git a/client/src/main/java/org/slc/sli/api/client/SLIClientFactory.java b/client/src/main/java/org/slc/sli/api/client/SLIClientFactory.java new file mode 100644 index 0000000..8f19ec7 --- /dev/null +++ b/client/src/main/java/org/slc/sli/api/client/SLIClientFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012 Shared Learning Collaborative, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.slc.sli.api.client; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; + +import org.scribe.exceptions.OAuthException; + +/** + * Factory for creating an SLI client + * + * @author nbrown + * + */ +public interface SLIClientFactory { + + /** + * Get an SLIClient with an authorization request code + * + * @param authorizationCode + * Authorization request code returned by oauth to the callbackURL. + * @return a connected SLIClient + */ + SLIClient getClientWithAuthCode(String authorizationCode) throws OAuthException, MalformedURLException, URISyntaxException; + + /** + * Get an SLIClient with a session token + * + * @param sessionToken + * SAML token + * @return a connected SLIClient + */ + SLIClient getClientWithSessionToken(String sessionToken) throws OAuthException, MalformedURLException, URISyntaxException; +} diff --git a/client/src/main/java/org/slc/sli/api/client/constants/EntityNames.java b/client/src/main/java/org/slc/sli/api/client/constants/EntityNames.java index 2d2acbe..19af942 100644 --- a/client/src/main/java/org/slc/sli/api/client/constants/EntityNames.java +++ b/client/src/main/java/org/slc/sli/api/client/constants/EntityNames.java @@ -34,6 +34,7 @@ public final class EntityNames { public static final String COURSE = "course"; public static final String COURSE_OFFERING = "courseOffering"; public static final String COURSE_SECTION_ASSOCIATION = "courseSectionAssociation"; + public static final String COURSE_TRANSCRIPT = "courseTranscript"; public static final String DISCIPLINE_INCIDENT = "disciplineIncident"; public static final String DISCIPLINE_ACTION = "disciplineAction"; public static final String EDUCATION_ORGANIZATION = "educationOrganization"; @@ -49,7 +50,6 @@ public final class EntityNames { public static final String REPORT_CARD = "reportCard"; public static final String SCHOOL = "school"; public static final String SECTION = "section"; - public static final String SECTION_ASSESSMENT_ASSOCIATION = "sectionAssessmentAssociation"; public static final String SESSION = "session"; public static final String STAFF = "staff"; public static final String STAFF_COHORT_ASSOCIATION = "staffCohortAssociation"; @@ -57,7 +57,7 @@ public final class EntityNames { public static final String STAFF_PROGRAM_ASSOCIATION = "staffProgramAssociation"; public static final String STUDENT = "student"; public static final String STUDENT_ACADEMIC_RECORD = "studentAcademicRecord"; - public static final String STUDENT_ASSESSMENT_ASSOCIATION = "studentAssessmentAssociation"; + public static final String STUDENT_ASSESSMENT = "studentAssessment"; public static final String STUDENT_COHORT_ASSOCIATION = "studentCohortAssociation"; public static final String STUDENT_COMPETENCY = "studentCompetency"; public static final String STUDENT_COMPETENCY_OBJECTIVE = "studentCompetencyObjective"; @@ -68,7 +68,6 @@ public final class EntityNames { public static final String STUDENT_SCHOOL_ASSOCIATION = "studentSchoolAssociation"; public static final String STUDENT_SECTION_ASSOCIATION = "studentSectionAssociation"; public static final String STUDENT_PARENT_ASSOCIATION = "studentParentAssociation"; - public static final String STUDENT_TRANSCRIPT_ASSOCIATION = "studentTranscriptAssociation"; public static final String TEACHER = "teacher"; public static final String TEACHER_SCHOOL_ASSOCIATION = "teacherSchoolAssociation"; public static final String TEACHER_SECTION_ASSOCIATION = "teacherSectionAssociation"; diff --git a/client/src/main/java/org/slc/sli/api/client/constants/ResourceNames.java b/client/src/main/java/org/slc/sli/api/client/constants/ResourceNames.java index a9439ff..dc54742 100644 --- a/client/src/main/java/org/slc/sli/api/client/constants/ResourceNames.java +++ b/client/src/main/java/org/slc/sli/api/client/constants/ResourceNames.java @@ -40,6 +40,7 @@ public final class ResourceNames { public static final String COMPETENCY_LEVEL_DESCRIPTORS = "competencyLevelDescriptor"; public static final String COMPETENCY_LEVEL_DESCRIPTOR_TYPES = "competencyLevelDescriptorTypes"; public static final String COURSES = "courses"; + public static final String COURSE_TRANSCRIPTS = "courseTranscripts"; public static final String DISCIPLINE_INCIDENTS = "disciplineIncidents"; public static final String DISCIPLINE_ACTIONS = "disciplineActions"; public static final String EDUCATION_ORGANIZATIONS = "educationOrganizations"; @@ -62,18 +63,16 @@ public final class ResourceNames { public static final String TEACHERS = "teachers"; public static final String GRADES = "grades"; - public static final String SCHOOL_SESSION_ASSOCIATIONS = "school-session-associations"; - public static final String SECTION_ASSESSMENT_ASSOCIATIONS = "section-assessment-associations"; + public static final String SCHOOL_SESSION_ASSOCIATIONS = "schoolSessionAssociations"; public static final String COURSE_OFFERINGS = "courseOfferings"; - public static final String STUDENT_ASSESSMENT_ASSOCIATIONS = "student-assessment-associations"; - public static final String STUDENT_SECTION_ASSOCIATIONS = "student-section-associations"; - public static final String STUDENT_SCHOOL_ASSOCIATIONS = "student-school-associations"; - public static final String TEACHER_SCHOOL_ASSOCIATIONS = "teacher-school-associations"; - public static final String TEACHER_SECTION_ASSOCIATIONS = "teacher-section-associations"; - public static final String STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS = "staff-educationOrganization-associations"; - public static final String STUDENT_TRANSCRIPT_ASSOCIATIONS = "studentTranscriptAssociations"; - public static final String STUDENT_PARENT_ASSOCIATIONS = "student-parent-associations"; - public static final String STUDENT_DISCIPLINE_INCIDENT_ASSOCIATIONS = "student-disciplineIncident-associations"; + public static final String STUDENT_ASSESSMENTS = "studentAssessments"; + public static final String STUDENT_SECTION_ASSOCIATIONS = "studentSectionAssociations"; + public static final String STUDENT_SCHOOL_ASSOCIATIONS = "studentSchoolAssociations"; + public static final String TEACHER_SCHOOL_ASSOCIATIONS = "teacherSchoolAssociations"; + public static final String TEACHER_SECTION_ASSOCIATIONS = "teacherSectionAssociations"; + public static final String STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS = "staffEducationOrganizationAssociations"; + public static final String STUDENT_PARENT_ASSOCIATIONS = "studentParentAssociations"; + public static final String STUDENT_DISCIPLINE_INCIDENT_ASSOCIATIONS = "studentDisciplineIncidentAssociations"; public static final String STAFF_PROGRAM_ASSOCIATIONS = "staffProgramAssociations"; public static final String STAFF_COHORT_ASSOCIATIONS = "staffCohortAssociations"; public static final String STUDENT_COHORT_ASSOCIATIONS = "studentCohortAssociations"; @@ -97,13 +96,12 @@ public final class ResourceNames { SINGULAR_LINK_NAMES.put(ResourceNames.STUDENT_SCHOOL_ASSOCIATIONS, "getStudentSchoolAssociation"); SINGULAR_LINK_NAMES.put(ResourceNames.TEACHER_SCHOOL_ASSOCIATIONS, "getTeacherSchoolAssociation"); SINGULAR_LINK_NAMES.put(ResourceNames.TEACHER_SECTION_ASSOCIATIONS, "getTeacherSectionAssociation"); - SINGULAR_LINK_NAMES.put(ResourceNames.SECTION_ASSESSMENT_ASSOCIATIONS, "getSectionAssessmentAssociation"); SINGULAR_LINK_NAMES.put(ResourceNames.COURSE_OFFERINGS, "getCourseOffering"); - SINGULAR_LINK_NAMES.put(ResourceNames.STUDENT_ASSESSMENT_ASSOCIATIONS, "getStudentAssessment"); + SINGULAR_LINK_NAMES.put(ResourceNames.STUDENT_ASSESSMENTS, "getStudentAssessment"); SINGULAR_LINK_NAMES.put(ResourceNames.STUDENT_SECTION_ASSOCIATIONS, "getStudentSectionAssociation"); SINGULAR_LINK_NAMES.put(ResourceNames.STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS, "getStaffEducationOrgAssignmentAssociation"); - SINGULAR_LINK_NAMES.put(ResourceNames.STUDENT_TRANSCRIPT_ASSOCIATIONS, "getCourseTranscript"); + SINGULAR_LINK_NAMES.put(ResourceNames.COURSE_TRANSCRIPTS, "getCourseTranscript"); SINGULAR_LINK_NAMES.put(ResourceNames.STUDENT_PARENT_ASSOCIATIONS, "getStudentParentAssociation"); SINGULAR_LINK_NAMES.put(ResourceNames.STUDENT_DISCIPLINE_INCIDENT_ASSOCIATIONS, "getStudentDisciplineIncidentAssociation"); diff --git a/client/src/main/java/org/slc/sli/api/client/constants/v1/ParameterConstants.java b/client/src/main/java/org/slc/sli/api/client/constants/v1/ParameterConstants.java index 514c147..ad82c84 100644 --- a/client/src/main/java/org/slc/sli/api/client/constants/v1/ParameterConstants.java +++ b/client/src/main/java/org/slc/sli/api/client/constants/v1/ParameterConstants.java @@ -152,7 +152,6 @@ public class ParameterConstants { public static final String STUDENT_SCHOOL_ASSOCIATION_ID = "studentSchoolAssociationId"; public static final String COURSE_OFFERING_ID = "courseOfferingId"; public static final String STUDENT_ASSESSMENT_ID = "studentAssessmentId"; - public static final String SECTION_ASSESSMENT_ASSOCIATION_ID = "sectionAssessmentAssociationId"; public static final String COURSE_TRANSCRIPT_ID = "courseTranscriptId"; public static final String STUDENT_PARENT_ASSOCIATION_ID = "studentParentAssociationId"; public static final String STUDENT_DISCIPLINE_INCIDENT_ASSOCIATION_ID = "studentDisciplineIncidentAssociationId"; @@ -162,7 +161,7 @@ public class ParameterConstants { public static final String STAFF_COHORT_ASSOCIATION_ID = "staffCohortAssociationId"; public static final String USER_ACCOUNT_ID = "userAccountId"; public static final String GRADE_ID = "gradeId"; - public static final String _ID = "_id"; + public static final String ID = "_id"; public static final String END_DATE = "endDate"; public static final String STUDENT_RECORD_ACCESS = "studentRecordAccess"; } diff --git a/client/src/main/java/org/slc/sli/api/client/constants/v1/PathConstants.java b/client/src/main/java/org/slc/sli/api/client/constants/v1/PathConstants.java index da0dcf5..8bca16d 100644 --- a/client/src/main/java/org/slc/sli/api/client/constants/v1/PathConstants.java +++ b/client/src/main/java/org/slc/sli/api/client/constants/v1/PathConstants.java @@ -34,9 +34,8 @@ public class PathConstants { public static final String TEACHER_SCHOOL_ASSOCIATIONS = "teacherSchoolAssociations"; public static final String TEACHER_SECTION_ASSOCIATIONS = "teacherSectionAssociations"; public static final String SCHOOL_SESSION_ASSOCIATIONS = "schoolSessionAssociations"; - public static final String SECTION_ASSESSMENT_ASSOCIATIONS = "sectionAssessmentAssociations"; public static final String COURSE_OFFERINGS = "courseOfferings"; - public static final String STUDENT_ASSESSMENT_ASSOCIATIONS = "studentAssessments"; + public static final String STUDENT_ASSESSMENTS = "studentAssessments"; public static final String STUDENT_SECTION_ASSOCIATIONS = "studentSectionAssociations"; public static final String STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS = "staffEducationOrgAssignmentAssociations"; public static final String EDUCATION_ORGANIZATION_ASSOCIATIONS = "educationOrganizationAssociations"; @@ -82,6 +81,8 @@ public class PathConstants { public static final String CUSTOM_ENTITIES = "custom"; + public static final String AGGREGATES = "aggregates"; + /** * Paths to various helper functions */ @@ -111,12 +112,11 @@ public class PathConstants { TEMP_MAP.put(ResourceNames.TEACHER_SCHOOL_ASSOCIATIONS, TEACHER_SCHOOL_ASSOCIATIONS); TEMP_MAP.put(ResourceNames.TEACHER_SECTION_ASSOCIATIONS, TEACHER_SECTION_ASSOCIATIONS); TEMP_MAP.put(ResourceNames.SCHOOL_SESSION_ASSOCIATIONS, SCHOOL_SESSION_ASSOCIATIONS); - TEMP_MAP.put(ResourceNames.SECTION_ASSESSMENT_ASSOCIATIONS, SECTION_ASSESSMENT_ASSOCIATIONS); TEMP_MAP.put(ResourceNames.COURSE_OFFERINGS, COURSE_OFFERINGS); - TEMP_MAP.put(ResourceNames.STUDENT_ASSESSMENT_ASSOCIATIONS, STUDENT_ASSESSMENT_ASSOCIATIONS); + TEMP_MAP.put(ResourceNames.STUDENT_ASSESSMENTS, STUDENT_ASSESSMENTS); TEMP_MAP.put(ResourceNames.STUDENT_SECTION_ASSOCIATIONS, STUDENT_SECTION_ASSOCIATIONS); TEMP_MAP.put(ResourceNames.STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS, STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS); - TEMP_MAP.put(ResourceNames.STUDENT_TRANSCRIPT_ASSOCIATIONS, COURSE_TRANSCRIPTS); + TEMP_MAP.put(ResourceNames.COURSE_TRANSCRIPTS, COURSE_TRANSCRIPTS); TEMP_MAP.put(ResourceNames.STUDENT_PARENT_ASSOCIATIONS, STUDENT_PARENT_ASSOCIATIONS); TEMP_MAP.put(ResourceNames.STUDENT_PROGRAM_ASSOCIATIONS, STUDENT_PROGRAM_ASSOCIATIONS); TEMP_MAP.put(ResourceNames.STAFF_PROGRAM_ASSOCIATIONS, STAFF_PROGRAM_ASSOCIATIONS); diff --git a/client/src/main/java/org/slc/sli/api/client/impl/BasicClient.java b/client/src/main/java/org/slc/sli/api/client/impl/BasicClient.java index 096162a..1e60220 100644 --- a/client/src/main/java/org/slc/sli/api/client/impl/BasicClient.java +++ b/client/src/main/java/org/slc/sli/api/client/impl/BasicClient.java @@ -14,17 +14,15 @@ * limitations under the License. */ - package org.slc.sli.api.client.impl; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.ws.rs.MessageProcessingException; import javax.ws.rs.core.Response; @@ -37,10 +35,10 @@ import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.node.ObjectNode; import org.codehaus.jackson.type.TypeReference; -import org.scribe.exceptions.OAuthException; import org.slc.sli.api.client.Entity; import org.slc.sli.api.client.Link; +import org.slc.sli.api.client.RESTClient; import org.slc.sli.api.client.SLIClient; import org.slc.sli.api.client.SLIClientException; import org.slc.sli.api.client.constants.v1.PathConstants; @@ -59,61 +57,65 @@ */ public class BasicClient implements SLIClient { + // handles the underlying communication with the API via HTTP private RESTClient restClient; - private static Logger logger = Logger.getLogger("BasicClient"); - ObjectMapper mapper = new ObjectMapper(); - @Override - public URL getLoginURL() { - return restClient.getLoginURL(); - } + // Entity (de-)serialization (from) to Json. + private ObjectMapper mapper = new ObjectMapper(); - @Override - public Response connect(final String requestCode, String authorizationToken) throws OAuthException { - try { - return restClient.connect(requestCode, authorizationToken); - } catch (MalformedURLException e) { - logger.log(Level.SEVERE, String.format("Invalid/malformed URL when connecting: %s", e.toString())); - } catch (URISyntaxException e) { - logger.log(Level.SEVERE, String.format("Invalid/malformed URL when connecting: %s", e.toString())); - } - return null; + /** + * Construct a new BasicClient instance, using the JSON message converter. + * + * @param restClient + * Instance of RESTClient that handles the low level HTTP operations. + */ + public BasicClient(final RESTClient restClient) { + this.restClient = restClient; } + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.SLIClient#create(org.slc.sli.api.client.Entity) + */ @Override - public void logout() { - // TODO - implement this when logout becomes available. + public String create(final Entity e) throws IOException, URISyntaxException, SLIClientException { + return create(e, null); } @Override - public String create(final Entity e) throws URISyntaxException, IOException, SLIClientException { - URL url = URLBuilder.create(restClient.getBaseURL()).entityType(e.getEntityType()).build(); - Response response = restClient.postRequest(url, mapper.writeValueAsString(e)); + public String create(final Entity e, String resourceUrl) throws IOException, URISyntaxException, SLIClientException { + URLBuilder builder = URLBuilder.create(restClient.getBaseURL()); + URL url; + if (resourceUrl != null && resourceUrl.startsWith(restClient.getBaseURL().toString())) { + url = new URL(resourceUrl); + } else { + if (resourceUrl == null) { + builder.entityType(e.getEntityType()); + } else if (!resourceUrl.startsWith(restClient.getBaseURL().toString())) { + builder.addPath(resourceUrl); + } + url = builder.build(); + } + Response response = restClient.postRequest(url, mapper.writeValueAsString(e.getData())); checkResponse(response, Status.CREATED, "Could not created entity."); // extract the id of the newly created entity from the header. String location = response.getHeader("Location"); return location.substring(location.lastIndexOf("/") + 1); - // return restClient.postRequest(this.getToken(), url, mapper.writeValueAsString(e)); NOTE: added + // return restClient.postRequest(this.getToken(), url, mapper.writeValueAsString(e)); NOTE: + // added } @Override - public Response create(final String sessionToken, final String resourceUrl, final Entity e) - throws URISyntaxException, IOException { - return restClient.postRequest(sessionToken, new URL(restClient.getBaseURL() + resourceUrl), - mapper.writeValueAsString(e)); + public void read(List entities, final String type, final Query query) throws URISyntaxException, + MessageProcessingException, IOException, SLIClientException { + read(entities, type, null, query); } @Override - public Response read(List entities, final String type, final Query query) throws URISyntaxException, - MessageProcessingException, IOException { - - return read(entities, type, null, query); - } - - @Override - public Response read(List entities, final String type, final String id, final Query query) - throws URISyntaxException, MessageProcessingException, IOException { + public void read(final List entities, final String type, final String id, final Query query) + throws URISyntaxException, MessageProcessingException, IOException, SLIClientException { entities.clear(); @@ -122,45 +124,49 @@ public Response read(List entities, final String type, final String id, builder.id(id); } - return getResource(entities, builder.build(), query); + Response response = getResource(entities, builder.build(), query); + checkResponse(response, Status.OK, "Unable to retrieve entity."); } @Override - public Response read(final String sessionToken, List entities, final String resourceUrl, Class entityClass) - throws URISyntaxException, MessageProcessingException, IOException { - entities.clear(); - return getResource(sessionToken, entities, new URL(restClient.getBaseURL() + resourceUrl), entityClass); + public List read(final String resourceUrl, Query query) throws URISyntaxException, + MessageProcessingException, IOException, SLIClientException { + List entities = new ArrayList(); + URL url = resourceUrl.startsWith(restClient.getBaseURL()) ? new URL(resourceUrl) : URLBuilder + .create(restClient.getBaseURL()).addPath(resourceUrl).build(); + Response response = getResource(entities, url, query); + checkResponse(response, Status.OK, "Unable to retrieve entity."); + return entities; } @Override - public Response update(final Entity e) throws URISyntaxException, MessageProcessingException, IOException { - URL url = URLBuilder.create(restClient.getBaseURL()).entityType(e.getEntityType()).id(e.getId()).build(); - return restClient.putRequest(url, mapper.writeValueAsString(e)); + public List read(final String resourceUrl) throws URISyntaxException, MessageProcessingException, + IOException, SLIClientException { + return read(resourceUrl, BasicQuery.EMPTY_QUERY); } @Override - public void delete(final String entityType, final String entityId) throws MalformedURLException, URISyntaxException, SLIClientException { - URL url = URLBuilder.create(restClient.getBaseURL()).entityType(entityType).id(entityId).build(); - checkResponse(restClient.deleteRequest(url), Status.NO_CONTENT, "Could not delete entity."); + public void update(final Entity e) throws URISyntaxException, MessageProcessingException, IOException, + SLIClientException { + URL url = URLBuilder.create(restClient.getBaseURL()).entityType(e.getEntityType()).id(e.getId()).build(); + Response response = restClient.putRequest(url, mapper.writeValueAsString(e)); + checkResponse(response, Status.NO_CONTENT, "Unable to update entity."); } @Override - public Response update(final String sessionToken, final String resourceUrl, final Entity e) - throws IOException, URISyntaxException { - return restClient.putRequest(sessionToken, new URL(restClient.getBaseURL() + resourceUrl), - mapper.writeValueAsString(e)); + public void delete(final String entityType, final String entityId) throws MalformedURLException, + URISyntaxException, SLIClientException { + URL url = URLBuilder.create(restClient.getBaseURL()).entityType(entityType).id(entityId).build(); + checkResponse(restClient.deleteRequest(url), Status.NO_CONTENT, "Could not delete entity."); } - - @SuppressWarnings("unchecked") @Override - public Response deleteByToken(final String sessionToken, final String resourceUrl) throws URISyntaxException, MalformedURLException { - return restClient.deleteRequest(sessionToken, new URL(restClient.getBaseURL() + resourceUrl)); + public void delete(Entity e) throws MalformedURLException, URISyntaxException, SLIClientException { + URL url = URLBuilder.create(restClient.getBaseURL()).entityType(e.getEntityType()).id(e.getId()).build(); + checkResponse(restClient.deleteRequest(url), Status.NO_CONTENT, "Could not delete entity."); } - @SuppressWarnings("unchecked") - @Override - public Response getResource(List entities, URL resourceURL, Query query) throws URISyntaxException, + public Response getResource(final List entities, URL resourceURL, Query query) throws URISyntaxException, MessageProcessingException, IOException { entities.clear(); @@ -194,44 +200,6 @@ public Response getResource(List entities, URL resourceURL, Query query) return response; } - @SuppressWarnings("unchecked") - @Override - public Response getResource(final String sessionToken, List entities, URL restURL, Class entityClass) - throws URISyntaxException, MessageProcessingException, IOException { - entities.clear(); - - Response response = restClient.getRequest(sessionToken, restURL); - if (response.getStatus() == Response.Status.OK.getStatusCode()) { - - try { - JsonNode element = mapper.readValue(response.readEntity(String.class), JsonNode.class); - - if (element.isArray()) { - ArrayNode arrayNode = (ArrayNode) element; - for (int i = 0; i < arrayNode.size(); ++i) { - JsonNode jsonObject = arrayNode.get(i); - Object entity = mapper.readValue(jsonObject, entityClass); - entities.add(entity); - } - } else if (element instanceof ObjectNode) { - Object entity = mapper.readValue(element, entityClass); - entities.add(entity); - } else { - // not what was expected.... - ResponseBuilder builder = Response.fromResponse(response); - builder.status(Response.Status.INTERNAL_SERVER_ERROR); - return builder.build(); - } - } catch (JsonParseException e) { - // invalid Json, or non-Json response? - ResponseBuilder builder = Response.fromResponse(response); - builder.status(Response.Status.INTERNAL_SERVER_ERROR); - return builder.build(); - } - } - return response; - } - @Override public Response getHomeResource(Entity home) throws URISyntaxException, MessageProcessingException, IOException { @@ -255,40 +223,26 @@ public Response getHomeResource(Entity home) throws URISyntaxException, MessageP return response; } - /** - * Construct a new BasicClient instance, using the JSON message converter. - * - * @param apiServerURL - * Fully qualified URL to the root of the API server. - * @param clientId - * Unique client identifier for this application. - * @param clientSecret - * Unique client secret value for this application. - * @param callbackURL - * URL used to redirect after authentication. - */ - public BasicClient(final URL apiServerURL, final String clientId, final String clientSecret, final URL callbackURL) { - restClient = new RESTClient(apiServerURL, clientId, clientSecret, callbackURL); + public String sessionCheck(final String token) throws URISyntaxException, IOException { + return restClient.sessionCheck(token); } /** - * Set the sessionToken for all SLI API ReSTful service calls. * - * @param sessionToken */ @Override - public void setToken(String sessionToken) { - restClient.setSessionToken(sessionToken); - } - - @Override - public String getToken() { - return restClient.getSessionToken(); + public RESTClient getRESTClient() { + return restClient; } + /* + * Checks the response status of the HTTP request against the expected status and throws an + * exception with the provided error message if the status doesn't match. + */ private void checkResponse(Response response, Status status, String msg) throws SLIClientException { if (response.getStatus() != status.getStatusCode()) { - throw new SLIClientException(msg + "Receveived status code " + response.getStatus() + ". Expected " + status.getStatusCode() + "."); + throw new SLIClientException(msg + "Receveived status code " + response.getStatus() + ". Expected " + + status.getStatusCode() + "."); } } } diff --git a/client/src/main/java/org/slc/sli/api/client/impl/BasicClientFactory.java b/client/src/main/java/org/slc/sli/api/client/impl/BasicClientFactory.java new file mode 100644 index 0000000..863c05a --- /dev/null +++ b/client/src/main/java/org/slc/sli/api/client/impl/BasicClientFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012 Shared Learning Collaborative, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.slc.sli.api.client.impl; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; + +import org.scribe.exceptions.OAuthException; + +import org.slc.sli.api.client.SLIClient; +import org.slc.sli.api.client.SLIClientFactory; + +/** + * Basic implmentation of an SLIClientFactory + * + * @author nbrown + * + */ +public class BasicClientFactory implements SLIClientFactory { + private final URL apiServerURL; + private final String clientId; + private final String clientSecret; + private final URL callbackURL; + + public BasicClientFactory(URL apiServerURL, String clientId, String clientSecret, URL callbackURL) { + super(); + this.apiServerURL = apiServerURL; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.callbackURL = callbackURL; + } + + @Override + public SLIClient getClientWithAuthCode(String authorizationCode) throws OAuthException, MalformedURLException, + URISyntaxException { + BasicRESTClient restClient = new BasicRESTClient(apiServerURL, clientId, clientSecret, callbackURL); + restClient.connect(authorizationCode); + SLIClient client = new BasicClient(restClient); + return client; + } + + @Override + public SLIClient getClientWithSessionToken(String sessionToken) { + BasicRESTClient restClient = new BasicRESTClient(apiServerURL, clientId, clientSecret, callbackURL); + restClient.connectWithToken(sessionToken); + SLIClient client = new BasicClient(restClient); + return client; + } + +} diff --git a/client/src/main/java/org/slc/sli/api/client/impl/BasicRESTClient.java b/client/src/main/java/org/slc/sli/api/client/impl/BasicRESTClient.java new file mode 100644 index 0000000..dfd4b65 --- /dev/null +++ b/client/src/main/java/org/slc/sli/api/client/impl/BasicRESTClient.java @@ -0,0 +1,353 @@ +/* + * Copyright 2012 Shared Learning Collaborative, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.slc.sli.api.client.impl; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientFactory; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.scribe.builder.ServiceBuilder; +import org.scribe.exceptions.OAuthException; +import org.scribe.model.OAuthConfig; +import org.scribe.model.Token; +import org.scribe.model.Verifier; +import org.scribe.oauth.OAuthService; + +import org.slc.sli.api.client.RESTClient; +import org.slc.sli.api.client.constants.v1.PathConstants; +import org.slc.sli.api.client.security.SliApi; + +/** + * + * Generic REST client. Provides the ability to connect to a ReSTful web service and make + * + * requests. + */ + +public class BasicRESTClient implements RESTClient { + + private static final String SESSION_CHECK_PREFIX = "system/session/check"; + private static Logger logger = Logger.getLogger("RESTClient"); + private String apiServerUri = null; + private Client client = null; + private SliApi sliApi = null; + private String sessionToken = null; + private OAuthConfig config; + private Token accessToken; + + /** + * + * Construct a new RESTClient instance. + * + * @param apiServerURL + * Fully qualified URL to the root of the API server. + * + * @param clientId + * Unique client identifier for this application. + * + * @param clientSecret + * Unique client secret value for this application. + * + * @param callbackURL + * URL used to redirect after authentication. + */ + + public BasicRESTClient(final URL apiServerURL, final String clientId, final String clientSecret, + final URL callbackURL) { + + client = ClientFactory.newClient(); + + apiServerUri = apiServerURL.toString().endsWith("/") ? apiServerURL.toString() + PathConstants.API_SERVER_PATH + : apiServerURL.toString() + "/" + PathConstants.API_SERVER_PATH; + + sliApi = new SliApi(); + SliApi.setBaseUrl(apiServerURL); + config = new OAuthConfig(clientId, clientSecret, callbackURL.toString(), null, null, null); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#getLoginURL() + */ + @Override + public URL getLoginURL() { + try { + return new URL(sliApi.getAuthorizationUrl(config)); + } catch (MalformedURLException e) { + logger.log(Level.SEVERE, String.format("Failed to create login URL: %s", e.toString())); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#connect(java.lang.String, java.lang.String) + */ + @Override + public Response connect(final String authorizationCode) throws OAuthException, MalformedURLException, + URISyntaxException { + OAuthService service = new ServiceBuilder().provider(SliApi.class).apiKey(config.getApiKey()) + .apiSecret(config.getApiSecret()).callback(config.getCallback()).build(); + Verifier verifier = new Verifier(authorizationCode); + Token t = null; + SliApi.TokenResponse r = ((SliApi.SLIOauth20ServiceImpl) service).getAccessToken( + new Token(config.getApiSecret(), authorizationCode), verifier, t); + + if (r != null && r.getToken() != null) { + accessToken = r.getToken(); + sessionToken = accessToken.getToken(); + } + + ResponseBuilder builder = Response.status(r.getOauthResponse().getCode()); + for (Map.Entry entry : r.getOauthResponse().getHeaders().entrySet()) { + if (entry.getKey() == null) { + builder.header("Status", entry.getValue()); + } else { + builder.header(entry.getKey(), entry.getValue()); + } + } + builder.entity(r.getOauthResponse().getBody()); + + return builder.build(); + } + + @Override + public void connectWithToken(String sessionToken) { + this.sessionToken = sessionToken; + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#disconnect() + */ + @Override + public void disconnect() { + // TODO... + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#sessionCheck(java.lang.String) + */ + @Override + public String sessionCheck(final String token) throws URISyntaxException, IOException { + logger.info("Session check URL = " + SESSION_CHECK_PREFIX); + URL url = new URL(apiServerUri + "/" + SESSION_CHECK_PREFIX); + Response response = getRequest(url); + String jsonText = response.readEntity(String.class); + ObjectMapper mapper = new ObjectMapper(); + JsonNode obj = mapper.readTree(jsonText); + if (obj.has("authenticated")) { + JsonNode e = obj.get("authenticated"); + if (e.getBooleanValue()) { + // e = obj.get("sessionId"); + // sessionToken = e.asText(); + return sessionToken; + } + } + return ""; + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#getRequest(java.net.URL) + */ + @Override + public Response getRequest(final URL url) throws MalformedURLException, URISyntaxException { + return getRequestWithHeaders(url, null); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#getRequestWithHeaders(java.net.URL, + * java.util.Map) + */ + @Override + public Response getRequestWithHeaders(final URL url, final Map headers) throws URISyntaxException { + if (sessionToken == null) { + logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); + return null; + } + Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); + builder = getCommonRequestBuilder(sessionToken, builder, headers); + Invocation i = builder.buildGet(); + return i.invoke(); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#getSessionToken() + */ + @Override + public String getSessionToken() { + return this.sessionToken; + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#postRequest(java.net.URL, java.lang.String) + */ + @Override + public Response postRequest(final URL url, final String json) throws URISyntaxException, MalformedURLException { + return postRequestWithHeaders(url, json, null); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#postRequestWithHeaders(java.net.URL, + * java.lang.String, java.util.Map) + */ + @Override + public Response postRequestWithHeaders(final URL url, final String json, final Map headers) + throws URISyntaxException, MalformedURLException { + if (sessionToken == null) { + logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); + return null; + } + + Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); + builder = getCommonRequestBuilder(sessionToken, builder, headers); + Invocation i = builder.buildPost(javax.ws.rs.client.Entity.entity(json, MediaType.APPLICATION_JSON)); + return i.invoke(); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#putRequest(java.net.URL, java.lang.String) + */ + @Override + public Response putRequest(final URL url, final String json) throws MalformedURLException, URISyntaxException { + return putRequestWithHeaders(url, json, null); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#putRequestWithHeaders(java.net.URL, + * java.lang.String, java.util.Map) + */ + @Override + public Response putRequestWithHeaders(final URL url, final String json, final Map headers) + throws MalformedURLException, URISyntaxException { + if (sessionToken == null) { + logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); + return null; + } + + Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); + builder = getCommonRequestBuilder(sessionToken, builder, headers); + Invocation i = builder.buildPut(javax.ws.rs.client.Entity.entity(json, MediaType.APPLICATION_JSON)); + + return i.invoke(); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#deleteRequest(java.net.URL) + */ + @Override + public Response deleteRequest(final URL url) throws MalformedURLException, URISyntaxException { + return deleteRequestWithHeaders(url, null); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#deleteRequestWithHeaders(java.net.URL, + * java.util.Map) + */ + @Override + public Response deleteRequestWithHeaders(final URL url, final Map headers) + throws MalformedURLException, URISyntaxException { + if (sessionToken == null) { + logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); + return null; + } + Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); + builder = getCommonRequestBuilder(sessionToken, builder, headers); + Invocation i = builder.buildDelete(); + return i.invoke(); + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#getBaseURL() + */ + @Override + public String getBaseURL() { + return apiServerUri; + } + + /* + * (non-Javadoc) + * + * @see org.slc.sli.api.client.impl.IRESTClient#setSessionToken(java.lang.String) + */ + @Override + public void setSessionToken(String sessionToken) { + this.sessionToken = sessionToken; + } + + /** + * + * Get a ClientRequest.Builder with common properties already set. + * + * @param headers + * + * @return + */ + private Invocation.Builder getCommonRequestBuilder(String sessionToken, Invocation.Builder builder, + Map headers) { + if (headers == null) { + headers = new HashMap(); + } + + headers.put("Authorization", String.format("Bearer %s", sessionToken)); + + for (Map.Entry entry : headers.entrySet()) { + builder.header(entry.getKey(), entry.getValue()); + } + + return builder; + } +} \ No newline at end of file diff --git a/client/src/main/java/org/slc/sli/api/client/impl/GenericEntity.java b/client/src/main/java/org/slc/sli/api/client/impl/GenericEntity.java index c36bdc2..94b178f 100644 --- a/client/src/main/java/org/slc/sli/api/client/impl/GenericEntity.java +++ b/client/src/main/java/org/slc/sli/api/client/impl/GenericEntity.java @@ -17,33 +17,36 @@ package org.slc.sli.api.client.impl; +import java.net.URL; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.codehaus.jackson.map.annotate.JsonDeserialize; import org.codehaus.jackson.map.annotate.JsonSerialize; + import org.slc.sli.api.client.Entity; import org.slc.sli.api.client.Link; import org.slc.sli.api.client.impl.transform.GenericEntityDeserializer; import org.slc.sli.api.client.impl.transform.GenericEntitySerializer; /** - * Generic implementation of the Entity interface. This is implements the Entity interface + * Generic implementation of the Entity interface. This implements the Entity interface * in the most generic way possible. - * + * * @author asaarela */ @JsonSerialize(using = GenericEntitySerializer.class, include = JsonSerialize.Inclusion.NON_NULL) @JsonDeserialize(using = GenericEntityDeserializer.class) public class GenericEntity implements Entity { - + private final String type; - + private final Map data; - + /** * Construct a new generic entity. - * + * * @param type * Entity type for this entity. * @param body @@ -55,7 +58,7 @@ public GenericEntity(final String type, final Map data) { this.type = type; this.data = data; } - + @Override public String getId() { if (data.containsKey(ENTITY_ID_KEY)) { @@ -63,24 +66,81 @@ public String getId() { } return null; } - + @Override public Map getData() { return data; } - + @SuppressWarnings("unchecked") @Override public List getLinks() { - + if (data.containsKey(LINKS_KEY)) { return (List) data.get(LINKS_KEY); } return null; } - + @Override public String getEntityType() { return type; } + + @Override + public String toString() { + return "GenericEntity [type=" + type + ", data=" + data + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((data == null) ? 0 : data.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GenericEntity other = (GenericEntity) obj; + if (data == null) { + if (other.data != null) { + return false; + } + } else if (!data.equals(other.data)) { + return false; + } + if (type == null) { + if (other.type != null) { + return false; + } + } else if (!type.equals(other.type)) { + return false; + } + return true; + } + + @Override + public Map getLinkMap() { + List links = getLinks(); + if (links != null) { + Map linkMap = new HashMap(); + for (Link link : links) { + linkMap.put(link.getLinkName(), link.getResourceURL()); + } + return linkMap; + } + return null; + } + } diff --git a/client/src/main/java/org/slc/sli/api/client/impl/RESTClient.java b/client/src/main/java/org/slc/sli/api/client/impl/RESTClient.java deleted file mode 100644 index 7ea646b..0000000 --- a/client/src/main/java/org/slc/sli/api/client/impl/RESTClient.java +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright 2012 Shared Learning Collaborative, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.slc.sli.api.client.impl; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientFactory; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; - -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.map.ObjectMapper; -import org.scribe.builder.ServiceBuilder; -import org.scribe.model.OAuthConfig; -import org.scribe.model.Token; -import org.scribe.model.Verifier; -import org.scribe.oauth.OAuthService; - -import org.slc.sli.api.client.constants.v1.PathConstants; -import org.slc.sli.api.client.security.SliApi; - -/** - * Generic REST client. Provides the ability to connect to a ReSTful web service and make - * requests. - */ -public class RESTClient { - - private static final String SESSION_CHECK_PREFIX = "api/rest/v1/system/session/check"; - - private static Logger logger = Logger.getLogger("RESTClient"); - private String apiServerUri = null; - private Client client = null; - private SliApi sliApi = null; - private String sessionToken = null; - private OAuthConfig config; - - private Token accessToken; - - /** - * Construct a new RESTClient instance. - * - * @param apiServerURL - * Fully qualified URL to the root of the API server. - * @param clientId - * Unique client identifier for this application. - * @param clientSecret - * Unique client secret value for this application. - * @param callbackURL - * URL used to redirect after authentication. - */ - public RESTClient(final URL apiServerURL, final String clientId, final String clientSecret, final URL callbackURL) { - client = ClientFactory.newClient(); - apiServerUri = apiServerURL.toString().endsWith("/") ? apiServerURL.toString() + PathConstants.API_SERVER_PATH - : apiServerURL.toString() + "/" + PathConstants.API_SERVER_PATH; - - sliApi = new SliApi(); - SliApi.setBaseUrl(apiServerURL); - - config = new OAuthConfig(clientId, clientSecret, callbackURL.toString(), null, null, null); - } - - /** - * Get the URL used to authenticate with the IDP. - * - * @return URL - */ - public URL getLoginURL() { - try { - return new URL(sliApi.getAuthorizationUrl(config)); - } catch (MalformedURLException e) { - logger.log(Level.SEVERE, String.format("Failed to create login URL: %s", e.toString())); - } - return null; - } - - /** - * Connect to the IDP and redirect to the callback URL. - * - * @param requestCode - * Authorization request code returned by oauth to the callbackURL. - * @param authorizationToken - * for the authenticated user, or null if the request failed. - * @return Response containing the status code, headers, and body values. - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response connect(final String authorizationCode, String authorizationToken) throws MalformedURLException, - URISyntaxException { - - OAuthService service = new ServiceBuilder().provider(SliApi.class).apiKey(config.getApiKey()) - .apiSecret(config.getApiSecret()).callback(config.getCallback()).build(); - - Verifier verifier = new Verifier(authorizationCode); - - Token t = null; - - SliApi.TokenResponse r = ((SliApi.SLIOauth20ServiceImpl) service).getAccessToken( - new Token(config.getApiSecret(), authorizationCode), verifier, t); - - if (r != null && r.getToken() != null) { - accessToken = r.getToken(); - sessionToken = accessToken.getToken(); - } - - ResponseBuilder builder = Response.status(r.getOauthResponse().getCode()); - for (Map.Entry entry : r.getOauthResponse().getHeaders().entrySet()) { - if (entry.getKey() == null) { - builder.header("Status", entry.getValue()); - } else { - builder.header(entry.getKey(), entry.getValue()); - } - } - builder.entity(r.getOauthResponse().getBody()); - - return builder.build(); - } - - /** - * Disconnect from the IDP. - */ - public void disconnect() { - // TODO... - } - - /** - * Call the session/check API. If the SAML token is invalid or null, this will redirect - * to the realm selector page. - * - * @param token - * SAML token or null. - * @param redirectUrl - * The redirect URL after a successful authentication - set by the Security API. - * @return String containing the authentication token. - * @throws URISyntaxException - * @throws IOException - */ - public String sessionCheck(final String token) throws URISyntaxException, IOException { - logger.info("Session check URL = " + SESSION_CHECK_PREFIX); - - URL url = new URL(apiServerUri + "/" + SESSION_CHECK_PREFIX); - - Response response = getRequest(token, url); - - String jsonText = response.readEntity(String.class); - - ObjectMapper mapper = new ObjectMapper(); - JsonNode obj = mapper.readTree(jsonText); - - if (obj.has("authenticated")) { - JsonNode e = obj.get("authenticated"); - if (e.getBooleanValue()) { - e = obj.get("sessionId"); - sessionToken = e.asText(); - } - } - - return sessionToken; - } - - /** - * Make a synchronous GET request to a REST service. - * - * @param url - * full URL to the request. - * @return ClientResponse containing the status code and return values. - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response getRequest(final URL url) throws MalformedURLException, URISyntaxException { - return getRequest(this.sessionToken, url); - } - - /** - * Make a synchronous GET request to a REST service. - * - * @param sessionToken - * Session token. - * @param url - * full URL to the request. - * @return ClientResponse containing the status code and return values. - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response getRequest(final String sessionToken, final URL url) throws MalformedURLException, - URISyntaxException { - return getRequestWithHeaders(sessionToken, url, null); - } - - /** - * Make a synchronous GET request to a REST service. The request includes additional header - * information. - * - * @param url - * - * @param URL - * Fully qualified URL to the ReSTful resource. - * @param headers - * key / value pairs of the headers to attach to the request. - * @return ClientResponse containing the status code and return value(s). - * @throws URISyntaxException - */ - public Response getRequestWithHeaders(final URL url, final Map headers) throws URISyntaxException { - return this.getRequestWithHeaders(this.sessionToken, url, headers); - } - - /** - * Get the sessionToken for all SLI API ReSTful service calls. - * - * @return sessionToken - */ - public String getSessionToken() { - return this.sessionToken; - } - - /** - * Make a synchronous GET request to a REST service. The request includes additional header - * information. - * - * @param sessionToken - * Session token. - * - * @param url - * - * @param URL - * Fully qualified URL to the ReSTful resource. - * @param headers - * key / value pairs of the headers to attach to the request. - * @return ClientResponse containing the status code and return value(s). - * @throws URISyntaxException - */ - public Response getRequestWithHeaders(final String sessionToken, final URL url, final Map headers) - throws URISyntaxException { - - if (sessionToken == null) { - logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); - return null; - } - - Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); - builder = getCommonRequestBuilder(sessionToken, builder, headers); - - Invocation i = builder.buildGet(); - return i.invoke(); - } - - - /** - * Synchronously post a new entity to the REST service. This corresponds to a create operation. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * Json entity to post. - * @return ClientResponse containing the status code and return value(s). - * @throws URISyntaxException - * @throws MalformedURLException - */ - public Response postRequest(final URL url, final String json) throws URISyntaxException, MalformedURLException { - return postRequest(this.sessionToken, url, json); - } - - /** - * Synchronously post a new entity to the REST service. This corresponds to a create operation. - * - * @param sessionToken - * Session token. - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * Json entity to post. - * @return ClientResponse containing the status code and return value(s). - * @throws URISyntaxException - * @throws MalformedURLException - */ - public Response postRequest(final String sessionToken, final URL url, final String json) throws URISyntaxException, - MalformedURLException { - return postRequestWithHeaders(sessionToken, url, json, null); - } - - /** - * Synchronously post a new entity to the REST service. This request includes additional header - * information. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * JSON to post. - * @param headers - * key / value pairs of the headers to attach to the request. A key can map - * to multiple values. - * @return ClientResponse containing the status code and return value(s). - * @throws URISyntaxException - * @throws MalformedURLException - */ - public Response postRequestWithHeaders(final URL url, final String json, final Map headers) - throws URISyntaxException, MalformedURLException { - return postRequestWithHeaders(this.sessionToken, url, json, headers); - } - - /** - * Synchronously post a new entity to the REST service. This request includes additional header - * information. - * - * @param sessionToken - * Session token. - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * JSON to post. - * @param headers - * key / value pairs of the headers to attach to the request. A key can map - * to multiple values. - * @return ClientResponse containing the status code and return value(s). - * @throws URISyntaxException - * @throws MalformedURLException - */ - public Response postRequestWithHeaders(final String sessionToken, final URL url, final String json, - final Map headers) throws URISyntaxException, MalformedURLException { - - if (sessionToken == null) { - logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); - return null; - } - - Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); - builder = getCommonRequestBuilder(sessionToken, builder, headers); - - Invocation i = builder.buildPost(javax.ws.rs.client.Entity.entity(json, MediaType.APPLICATION_JSON)); - return i.invoke(); - } - - /** - * Synchronous Put request to the REST service. This corresponds to an update operation. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * JSON of the entity to PUT. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response putRequest(final URL url, final String json) throws MalformedURLException, URISyntaxException { - return putRequest(this.sessionToken, url, json); - } - - /** - * Synchronous Put request to the REST service. This corresponds to an update operation. - * - * @param sessionToken - * Session token. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * JSON of the entity to PUT. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response putRequest(final String sessionToken, final URL url, final String json) - throws MalformedURLException, URISyntaxException { - return putRequestWithHeaders(sessionToken, url, json, null); - } - - /** - * Synchronous Put request to the REST service. This corresponds to an update operation. - * This request includes additional header information. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * JSON of the entity to PUT. - * @param headers - * key / value pairs of the headers to attach to the request. A key can map - * to multiple values. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response putRequestWithHeaders(final URL url, final String json, final Map headers) - throws MalformedURLException, URISyntaxException { - return putRequestWithHeaders(this.sessionToken, url, json, headers); - } - - /** - * Synchronous Put request to the REST service. This corresponds to an update operation. - * This request includes additional header information. - * - * @param sessionToken - * Session token. - * @param url - * Fully qualified URL to the ReSTful resource. - * @param json - * JSON of the entity to PUT. - * @param headers - * key / value pairs of the headers to attach to the request. A key can map - * to multiple values. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response putRequestWithHeaders(final String sessionToken, final URL url, final String json, - final Map headers) throws MalformedURLException, URISyntaxException { - - if (sessionToken == null) { - logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); - return null; - } - - Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); - builder = getCommonRequestBuilder(sessionToken, builder, headers); - - Invocation i = builder.buildPut(javax.ws.rs.client.Entity.entity(json, MediaType.APPLICATION_JSON)); - return i.invoke(); - } - - /** - * Synchronously delete an existing entity using the REST service. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response deleteRequest(final URL url) throws MalformedURLException, URISyntaxException { - return deleteRequest(this.sessionToken, url); - } - - /** - * Synchronously delete an existing entity using the REST service. - * - * @param sessionToken - * Session token. - * @param url - * Fully qualified URL to the ReSTful resource. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response deleteRequest(final String sessionToken, final URL url) throws MalformedURLException, - URISyntaxException { - return deleteRequestWithHeaders(sessionToken, url, null); - } - - /** - * Synchronously delete an existing entity using the REST service. This request includes - * additional header - * information. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @param headers - * key / value pairs of the headers to attach to the request. A key can map - * to multiple values. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response deleteRequestWithHeaders(final URL url, final Map headers) - throws MalformedURLException, URISyntaxException { - return deleteRequestWithHeaders(this.sessionToken, url, headers); - } - - /** - * Synchronously delete an existing entity using the REST service. This request includes - * additional header - * information. - * - * @param sessionToken - * Session token. - * - * @param url - * Fully qualified URL to the ReSTful resource. - * @param headers - * key / value pairs of the headers to attach to the request. A key can map - * to multiple values. - * @return ClientResponse containing the status code and return value(s). - * @throws MalformedURLException - * @throws URISyntaxException - */ - public Response deleteRequestWithHeaders(final String sessionToken, final URL url, final Map headers) - throws MalformedURLException, URISyntaxException { - - if (sessionToken == null) { - logger.log(Level.SEVERE, String.format("Token is null in call to RESTClient for url: %s", url.toString())); - return null; - } - - Invocation.Builder builder = client.target(url.toURI()).request(MediaType.APPLICATION_JSON); - builder = getCommonRequestBuilder(sessionToken, builder, headers); - - Invocation i = builder.buildDelete(); - return i.invoke(); - } - - /** - * Get the base URL for all SLI API ReSTful service calls. - * - * @return Server URL string. - */ - public String getBaseURL() { - return apiServerUri; - } - - /** - * Set the sessionToken for all SLI API ReSTful service calls. - * - * @param sessionToken - */ - public void setSessionToken(String sessionToken) { - this.sessionToken = sessionToken; - } - - /** - * Get a ClientRequest.Builder with common properties already set. - * - * @param headers - * @return - */ - private Invocation.Builder getCommonRequestBuilder(String sessionToken, Invocation.Builder builder, Map headers) { - if (headers == null) { - headers = new HashMap(); - } - - headers.put("Authorization", String.format("Bearer %s", sessionToken)); - - for (Map.Entry entry : headers.entrySet()) { - builder.header(entry.getKey(), entry.getValue()); - } - - return builder; - } - - -} diff --git a/client/src/main/java/org/slc/sli/api/client/security/SliApi.java b/client/src/main/java/org/slc/sli/api/client/security/SliApi.java index 1cec27b..a8cf38d 100644 --- a/client/src/main/java/org/slc/sli/api/client/security/SliApi.java +++ b/client/src/main/java/org/slc/sli/api/client/security/SliApi.java @@ -80,7 +80,7 @@ public OAuthService createService(OAuthConfig config) { /** * TODO: add javadoc */ - public class TokenResponse { + public static class TokenResponse { private Token token; private Response oauthResponse; public Token getToken() { diff --git a/client/src/main/java/org/slc/sli/api/client/util/URLBuilder.java b/client/src/main/java/org/slc/sli/api/client/util/URLBuilder.java index 3872652..607e422 100644 --- a/client/src/main/java/org/slc/sli/api/client/util/URLBuilder.java +++ b/client/src/main/java/org/slc/sli/api/client/util/URLBuilder.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package org.slc.sli.api.client.util; import java.io.UnsupportedEncodingException; @@ -36,7 +35,7 @@ public final class URLBuilder { /** * Start building a new URL with the provided base location. - * + * * @param baseUrl * - base URL of the ReSTful API. * @return URLBuilder instance @@ -49,26 +48,31 @@ public static URLBuilder create(final String baseUrl) { /** * Append a path fragment to the current URL path. - * + * * @param path * URL fragment to add. * @return Updated URLBuilder instance. */ public URLBuilder addPath(final String path) { - addPathSeparaterIfNeeded(); + if (!path.startsWith("/")) { + addPathSeparaterIfNeeded(); + } url.append(path); return this; } /** * Append a path element for accessing the provided entity type. - * + * * @param type * Entity type of interest. * @return Updated URLBuilder instance. */ public URLBuilder entityType(final String type) { String path = PathConstants.TEMP_MAP.get(type); + if (path == null) { + path = PathConstants.TEMP_MAP.get(type + "s"); + } if (path == null && type.equals(PathConstants.SECURITY_SESSION_DEBUG)) { path = type; } else if (path == null && type.equals(PathConstants.HOME)) { @@ -82,7 +86,7 @@ public URLBuilder entityType(final String type) { /** * Append an entity id to the path - * + * * @param id * Entity ID * @return Updated URLBuilder instance. @@ -94,7 +98,7 @@ public URLBuilder id(final String id) { /** * Append a collection of entity ids to the path - * + * * @param ids * a collection of Entity IDs * @return Updated URLBuilder instance. @@ -113,25 +117,26 @@ public URLBuilder ids(final List ids) { /** * Apply the given query to the URL. - * + * * @param query * @return Updated URLBuilder instance. */ public URLBuilder query(final Query query) { - Map params = query.getParameters(); - if (params != null) { - for (Map.Entry entry : params.entrySet()) { - addQueryParameter(entry.getKey(), entry.getValue()); + if (query != null) { + Map params = query.getParameters(); + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + addQueryParameter(entry.getKey(), entry.getValue()); + } } } - return this; } /** * Builds the URL. - * + * * @return URL represented by the values set in this builder. * @throws MalformedURLException * if the URL is not valid. @@ -143,7 +148,7 @@ public URL build() throws MalformedURLException { /** * Add a URL Query parameter to the URL. - * + * * @param key * query parameter name * @param value @@ -161,7 +166,7 @@ private URLBuilder addQueryParameter(final String key, final Object value) { } url.append(key).append("="); try { - url.append(URLEncoder.encode(value.toString(), ENCODING)); + url.append(URLEncoder.encode(value.toString(), ENCODING).replace("+", "%20")); } catch (UnsupportedEncodingException e) { url.append(value); } diff --git a/client/src/test/java/org/slc/sli/api/client/URLBuilderTest.java b/client/src/test/java/org/slc/sli/api/client/URLBuilderTest.java index bbe0bf2..2ed0206 100644 --- a/client/src/test/java/org/slc/sli/api/client/URLBuilderTest.java +++ b/client/src/test/java/org/slc/sli/api/client/URLBuilderTest.java @@ -53,4 +53,14 @@ public void testBuildException() throws Exception { builder.addPath(PathConstants.API_SERVER_PATH).addPath(ResourceNames.STUDENTS); builder.build(); } + + @Test + public void testTypeToResourceConversion() throws Exception{ + URLBuilder builder = URLBuilder.create("http://localhost"); + builder.addPath(PathConstants.API_SERVER_PATH); + builder.entityType("staffEducationOrganizationAssociation"); + URL url = builder.build(); + assertEquals("the url should be http://localhost/api/rest/v1/staffEducationOrgAssignmentAssociations", "http://localhost/" + + PathConstants.API_SERVER_PATH + "/staffEducationOrgAssignmentAssociations", url.toString()); + } } diff --git a/client/src/test/java/org/slc/sli/api/client/impl/BasicClientTest.java b/client/src/test/java/org/slc/sli/api/client/impl/BasicClientTest.java new file mode 100644 index 0000000..95a72b7 --- /dev/null +++ b/client/src/test/java/org/slc/sli/api/client/impl/BasicClientTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2012 Shared Learning Collaborative, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.slc.sli.api.client.impl; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.MessageProcessingException; +import javax.ws.rs.core.Response; + +import org.codehaus.jackson.map.ObjectMapper; +import org.junit.Before; +import org.junit.Test; + +import org.slc.sli.api.client.Entity; +import org.slc.sli.api.client.Link; +import org.slc.sli.api.client.RESTClient; +import org.slc.sli.api.client.SLIClient; +import org.slc.sli.api.client.SLIClientException; +import org.slc.sli.api.client.constants.ResourceNames; + +/** + * JUnit for BasicClient + * + * @author nbrown + * + */ +public class BasicClientTest { + private RESTClient restClient = mock(RESTClient.class); + private SLIClient underTest = new BasicClient(restClient); + private ObjectMapper mapper = new ObjectMapper(); + private Map studentData = new HashMap(); + private Map studentDataWithType = new HashMap(); + private Map resourceWithLinks = new HashMap(); + + @Before + public void init() throws MalformedURLException { + when(restClient.getBaseURL()).thenReturn("http://baseURL"); + studentData.put("id", "42"); + studentData.put("name", "River Tam"); + + studentDataWithType.putAll(studentData); + studentDataWithType.put("entityType", ResourceNames.STUDENTS); + + List links = new ArrayList(); + links.add(new BasicLink("Google", new URL("http://www.google.com"))); + resourceWithLinks.put(Entity.LINKS_KEY, links); + } + + @Test + public void testCreate() throws IOException, URISyntaxException, SLIClientException { + Entity e = new GenericEntity(ResourceNames.STUDENTS, studentData); + Response r = mock(Response.class); + when(r.getStatus()).thenReturn(201); + when(r.getHeader("Location")).thenReturn("http://baseURL/students/42"); + when(restClient.postRequest(any(URL.class), anyString())).thenReturn(r); + assertEquals("42", underTest.create(e)); + + Entity e2 = new GenericEntity("", studentData); + assertEquals("42", underTest.create(e2, "students")); + assertEquals("42", underTest.create(e2, "/students")); + assertEquals("42", underTest.create(e2, "http://baseURL/students")); + + verify(restClient, times(4)).postRequest(new URL("http://baseURL/students"), + mapper.writeValueAsString(studentData)); + + Entity e3 = new GenericEntity("", resourceWithLinks); + assertEquals(1, e3.getLinks().size()); + Set keySet = e3.getLinkMap().keySet(); + assertEquals(1, keySet.size()); + for (String key : keySet) { + assertEquals("Google", key); + assertEquals("http", e3.getLinkMap().get(key).getProtocol()); + assertEquals("www.google.com", e3.getLinkMap().get(key).getHost()); + } + } + + @Test + public void testRead() throws MessageProcessingException, URISyntaxException, IOException, SLIClientException { + Response r = mock(Response.class); + when(r.getStatus()).thenReturn(200); + when(r.readEntity(String.class)).thenReturn(mapper.writeValueAsString(studentDataWithType)); + when(restClient.getRequest(new URL("http://baseURL/students/42"))).thenReturn(r); + when(restClient.getRequest(new URL("http://baseURL/students?limit=1"))).thenReturn(r); + assertEquals(Arrays.asList(new GenericEntity(ResourceNames.STUDENTS, studentData)), + underTest.read("/students/42")); + assertEquals(Arrays.asList(new GenericEntity(ResourceNames.STUDENTS, studentData)), + underTest.read("http://baseURL/students/42")); + List studentList = new ArrayList(); + underTest.read(studentList, ResourceNames.STUDENTS, BasicQuery.Builder.create().maxResults(1).build()); + assertEquals(Arrays.asList(new GenericEntity(ResourceNames.STUDENTS, studentData)), studentList); + + } + + @Test + public void testUpdate() throws URISyntaxException, IOException, MessageProcessingException, SLIClientException { + Entity e = new GenericEntity(ResourceNames.STUDENTS, studentData); + Response r = mock(Response.class); + when(r.getStatus()).thenReturn(204); + when(restClient.putRequest(any(URL.class), anyString())).thenReturn(r); + underTest.update(e); + verify(restClient).putRequest(new URL("http://baseURL/students/42"), mapper.writeValueAsString(studentData)); + } + + @Test + public void testDelete() throws MalformedURLException, URISyntaxException, SLIClientException { + Entity e = new GenericEntity(ResourceNames.STUDENTS, studentData); + Response r = mock(Response.class); + when(r.getStatus()).thenReturn(204); + when(restClient.deleteRequest(any(URL.class))).thenReturn(r); + underTest.delete(e); + underTest.delete(ResourceNames.STUDENTS, "42"); + verify(restClient, times(2)).deleteRequest(new URL("http://baseURL/students/42")); + } +} diff --git a/sample/sample.properties b/sample/sample.properties index 1416b8c..b3a4096 100644 --- a/sample/sample.properties +++ b/sample/sample.properties @@ -1,4 +1,4 @@ -sli.sample.apiUrl=http://local.slidev.org:8080/ +sli.sample.apiUrl=http://local.slidev.org:8080 sli.sample.callbackUrl=http://local.slidev.org:8081/sample/callback sli.sample.clientId= sli.sample.clientSecret= diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/AuthFilter.java b/sample/src/main/java/org/slc/sli/sample/oauth/AuthFilter.java index 859d382..d03a0f3 100644 --- a/sample/src/main/java/org/slc/sli/sample/oauth/AuthFilter.java +++ b/sample/src/main/java/org/slc/sli/sample/oauth/AuthFilter.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.util.Properties; @@ -38,7 +39,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slc.sli.api.client.RESTClient; +import org.slc.sli.api.client.SLIClient; import org.slc.sli.api.client.impl.BasicClient; +import org.slc.sli.api.client.impl.BasicRESTClient; /** * Basic authentication example using the SLI SDK. @@ -46,6 +50,8 @@ public class AuthFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AuthFilter.class); + private static final String CALL_BACK_PATH = "/sample/callback"; + private String clientId; private String clientSecret; private URL apiUrl; @@ -58,71 +64,70 @@ public void destroy() { } @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, - ServletException { - HttpServletRequest req = (HttpServletRequest) request; - LOG.info("URI:" + req.getRequestURI()); - if (req.getRequestURI().equals("/sample/callback")) { - if (handleCallback(request, response)) { + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + SLIClient client = (SLIClient) httpRequest.getSession().getAttribute("client"); + + LOG.info("URI:" + httpRequest.getRequestURI()); + + if (client == null) { + // this use is not authenticat yet + LOG.info("authenticate"); + RESTClient restClient = new BasicRESTClient(apiUrl, clientId, clientSecret, callbackUrl); + client = new BasicClient(restClient); + ((HttpServletResponse) response).sendRedirect(client.getRESTClient().getLoginURL().toExternalForm()); + httpRequest.getSession().setAttribute("client", client); + } else if (CALL_BACK_PATH.equals(httpRequest.getRequestURI())) { + // Authentication was successful lets connect to the API + String code = httpRequest.getParameter("code"); + Response apiResponse = null; + try { + apiResponse = client.getRESTClient().connect(code); + } catch (MalformedURLException e) { + LOG.error(String.format("Invalid/malformed URL when connecting: %s", e.toString())); + } catch (URISyntaxException e) { + LOG.error(String.format("Invalid/malformed URL when connecting: %s", e.toString())); + } + + if ((apiResponse == null) || (apiResponse.getStatus() != 200)) { + handleAPIResponseFailure(apiResponse, response); + } else { ((HttpServletResponse) response).sendRedirect(afterCallbackRedirect); } LOG.info("callback"); - return; - } else if (req.getSession().getAttribute("client") == null) { - LOG.info("authenticate"); - authenticate(request, response); } else { - try { - LOG.info("chain"); - chain.doFilter(request, response); - } catch (Exception e) { - // Redirect to login on any errors - // TODO - we should handle responses correctly here. If the session is invalidated, - // we need to handle this properly. Same with the other HTTP response codes. - authenticate(request, response); - } + // Assuming authenticated user, process request as necessary. + LOG.info("chain"); + chain.doFilter(request, response); + + // Redirect to login on any errors + // TODO - we should handle responses correctly here. If the session is invalidated, + // we need to handle this properly. Same with the other HTTP response codes. + // Possible solution: + // authenticate(request, response); } } - private boolean handleCallback(ServletRequest request, ServletResponse response) throws IOException { - BasicClient client = (BasicClient) ((HttpServletRequest) request).getSession().getAttribute("client"); - String code = ((HttpServletRequest) request).getParameter("code"); - - if (client != null) { - String token = null; - Response rval = client.connect(code, token); - - switch (rval.getStatus()) { - case 200: // OK - return true; + private void handleAPIResponseFailure(Response apiResponse, ServletResponse response) throws IOException { + if (apiResponse == null) { + ((HttpServletResponse) response).sendRedirect("404.html"); + } else { + switch(apiResponse.getStatus()) { case 403: ((HttpServletResponse) response).sendRedirect("403.html"); - return false; + break; case 404: ((HttpServletResponse) response).sendRedirect("404.html"); - return false; + break; case 422: ((HttpServletResponse) response).sendRedirect("422.html"); - return false; + break; case 400: case 500: ((HttpServletResponse) response).sendRedirect("500.html"); - return false; + break; } } - - return true; - } - - private void authenticate(ServletRequest req, ServletResponse res) { - - BasicClient client = new BasicClient(apiUrl, clientId, clientSecret, callbackUrl); - try { - ((HttpServletResponse) res).sendRedirect(client.getLoginURL().toExternalForm()); - } catch (IOException e) { - LOG.error("Bad redirect", e); - } - ((HttpServletRequest) req).getSession().setAttribute("client", client); } @Override diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/TestRESTClientServlet.java b/sample/src/main/java/org/slc/sli/sample/oauth/TestRESTClientServlet.java new file mode 100644 index 0000000..b7a469a --- /dev/null +++ b/sample/src/main/java/org/slc/sli/sample/oauth/TestRESTClientServlet.java @@ -0,0 +1,330 @@ +/* + * Copyright 2012 Shared Learning Collaborative, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.slc.sli.sample.oauth; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Iterator; +import java.util.UUID; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.JsonProcessingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.node.ObjectNode; +import org.slc.sli.api.client.RESTClient; +import org.slc.sli.api.client.constants.ResourceNames; +import org.slc.sli.api.client.impl.BasicClient; +import org.slc.sli.api.client.impl.BasicQuery; +import org.slc.sli.api.client.util.Query; +import org.slc.sli.api.client.util.URLBuilder; + +/** + * Servlet that do CRUD test against Java SDK via RESTClient interface. + * + * This is almost a clone of TestSDKServlet + * + * @author ycao + * + */ +public class TestRESTClientServlet extends HttpServlet { + + private static final long serialVersionUID = 6114075027060L; + + private static final String SUCCEED = "succeed"; + private static final String FAILED = "failed"; + + // build the test student entity that can pass schema validation + private String getUniqueStudent() { + return "{ " + + " \"studentUniqueStateId\":\"" + + UUID.randomUUID().toString().substring(0, 8) + + "\"," + + " \"sex\":\"Female\", " + + " \"economicDisadvantaged\":false, " + + " \"address\": " + + " [ { " + + " \"nameOfCounty\":\"Orange\", " + + " \"apartmentRoomSuiteNumber\":\"200\", " + + " \"postalCode\":\"45678\", " + + " \"streetNumberName\":\"817 Oakridge Farm Lane\", " + + " \"stateAbbreviation\":\"CA\", " + + " \"addressType\":\"Physical\", " + + " \"city\":\"Los Angeles\" " + + " } ], " + + " \"name\": " + + " { " + + " \"middleName\":\"L\", " + + " \"lastSurname\":\"Christie\", " + + " \"firstName\":\"Monique\" " + + " }, " + + " \"birthData\": " + + " {\"birthDate\":\"1993-12-31\"} " + + "} "; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + RESTClient client = ((BasicClient) req.getSession().getAttribute("client")).getRESTClient(); + String testType = req.getParameter("test"); + String testResult = "failed"; + if (testType.equals("read")) { + testResult = testRead(client); + } else if (testType.equals("create")) { + testResult = testCreate(client); + } else if (testType.equals("update")) { + testResult = testUpdate(client); + } else if (testType.equals("delete")) { + testResult = testDelete(client); + } else if (testType.equals("query")) { + testResult = testQuery(client); + } else { + testResult = "unsupported test!"; + } + req.setAttribute("testResult", testResult); + req.getRequestDispatcher("WEB-INF/restsdktest.jsp").forward(req, resp); + } + + // the read test has been done in list student, so always return succeed + private String testRead(RESTClient client) { + return SUCCEED; + } + + // test the create for Java SDK + private String testCreate(RESTClient client) { + + String[] inDB = persistAndRead(getUniqueStudent(), client, ResourceNames.STUDENTS); + if (inDB == null) { + return FAILED; + } + + String studentBody = inDB[1]; + + ObjectMapper mapper = new ObjectMapper(); + try { + JsonNode jsonNode = mapper.readTree(studentBody); + if (jsonNode.path("name").path("lastSurname").getTextValue().equals("Christie") + && jsonNode.path("name").path("firstName").getTextValue().equals("Monique")) { + return SUCCEED; + } + } catch (JsonProcessingException e) { + return FAILED; + } catch (IOException e) { + return FAILED; + } + + return FAILED; + } + + // test the update for Java SDK + private String testUpdate(RESTClient client) { + + String[] inDB = persistAndRead(getUniqueStudent(), client, ResourceNames.STUDENTS); + if (inDB == null) { + return FAILED; + } + String resourceLocation = inDB[0]; + String studentBody = inDB[1]; + + ObjectMapper mapper = new ObjectMapper(); + String updatedMonique = null; + String newAddress = "2817 New Found Lane"; + + try { + + JsonNode student = mapper.readTree(studentBody); + Iterator it = student.path("address").getElements(); + while (it.hasNext()) { + JsonNode address = it.next(); + String streetNum = address.path("streetNumberName").getTextValue(); + if ("817 Oakridge Farm Lane".equals(streetNum)) { + ((ObjectNode) address).put("streetNumberName", newAddress); + } + } + + updatedMonique = mapper.writeValueAsString(student); + + } catch (JsonProcessingException e1) { + return FAILED; + } catch (IOException e1) { + return FAILED; + } + + try { + + URL resourceURL = new URL(resourceLocation); + + Response response = client.putRequest(resourceURL, updatedMonique); + if (response.getStatus() != Status.NO_CONTENT.getStatusCode()) { + return FAILED; + } + response = client.getRequest(resourceURL); + + JsonNode student = mapper.readTree(response.readEntity(String.class)); + Iterator it = student.path("address").getElements(); + while (it.hasNext()) { + JsonNode address = it.next(); + String streetNum = address.path("streetNumberName").getTextValue(); + if (newAddress.equals(streetNum)) { + return SUCCEED; + } + } + + } catch (MalformedURLException e) { + return FAILED; + } catch (URISyntaxException e) { + return FAILED; + } catch (JsonProcessingException e) { + return FAILED; + // } catch (MessageProcessingException e) { + // return FAILED; + } catch (IOException e) { + return FAILED; + } + + return FAILED; + } + + // test the delete of Java SDK + private String testDelete(RESTClient client) { + + try { + String resourceLocation = create(getUniqueStudent(), ResourceNames.STUDENTS, client); + if (resourceLocation != null) { + URL resourceURL = new URL(resourceLocation); + + Response response = client.deleteRequest(resourceURL); + if (!requestSucceed(response)) { + return FAILED; + } + + response = client.getRequest(resourceURL); + if (response.getStatus() != Status.NOT_FOUND.getStatusCode()) { + return FAILED; + } + + return SUCCEED; + } + } catch (MalformedURLException e) { + return FAILED; + } catch (URISyntaxException e) { + return FAILED; + } + + return FAILED; + } + + // test query and sorting of Java SDK + private String testQuery(RESTClient client) { + + Query query = BasicQuery.Builder.create().filterEqual("sex", "Male").sortBy("name.firstName").sortDescending() + .build(); + + URLBuilder urlBuilder = URLBuilder.create(client.getBaseURL()).entityType(ResourceNames.STAFF); + urlBuilder.query(query); + + String result = null; + try { + + URL url = urlBuilder.build(); + Response response = client.getRequest(url); + result = response.readEntity(String.class); + + } catch (MalformedURLException e) { + return FAILED; + } catch (URISyntaxException e) { + return FAILED; + } + + ObjectMapper mapper = new ObjectMapper(); + try { + + JsonNode teachers = mapper.readTree(result); + Iterator it = teachers.getElements(); + JsonNode teacher = it.next(); + String firstName = teacher.path("name").path("firstName").getTextValue(); + if ("Rick".equals(firstName)) { + return SUCCEED; + } + + } catch (JsonProcessingException e1) { + return FAILED; + } catch (IOException e1) { + return FAILED; + } + + return FAILED; + } + + private String[] persistAndRead(String jsonObj, RESTClient client, String resourceType) { + + String[] result = new String[2]; + Response response = null; + try { + + String location = create(jsonObj, resourceType, client); + if (location == null) { + return null; + } + result[0] = location; + + response = client.getRequest(new URL(location)); + if (response == null || !requestSucceed(response)) { + return null; + } + + } catch (MalformedURLException e1) { + return null; + } catch (URISyntaxException e1) { + return null; + } + + result[1] = response.readEntity(String.class); + + return result; + } + + private String create(String jsonObj, String resourceType, RESTClient client) throws MalformedURLException, + URISyntaxException { + + URL url = URLBuilder.create(client.getBaseURL()).entityType(resourceType).build(); + Response response = client.postRequest(url, jsonObj); + + if (response.getStatus() != Status.CREATED.getStatusCode()) { + return null; + } + + return getLocation(response); + } + + private String getLocation(Response response) { + return response.getHeader("Location"); + } + + private boolean requestSucceed(Response response) { + // 2xx are successful status + return response.getStatus() >= 200 && response.getStatus() < 300; + } +} diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/TestSDKServlet.java b/sample/src/main/java/org/slc/sli/sample/oauth/TestSDKServlet.java index 54b950d..87da9e2 100644 --- a/sample/src/main/java/org/slc/sli/sample/oauth/TestSDKServlet.java +++ b/sample/src/main/java/org/slc/sli/sample/oauth/TestSDKServlet.java @@ -21,12 +21,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -84,13 +84,12 @@ private String testRead(BasicClient client) { @SuppressWarnings("unchecked") private String testCreate(BasicClient client) { String testResult = "failed"; - String id = ""; Entity student = new GenericEntity(ResourceNames.STUDENTS, createStudentBody()); List collection = new ArrayList(); try { - id = client.create(student); + String id = client.create(student); client.read(collection, ResourceNames.STUDENTS, id, BasicQuery.EMPTY_QUERY); - if (collection != null && collection.size() == 1) { + if (collection.size() == 1) { String firstName = ((Map) collection.get(0).getData().get("name")).get("firstName"); String lastSurname = ((Map) collection.get(0).getData().get("name")).get("lastSurname"); @@ -103,7 +102,7 @@ private String testCreate(BasicClient client) { } } catch (SLIClientException e) { - LOG.error("Exception:" + e.getMessage()); + LOG.error("Exception:" + e.getMessage()); } catch (Exception e) { LOG.error("Exception:" + e.getMessage()); testResult = "failed"; @@ -124,18 +123,16 @@ private String testUpdate(BasicClient client) { id = client.create(student); client.read(collection, ResourceNames.STUDENTS, id, BasicQuery.EMPTY_QUERY); - if (collection != null && collection.size() == 1) { - student = collection.get(0); - ((List>) student.getData().get("address")).get(0).put("streetNumberName", + if (collection.size() == 1) { + Entity foundStudent = collection.get(0); + Map studentData = foundStudent.getData(); + + ((List>) studentData.get("address")).get(0).put("streetNumberName", "2817 Oakridge Farm Lane"); - Response response = client.update(student); - if (response.getStatus() != 204) { - testResult = "failed"; - return testResult; - } + client.update(foundStudent); } client.read(collection, ResourceNames.STUDENTS, id, BasicQuery.EMPTY_QUERY); - if (collection != null && collection.size() == 1) { + if (collection.size() == 1) { student = collection.get(0); String address = ((List>) student.getData().get("address")).get(0).get( "streetNumberName"); @@ -146,7 +143,9 @@ private String testUpdate(BasicClient client) { } } } catch (SLIClientException e) { - LOG.error("SLIClientException:" + e.getMessage()); + // either the update or read call failed + LOG.error("SLIClientException:" + e.getMessage()); + testResult = "failed"; } catch (Exception e) { LOG.error("RESPONSE:" + e.getMessage()); testResult = "failed"; @@ -165,18 +164,22 @@ private String testDelete(BasicClient client) { id = client.create(student); client.read(collection, ResourceNames.STUDENTS, id, BasicQuery.EMPTY_QUERY); - if (collection != null && collection.size() == 1) { + if (collection.size() == 1) { student = collection.get(0); client.delete(ResourceNames.STUDENTS, id); } - Response response = client.read(collection, ResourceNames.STUDENTS, id, BasicQuery.EMPTY_QUERY); - if (response.getStatus() == 404) { - testResult = "succeed"; - } else { + // make sure the read fails + try { + client.read(collection, ResourceNames.STUDENTS, id, BasicQuery.EMPTY_QUERY); testResult = "failed"; - return testResult; + } catch (SLIClientException e) { + // unable to read therefore the delete succeded. + testResult = "succeed"; } - + } catch (SLIClientException e) { + // either the create or delete failed + LOG.error("Response:" + e.getMessage()); + testResult = "failed"; } catch (Exception e) { LOG.error("RESPONSE:" + e.getMessage()); testResult = "failed"; @@ -190,24 +193,28 @@ private String testQuery(BasicClient client) { List collection = new ArrayList(); String testResult = ""; try { - client.read(collection, ResourceNames.TEACHERS, BasicQuery.Builder.create().filterEqual("sex", "Male") - .sortBy("name.firstName").sortDescending().build()); - if (collection != null && collection.size() > 0) { + client.read(collection, ResourceNames.STAFF, + BasicQuery.Builder.create().filterEqual("sex", "Male") + .sortBy("name.firstName") + .sortDescending() + .build()); + if (collection.size() > 0) { String firstName = ((Map) collection.get(0).getData().get("name")).get("firstName"); - if (firstName.equals("Mark")) { + if (firstName.equals("Rick")) { testResult = "succeed"; } else { testResult = "failed"; return testResult; } } + } catch (SLIClientException e) { + LOG.error("RESPONSE:" + e.getMessage()); } catch (Exception e) { LOG.error("RESPONSE:" + e.getMessage()); testResult = "failed"; } return testResult; - } // build the test student entity that can pass schema validation @@ -222,7 +229,7 @@ private Map createStudentBody() { Map birthDate = new HashMap(); birthDate.put("birthDate", "1995-01-01"); body.put("birthData", birthDate); - body.put("studentUniqueStateId", "123456"); + body.put("studentUniqueStateId", UUID.randomUUID().toString().substring(0, 8)); body.put("economicDisadvantaged", false); List> addresses = new ArrayList>(); Map address = new HashMap(); diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/model/Cohorts.java b/sample/src/main/java/org/slc/sli/sample/oauth/model/Cohorts.java index 39b4d96..02f5e35 100644 --- a/sample/src/main/java/org/slc/sli/sample/oauth/model/Cohorts.java +++ b/sample/src/main/java/org/slc/sli/sample/oauth/model/Cohorts.java @@ -24,26 +24,31 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.slc.sli.api.client.Entity; +import org.slc.sli.api.client.SLIClientException; import org.slc.sli.api.client.constants.ResourceNames; import org.slc.sli.api.client.impl.BasicClient; import org.slc.sli.api.client.impl.BasicQuery; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Sample domain wrapper. */ public class Cohorts { - + private static final Logger LOG = LoggerFactory.getLogger(Cohorts.class); - + public static Map getIdDesc(BasicClient client) throws IOException { List collection = new ArrayList(); try { client.read(collection, ResourceNames.COHORTS, BasicQuery.EMPTY_QUERY); } catch (URISyntaxException e) { LOG.error("Exception occurred", e); + } catch (SLIClientException e) { + // the read was unsucessful + LOG.error("Exception occurred", e); } HashMap toReturn = new HashMap(); for (Entity cohort : collection) { @@ -51,8 +56,8 @@ public static Map getIdDesc(BasicClient client) throws IOExcepti String desc = (String) cohort.getData().get("cohortDescription"); toReturn.put(id, desc); } - + return toReturn; } - + } diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/model/DisciplineIncidents.java b/sample/src/main/java/org/slc/sli/sample/oauth/model/DisciplineIncidents.java index 6721c6a..e479858 100644 --- a/sample/src/main/java/org/slc/sli/sample/oauth/model/DisciplineIncidents.java +++ b/sample/src/main/java/org/slc/sli/sample/oauth/model/DisciplineIncidents.java @@ -24,35 +24,41 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.slc.sli.api.client.Entity; +import org.slc.sli.api.client.SLIClientException; import org.slc.sli.api.client.constants.ResourceNames; import org.slc.sli.api.client.impl.BasicClient; import org.slc.sli.api.client.impl.BasicQuery; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Sample domain wrapper. */ public class DisciplineIncidents { - + private static final Logger LOG = LoggerFactory.getLogger(DisciplineIncidents.class); - + public static Map getInfo(BasicClient client) throws IOException { List collection = new ArrayList(); try { client.read(collection, ResourceNames.DISCIPLINE_INCIDENTS, BasicQuery.EMPTY_QUERY); } catch (URISyntaxException e) { LOG.error("Exception occurred", e); + } catch (SLIClientException e) { + // the read was unsucessful + LOG.error("Exception occurred", e); } + Map toReturn = new HashMap(); for (Entity disciplineIncident : collection) { String id = (String) disciplineIncident.getData().get("incidentIdentifier"); String time = (String) disciplineIncident.getData().get("incidentTime"); toReturn.put(id, "date " + ", " + time); } - + return toReturn; } - + } diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/model/Programs.java b/sample/src/main/java/org/slc/sli/sample/oauth/model/Programs.java index 9b2c2f8..7170224 100644 --- a/sample/src/main/java/org/slc/sli/sample/oauth/model/Programs.java +++ b/sample/src/main/java/org/slc/sli/sample/oauth/model/Programs.java @@ -22,34 +22,40 @@ import java.util.ArrayList; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.slc.sli.api.client.Entity; +import org.slc.sli.api.client.SLIClientException; import org.slc.sli.api.client.constants.ResourceNames; import org.slc.sli.api.client.impl.BasicClient; import org.slc.sli.api.client.impl.BasicQuery; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Sample domain wrapper. */ public class Programs { - + private static final Logger LOG = LoggerFactory.getLogger(Programs.class); - + public static List getIds(BasicClient client) throws IOException { List collection = new ArrayList(); try { client.read(collection, ResourceNames.PROGRAMS, BasicQuery.EMPTY_QUERY); } catch (URISyntaxException e) { LOG.error("Exception occurred", e); + } catch (SLIClientException e) { + // the read was unsucessful + LOG.error("Exception occurred", e); } + List toReturn = new ArrayList(); for (Entity program : collection) { String id = (String) program.getData().get("programId"); toReturn.add(id); } - + return toReturn; } - + } diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/model/Students.java b/sample/src/main/java/org/slc/sli/sample/oauth/model/Students.java index 3192af6..2146760 100644 --- a/sample/src/main/java/org/slc/sli/sample/oauth/model/Students.java +++ b/sample/src/main/java/org/slc/sli/sample/oauth/model/Students.java @@ -23,20 +23,22 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.slc.sli.api.client.Entity; +import org.slc.sli.api.client.SLIClientException; import org.slc.sli.api.client.constants.ResourceNames; import org.slc.sli.api.client.impl.BasicClient; import org.slc.sli.api.client.impl.BasicQuery; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Sample domain wrapper. */ public class Students { - + private static final Logger LOG = LoggerFactory.getLogger(Students.class); - + @SuppressWarnings("unchecked") public static List getNames(BasicClient client) throws IOException { List collection = new ArrayList(); @@ -45,6 +47,9 @@ public static List getNames(BasicClient client) throws IOException { .build()); } catch (URISyntaxException e) { LOG.error("Exception occurred", e); + } catch (SLIClientException e) { + // the read was unsucessful + LOG.error("Exception occurred", e); } ArrayList toReturn = new ArrayList(); for (Entity student : collection) { @@ -54,10 +59,10 @@ public static List getNames(BasicClient client) throws IOException { } return toReturn; } - + @SuppressWarnings("javadoc") public static int getGrade(BasicClient client, String studentName) { return 0; } - + } diff --git a/sample/src/main/java/org/slc/sli/sample/oauth/model/Teachers.java b/sample/src/main/java/org/slc/sli/sample/oauth/model/Teachers.java index 37f9293..80a8c91 100644 --- a/sample/src/main/java/org/slc/sli/sample/oauth/model/Teachers.java +++ b/sample/src/main/java/org/slc/sli/sample/oauth/model/Teachers.java @@ -25,25 +25,26 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.slc.sli.api.client.Entity; -import org.slc.sli.api.client.Link; +import org.slc.sli.api.client.SLIClientException; import org.slc.sli.api.client.constants.v1.PathConstants; import org.slc.sli.api.client.impl.BasicClient; import org.slc.sli.api.client.impl.BasicQuery; import org.slc.sli.api.client.impl.GenericEntity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * domain wrapper for teacher and staff with authorized roles and access right information - * + * * @author dliu - * + * */ public class Teachers { - + private static final Logger LOG = LoggerFactory.getLogger(Teachers.class); - + @SuppressWarnings("unchecked") public static Map getTenantIdMap(BasicClient client) throws IOException { Entity home = new GenericEntity(PathConstants.HOME, new HashMap()); @@ -52,22 +53,17 @@ public static Map getTenantIdMap(BasicClient client) throws IOEx } catch (URISyntaxException e) { LOG.error("Exception occurred", e); } + Map linkMap = home.getLinkMap(); URL myURL = null; - List links = home.getLinks(); - - if (links != null) { - for (Link link : links) { - if (link.getLinkName().equals("self")) { - myURL = link.getResourceURL(); - break; - } - } + + if (linkMap != null) { + myURL = linkMap.get("self"); } - + if (myURL == null) { return null; } - + List collection = new ArrayList(); try { client.getResource(collection, myURL, BasicQuery.EMPTY_QUERY); @@ -80,12 +76,12 @@ public static Map getTenantIdMap(BasicClient client) throws IOEx .get("firstName"); String lastName = (String) ((Map) collection.get(0).getData().get("name")) .get("lastSurname"); - String tenantId = (String) ((Map) collection.get(0).getData()).get("id"); + String tenantId = (String) collection.get(0).getData().get("id"); toReturn.put(firstName + " " + lastName, tenantId); } return toReturn; } - + @SuppressWarnings("unchecked") public static List getRoles(BasicClient client) throws IOException { List roles = new ArrayList(); @@ -94,8 +90,12 @@ public static List getRoles(BasicClient client) throws IOException { client.read(collection, PathConstants.SECURITY_SESSION_DEBUG, BasicQuery.EMPTY_QUERY); } catch (URISyntaxException e) { LOG.error("Exception occurred", e); + } catch (SLIClientException e) { + // the read was unsucessful + LOG.error("Exception occurred", e); } - + + if (collection != null && collection.size() >= 1) { Map auth = (Map) collection.get(0).getData().get("authentication"); Map principal = (Map) auth.get("principal"); @@ -103,7 +103,7 @@ public static List getRoles(BasicClient client) throws IOException { } return roles; } - + @SuppressWarnings("unchecked") public static List getAccessRights(BasicClient client) throws IOException { List accessRights = new ArrayList(); @@ -112,8 +112,12 @@ public static List getAccessRights(BasicClient client) throws IOExceptio client.read(collection, PathConstants.SECURITY_SESSION_DEBUG, BasicQuery.EMPTY_QUERY); } catch (URISyntaxException e) { LOG.error("Exception occurred", e); + } catch (SLIClientException e) { + // the read was unsucessful + LOG.error("Exception occurred", e); } - + + if (collection != null && collection.size() >= 1) { Map auth = (Map) collection.get(0).getData().get("authentication"); accessRights = (List) auth.get("authorities"); diff --git a/sample/src/main/webapp/WEB-INF/restsdktest.jsp b/sample/src/main/webapp/WEB-INF/restsdktest.jsp new file mode 100644 index 0000000..0553f11 --- /dev/null +++ b/sample/src/main/webapp/WEB-INF/restsdktest.jsp @@ -0,0 +1,14 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> + + + + +Test CRUD SDK + + +

+ ${testResult} + + \ No newline at end of file diff --git a/sample/src/main/webapp/WEB-INF/web.xml b/sample/src/main/webapp/WEB-INF/web.xml index aec168a..d2dd225 100644 --- a/sample/src/main/webapp/WEB-INF/web.xml +++ b/sample/src/main/webapp/WEB-INF/web.xml @@ -38,7 +38,18 @@ TestSDK /testsdk + + + Test REST Servlets + TestREST + org.slc.sli.sample.oauth.TestRESTClientServlet + 1 + + + TestREST + /testrest + Cohort Servlets Cohorts From 8c64ba542954a385e747c0779e0644c131c3cc6b Mon Sep 17 00:00:00 2001 From: Adam Oren Date: Fri, 14 Dec 2012 13:34:07 -0500 Subject: [PATCH 2/2] merged from 1.0.65 --- .gitignore | 1 + .project | 17 +++++++++++++++++ client/.DS_Store | Bin 0 -> 6148 bytes client/.gitignore | 1 + sample/.DS_Store | Bin 0 -> 6148 bytes sample/.gitignore | 2 ++ sample/.project | 42 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 63 insertions(+) create mode 100644 .gitignore create mode 100644 .project create mode 100644 client/.DS_Store create mode 100644 client/.gitignore create mode 100644 sample/.DS_Store create mode 100644 sample/.gitignore create mode 100644 sample/.project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d027396 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.settings/ diff --git a/.project b/.project new file mode 100644 index 0000000..4d59bc2 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + SDK + + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + + diff --git a/client/.DS_Store b/client/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 + + sample + + + + + + org.eclipse.wst.jsdt.core.javascriptValidator + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + +