diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 2bfe06a..dbe61ee --- a/README.md +++ b/README.md @@ -1,49 +1,158 @@ # Review Microservice -A microservice for generic units of work (e.g., a challenge) +Microservice for supporting Topcoder Online Review platform. -## Getting Started -This microservice is using [dropwizard](dropwizard.io) as the REST API framework and implements the [v3 API spec](https://docs.google.com/a/appirio.com/presentation/d/15pucEI0MHj9y778EyaAWGh4MBH-I73i1-GS0ir7FhxE/edit#slide=id.g29c3ffcc3_020). If you are unfamiliar with dropwizard, take a look at their [Getting Started](http://dropwizard.io/getting-started.html) page. +# Prerequisities -### Building -We are using Maven 2 to build and building is as simple as `mvn package`. *Make sure you are using Java 8 for build and runtime* +- Java 8 with Maven 2+ +- Docker -### Configuration -Configuration is managed through a yaml file. The `src/main/resources/review-service.yaml` file should contain the necessary configuration for you to run locally. +# Local Deployment -### Framework Stack -- dropwizard-core - core REST API -- dropwizard-auth - user authentication -- dropwizard-jdbi - database access -- dropwizard-testing - unit testing -- mysql-connector-java - mysql JDBC driver -- fest-assert-core, mockito-core - unit testing -- slf4j - logging +### Start Informix instance via Docker -### Database -We are using mysql RDS for the database. For development use, connect to the topcoder-dev instance. Database access should be limited to the DAO layer. We are using the [JDBI](http://jdbi.org/) framework for database access. Where possible we should use the [SQL Objects API](http://jdbi.org/sql_object_overview/) approach listed in the JDBI documentation. +- Set an IP for Docker by setting the environment variable `DOCKER_IP`. Recommended to set to `0.0.0.0` -For this service, the DAO is managed by `ReviewDAO` +- Command to set the environment variable `DOCKER_IP` -**We need to keep the data model clean and name properties of our representation (model) objects as the column names so we can use the automatic bean mapping facilities instead of needing to manually wire things together. Any deviation requires prior approval.** +``` +export DOCKER_IP=0.0.0.0 +``` + +- To run Informix locally, go to the `local` folder and execute the following command: + +``` +docker-compose up +``` -### API Endpoint -The API endpoint for this service is `com.appirio.service.review.resources.ReviewResource` It contains the specific resource paths supported by this service. The root of the service is /v3/reviews. API methods should be annotated with the appropriate jax-rs annotations and the `@Timed` annotation for metrics. An example method is shown below. +If you should ever encounter the error: + +``` +[java] Caused by: com.topcoder.db.connectionfactory.DBConnectionException: error occurs while creating the connection. +You already have 20 connections at this +time. Any further connections ``` -@GET -@Timed -public ApiResponse getReviews(@Auth AuthUser auth, - @APIQueryParam(repClass = Review.class) QueryParameter queryParameter) - logger.debug("getReviews, filter : " + queryParameter.getFilter().getFields()); - return MetadataApiResponseFactory.createResponse(reviewManager.getReviews(auth, queryParameter, - new BaseAuthorizationContext(auth))); -} +Try to restart the informix database server as below: +``` +#### log into the container based on the container name got in the previous step +docker exec -it local_informix bash + +#### restart the database server +onmode -ky +oninit +``` + +### Inserting test records + +- Test data is present in SQL file (`test_data.sql`) in `local` folder. + +- Transfer the `test_data.sql` to docker environment by executing the following command + ``` +docker cp test_data.sql local_informix:/home/informix +``` + +- Insert the records in `tcs_catalog` database by executing the following command + +``` +docker exec -it local_informix dbaccess tcs_catalog test_data.sql +``` + +- Docker environment is ready to be used in our service. + +### Configuration Variables +The configuration file `service/src/main/resources/review-service.yaml` contain the necessary configuration to run the microservice locally. Below configuration variables need to be set using environment variables. + +|Name | Description | +|--------------------|:-----------------------------------------:| +|AUTH_DOMAIN | Authentication domain of JWT, Dont set unless you have valid tokens with given AUTH_DOMAIN, Defaulted to 'topcoder-dev.com' | +|IP | The IP of your docker container | +|OLTP_USER | The Informix OLTP user | +|OLTP_PW | The Informix OLTP user password | +|OLTP_URL | The JDBC URL of Informix OLTP database | +|DW_USER | The Informix DW user | +|DW_PW | The Informix DW user password | +|DW_URL | The JDBC URL of Informix DW database | +|TC_JWT_KEY | Secret used in JWT Service, Used for signature verification as well | + +In Linux, Set the environment variables as + +``` +export AUTH_DOMAIN=... +export IP=... +export OLTP_USER=... +export OLTP_PW=... +export OLTP_URL=... +export DW_USER=... +export DW_PW=... +export DW_URL=... +``` + +### Working credentials for local deployment + +``` +export IP=0.0.0.0 +export OLTP_USER=informix +export OLTP_PW=1nf0rm1x +export OLTP_URL=jdbc:informix-sqli://$IP:2021/tcs_catalog:INFORMIXSERVER=informixoltp_tcp;IFX_LOCK_MODE_WAIT=5;OPTCOMPIND=0;STMT_CACHE=1 +export DW_USER=informix +export DW_PW=1nf0rm1x +export DW_URL=jdbc:informix-sqli://$IP:2021/common_dw:INFORMIXSERVER=informixoltp_tcp;IFX_LOCK_MODE_WAIT=5;OPTCOMPIND=0;STMT_CACHE=1 +export TC_JWT_KEY=secret +``` + +### Set up Local Maven repository for Appirio Maven + +- Since maven.appirio.net is not accessible, We need to set up a local maven repository with the zip file `temp-maven-repo-master.zip` + +- Unzip the contents to any of the directory in your System. **In Linux, the directory should not be a mounted directory like pen drive, hard disk, etc..** + +- Open `pom.xml` in `service` directory and replace the `` of Local repository with `absolute path of the unzipped file`. E.g. If you have unzipped the files to `/home/user/temp-maven-repo-master`, then POM entry will look like + +``` + + 1_temp_appirio_maven_repo + Appirio Maven Local Repository + file:///home/user/temp-maven-repo-master + +``` + +### Build Microservice + +In the `service` folder of this project, run the following command to build the service. + +``` +mvn clean package +``` + +### Start Microservice + +- Before starting please ensure that all environment variables listed above is set properly and docker is up and running. + +- Then start the service using the following command in the 'service' folder: + +``` +java -jar ./target/review-microservice-*.jar server ./src/main/resources/review-service.yaml +``` + +### Verification + +Though there are many endpoints, For starters, it's sufficient to verify two end points. + +- Endpoint `/v3/scorecards` doesn't require Authentication + +- Endpoint `/v3/reviews` require Authentication + +Use the postman collection in `Postman` directory to verify the end points. + +- **Ensure to import the postman environment before testing the endpoints** + +- Request `Get list of all Scorecards` returns all scorecard templates present in the database + +**Loaded test data has details for 3 submissions for a single competition reviewed by 2 reviewers** -Note the `TCAuth` parameter provided by the `ServiceAuthenticator` class will provide user auth information and is a required parameterfor all secured API calls. -You must set up `TC_JWT_KEY` environment variable, the sample value is `4WvoZLWhFPZ5jauw2+XCU+p772S4oBN25tNPyjHR`. +- Request `Get list of all reviews Reviewer1` return the reviews done by Reviewer 1. -### Testing -All API endpoint methods should have unit tests. For this service we are following the dropwizard testing recommendations as described [here](http://dropwizard.io/manual/testing.html) +- Request `Get list of all reviews Reviewer2` return the reviews done by Reviewer 2. -For manual API testing, the [postman chrome app](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en) is useful. + **Note: Some of the fields in returned output will be NULL, It's expected. That's how the internal controllers and services are written** diff --git a/local/docker-compose.yml b/local/docker-compose.yml index f8f55a9..4c69346 100644 --- a/local/docker-compose.yml +++ b/local/docker-compose.yml @@ -1,14 +1,15 @@ version: '2' services: informix: + container_name: local_informix image: "appiriodevops/informix:1b3d4ef" ports: - "2021:2021" kafka: image: spotify/kafka environment: - ADVERTISED_HOST: $DOCKER_IP - ADVERTISED_PORT: 9092 + ADVERTISED_HOST: ${DOCKER_IP} + ADVERTISED_PORT: 2181 ports: - "2181:2181" - "9092:9092" diff --git a/local/run.sh b/local/run.sh new file mode 100755 index 0000000..7d47abb --- /dev/null +++ b/local/run.sh @@ -0,0 +1,11 @@ +export IP=192.168.1.2 +export OLTP_USER=informix +export OLTP_PW=1nf0rm1x +export OLTP_URL=jdbc:informix-sqli://$IP:2021/tcs_catalog:INFORMIXSERVER=informixoltp_tcp;IFX_LOCK_MODE_WAIT=5;OPTCOMPIND=0;STMT_CACHE=1 +export DW_USER=informix +export DW_PW=1nf0rm1x +export DW_URL=jdbc:informix-sqli://$IP:2021/common_dw:INFORMIXSERVER=informixoltp_tcp;IFX_LOCK_MODE_WAIT=5;OPTCOMPIND=0;STMT_CACHE=1 +export TC_JWT_KEY=secret + + +java -jar ../service/target/review-microservice-*.jar server ../service/src/main/resources/review-service.yaml \ No newline at end of file diff --git a/local/test_data.sql b/local/test_data.sql new file mode 100755 index 0000000..6a63f2b --- /dev/null +++ b/local/test_data.sql @@ -0,0 +1,58 @@ +database tcs_catalog; + +/* Data for a project*/ + +insert into project(project_id, project_status_id, project_category_id, create_user, create_date, modify_user, modify_date) +values(1, 1, 1, 132456, current, 132456, current); + +/* Data for 2 reviewers and 3 submitters based on the token provided */ +insert into resource(resource_id, resource_role_id, project_id, project_phase_id, user_id, create_user, create_date, modify_user, modify_date) values(2, 4, 1, 13, 1234, 132456, current, 132456, current); + +insert into resource(resource_id, resource_role_id, project_id, project_phase_id, user_id, create_user, create_date, modify_user, modify_date) values(3, 4, 1, 13, 1235, 132456, current, 132456, current); + +insert into resource(resource_id, resource_role_id, project_id, project_phase_id, user_id, create_user, create_date, modify_user, modify_date) values(4, 1, 1, 13, 1200, 132456, current, 132456, current); + +insert into resource(resource_id, resource_role_id, project_id, project_phase_id, user_id, create_user, create_date, modify_user, modify_date) values(5, 1, 1, 13, 1201, 132456, current, 132456, current); + +insert into resource(resource_id, resource_role_id, project_id, project_phase_id, user_id, create_user, create_date, modify_user, modify_date) values(6, 1, 1, 13, 1202, 132456, current, 132456, current); + +/* Data for 3 uploads */ + +insert into upload(upload_id, project_id, project_phase_id, resource_id, upload_type_id, upload_status_id, parameter, create_user, create_date, modify_user, modify_date) values(1, 1, 12, 4, 1, 1, 'someparam', 132456, current, 132456, current); + +insert into upload(upload_id, project_id, project_phase_id, resource_id, upload_type_id, upload_status_id, parameter, create_user, create_date, modify_user, modify_date) values(2, 1, 12, 5, 1, 1, 'someparam', 132456, current, 132456, current); + +insert into upload(upload_id, project_id, project_phase_id, resource_id, upload_type_id, upload_status_id, parameter, create_user, create_date, modify_user, modify_date) values(3, 1, 12, 6, 1, 1, 'someparam', 132456, current, 132456, current); + + +/* Data for 3 submission */ + +insert into submission(submission_id, upload_id, submission_status_id, submission_type_id, create_user, create_date, modify_user, modify_date) +values(1, 1, 1, 1, 132456, current, 132456, current); + +insert into submission(submission_id, upload_id, submission_status_id, submission_type_id, create_user, create_date, modify_user, modify_date) +values(2, 2, 1, 1, 132456, current, 132456, current); + +insert into submission(submission_id, upload_id, submission_status_id, submission_type_id, create_user, create_date, modify_user, modify_date) +values(3, 3, 1, 1, 132456, current, 132456, current); + + +/* 3 submissions for a single competition reviewed by 2 reviewers*/ + +insert into review(review_id, resource_id, submission_id, project_phase_id, scorecard_id, committed, score, initial_score, create_user, create_date, modify_user, modify_date) + values(1, 2, 1, 13, 30000410, 1, 100, 95, 132456, current, 132456, current); + +insert into review(review_id, resource_id, submission_id, project_phase_id, scorecard_id, committed, score, initial_score, create_user, create_date, modify_user, modify_date) + values(2, 3, 1, 13, 30000410, 1, 100, 95, 132456, current, 132456, current); + +insert into review(review_id, resource_id, submission_id, project_phase_id, scorecard_id, committed, score, initial_score, create_user, create_date, modify_user, modify_date) + values(3, 2, 2, 13, 30000410, 1, 65, 55, 132456, current, 132456, current); + +insert into review(review_id, resource_id, submission_id, project_phase_id, scorecard_id, committed, score, initial_score, create_user, create_date, modify_user, modify_date) + values(4, 3, 2, 13, 30000410, 1, 70, 60, 132456, current, 132456, current); + +insert into review(review_id, resource_id, submission_id, project_phase_id, scorecard_id, committed, score, initial_score, create_user, create_date, modify_user, modify_date) + values(5, 2, 3, 13, 30000410, 1, 90, 87, 132456, current, 132456, current); + +insert into review(review_id, resource_id, submission_id, project_phase_id, scorecard_id, committed, score, initial_score, create_user, create_date, modify_user, modify_date) + values(6, 3, 3, 13, 30000410, 1, 84.36, 78, 132456, current, 132456, current); diff --git a/postman/review-microservice.postman_collection.json b/postman/review-microservice.postman_collection.json new file mode 100755 index 0000000..2bba9a1 --- /dev/null +++ b/postman/review-microservice.postman_collection.json @@ -0,0 +1,812 @@ +{ + "variables": [], + "info": { + "name": "review-microservice", + "_postman_id": "550e2ca7-d86d-77a7-a597-98a1945ebc8f", + "description": "", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" + }, + "item": [ + { + "name": "Delete Review", + "item": [ + { + "name": "Anonymous - Delete Review - 401", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/71492" + }, + "response": [] + }, + { + "name": "Admin - Delete Review", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/71492" + }, + "response": [] + }, + { + "name": "Admin - Delete Review - Not Found", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/4582" + }, + "response": [] + }, + { + "name": "Non-Admin - Delete Review", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "{{Reviewer1 Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/71492" + }, + "response": [] + } + ] + }, + { + "name": "Re-open Review", + "item": [ + { + "name": "Admin - Reopen Review", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/71492/reopen" + }, + "response": [] + }, + { + "name": "Anonymous - Reopen Review - 401", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/71492/reopen" + }, + "response": [] + }, + { + "name": "Admin - Reopen Review - Not Found", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/7452/reopen" + }, + "response": [] + }, + { + "name": "Non-Admin - Reopen Review", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Reviewer1 Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/71492/reopen" + }, + "response": [] + } + ] + }, + { + "name": "Reset Aggregation", + "item": [ + { + "name": "Admin - Reset Aggregation", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/resetAggregation" + }, + "response": [] + }, + { + "name": "Non Admin - Reset Aggregation", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Reviewer1 Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/resetAggregation" + }, + "response": [] + }, + { + "name": "Anonymous - Reset Aggregation - 401", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/resetAggregation" + }, + "response": [] + }, + { + "name": "Admin - Reset Aggregation - Not Found", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/13/resetAggregation" + }, + "response": [] + }, + { + "name": "Admin - Reset Aggregation - Challenge Not Active - 400", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/11/resetAggregation" + }, + "response": [] + } + ] + }, + { + "name": "Reset Review", + "item": [ + { + "name": "Admin - Reset Review", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/resetReview" + }, + "response": [] + }, + { + "name": "Admin - Reset Review - Not Found", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/resetReview" + }, + "response": [] + }, + { + "name": "Non Admin - Reset Review", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Reviewer1 Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/resetReview" + }, + "response": [] + }, + { + "name": "Anonymous - Reset Review - Unauthorized - 401", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/resetReview" + }, + "response": [] + } + ] + }, + { + "name": "Update Autopilot", + "item": [ + { + "name": "Admin - Enable Autopilot", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"param\" : {\n \t\"enable\" : true\n }\n \n}" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/updateAutopilot" + }, + "response": [] + }, + { + "name": "Admin - Enable Autopilot - Bad Request - 400", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"param\" : {\n \t\"enable\" : \n }\n \n}" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/updateAutopilot" + }, + "response": [] + }, + { + "name": "Admin - Disable Autopilot", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"param\" : {\n \t\"enable\" : false\n }\n \n}" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/updateAutopilot" + }, + "response": [] + }, + { + "name": "Non Admin - Enable Autopilot", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Reviewer1 Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"param\" : {\n \t\"enable\" : true\n }\n \n}" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/updateAutopilot" + }, + "response": [] + }, + { + "name": "Anonymous - Enable Autopilot - Unauthorized - 401", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"param\" : {\n \t\"enable\" : true\n }\n \n}" + }, + "url": "{{apiUrl}}/v3/reviews/30005520/updateAutopilot" + }, + "response": [] + }, + { + "name": "Admin - Enable Autopilot - Not Found", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{Administrator Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"param\" : {\n \t\"enable\" : true\n }\n \n}" + }, + "url": "{{apiUrl}}/v3/reviews/13/updateAutopilot" + }, + "response": [] + } + ] + }, + { + "name": "Review Opportunities", + "item": [ + { + "name": "Get review opportunities for dev F2F (iterative review)", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJoZWZmYW4iLCJleHAiOjE3NjYyODkyNDYsInVzZXJJZCI6IjEzMjQ1NiIsImlhdCI6MTQ1MDkyOTI0NiwiZW1haWwiOm51bGwsImp0aSI6IjEzNjljNjAwLWUwYTEtNDUyNS1hN2M3LTU2YmU3ZDgxM2Y1MSJ9.hp5peSoj-fh3KFkskvBpfUFIcJNtsv4zIMFV-D8F3JA", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{apiUrl}}/v3/reviewOpportunities?challengeTypeId=38", + "host": [ + "{{apiUrl}}" + ], + "path": [ + "v3", + "reviewOpportunities" + ], + "query": [ + { + "key": "challengeTypeId", + "value": "38", + "equals": true + } + ] + } + }, + "response": [] + }, + { + "name": "Get review opportunities for UI prototype", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJoZWZmYW4iLCJleHAiOjE3NjYyODkyNDYsInVzZXJJZCI6IjEzMjQ1NiIsImlhdCI6MTQ1MDkyOTI0NiwiZW1haWwiOm51bGwsImp0aSI6IjEzNjljNjAwLWUwYTEtNDUyNS1hN2M3LTU2YmU3ZDgxM2Y1MSJ9.hp5peSoj-fh3KFkskvBpfUFIcJNtsv4zIMFV-D8F3JA", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{apiUrl}}/v3/reviewOpportunities?challengeTypeId=19", + "host": [ + "{{apiUrl}}" + ], + "path": [ + "v3", + "reviewOpportunities" + ], + "query": [ + { + "key": "challengeTypeId", + "value": "19", + "equals": true + } + ] + } + }, + "response": [] + }, + { + "name": "Get review opportunities", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJoZWZmYW4iLCJleHAiOjE3NjYyODkyNDYsInVzZXJJZCI6IjEzMjQ1NiIsImlhdCI6MTQ1MDkyOTI0NiwiZW1haWwiOm51bGwsImp0aSI6IjEzNjljNjAwLWUwYTEtNDUyNS1hN2M3LTU2YmU3ZDgxM2Y1MSJ9.hp5peSoj-fh3KFkskvBpfUFIcJNtsv4zIMFV-D8F3JA", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviewOpportunities" + }, + "response": [] + }, + { + "name": "Get review opportunities for assembly (contest review and spec)", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJoZWZmYW4iLCJleHAiOjE3NjYyODkyNDYsInVzZXJJZCI6IjEzMjQ1NiIsImlhdCI6MTQ1MDkyOTI0NiwiZW1haWwiOm51bGwsImp0aSI6IjEzNjljNjAwLWUwYTEtNDUyNS1hN2M3LTU2YmU3ZDgxM2Y1MSJ9.hp5peSoj-fh3KFkskvBpfUFIcJNtsv4zIMFV-D8F3JA", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{apiUrl}}/v3/reviewOpportunities?challengeTypeId=14", + "host": [ + "{{apiUrl}}" + ], + "path": [ + "v3", + "reviewOpportunities" + ], + "query": [ + { + "key": "challengeTypeId", + "value": "14", + "equals": true + } + ] + } + }, + "response": [] + }, + { + "name": "Get review opportunities (challenge type id not supported)", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJoZWZmYW4iLCJleHAiOjE3NjYyODkyNDYsInVzZXJJZCI6IjEzMjQ1NiIsImlhdCI6MTQ1MDkyOTI0NiwiZW1haWwiOm51bGwsImp0aSI6IjEzNjljNjAwLWUwYTEtNDUyNS1hN2M3LTU2YmU3ZDgxM2Y1MSJ9.hp5peSoj-fh3KFkskvBpfUFIcJNtsv4zIMFV-D8F3JA", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{apiUrl}}/v3/reviewOpportunities?challengeTypeId=100", + "host": [ + "{{apiUrl}}" + ], + "path": [ + "v3", + "reviewOpportunities" + ], + "query": [ + { + "key": "challengeTypeId", + "value": "100", + "equals": true + } + ] + } + }, + "response": [] + }, + { + "name": "Get review opportunities (challenge type id is not positive)", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJoZWZmYW4iLCJleHAiOjE3NjYyODkyNDYsInVzZXJJZCI6IjEzMjQ1NiIsImlhdCI6MTQ1MDkyOTI0NiwiZW1haWwiOm51bGwsImp0aSI6IjEzNjljNjAwLWUwYTEtNDUyNS1hN2M3LTU2YmU3ZDgxM2Y1MSJ9.hp5peSoj-fh3KFkskvBpfUFIcJNtsv4zIMFV-D8F3JA", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{apiUrl}}/v3/reviewOpportunities?challengeTypeId=-14", + "host": [ + "{{apiUrl}}" + ], + "path": [ + "v3", + "reviewOpportunities" + ], + "query": [ + { + "key": "challengeTypeId", + "value": "-14", + "equals": true + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Get list of all Scorecards", + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/scorecards" + }, + "response": [] + }, + { + "name": "Get list of all reviews Reviewer1", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "{{Reviewer1 Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews" + }, + "response": [] + }, + { + "name": "Get list of all reviews Reviewer2", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "{{Reviewer2 Token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{apiUrl}}/v3/reviews" + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/postman/review-microservice.postman_environment.json b/postman/review-microservice.postman_environment.json new file mode 100755 index 0000000..748b0f3 --- /dev/null +++ b/postman/review-microservice.postman_environment.json @@ -0,0 +1,34 @@ +{ + "id": "de37a461-f5ae-23ea-9c06-fdf7e8e05622", + "name": "review-microservice", + "values": [ + { + "enabled": true, + "key": "apiUrl", + "value": "http://localhost:8080", + "type": "text" + }, + { + "enabled": true, + "key": "Reviewer1 Token", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJyZXZpZXdlciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoicmV2aWV3ZXIxIiwiZXhwIjoxNzY2Mjg5MjQ2LCJ1c2VySWQiOiIxMjM0IiwiaWF0IjoxNDUwOTI5MjQ2LCJlbWFpbCI6bnVsbCwianRpIjoiMTM2OWM2MDAtZTBhMS00NTI1LWE3YzctNTZiZTdkODEzZjUxIn0.4AOKcnvm4Wms_lY_RyYv61OHXCC-I__JnEPRZEN3-vM", + "type": "text" + }, + { + "enabled": true, + "key": "Reviewer2 Token", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJyZXZpZXdlciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoicmV2aWV3ZXIyIiwiZXhwIjoxNzY2Mjg5MjQ2LCJ1c2VySWQiOiIxMjM1IiwiaWF0IjoxNDUwOTI5MjQ2LCJlbWFpbCI6bnVsbCwianRpIjoiMTM2OWM2MDAtZTBhMS00NTI1LWE3YzctNTZiZTdkODEzZjUxIn0.RrkQ7srca99ctABa_VM9VYediC2mcg4hPOiQ1Roou14", + "type": "text" + }, + { + "enabled": true, + "key": "Administrator Token", + "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJhZG1pbmlzdHJhdG9yIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJhZG1pbiIsImV4cCI6MTc2NjI4OTI0NiwidXNlcklkIjoiNTU1NSIsImlhdCI6MTQ1MDkyOTI0NiwiZW1haWwiOm51bGwsImp0aSI6IjEzNjljNjAwLWUwYTEtNDUyNS1hN2M3LTU2YmU3ZDgxM2Y1MSJ9.Wg4nPCva9opNyrYQTngSoXetXr-W-ZSDtD2jae1gswk", + "type": "text" + } + ], + "timestamp": 1507999856202, + "_postman_variable_scope": "environment", + "_postman_exported_at": "2017-10-14T21:32:17.437Z", + "_postman_exported_using": "Postman/5.3.0" +} diff --git a/service/build/Dockerfile.template b/service/build/Dockerfile.template index 323f307..02c9481 100644 --- a/service/build/Dockerfile.template +++ b/service/build/Dockerfile.template @@ -1,6 +1,6 @@ FROM appiriodevops/ap-microservice-base:0.0.1 -MAINTAINER mdesiderio@appirio.com +MAINTAINER devops+jenkins@topcoder.com WORKDIR /data diff --git a/service/build/deploy.sh b/service/build/deploy.sh index 1dc8864..4132c22 100755 --- a/service/build/deploy.sh +++ b/service/build/deploy.sh @@ -41,10 +41,11 @@ cp $WORKSPACE/service/target/review-microservice*.jar review-microservice.jar cp $WORKSPACE/service/src/main/resources/review-service.yaml review-service.yaml echo "Building docker image $DOCKER_REPO/$IMAGE" -sudo docker build -t $DOCKER_REPO/$IMAGE $DEPLOY_DIR +docker build -t $DOCKER_REPO/$IMAGE $DEPLOY_DIR echo "Pushing image to docker $DOCKER_REPO/$IMAGE" -sudo docker push $DOCKER_REPO/$IMAGE +#docker login -u ykohata +docker push $DOCKER_REPO/$IMAGE echo "Generating dockerrun file" cat $DOCKERRUN_TEMPLATE | sed -e "s/@IMAGE@/${IMAGE}/g" > $DOCKERRUN diff --git a/service/pom.xml b/service/pom.xml index 51aa367..20c1190 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -10,8 +10,8 @@ 1.0.0 1.5.4 2.7.3 - 4.0.0 - 1.0.16-SNAPSHOT + 4.0.1-DEV + 1.0.20-SNAPSHOT @@ -105,7 +105,7 @@ - + org.javassist @@ -242,12 +242,12 @@ appirio-repo Appirio Maven Repository - file:///mnt/maven/repository + http://maven.topcoder-dev.com:8080/ appirio-repo Appirio Maven Repository - file:///mnt/maven/repository + http://maven.topcoder-dev.com:8080/ @@ -265,7 +265,7 @@ false - org.apache.maven.plugins @@ -294,26 +294,33 @@ + + temp-maven-repo + https://github.com/appirio-tech/temp-maven-repo/raw/master + + true + always + + + + Appirio Technology Maven Repository + http://maven.topcoder-dev.com:8080/ + + always + + + always + + com.springsource.repository.bundles.release SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases http://repository.springsource.com/maven/bundles/release - com.springsource.repository.bundles.external SpringSource Enterprise Bundle Repository - External Bundle Releases http://repository.springsource.com/maven/bundles/external - - Appirio Technology Maven Repository - http://maven.appirio.net:8080/ - - always - - - always - - diff --git a/service/sql/populate-db-samples.sql b/service/sql/populate-db-samples.sql old mode 100644 new mode 100755 index 5da42b3..84f4012 --- a/service/sql/populate-db-samples.sql +++ b/service/sql/populate-db-samples.sql @@ -394,4 +394,4 @@ INSERT INTO REVIEW_PROCESS ( 'PEER', 'PEER', 1 -); \ No newline at end of file +); diff --git a/service/sql/review-process.sql b/service/sql/review-process.sql old mode 100644 new mode 100755 index 53afeb2..cf85813 --- a/service/sql/review-process.sql +++ b/service/sql/review-process.sql @@ -90,4 +90,4 @@ CREATE TABLE SCORECARD_ITEM ( FOREIGN KEY (scorecardId) references SCORECARD(id), FOREIGN KEY (questionId) references SCORECARD_QUESTION(id), FOREIGN KEY (questionOptionId) references SCORECARD_QUESTION_OPTION(id) -); \ No newline at end of file +); diff --git a/service/src/main/java/com/appirio/service/resourcefactory/ReviewFactory.java b/service/src/main/java/com/appirio/service/resourcefactory/ReviewFactory.java index 9abab88..3b126d7 100644 --- a/service/src/main/java/com/appirio/service/resourcefactory/ReviewFactory.java +++ b/service/src/main/java/com/appirio/service/resourcefactory/ReviewFactory.java @@ -3,6 +3,7 @@ import io.dropwizard.setup.Environment; import com.appirio.service.review.ReviewServiceConfiguration; +import com.appirio.service.review.manager.ChallengeManager; import com.appirio.service.review.manager.ReviewManager; import com.appirio.service.review.resources.ReviewResource; import com.appirio.service.supply.resources.ResourceFactory; @@ -10,8 +11,12 @@ /** * Factory for ReviewResource - * - * @author rrecharla@appirio.com + * + * v1.1 Updates : https://www.topcoder.com/challenge-details/30059779/?type=develop + * Updated getResourceInstance() to set the ChallengeManager field. + * + * @author rrecharla@appirio.com, TCSCODE + * @version 1.1 */ public class ReviewFactory implements ResourceFactory { @@ -43,6 +48,7 @@ public ReviewFactory(ReviewServiceConfiguration config, Environment env) { @Override public ReviewResource getResourceInstance() throws SupplyException { final ReviewManager reviewManager = new ReviewManager(); - return new ReviewResource(reviewManager); + final ChallengeManager challengeManager = new ChallengeManager(); + return new ReviewResource(reviewManager, challengeManager); } } diff --git a/service/src/main/java/com/appirio/service/resourcefactory/ReviewOpportunitiesFactory.java b/service/src/main/java/com/appirio/service/resourcefactory/ReviewOpportunitiesFactory.java new file mode 100644 index 0000000..d9e0baf --- /dev/null +++ b/service/src/main/java/com/appirio/service/resourcefactory/ReviewOpportunitiesFactory.java @@ -0,0 +1,35 @@ +package com.appirio.service.resourcefactory; + +import com.appirio.service.review.manager.ReviewOpportunitiesManager; +import com.appirio.service.review.resources.ReviewOpportunitiesResource; +import com.appirio.service.supply.resources.ResourceFactory; +import com.appirio.supply.SupplyException; + +/** + * ReviewOpportunitiesFactory is used to create the ReviewOpportunitiesResource + * + * It's added in Topcoder - Create Endpoint to Fetch Review Opportunities v1.0 + * + * @author TCCoder + * @version 1.0 + * + */ +public class ReviewOpportunitiesFactory implements ResourceFactory { + + /** + * Simple constructor to initialize ReviewOpportunitiesFactory + */ + public ReviewOpportunitiesFactory() { + } + + /** + * Get ReviewOpportunitiesResource object + * @return ReviewOpportunitiesResource the review opportunities resource + * @throws SupplyException exception for supply server + */ + @Override + public ReviewOpportunitiesResource getResourceInstance() throws SupplyException { + final ReviewOpportunitiesManager reviewOpportunitiesManager = new ReviewOpportunitiesManager(); + return new ReviewOpportunitiesResource(reviewOpportunitiesManager); + } +} \ No newline at end of file diff --git a/service/src/main/java/com/appirio/service/review/ReviewServiceApplication.java b/service/src/main/java/com/appirio/service/review/ReviewServiceApplication.java index e596c7a..5b7d754 100644 --- a/service/src/main/java/com/appirio/service/review/ReviewServiceApplication.java +++ b/service/src/main/java/com/appirio/service/review/ReviewServiceApplication.java @@ -6,6 +6,7 @@ import com.appirio.service.resourcefactory.PeerReviewFactory; import com.appirio.service.resourcefactory.ReviewFactory; import com.appirio.service.resourcefactory.ReviewItemFactory; +import com.appirio.service.resourcefactory.ReviewOpportunitiesFactory; import com.appirio.service.resourcefactory.ScorecardFactory; import com.appirio.service.resourcefactory.ScorecardQuestionFactory; import com.appirio.service.supply.resources.SupplyDatasourceFactory; @@ -15,8 +16,13 @@ /** * Main application class for the review service application. + * + * Version 1.1 - Topcoder - Create Endpoint to Fetch Review Opportunities v1.0 + * - register ReviewOpportunitiesFactory in registerResources + * * * @author TCDEVELOPER + * @version 1.1 */ public class ReviewServiceApplication extends BaseApplication { @@ -56,9 +62,10 @@ protected void logServiceSpecificConfigs(ReviewServiceConfiguration config) { } /** + * Register resources for the application * @see BaseApplication - * @param config ReviewServiceConfiguration - * @param env Environment + * @param config the ReviewServiceConfiguration + * @param env the Environment */ @Override protected void registerResources(ReviewServiceConfiguration config, Environment env) throws Exception { @@ -70,6 +77,7 @@ protected void registerResources(ReviewServiceConfiguration config, Environment env.jersey().register(new ReviewItemFactory(config, env).getResourceInstance()); env.jersey().register(new ScorecardFactory(config, env).getResourceInstance()); env.jersey().register(new ScorecardQuestionFactory(config, env).getResourceInstance()); + env.jersey().register(new ReviewOpportunitiesFactory().getResourceInstance()); } /** diff --git a/service/src/main/java/com/appirio/service/review/api/AutoPilot.java b/service/src/main/java/com/appirio/service/review/api/AutoPilot.java new file mode 100644 index 0000000..7b22a8a --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/api/AutoPilot.java @@ -0,0 +1,20 @@ +package com.appirio.service.review.api; + +import javax.validation.constraints.NotNull; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * This class represents a challenge instance + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class AutoPilot { + @NotNull + private Boolean enable; +} \ No newline at end of file diff --git a/service/src/main/java/com/appirio/service/review/api/Challenge.java b/service/src/main/java/com/appirio/service/review/api/Challenge.java new file mode 100644 index 0000000..eafa289 --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/api/Challenge.java @@ -0,0 +1,35 @@ +package com.appirio.service.review.api; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * This class represents a challenge instance + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class Challenge { + /** + * The challenge id. + */ + private Long id; + + /** + * The challenge name. + */ + private String name; + + /** + * The challenge status. + */ + private String status; + + /** + * The challenge current active phase. + */ + private String activePhase; +} \ No newline at end of file diff --git a/service/src/main/java/com/appirio/service/review/api/Payment.java b/service/src/main/java/com/appirio/service/review/api/Payment.java new file mode 100644 index 0000000..a1a6d3c --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/api/Payment.java @@ -0,0 +1,41 @@ +package com.appirio.service.review.api; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Represents the Payment entity. + * + * It's added in Topcoder - Update Review Opportunities Endpoint + * + * @author TCCoder + * @version 1.0 + */ +@AllArgsConstructor +@NoArgsConstructor +public class Payment { + + /** + * Represents the reviewer role + */ + @Setter + @Getter + private String role; + + /** + * Represents the reviewer role id + */ + @Setter + @Getter + private long roleId; + + + /** + * Represents the reviewer payment + */ + @Setter + @Getter + private Double payment; +} diff --git a/service/src/main/java/com/appirio/service/review/api/ReviewOpportunitiesDTO.java b/service/src/main/java/com/appirio/service/review/api/ReviewOpportunitiesDTO.java new file mode 100644 index 0000000..50ad585 --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/api/ReviewOpportunitiesDTO.java @@ -0,0 +1,150 @@ +package com.appirio.service.review.api; + +import java.util.Date; + +import lombok.Getter; +import lombok.Setter; + +/** + * Represents the ReviewOpportunitiesDTO entity. + * + * It's added in Topcoder - Create Endpoint to Fetch Review Opportunities v1.0 + * + * @author TCCoder + * @version 1.0 + */ +public class ReviewOpportunitiesDTO { + /** + * Represents the review auction id attribute. + */ + @Getter + @Setter + private long reviewAuctionId; + + /** + * Represents the review auction type id attribute. + */ + @Getter + @Setter + private int reviewAuctionTypeId; + + /** + * Represents the challenge id attribute. + */ + @Getter + @Setter + private long challengeId; + + /** + * Represents the number of submissions attribute. + */ + @Getter + @Setter + private int numberOfSubmissions; + + /** + * Represents the review start attribute. + */ + @Getter + @Setter + private Date reviewStart; + + /** + * Represents the review end attribute. + */ + @Getter + @Setter + private Date reviewEnd; + + /** + * Represents the number of review positions available attribute. + */ + @Getter + @Setter + private int numberOfReviewPositionsAvailable; + + /** + * Represents the challenge type attribute. + */ + @Getter + @Setter + private String challengeType; + + /** + * Represents the challenge type id attribute. + */ + @Getter + @Setter + private int challengeTypeId; + + /** + * Represents the review type attribute. + */ + @Getter + @Setter + private String reviewType; + + /** + * Represents the challenge name attribute. + */ + @Getter + @Setter + private String challengeName; + + /** + * Represents the challenge version attribute. + */ + @Getter + @Setter + private String ChallengeVersion; + + /** + * Represents the review application role id attribute. + */ + @Getter + @Setter + private long reviewApplicationRoleId; + + /** + * Represents the resource role id attribute. + */ + @Getter + @Setter + private long resourceRoleId; + + /** + * Represents the fixed amount attribute. + */ + @Getter + @Setter + private double fixedAmount; + + /** + * Represents the base coefficient attribute. + */ + @Getter + @Setter + private double baseCoefficient; + + /** + * Represents the incremental coefficient attribute. + */ + @Getter + @Setter + private double incrementalCoefficient; + + /** + * Represents the prize attribute. + */ + @Getter + @Setter + private double prize; + + /** + * Represents the submission count attribute. + */ + @Getter + @Setter + private int submissionCount; + +} \ No newline at end of file diff --git a/service/src/main/java/com/appirio/service/review/api/ReviewOpportunityItem.java b/service/src/main/java/com/appirio/service/review/api/ReviewOpportunityItem.java new file mode 100644 index 0000000..2ce39cc --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/api/ReviewOpportunityItem.java @@ -0,0 +1,93 @@ +package com.appirio.service.review.api; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import lombok.Getter; +import lombok.Setter; + +/** + * Represents the ReviewOpportunityItem entity. + * + * It's added in Topcoder - Create Endpoint to Fetch Review Opportunities version 1.0 + * + * Updates in Topcoder - Update Review Opportunities Endpoint + * - remove payment + * - add payments as a list of Payment + * + * @author TCCoder + * @version 1.0 + */ +public class ReviewOpportunityItem { + /** + * Represents the id attribute. + */ + @Getter + @Setter + private long id; + + /** + * Represents the start date attribute. + */ + @Getter + @Setter + @JsonFormat(pattern = "EEE MMM dd yyyy hh:mm:ss z", locale = "us") + private Date startDate; + + /** + * Represents the submissions attribute. + */ + @Getter + @Setter + private int submissions; + + /** + * Represents the open positions attribute. + */ + @Getter + @Setter + private int openPositions; + + /** + * Represents the type attribute. + */ + @Getter + @Setter + private String type; + + /** + * Represents the challenge id attribute. + */ + @Getter + @Setter + @JsonIgnore + private long challengeId; + + /** + * Represents the challenge name attribute. + */ + @Getter + @Setter + @JsonIgnore + private String challengeName; + + /** + * Represents the challenge attribute. + * The keys will be 'id', 'title' and 'version', and the corresponding values will be the id, name and version of the challenge. + */ + @Getter + @Setter + private Map challenge; + + /** + * Represents the payment attribute. + */ + @Getter + @Setter + private List payments; + +} \ No newline at end of file diff --git a/service/src/main/java/com/appirio/service/review/authorization/manager/ReviewAuthorizationManager.java b/service/src/main/java/com/appirio/service/review/authorization/manager/ReviewAuthorizationManager.java index ec05fd4..8346f8f 100644 --- a/service/src/main/java/com/appirio/service/review/authorization/manager/ReviewAuthorizationManager.java +++ b/service/src/main/java/com/appirio/service/review/authorization/manager/ReviewAuthorizationManager.java @@ -11,9 +11,16 @@ * Review authorization manager * * @see AuthorizationManager + *

+ * v1.1 (ADMIN APP CHALLENGE FEE AND REVIEW MANAGEMENT API) + *

    + *
  • Updated canRead() to allow the administrator to read a review
  • + *
  • Updated canReadWriteDelete() to allow administrator to delete reviews
  • + *
+ *

* - * @author mdesiderio@appirio.com - * + * @author mdesiderio@appirio.com, TCSCODER + * @version 1.1 */ public class ReviewAuthorizationManager extends AuthorizationManager { @@ -91,7 +98,8 @@ public boolean canRead(Review model, AuthorizationContext context) { // -- The user is the reviewer resource // -- The user is the submitter and the review is committed return principalIsReviewer(model, context) - || (principalIsSubmitter(model, context) && reviewIsCommitted(model)); + || (principalIsSubmitter(model, context) && reviewIsCommitted(model)) + || context.getPrincipal().hasRole("administrator"); } /** @@ -119,10 +127,7 @@ public boolean canReadWrite(Review model, AuthorizationContext context) { */ @Override public boolean canReadWriteDelete(Review model, AuthorizationContext context) { - // Only administrators can delete reviews, and this functionality is not - // yet implemented in the - // review microservice - return false; + // Only administrators are allowed to delete reviews. + return context.getPrincipal().hasRole("administrator"); } - } diff --git a/service/src/main/java/com/appirio/service/review/dao/ChallengeDAO.java b/service/src/main/java/com/appirio/service/review/dao/ChallengeDAO.java new file mode 100644 index 0000000..cf50dd3 --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/dao/ChallengeDAO.java @@ -0,0 +1,25 @@ +package com.appirio.service.review.dao; + +import org.skife.jdbi.v2.sqlobject.Bind; +import com.appirio.service.review.api.Challenge; +import com.appirio.supply.dataaccess.DatasourceName; +import com.appirio.supply.dataaccess.SqlQueryFile; + +/** + * DAO to access challenge related data + * + * @author TCSCODER + * @version 1.0 + */ +@DatasourceName("oltp") +public interface ChallengeDAO { + + /** + * Get a challenge by id. + * + * @param challengeId the id of the challenge to retrieve. + * @return The Challenge instance + */ + @SqlQueryFile("sql/challenge/get-by-id.sql") + Challenge getChallenge(@Bind("challengeId") Long challengeId); +} \ No newline at end of file diff --git a/service/src/main/java/com/appirio/service/review/dao/ReviewDAO.java b/service/src/main/java/com/appirio/service/review/dao/ReviewDAO.java index f264ee4..082e3a2 100644 --- a/service/src/main/java/com/appirio/service/review/dao/ReviewDAO.java +++ b/service/src/main/java/com/appirio/service/review/dao/ReviewDAO.java @@ -18,13 +18,24 @@ import java.util.Map; /** - * DAO to handle review data + * DAO to handle review data. + * + *

+ * Version 1.1 (ADMIN APP CHALLENGE FEE AND REVIEW MANAGEMENT API) + *

    + *
  • Added method resetAggregation()
  • + *
  • Added method resetReview()
  • + *
  • Added method updateAutoPilot()
  • + *
  • Added method reopenReview()
  • + *
  • Added method deleteReview()
  • + *
+ *

* - * @author mdesiderio@appirio.com + * @author mdesiderio@appirio.com, TCSCODER + * @version 1.1 */ @DatasourceName("oltp") public interface ReviewDAO { - /** * Get a single review by id. Authenticated version to be invoked by * managers / resources @@ -35,8 +46,7 @@ public interface ReviewDAO { */ @AuthManager(ReviewAuthorizationManager.class) @SqlQueryFile("sql/review/review-by-id-query.sql") - Review getReview(@Bind("reviewId") Long reviewId, - @AuthContext AuthorizationContext authContext); + Review getReview(@Bind("reviewId") Long reviewId, @AuthContext AuthorizationContext authContext); /** * Get a single review by id. Unauthenticated version for internal use @@ -49,16 +59,15 @@ Review getReview(@Bind("reviewId") Long reviewId, // TODO: document this @SqlUpdateFile("sql/review/review-update.sql") - void updateReview(@Audit @Validate @BindBean Review review, - @AuditActionPerformer @Bind("userId") Long userId); + void updateReview(@Audit @Validate @BindBean Review review, @AuditActionPerformer @Bind("userId") Long userId); // TODO: document this // TODO: Test this @SqlUpdateFile("sql/review/review-update-user-editable-fields.sql") void updateReviewUserEditableFields( - @AuthManager(ReviewAuthorizationManager.class) @Audit @Validate @BindBean Review review, - @AuditActionPerformer @Bind("userId") Long userId, - @AuthContext AuthorizationContext authContext); + @AuthManager(ReviewAuthorizationManager.class) @Audit @Validate @BindBean Review review, + @AuditActionPerformer @Bind("userId") Long userId, + @AuthContext AuthorizationContext authContext); /** * Creates a new review @@ -67,8 +76,7 @@ void updateReviewUserEditableFields( * @param userId user id */ @SqlUpdateFile("sql/review/review-insert.sql") - void createReview(@Audit @Validate @BindBean Review review, - @AuditActionPerformer @Bind("userId") Long userId); + void createReview(@Audit @Validate @BindBean Review review, @AuditActionPerformer @Bind("userId") Long userId); /** * For a given user and a given challenge, returns the counts of how many @@ -79,8 +87,7 @@ void createReview(@Audit @Validate @BindBean Review review, * @return returns counts of pending, submitted and total reviews */ @SqlQueryFile("sql/review/user-review-count-query.sql") - Map getUserReviewsCount(@Bind("userId") Long userId, - @Bind("challengeId") Long challengeId); + Map getUserReviewsCount(@Bind("userId") Long userId, @Bind("challengeId") Long challengeId); /** * Submissions associated with a challenge, not reviewed by the user, not @@ -95,9 +102,8 @@ Map getUserReviewsCount(@Bind("userId") Long userId, * @return list of submissions */ @SqlQueryFile("sql/review/next-submission-to-review-by-required-reviews-query.sql") - List> getNextSubmissionToReviewByRequiredReviews( - @Bind("userId") Long userId, @Bind("challengeId") Long challengeId, - @Bind("requiredReviews") Long requiredReviews); + List> getNextSubmissionToReviewByRequiredReviews(@Bind("userId") Long userId, + @Bind("challengeId") Long challengeId, @Bind("requiredReviews") Long requiredReviews); /** * find the submission not submitted by the user, not yet reviewed by the @@ -108,8 +114,8 @@ List> getNextSubmissionToReviewByRequiredReviews( * @return list of submissions */ @SqlQueryFile("sql/review/next-submission-to-revew-by-least-reviewed-query.sql") - List> getNextSubmissionToReviewByLeastReviewed( - @Bind("userId") Long userId, @Bind("challengeId") Long challengeId); + List> getNextSubmissionToReviewByLeastReviewed(@Bind("userId") Long userId, + @Bind("challengeId") Long challengeId); /** * Finds review based on API user input @@ -121,10 +127,9 @@ List> getNextSubmissionToReviewByLeastReviewed( */ @SqlQueryFile("sql/review/review-query.sql") @AuthManager(ReviewAuthorizationManager.class) - QueryResult> getReviews(@ApiUser AuthUser auth, - @ApiQueryInput QueryParameter queryParameter, - @AuthContext AuthorizationContext authContext); - + QueryResult> getReviews(@ApiUser AuthUser auth, @ApiQueryInput QueryParameter queryParameter, + @AuthContext AuthorizationContext authContext); + /** * Check whether the phase is closed for review * @@ -133,12 +138,66 @@ QueryResult> getReviews(@ApiUser AuthUser auth, */ @SqlQueryFile("sql/review/is-review-phase-closed.sql") Integer isPhaseClosedForReview(@Bind("reviewId") Long reviewId); - + // TODO: // Document this // This query is using select *, please specify only needed fields @SqlQueryFile("sql/review/get-phase-by-challengeid-phasetype.sql") - Map getPhaseId(@Bind("challengeId") Long challengeId, @Bind("phaseTypeId") Long phaseTypeId); - - + Map getPhaseId(@Bind("challengeId") Long challengeId, @Bind("phaseTypeId") Long phaseTypeId); + + /** + * Resets the aggregation of the challenge identified by the given challengeId. + * + * @param challengeId The id of the challenge for which to reset the aggregation. + * @param userId The id of the user who performs the reset operation. + * + * @since 1.1 + */ + @SqlUpdateFile("sql/review/reset-aggregation.sql") + void resetAggregation(@Bind("challengeId") Long challengeId, @Bind("userId") Long userId); + + /** + * Resets the review of the challenge identified by the given challengeId + * + * @param challengeId The id of the challenge dor which to reset the review. + * @param userId The id of the user who performs the operation. + * + * @since 1.1 + */ + @SqlUpdateFile("sql/review/reset-review.sql") + void resetReview(@Bind("challengeId") Long challengeId, @Bind("userId") Long userId); + + /** + * Updates the autopilot setting for the challenge identified by the given id. + * + * @param challengeId The id of the challenge for which to update the autopilot setting. + * @param value The autopilot setting value ("On" or "Off") + * @param userId The id of the user who is performing the update operation. + * + * @since 1.1 + */ + @SqlUpdateFile("sql/review/update-autopilot.sql") + void updateAutoPilot(@Bind("challengeId") Long challengeId, @Bind("value") String value, + @Bind("userId") Long userId); + + /** + * Reopens the review identified by the given reviewId. + * + * @param reviewId The id of the review to reopen. + * @param userId The id of the user who is performing the operation. it is used for auditing purpose. + * + * @since 1.1 + */ + @SqlUpdateFile("sql/review/reopen-review.sql") + void reopenReview(@Bind("reviewId") Long reviewId, @Bind("userId") Long userId); + + /** + * Deletes the review identified by the given reviewId from the persistence. + * + * @param reviewId The id of the review to delete. + * + * @since 1.1 + */ + @SqlUpdateFile("sql/review/delete-review.sql") + void deleteReview(@Bind("reviewId") Long reviewId); } diff --git a/service/src/main/java/com/appirio/service/review/dao/ReviewOpportunitiesDAO.java b/service/src/main/java/com/appirio/service/review/dao/ReviewOpportunitiesDAO.java new file mode 100644 index 0000000..0309c9e --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/dao/ReviewOpportunitiesDAO.java @@ -0,0 +1,36 @@ +package com.appirio.service.review.dao; + +import java.util.List; + +import com.appirio.supply.dataaccess.ApiQueryInput; +import com.appirio.tech.core.api.v3.request.QueryParameter; +import org.skife.jdbi.v2.sqlobject.Bind; + +import com.appirio.service.review.api.ReviewOpportunitiesDTO; +import com.appirio.supply.dataaccess.DatasourceName; +import com.appirio.supply.dataaccess.SqlQueryFile; + +/** + * ReviewOpportunitiesDAO is used to manage the review opportunities related data. + * + * It's added in Topcoder - Create Endpoint to Fetch Review Opportunities v1.0 + * + * Updates in Topcoder - Update Review Opportunities Endpoint + * - pass QueryParameter to getReviewOpportunities instead of challengeTypeId + * + * @author TCCoder + * @version 1.0 + * + */ +@DatasourceName("oltp") +public interface ReviewOpportunitiesDAO { + + /** + * Get review opportunities + * + * @param queryParameter query parameter + * @return a list of review opportunity + */ + @SqlQueryFile("sql/review-opportunities/get-review-opportunities.sql") + List getReviewOpportunities(@ApiQueryInput QueryParameter queryParameter); +} diff --git a/service/src/main/java/com/appirio/service/review/manager/ChallengeManager.java b/service/src/main/java/com/appirio/service/review/manager/ChallengeManager.java new file mode 100644 index 0000000..c93f17f --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/manager/ChallengeManager.java @@ -0,0 +1,40 @@ +package com.appirio.service.review.manager; + +import com.appirio.service.review.api.Challenge; +import com.appirio.service.review.dao.ChallengeDAO; +import com.appirio.supply.DAOFactory; +import com.appirio.supply.SupplyException; + +/** + * This class manages the challenge data. it uses the ChallengeDAO instance to access the persistence + * + * @author TCSCODER + * @version 1.0 + */ +public class ChallengeManager { + + /** + * The ChallengeDAO to be used to access the challenge data in the persistence. + */ + private ChallengeDAO challengeDAO; + + /** + * Creates a new instance of ChallengeManager and initializes the ChallengeDAO instance field. + */ + public ChallengeManager() throws SupplyException { + this.challengeDAO = DAOFactory.getInstance().createDAO(ChallengeDAO.class); + } + + /** + * Gets a challenge by the given id. returns null if such challenge does not exist. + * + * @param challengeId Id of the challenge to retrieve. + * + * @return The retrieved Challenge. + * + * @throws SupplyException If any error occurs during the operation. + */ + public Challenge getChallenge(Long challengeId) throws SupplyException { + return challengeDAO.getChallenge(challengeId); + } +} \ No newline at end of file diff --git a/service/src/main/java/com/appirio/service/review/manager/ReviewManager.java b/service/src/main/java/com/appirio/service/review/manager/ReviewManager.java index 499a687..84a6ae3 100644 --- a/service/src/main/java/com/appirio/service/review/manager/ReviewManager.java +++ b/service/src/main/java/com/appirio/service/review/manager/ReviewManager.java @@ -1,7 +1,9 @@ package com.appirio.service.review.manager; +import com.appirio.service.review.api.Challenge; import com.appirio.service.review.api.Review; import com.appirio.service.review.dao.ReviewDAO; +import com.appirio.service.review.util.Helper; import com.appirio.supply.AuthorizationException; import com.appirio.supply.DAOFactory; import com.appirio.supply.SupplyException; @@ -17,7 +19,21 @@ /** * Manager for Review related business logic * - * @author dbabu@appirio.com + *

+ * Version 1.1 (ADMIN APP CHALLENGE FEE AND REVIEW MANAGEMENT API) + *

    + *
  • Added method resetAggregation()
  • + *
  • Added method resetReview()
  • + *
  • Added method updateAutoPilot()
  • + *
  • Added method reopenReview()
  • + *
  • Added method deleteReview()
  • + *
  • Added RESET_AGGREGATION_VALID_PHASES field
  • + *
  • Added RESET_REVIEW_VALID_PHASES field
  • + *
+ *

+ * + * @author dbabu@appirio.com, TCSCODER + * @version 1.1 * */ public class ReviewManager { @@ -26,7 +42,23 @@ public class ReviewManager { * DAO for review */ private ReviewDAO reviewDAO; - + + /** + * The valid phases which reset aggregation can be run on. + * + * @since 1.1 + */ + private static final String[] RESET_AGGREGATION_VALID_PHASES = new String[] { "Aggregation", "Final Fix", + "Final Review" }; + + /** + * The valid phases which reset review can be run on. + * + * @since 1.1 + */ + private static final String[] RESET_REVIEW_VALID_PHASES = new String[] { "Review", "Appeals", "Appeals Response", + "Aggregation", "Final Fix", "Final Review" }; + /** * Default constructor that initializes the DAO * @throws SupplyException exception for the supply @@ -67,7 +99,7 @@ public void updateReview(Review review, Long userId) { public void updateReviewUserEditableFields(Review review, Long userId, AuthorizationContext authContext) { reviewDAO.updateReviewUserEditableFields(review, userId, authContext); } - + /** * Gets the count of how many reviews a user has completed in a challenge, it includes * the following fields: @@ -97,8 +129,8 @@ public Map getUserReviewsCount(Long userId, Long challengeId) { * @param requiredReviews number of required reviews * @return list of submissions */ - public List> getNextSubmissionToReviewByRequiredReviews( - Long userId, Long challengeId, long requiredReviews) { + public List> getNextSubmissionToReviewByRequiredReviews(Long userId, Long challengeId, + long requiredReviews) { return reviewDAO.getNextSubmissionToReviewByRequiredReviews(userId, challengeId, requiredReviews); } @@ -116,8 +148,7 @@ public List> getNextSubmissionToReviewByRequiredReviews( * @param challengeId id of the challenge * @return list of submissions */ - public List> getNextSubmissionToReviewByLeastReviewed( - Long userId, Long challengeId) { + public List> getNextSubmissionToReviewByLeastReviewed(Long userId, Long challengeId) { return reviewDAO.getNextSubmissionToReviewByLeastReviewed(userId, challengeId); } @@ -140,9 +171,96 @@ public void createReview(Review review, Long userId) { * @return list of reviews * @throws AuthorizationException exception for authorization */ - public QueryResult> getReviews(AuthUser auth, QueryParameter queryParameter, AuthorizationContext authContext) throws SupplyException { + public QueryResult> getReviews(AuthUser auth, QueryParameter queryParameter, + AuthorizationContext authContext) throws SupplyException { LimitQueryHandler.limitQuery(queryParameter); return reviewDAO.getReviews(auth, queryParameter, authContext); } + /** + * Resets the aggregation of the given challenge. + * + * @param challenge the the Challenge for which to reset the aggregation. + * @param userId id of the user who is performing the the operation. + * + * @throws SupplyException If any error occurs during the operation. + * + * @since 1.1 + */ + public void resetAggregation(Challenge challenge, Long userId) throws SupplyException { + // Check if the challenge is active + Helper.validateChallengeStatus(challenge, "active"); + + // Check if resetting the aggregation is allowed in the current challenge active phase. + Helper.validateValidPhases(challenge, RESET_AGGREGATION_VALID_PHASES); + + // Reset the aggregation phase. + reviewDAO.resetAggregation(challenge.getId(), userId); + } + + /** + * Resets the review of the given challenge. + * + * @param challenge the Challenge for which to reset the review + * @param userId id of the user who resets the review. + * + * @throws SupplyException If any error occurs during the operation. + * + * @since 1.1 + */ + public void resetReview(Challenge challenge, Long userId) throws SupplyException { + // Check if the challenge is active + Helper.validateChallengeStatus(challenge, "active"); + + // Check if resetting the review is allowed in the current challenge active phase. + Helper.validateValidPhases(challenge, RESET_REVIEW_VALID_PHASES); + + // Reset the aggregation. + reviewDAO.resetAggregation(challenge.getId(), userId); + + // Reset the review + reviewDAO.resetReview(challenge.getId(), userId); + } + + /** + * Updates the Autopilot for the challenge identified by the given challengeId. + * + * @param challengeId the id of the challenge for which to update autopilot setting. + * @param enableAutopilot The flag indicating whether to enable/disable the autopilot setting for the challenge. + * @param userId id of the user who is performing the operation, it is used for auditing purpose. + * + * @throws SupplyException If any error occurs during the operation. + * + * @since 1.1 + */ + public void updateAutoPilot(Long challengeId, Boolean enableAutoPilot, Long userId) throws SupplyException { + reviewDAO.updateAutoPilot(challengeId, enableAutoPilot ? "On" : "Off", userId); + } + + /** + * Reopens the review identified by the given reviewId. + * + * @param reviewId the id of the review to reopen + * @param userId id of the user who is performing the operation. + * + * @throws SupplyException If any error occurs during the operation. + * + * @since 1.1 + */ + public void reopenReview(Long reviewId, Long userId) throws SupplyException { + reviewDAO.reopenReview(reviewId, userId); + } + + /** + * Deletes a review from the persistence. + * + * @param reviewId The identifier of the review to delete. + * + * @throws SupplyException If any error occurs during the operation. + * + * @since 1.1 + */ + public void deleteReview(Long reviewId) throws SupplyException { + reviewDAO.deleteReview(reviewId); + } } diff --git a/service/src/main/java/com/appirio/service/review/manager/ReviewOpportunitiesManager.java b/service/src/main/java/com/appirio/service/review/manager/ReviewOpportunitiesManager.java new file mode 100644 index 0000000..8b44a92 --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/manager/ReviewOpportunitiesManager.java @@ -0,0 +1,280 @@ +package com.appirio.service.review.manager; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import com.appirio.service.review.api.Payment; +import com.appirio.service.review.api.ReviewOpportunitiesDTO; +import com.appirio.service.review.api.ReviewOpportunityItem; +import com.appirio.service.review.dao.ReviewOpportunitiesDAO; +import com.appirio.supply.DAOFactory; +import com.appirio.supply.SupplyException; +import com.appirio.tech.core.api.v3.request.FieldSelector; +import com.appirio.tech.core.api.v3.request.FilterParameter; +import com.appirio.tech.core.api.v3.request.QueryParameter; + +/** + * ReviewOpportunitiesManager is used to manage the review opportunities data. + * + * It's added in Topcoder - Create Endpoint to Fetch Review Opportunities v1.0 + * + * Updates in Topcoder - Update Review Opportunities Endpoint: + * - challengeTypeId is optional, if it does not exist, returns all review opportunities + * - get list of Payment instead of max payment value + * + * @author TCCoder + * @version 1.0 + * + */ +public class ReviewOpportunitiesManager { + /** + * The CHALLENGE_TYPE_IDS contains all the supported challenge type ids + */ + private static final HashSet CHALLENGE_TYPE_IDS = new HashSet(); + static { + CHALLENGE_TYPE_IDS.add(1); + CHALLENGE_TYPE_IDS.add(2); + CHALLENGE_TYPE_IDS.add(5); + CHALLENGE_TYPE_IDS.add(6); + CHALLENGE_TYPE_IDS.add(7); + CHALLENGE_TYPE_IDS.add(9); + CHALLENGE_TYPE_IDS.add(13); + CHALLENGE_TYPE_IDS.add(14); + CHALLENGE_TYPE_IDS.add(23); + CHALLENGE_TYPE_IDS.add(19); + CHALLENGE_TYPE_IDS.add(24); + CHALLENGE_TYPE_IDS.add(25); + CHALLENGE_TYPE_IDS.add(26); + CHALLENGE_TYPE_IDS.add(29); + CHALLENGE_TYPE_IDS.add(35); + CHALLENGE_TYPE_IDS.add(36); + CHALLENGE_TYPE_IDS.add(38); + CHALLENGE_TYPE_IDS.add(39); + } + + /** + * The REVIEW_APPLICATION_ID_NAME_MAPPING contains mapping of review application role id and review application name. + */ + private static final HashMap REVIEW_APPLICATION_ID_NAME_MAPPING = new HashMap<>(); + static { + REVIEW_APPLICATION_ID_NAME_MAPPING.put(1L, "Primary Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(2L, "Secondary Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(3L, "Primary Failure Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(4L, "Accuracy Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(5L, "Stress Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(6L, "Failure Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(7L, "Specification Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(8L, "Iterative Reviewer"); + REVIEW_APPLICATION_ID_NAME_MAPPING.put(9L, "Reviewer"); + } + + /** + * The REVIEW_AUCTION_TYPE_ID_FOR_REGULAR_REVIEW + */ + private static final int REVIEW_AUCTION_TYPE_ID_FOR_REGULAR_REVIEW = 1; + + /** + * The REVIEW_AUCTION_TYPE_ID_FOR_COMPONENT_DEV_REVIEW + */ + private static final int REVIEW_AUCTION_TYPE_ID_FOR_COMPONENT_DEV_REVIEW = 2; + + /** + * The REVIEW_AUCTION_TYPE_ID_FOR_SPEC_REVIEW + */ + private static final int REVIEW_AUCTION_TYPE_ID_FOR_SPEC_REVIEW = 3; + + /** + * The REVIEW_AUCTION_TYPE_ID_FOR_ITERATIVE_REVIEW + */ + private static final int REVIEW_AUCTION_TYPE_ID_FOR_ITERATIVE_REVIEW = 4; + + /** + * The REVIEW_AUCTION_TYPE_ID_FOR_3_SCENARIOS_REVIEW + */ + private static final int REVIEW_AUCTION_TYPE_ID_FOR_3_SCENARIOS_REVIEW = 5; + + /** + * The reviewOpportunitiesDAO used to query the review opportunities data + */ + private ReviewOpportunitiesDAO reviewOpportunitiesDAO; + + /** + * Create ReviewOpportunitiesManager + * + * @throws SupplyException if any error occurs + */ + public ReviewOpportunitiesManager() throws SupplyException { + this.reviewOpportunitiesDAO = DAOFactory.getInstance().createDAO(ReviewOpportunitiesDAO.class); + } + + /** + * Get review opportunities by challenge type id + * if challenge type id is 0, all review opportunities will be returned + * + * @param challengeTypeId the challengeType id to get review opportunities + * @throws SupplyException if error occurs + * @return a list of review opportunities + */ + public List getReviewOpportunities(int challengeTypeId) throws SupplyException { + + if (challengeTypeId < 0) { + throw new SupplyException("The challenge type id should be positive", HttpServletResponse.SC_BAD_REQUEST); + + } + + QueryParameter queryParameter = new QueryParameter(new FieldSelector()); + + if (challengeTypeId == 0) { + // challengeTypeId is not provided in query string + queryParameter.setFilter(new FilterParameter("")); + + } else { + // if challengeTypeId is in query string + // double check if its value is valid + if (!CHALLENGE_TYPE_IDS.contains(challengeTypeId)) { + throw new SupplyException("The challenge type id is not supported:" + challengeTypeId, HttpServletResponse.SC_NOT_FOUND); + } + + queryParameter.setFilter(new FilterParameter("challenge_type_id=" + String.valueOf(challengeTypeId))); + } + + List revOpportunities = this.reviewOpportunitiesDAO.getReviewOpportunities(queryParameter); + + // extract the challenge ids + List challengeIds = new ArrayList(); + for (ReviewOpportunitiesDTO dto : revOpportunities) { + if (!challengeIds.contains(dto.getChallengeId())) { + challengeIds.add(dto.getChallengeId()); + } + } + + List items = new ArrayList(); + for (long challengeId : challengeIds) { + List regularReviewOppos = new ArrayList(); + List iterativeReviewOppos = new ArrayList(); + List specReviewOppos = new ArrayList(); + for (ReviewOpportunitiesDTO dto : revOpportunities) { + if (challengeId == dto.getChallengeId()) { + if (dto.getReviewAuctionTypeId() == REVIEW_AUCTION_TYPE_ID_FOR_REGULAR_REVIEW + || dto.getReviewAuctionTypeId() == REVIEW_AUCTION_TYPE_ID_FOR_COMPONENT_DEV_REVIEW + || dto.getReviewAuctionTypeId() == REVIEW_AUCTION_TYPE_ID_FOR_3_SCENARIOS_REVIEW) {// find the contest reviews + regularReviewOppos.add(dto); + } else if (dto.getReviewAuctionTypeId() == REVIEW_AUCTION_TYPE_ID_FOR_SPEC_REVIEW) {// find the spec reviews + specReviewOppos.add(dto); + } else if (dto.getReviewAuctionTypeId() == REVIEW_AUCTION_TYPE_ID_FOR_ITERATIVE_REVIEW) {// find the iterative reviews + iterativeReviewOppos.add(dto); + } + } + } + + // add the regular review item + this.addReviewItem(items, regularReviewOppos); + + // add the spec review item + this.addReviewItem(items, specReviewOppos); + + // add the iterative review item + this.addReviewItem(items, iterativeReviewOppos); + } + + return items; + } + + /** + * Creates ReviewOpportunityItem and insert it into items + * + * @param items list of ReviewOpportunityItem to be inserted into + * @param reviewOppos list of ReviewOpportunitiesDTO + */ + private void addReviewItem(List items, List reviewOppos) { + // add the iterative review item + if (!reviewOppos.isEmpty()) { + ReviewOpportunityItem item = this.convertReviewOpportunitiesDTO(reviewOppos.get(0)); + List payments = this.getPayments(reviewOppos); + item.setPayments(payments); + items.add(item); + } + } + + /** + * Convert ReviewOpportunitiesDTO to ReviewOpportunityItem + * + * @param dto the ReviewOpportunitiesDTO to convert + * @return the ReviewOpportunityItem result + */ + private ReviewOpportunityItem convertReviewOpportunitiesDTO(ReviewOpportunitiesDTO dto) { + ReviewOpportunityItem item = new ReviewOpportunityItem(); + item.setId(dto.getReviewAuctionId()); + item.setOpenPositions(dto.getNumberOfReviewPositionsAvailable()); + item.setStartDate(dto.getReviewStart()); + item.setSubmissions(dto.getNumberOfSubmissions()); + item.setType(dto.getReviewType().trim()); + + Map challenge = new HashMap(); + challenge.put("id", dto.getChallengeId()); + challenge.put("title", dto.getChallengeName()); + challenge.put("version", dto.getChallengeVersion()); + item.setChallenge(challenge); + + return item; + } + + /** + * Get list of Payment from the review opportunities + * + * @param dtos the ReviewOpportunitiesDTO instances to use + * @return a list of Payment + */ + private List getPayments(List dtos) { + + Map paymentsMap = new HashMap(); + for (ReviewOpportunitiesDTO dto : dtos) { + Double value = paymentsMap.get(dto.getReviewApplicationRoleId()); + if (value == null) { + value = 0D; + } + BigDecimal fixedAmount = new BigDecimal(dto.getFixedAmount()).setScale(2, RoundingMode.HALF_UP); + + BigDecimal prize = new BigDecimal(dto.getPrize()); + double baseCoefficient = dto.getBaseCoefficient(); + double incrementalCoefficient = dto.getIncrementalCoefficient(); + int submissionsCount = dto.getSubmissionCount(); + int count = isSubmissionRequired(dto.getResourceRoleId()) ? submissionsCount != 0 ? submissionsCount : 1 : 0; + BigDecimal scaledPrize = prize.setScale(2, RoundingMode.HALF_UP); + BigDecimal multiplicandCoefficient = BigDecimal + .valueOf(baseCoefficient + incrementalCoefficient * (float) count); + BigDecimal payment = fixedAmount.add(scaledPrize.multiply(multiplicandCoefficient)).setScale(2, + RoundingMode.HALF_UP); + + value += payment.doubleValue(); + paymentsMap.put(dto.getReviewApplicationRoleId(), value); + } + + List payments = new ArrayList<>(); + + for (Long roleId : paymentsMap.keySet()) { + Payment payment = new Payment(REVIEW_APPLICATION_ID_NAME_MAPPING.get(roleId), roleId, paymentsMap.get(roleId)); + payments.add(payment); + } + + return payments; + } + + /** + * Check submission is required for the resource role id. + * + * @param resourceRoleId the resourceRoleId to use + * @return the true if required, false otherwise + */ + private boolean isSubmissionRequired(long resourceRoleId) { + return resourceRoleId == 2L || resourceRoleId == 4L || resourceRoleId == 5L || resourceRoleId == 6L + || resourceRoleId == 7L || resourceRoleId == 19L || resourceRoleId == 20L || resourceRoleId == 21L; + } +} diff --git a/service/src/main/java/com/appirio/service/review/resources/ReviewOpportunitiesResource.java b/service/src/main/java/com/appirio/service/review/resources/ReviewOpportunitiesResource.java new file mode 100644 index 0000000..25c8d28 --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/resources/ReviewOpportunitiesResource.java @@ -0,0 +1,72 @@ +package com.appirio.service.review.resources; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.appirio.service.review.manager.ReviewOpportunitiesManager; +import com.appirio.supply.ErrorHandler; +import com.appirio.tech.core.api.v3.request.annotation.AllowAnonymous; +import com.appirio.tech.core.api.v3.response.ApiResponse; +import com.appirio.tech.core.api.v3.response.ApiResponseFactory; +import com.codahale.metrics.annotation.Timed; + +/** + * ReviewOpportunitiesResource is used to provide the endpoints for review opportunities + * + * It's added in Topcoder - Create Endpoint to Fetch Review Opportunities v1.0 + * + * @author TCCoder + * @version 1.0 + * + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path("reviewOpportunities") +public class ReviewOpportunitiesResource { + + /** + * Logger used to log the events + */ + private static final Logger logger = LoggerFactory.getLogger(ReviewOpportunitiesResource.class); + + /** + * The reviewOpportunitiesManager used to manage the review opportunities data + */ + private ReviewOpportunitiesManager reviewOpportunitiesManager; + + /** + * Create ReviewOpportunitiesResource + * + * @param reviewOpportunitiesManager the reviewOpportunitiesManager to use + */ + public ReviewOpportunitiesResource(ReviewOpportunitiesManager reviewOpportunitiesManager) { + this.reviewOpportunitiesManager = reviewOpportunitiesManager; + } + + /** + * Get review opportunities + * + * @param challengeTypeId the challengeTypeId to get the open review opportunities + * @return the ApiResponse result contains the open review opportunities + */ + @GET + @Timed + @AllowAnonymous + public ApiResponse getReviewOpportunities(@QueryParam("challengeTypeId") int challengeTypeId) { + try { + logger.debug("getReviewOpportunities, challengeTypeId : " + challengeTypeId); + return ApiResponseFactory + .createResponse(reviewOpportunitiesManager.getReviewOpportunities(challengeTypeId)); + } catch (Exception e) { + return ErrorHandler.handle(e, logger); + } + } + +} diff --git a/service/src/main/java/com/appirio/service/review/resources/ReviewResource.java b/service/src/main/java/com/appirio/service/review/resources/ReviewResource.java index 38975d9..39bfaa2 100644 --- a/service/src/main/java/com/appirio/service/review/resources/ReviewResource.java +++ b/service/src/main/java/com/appirio/service/review/resources/ReviewResource.java @@ -1,10 +1,16 @@ package com.appirio.service.review.resources; +import com.appirio.service.review.api.AutoPilot; +import com.appirio.service.review.api.Challenge; import com.appirio.service.review.api.Review; +import com.appirio.service.review.manager.ChallengeManager; import com.appirio.service.review.manager.ReviewManager; +import com.appirio.service.review.util.Helper; import com.appirio.service.supply.resources.MetadataApiResponseFactory; import com.appirio.supply.ErrorHandler; +import com.appirio.supply.SupplyException; import com.appirio.supply.dataaccess.authorization.BaseAuthorizationContext; +import com.appirio.tech.core.api.v3.request.PostPutRequest; import com.appirio.tech.core.api.v3.request.QueryParameter; import com.appirio.tech.core.api.v3.request.annotation.APIQueryParam; import com.appirio.tech.core.api.v3.response.ApiResponse; @@ -15,15 +21,35 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import javax.ws.rs.*; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.PUT; +import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; /** * Resource to expose Review REST API * - * @author dbabu@appirio.com - * + *

+ * Version 1.1 (ADMIN APP CHALLENGE FEE AND REVIEW MANAGEMENT API) + *

    + *
  • Added method resetAggregation()
  • + *
  • Added method resetReview()
  • + *
  • Added method updateAutopilot()
  • + *
  • Added method reopenReview()
  • + *
  • Added method deleteReview()
  • + *
  • Added challengeManager field
  • + *
+ *

+ * + * @author dbabu@appirio.com, TCSCODER + * @version 1.1 */ @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -40,13 +66,21 @@ public class ReviewResource { */ private ReviewManager reviewManager; + /** + * Manager to access the challenge business logic. + * + * @since 1.1 + */ + private ChallengeManager challengeManager; + /** * Constructor that initializes the manager * * @param reviewManager manager for the review */ - public ReviewResource(ReviewManager reviewManager){ + public ReviewResource(ReviewManager reviewManager, ChallengeManager challengeManager) { this.reviewManager = reviewManager; + this.challengeManager = challengeManager; } /** @@ -58,12 +92,12 @@ public ReviewResource(ReviewManager reviewManager){ */ @GET @Timed - public ApiResponse getReviews(@Auth AuthUser auth, + public ApiResponse getReviews(@Auth AuthUser auth, @APIQueryParam(repClass = Review.class) QueryParameter queryParameter) { try { logger.debug("getReviews, filter : " + queryParameter.getFilter().getFields()); - return MetadataApiResponseFactory.createResponse(reviewManager.getReviews(auth, queryParameter, - new BaseAuthorizationContext(auth))); + return MetadataApiResponseFactory + .createResponse(reviewManager.getReviews(auth, queryParameter, new BaseAuthorizationContext(auth))); } catch (Exception e) { return ErrorHandler.handle(e, logger); } @@ -82,7 +116,8 @@ public ApiResponse getReviews(@Auth AuthUser auth, public ApiResponse getReview(@Auth AuthUser auth, @PathParam("reviewId") Long reviewId) { try { logger.debug("getReview, reviewId : " + reviewId); - return ApiResponseFactory.createResponse(reviewManager.getReview(reviewId, new BaseAuthorizationContext(auth))); + return ApiResponseFactory + .createResponse(reviewManager.getReview(reviewId, new BaseAuthorizationContext(auth))); } catch (Exception e) { return ErrorHandler.handle(e, logger); } @@ -106,10 +141,195 @@ public ApiResponse updateReview(@Auth AuthUser auth, @Valid Review review, @Path // We primarily use the reviewid from the url and overwrite it into the review object review.setId(reviewId); - reviewManager.updateReviewUserEditableFields(review, Long.valueOf(auth.getUserId().toString()), new BaseAuthorizationContext(auth)); + reviewManager.updateReviewUserEditableFields(review, Long.valueOf(auth.getUserId().toString()), + new BaseAuthorizationContext(auth)); return ApiResponseFactory.createResponse(null); } catch (Exception e) { return ErrorHandler.handle(e, logger); } } + + /** + * Resets the aggregation of the challenge identified by the given challengeId. + * + * @param auth authentication user + * @param challengeId id for the Challenge for which to reset the aggregation + * + * @return response for the api + * + * @since 1.1 + */ + @POST + @Path("{challengeId}/resetAggregation") + @Timed + public ApiResponse resetAggregation(@Auth AuthUser user, @PathParam("challengeId") Long challengeId) { + try { + logger.debug("resetAggregation, challengeId : " + challengeId); + + // Check user permissions and challenge existence + Challenge challenge = checkPermissionsAndChallengeExistence(user, challengeId); + + // reset the aggregation of the challenge + reviewManager.resetAggregation(challenge, Long.valueOf(user.getUserId().toString())); + return ApiResponseFactory.createResponse(challengeId); + } catch (Exception e) { + return ErrorHandler.handle(e, logger); + } + } + + /** + * Resets the review of the challenge identified by the given challengeId. + * + * @param auth authentication user + * @param challengeId The id of the challenge for which to reset the review + * + * @return response for the api + * + * @since 1.1 + */ + @POST + @Path("{challengeId}/resetReview") + @Timed + public ApiResponse resetReview(@Auth AuthUser user, @PathParam("challengeId") Long challengeId) { + try { + logger.debug("resetReview, challengeId : " + challengeId); + + // Check the user permissions and get the challenge. + Challenge challenge = checkPermissionsAndChallengeExistence(user, challengeId); + + // reset the challenge review + reviewManager.resetReview(challenge, Long.valueOf(user.getUserId().toString())); + return ApiResponseFactory.createResponse(challengeId); + } catch (Exception e) { + return ErrorHandler.handle(e, logger); + } + } + + /** + * updates the autopilot setting for the challenge identified by the given challengeId + * + * @param auth authentication user + * @param challengeId The id of the challenge for which to update the autopilot setting. + * @param request The update autopilot request. + * + * @return response for the api + * + * @since 1.1 + */ + @POST + @Path("{challengeId}/updateAutopilot") + @Timed + public ApiResponse updateAutopilot(@Auth AuthUser user, @PathParam("challengeId") Long challengeId, + @Valid PostPutRequest request) { + try { + logger.debug("updateAutopilot, challengeId : " + challengeId); + + checkPermissionsAndChallengeExistence(user, challengeId); + + // update AutoPilot for the challenge. + reviewManager.updateAutoPilot(challengeId, request.getParam().getEnable(), + Long.valueOf(user.getUserId().toString())); + return ApiResponseFactory.createResponse(challengeId); + } catch (Exception e) { + return ErrorHandler.handle(e, logger); + } + } + + /** + * Reopens the review identified by the given reviewId. + * + * @param auth authentication user + * @param reviewId id for the review to reopen + * + * @return response for the api + * + * @since 1.1 + */ + @POST + @Path("{reviewId}/reopen") + @Timed + public ApiResponse reopenReview(@Auth AuthUser user, @PathParam("reviewId") Long reviewId) { + try { + logger.debug("reopenReview, reviewId : " + reviewId); + + checkPermissionsAndReviewExistence(user, reviewId); + + // reopen the review + reviewManager.reopenReview(reviewId, Long.valueOf(user.getUserId().toString())); + return ApiResponseFactory.createResponse(reviewId); + } catch (Exception e) { + return ErrorHandler.handle(e, logger); + } + } + + /** + * Deletes the review identified by the given reviewId. + * + * @param auth authentication user + * @param reviewId id for the review to delete + * + * @return response for the api + * + * @since 1.1 + */ + @DELETE + @Path("{reviewId}") + @Timed + public ApiResponse deleteReview(@Auth AuthUser user, @PathParam("reviewId") Long reviewId) { + try { + logger.debug("deleteReview, reviewId : " + reviewId); + + checkPermissionsAndReviewExistence(user, reviewId); + + // delete the review + reviewManager.deleteReview(reviewId); + return ApiResponseFactory.createResponse(reviewId); + } catch (Exception e) { + return ErrorHandler.handle(e, logger); + } + } + + /** + * This private method checks whether the user has administrator permissions and if the given review exists. + * + * @param user The user for whome to check administrator permissions. + * @param reviewId The id of the review for which to check the existence in the persistence. + * + * @throws SupplyException if the user is not an administrator of the review does not exist + */ + private void checkPermissionsAndReviewExistence(AuthUser user, Long reviewId) throws SupplyException { + // Check if the user is an administrator + Helper.checkAdmin(user); + + // check if the review identified by the given id exists in the persistence + if (reviewManager.getReview(reviewId, new BaseAuthorizationContext(user)) == null) { + throw new SupplyException("The review identified by id = " + reviewId + " does not exist", + HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * This private method checks whether the user has administrator permissions and if the given challenge exists. + * + * @param user The user for whome to check administrator permissions. + * @param challengeId The id of the challenge for which to check the existence in the persistence. + * + * @throws SupplyException if the user is not an administrator of the challenge does not exist + * + * @return The Challenge identified by the specified id. + */ + private Challenge checkPermissionsAndChallengeExistence(AuthUser user, Long challengeId) throws SupplyException { + // Check if the user is an administrator + Helper.checkAdmin(user); + + // Get the challenge by id + Challenge challenge = challengeManager.getChallenge(challengeId); + + // check if the challenge identified by the given id exists in the persistence + if (challenge == null) { + throw new SupplyException("The challenge identified by id = " + challengeId + " does not exist", + HttpServletResponse.SC_NOT_FOUND); + } + return challenge; + } } diff --git a/service/src/main/java/com/appirio/service/review/util/Helper.java b/service/src/main/java/com/appirio/service/review/util/Helper.java new file mode 100644 index 0000000..cc506aa --- /dev/null +++ b/service/src/main/java/com/appirio/service/review/util/Helper.java @@ -0,0 +1,62 @@ +package com.appirio.service.review.util; + +import com.appirio.service.review.api.Challenge; +import com.appirio.supply.SupplyException; +import com.appirio.tech.core.auth.AuthUser; +import java.util.Arrays; + +/** + * This class contains helper methods to be used by the classes defined in the review microservice. + * + * @author TCSCODER + * @version 1.0 + */ +public final class Helper { + + /** + * Checks if the user is an administrator. + * + * @param user + * The user to check if he is an administrator. + */ + public static void checkAdmin(AuthUser user) throws SupplyException { + if (!user.hasRole("administrator")) { + throw new SupplyException("Only administrators can access this resource.", 403); + } + } + + /** + * Validates whether the challenge is the specified status. + * + * @param challenge the challenge to validate. + * @param expectedStatus the expected challenge status. + */ + public static void validateChallengeStatus(Challenge challenge, String expectedStatus) { + if (!challenge.getStatus().equalsIgnoreCase(expectedStatus)) { + throw new IllegalArgumentException( + String.format("Status of challenge [%s] is not [%s]", challenge.getName(), expectedStatus)); + } + } + + /** + * Validate whether the challenge active phase is in the specified valid phase set. + * + * @param challenge the challenge to validate + * @param validPhases the set of valid phases. + */ + public static void validateValidPhases(Challenge challenge, String[] validPhases) { + boolean validPhaseExist = false; + + for (String validPhase : validPhases) { + if (validPhase.equalsIgnoreCase(challenge.getActivePhase())) { + validPhaseExist = true; + break; + } + } + + if (!validPhaseExist) { + throw new IllegalArgumentException(String.format("Active Phase of challenge [%s] should be in %s", + challenge.getName(), Arrays.toString(validPhases))); + } + } +} \ No newline at end of file diff --git a/service/src/main/resources/new-relic-url-patterns b/service/src/main/resources/new-relic-url-patterns index 5711892..f0837f5 100644 --- a/service/src/main/resources/new-relic-url-patterns +++ b/service/src/main/resources/new-relic-url-patterns @@ -8,4 +8,10 @@ GET reviewItems\/ POST reviewItems\/ PUT reviewItems\/ GET scorecards\/ -GET scorecardQuestions\/ \ No newline at end of file +GET scorecardQuestions\/ +POST reviews\/.*\/resetAggregation\/ +POST reviews\/.*\/resetReview\/ +POST reviews\/.*\/updateAutopilot\/ +POST reviews\/.*\/reopen\/ +DELETE reviews\/.*\/ +GET reviewOpportunities\/ \ No newline at end of file diff --git a/service/src/main/resources/sql/challenge/get-by-id.sql b/service/src/main/resources/sql/challenge/get-by-id.sql new file mode 100644 index 0000000..06c1ed4 --- /dev/null +++ b/service/src/main/resources/sql/challenge/get-by-id.sql @@ -0,0 +1,13 @@ +SELECT p.project_id as id, + (SELECT value from project_info where project_id = :challengeId and project_info_type_id = 6) as name, + psl.name as status, + NVL((select name from phase_type_lu where phase_type_id = + (select max(ptl.phase_type_id) from phase_type_lu ptl, project_phase current_phase + where ptl.phase_type_id = current_phase.phase_type_id + and current_phase.project_id = :challengeId + and current_phase.phase_status_id = 2 )), 'None') as activePhase + +FROM project p, project_category_lu pcl, project_status_lu psl +WHERE p.project_id = :challengeId + AND p.project_category_id = pcl.project_category_id + AND p.project_status_id = psl.project_status_id \ No newline at end of file diff --git a/service/src/main/resources/sql/review-opportunities/get-review-opportunities.properties b/service/src/main/resources/sql/review-opportunities/get-review-opportunities.properties new file mode 100644 index 0000000..705af51 --- /dev/null +++ b/service/src/main/resources/sql/review-opportunities/get-review-opportunities.properties @@ -0,0 +1,2 @@ +filter.challenge_type_id.template=challenge_type_id = :challenge_type_id +filter.challenge_type_id.type=Long \ No newline at end of file diff --git a/service/src/main/resources/sql/review-opportunities/get-review-opportunities.sql b/service/src/main/resources/sql/review-opportunities/get-review-opportunities.sql new file mode 100644 index 0000000..b85c202 --- /dev/null +++ b/service/src/main/resources/sql/review-opportunities/get-review-opportunities.sql @@ -0,0 +1,195 @@ +SELECT +review_auction_id AS reviewAuctionId, +review_auction_type_id AS reviewAuctionTypeId, +challenge_id AS challengeId, +number_of_submissions AS numberOfSubmissions, +review_start AS reviewStart, +review_end AS reviewEnd, +number_of_review_positions_available AS numberOfReviewPositionsAvailable, +challenge_type AS challengeType, +challenge_type_Id AS challengeTypeId, +review_type AS reviewType, +challenge_name AS challengeName, +version AS ChallengeVersion, +review_application_role_id AS reviewApplicationRoleId, +resource_role_id AS resourceRoleId, +fixed_amount AS fixedAmount, +base_coefficient AS baseCoefficient, +incremental_coefficient AS incrementalCoefficient, +prize AS prize, +submission_count AS submissionCount +FROM ( +SELECT + ra.review_auction_id +, ra.review_auction_type_id +, ra.project_id AS challenge_id +, (SELECT COUNT(DISTINCT u.resource_id) + FROM upload u + INNER JOIN submission s ON u.upload_id = s.upload_id + WHERE u.project_id = p.project_id + AND s.submission_type_id = 1 + AND s.submission_status_id IN (1,2,3,4) + AND u.upload_type_id = 1) AS number_of_submissions +, NVL(pp4.actual_start_time, pp4.scheduled_start_time) AS review_start +, pp4.scheduled_end_time AS review_end +, pc.parameter::int - (SELECT count(r.resource_id) FROM resource r WHERE r.project_id=p.project_id + AND r.resource_role_id IN (4,5,6,7)) AS number_of_review_positions_available +, pcl.name AS challenge_type +, pcl.project_category_id AS challenge_type_Id +, 'Contest Review' AS review_type +, pi6.value AS challenge_name +, pi7.value as version +, rarl.review_application_role_id +, dpp.resource_role_id +, dpp.fixed_amount +, dpp.base_coefficient +, dpp.incremental_coefficient +, (select max(prize_amount) from prize where project_id = p.project_id AND prize_type_id=15 AND place=1) as prize +, CASE WHEN dpp.resource_role_id = 2 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id IN (4,5,6,7) THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 19 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 3 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 20 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 21 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + END AS submission_count +FROM review_auction ra +INNER JOIN review_auction_type_lu ratl ON ratl.review_auction_type_id=ra.review_auction_type_id AND ratl.review_auction_category_id=1 +INNER JOIN project p ON p.project_id=ra.project_id +INNER JOIN project_category_lu pcl ON pcl.project_category_id = p.project_category_id +INNER JOIN project_phase pp2 ON pp2.project_id=p.project_id AND pp2.phase_type_id=2 +INNER JOIN project_phase pp4 ON pp4.project_id=p.project_id AND pp4.phase_type_id=4 +INNER JOIN phase_criteria pc ON pc.project_phase_id=pp4.project_phase_id AND pc.phase_criteria_type_id=6 +INNER JOIN project_info pi6 ON p.project_id = pi6.project_id AND pi6.project_info_type_id = 6 +INNER JOIN project_info pi7 ON p.project_id = pi7.project_id AND pi7.project_info_type_id = 7 +INNER JOIN review_application_role_lu rarl ON rarl.review_auction_type_id = ra.review_auction_type_id +INNER JOIN review_application_role_resource_role_xref rarrrx ON rarrrx.review_application_role_id = rarl.review_application_role_id +INNER JOIN default_project_payment dpp ON dpp.resource_role_id = rarrrx.resource_role_id AND p.project_category_id = dpp.project_category_id +WHERE p.project_status_id=1 +AND pcl.project_type_id IN (1,2) +AND pcl.project_category_id != 29 +AND pcl.project_category_id != 37 +AND pp2.phase_status_id IN (2,3) +AND pp4.phase_status_id IN (1,2) +AND not exists (SELECT 1 FROM project_phase pp12 WHERE pp12.project_id=p.project_id AND pp12.phase_type_id=12) +AND dpp.resource_role_id IN (2,4,5,6,7,8,9) + +UNION ALL + +SELECT + ra.review_auction_id +, ratl.review_auction_type_id +, ra.project_id AS challenge_id +, 0 AS number_of_submissions +, pp14.scheduled_start_time AS review_start +, pp14.scheduled_end_time AS review_end +, 1 - (SELECT count(r.resource_id) FROM resource r WHERE r.project_id=p.project_id AND + r.resource_role_id = 18) AS number_of_review_positions_available +, pcl.name AS challenge_type +, pcl.project_category_id AS challenge_type_Id +, 'Spec Review' AS review_type +, pi6.value AS challenge_name +, pi7.value as version +, rarl.review_application_role_id +, dpp.resource_role_id +, dpp.fixed_amount +, dpp.base_coefficient +, dpp.incremental_coefficient +, (select max(prize_amount) from prize where project_id = p.project_id AND prize_type_id=15 AND place=1) as prize +, CASE WHEN dpp.resource_role_id = 2 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id IN (4,5,6,7) THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 19 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 3 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 20 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 21 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + END AS submission_count +FROM review_auction ra +INNER JOIN review_auction_type_lu ratl ON ratl.review_auction_type_id=ra.review_auction_type_id AND ratl.review_auction_category_id=2 +INNER JOIN project p ON p.project_id=ra.project_id +INNER JOIN project_category_lu pcl ON pcl.project_category_id = p.project_category_id +INNER JOIN project_phase pp13 ON pp13.project_id=p.project_id AND pp13.phase_type_id=13 AND not exists (SELECT 1 FROM phase_dependency WHERE dependent_phase_id=pp13.project_phase_id) +INNER JOIN phase_dependency pd ON pd.dependency_phase_id=pp13.project_phase_id +INNER JOIN project_phase pp14 ON pp14.project_id=p.project_id AND pp14.phase_type_id=14 AND pp14.project_phase_id=pd.dependent_phase_id +INNER JOIN project_info pi6 ON p.project_id = pi6.project_id AND pi6.project_info_type_id = 6 +INNER JOIN project_info pi7 ON p.project_id = pi7.project_id AND pi7.project_info_type_id = 7 +INNER JOIN review_application_role_lu rarl ON rarl.review_auction_type_id = ra.review_auction_type_id +INNER JOIN review_application_role_resource_role_xref rarrrx ON rarrrx.review_application_role_id = rarl.review_application_role_id +INNER JOIN default_project_payment dpp ON dpp.resource_role_id = rarrrx.resource_role_id AND p.project_category_id = dpp.project_category_id +WHERE p.project_status_id = 1 +AND pcl.project_type_id IN (1,2) +AND pcl.project_category_id != 29 +AND pcl.project_category_id != 37 +AND pp13.phase_status_id IN (2,3) +AND pp14.phase_status_id IN (1,2) +AND not exists (SELECT 1 FROM project_phase pp12 WHERE pp12.project_id=p.project_id AND pp12.phase_type_id=12) +AND dpp.resource_role_id = 18 + +UNION ALL + +SELECT + ra.review_auction_id +, ra.review_auction_type_id +, ra.project_id AS challenge_id +, (SELECT COUNT(DISTINCT u.resource_id) + FROM upload u + INNER JOIN submission s ON u.upload_id = s.upload_id + WHERE u.project_id = p.project_id + AND s.submission_type_id = 1 + AND s.submission_status_id IN (1,2,3,4) + AND u.upload_type_id = 1) AS number_of_submissions +, pp18.scheduled_start_time AS review_start +, pp18.scheduled_end_time AS review_end +, pc.parameter::int - (SELECT count(r.resource_id) FROM resource r WHERE r.project_id=p.project_id + AND r.resource_role_id = 21) as number_of_review_positions_available +, pcl.name AS challenge_type +, pcl.project_category_id AS challenge_type_Id +, 'Iterative Review' AS review_type +, pi6.value AS challenge_name +, pi7.value as version +, rarl.review_application_role_id +, dpp.resource_role_id +, dpp.fixed_amount +, dpp.base_coefficient +, dpp.incremental_coefficient +, (select max(prize_amount) from prize where project_id = p.project_id AND prize_type_id=15 AND place=1) as prize +, CASE WHEN dpp.resource_role_id = 2 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id IN (4,5,6,7) THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 19 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 3 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 20 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + WHEN dpp.resource_role_id = 21 THEN (SELECT COUNT(*) FROM submission s, upload up WHERE s.upload_id = up.upload_id + AND s.submission_status_id IN (1,2,3,4,6,7) AND s.submission_type_id = 1 AND up.project_id = p.project_id AND up.upload_type_id = 1) + END AS submission_count +FROM review_auction ra +INNER JOIN review_auction_type_lu ratl ON ratl.review_auction_type_id=ra.review_auction_type_id AND ratl.review_auction_category_id=3 +INNER JOIN project p ON p.project_id=ra.project_id +INNER JOIN project_category_lu pcl ON pcl.project_category_id = p.project_category_id +INNER JOIN project_phase pp2 ON pp2.project_id=p.project_id AND pp2.phase_type_id=2 +INNER JOIN project_phase pp18 ON pp18.project_id=p.project_id AND pp18.phase_type_id=18 +INNER JOIN phase_dependency pd ON pd.dependent_phase_id=pp18.project_phase_id AND pd.dependent_start=1 AND pd.dependency_phase_id=pp2.project_phase_id AND pd.dependency_start=1 +INNER JOIN phase_criteria pc ON pc.project_phase_id=pp18.project_phase_id AND pc.phase_criteria_type_id=6 +INNER JOIN project_info pi6 ON p.project_id = pi6.project_id AND pi6.project_info_type_id = 6 +INNER JOIN project_info pi7 ON p.project_id = pi7.project_id AND pi7.project_info_type_id = 7 +INNER JOIN review_application_role_lu rarl ON rarl.review_auction_type_id = ra.review_auction_type_id +INNER JOIN review_application_role_resource_role_xref rarrrx ON rarrrx.review_application_role_id = rarl.review_application_role_id +INNER JOIN default_project_payment dpp ON dpp.resource_role_id = rarrrx.resource_role_id AND p.project_category_id = dpp.project_category_id +WHERE p.project_status_id = 1 +AND pcl.project_type_id IN (1,2) +AND pcl.project_category_id != 29 +AND pcl.project_category_id != 37 +AND pp2.phase_status_id IN (2,3) +AND pp18.phase_status_id IN (1,2) +AND not exists (SELECT 1 FROM project_phase pp12 WHERE pp12.project_id=p.project_id AND pp12.phase_type_id=12) +AND dpp.resource_role_id = 21 +) +WHERE {filter} and number_of_review_positions_available > 0; \ No newline at end of file diff --git a/service/src/main/resources/sql/review/delete-review.sql b/service/src/main/resources/sql/review/delete-review.sql new file mode 100644 index 0000000..ba8dd1c --- /dev/null +++ b/service/src/main/resources/sql/review/delete-review.sql @@ -0,0 +1,5 @@ +delete from review_item_comment + where review_item_id in (select review_item_id from review_item where review_id = :reviewId); +delete from review_item where review_id = :reviewId; +delete from review_comment where review_id = :reviewId; +delete from review where review_id = :reviewId; \ No newline at end of file diff --git a/service/src/main/resources/sql/review/reopen-review.sql b/service/src/main/resources/sql/review/reopen-review.sql new file mode 100644 index 0000000..951f4ca --- /dev/null +++ b/service/src/main/resources/sql/review/reopen-review.sql @@ -0,0 +1 @@ +update review set committed = 0, modify_user = :userId, modify_date = CURRENT where review_id = :reviewId \ No newline at end of file diff --git a/service/src/main/resources/sql/review/reset-aggregation.sql b/service/src/main/resources/sql/review/reset-aggregation.sql new file mode 100644 index 0000000..a34b745 --- /dev/null +++ b/service/src/main/resources/sql/review/reset-aggregation.sql @@ -0,0 +1,58 @@ +update project_info + set value = 'Off', + modify_user = :userId, + modify_date = CURRENT + where project_id = :challengeId and project_info_type_id = 9; + +update project_phase + set phase_status_id = 2 , + actual_end_time = null, + modify_user = :userId, + modify_date = CURRENT + where project_id = :challengeId and phase_type_id = 6; + +update project_phase + set phase_status_id = 1, + actual_start_time = null, + actual_end_time = null, + modify_user = :userId, + modify_date = CURRENT + where project_id = :challengeId and phase_type_id in (7,8,9,10,11); + +delete from review_item_comment where review_item_id in + (select review_item_id from review_item where review_id in + (select review_id from review where resource_id in + (select resource_id from resource where project_id = :challengeId and resource_role_id in (8, 9)))); + +delete from review_item where review_id in + (select review_id from review where resource_id in + (select resource_id from resource where project_id = :challengeId and resource_role_id in (8, 9))); + +delete from review_comment where review_id in + (select review_id from review where resource_id in + (select resource_id from resource where project_id = :challengeId and resource_role_id in (8, 9))); + +delete from review where resource_id in + (select resource_id from resource where project_id = :challengeId and resource_role_id in (8, 9)); + +delete from project_info where project_id = :challengeId and project_info_type_id in (23, 24); + +update submission + set submission_status_id = 1, + final_score = null, + placement = null, + prize_id=null, + modify_user = :userId, + modify_date = CURRENT + where upload_id in (select upload_id from upload where project_id = :challengeId) + and submission_status_id in (1, 3, 4) + and submission_type_id = 1; + +delete from upload where project_id = :challengeId and upload_type_id = 3; + +update project_result + set final_score = null, + payment = null, + placed = null, + passed_review_ind = null + where project_id = :challengeId; \ No newline at end of file diff --git a/service/src/main/resources/sql/review/reset-review.sql b/service/src/main/resources/sql/review/reset-review.sql new file mode 100644 index 0000000..306e029 --- /dev/null +++ b/service/src/main/resources/sql/review/reset-review.sql @@ -0,0 +1,36 @@ +update project_phase + set actual_start_time = null, + actual_end_time = null, + phase_status_id = 1, + modify_user = :userId, + modify_date = CURRENT + where project_id = :challengeId + and phase_type_id in (5, 6); + +update project_phase + set actual_end_time = null, + phase_status_id = 2, + modify_user = :userId, + modify_date = CURRENT + where project_id = :challengeId + and phase_type_id = 4; + +update review + set committed = 0, + modify_user = :userId, + modify_date = CURRENT + where resource_id in + (select resource_id from resource where project_id = :challengeId and resource_role_id in (4, 5, 6, 7)); + +delete from review_item_comment + where comment_type_id in (4, 5) + and review_item_id in + (select review_item_id from review_item where review_id in + (select review_id from review where resource_id in + (select resource_id from resource + where project_id = :challengeId + and resource_role_id in (4, 5, 6, 7)))); + +delete from resource_info + where resource_info_type_id=13 + and resource_id in (select resource_id from resource where project_id = :challengeId); \ No newline at end of file diff --git a/service/src/main/resources/sql/review/update-autopilot.sql b/service/src/main/resources/sql/review/update-autopilot.sql new file mode 100644 index 0000000..0482358 --- /dev/null +++ b/service/src/main/resources/sql/review/update-autopilot.sql @@ -0,0 +1,5 @@ +UPDATE project_info +SET value = :value, + modify_date = CURRENT, + modify_user = :userId +WHERE project_info_type_id = 9 and project_id = :challengeId \ No newline at end of file diff --git a/service/src/test/java/com/appirio/service/test/resources/ReviewResourceTest.java b/service/src/test/java/com/appirio/service/test/resources/ReviewResourceTest.java index 6145703..c891080 100644 --- a/service/src/test/java/com/appirio/service/test/resources/ReviewResourceTest.java +++ b/service/src/test/java/com/appirio/service/test/resources/ReviewResourceTest.java @@ -2,6 +2,7 @@ import com.appirio.service.review.api.Review; import com.appirio.service.review.manager.ReviewManager; +import com.appirio.service.review.manager.ChallengeManager; import com.appirio.service.review.resources.ReviewResource; import com.appirio.service.test.BaseTest; import static org.mockito.Mockito.*; @@ -13,7 +14,8 @@ public class ReviewResourceTest extends BaseTest{ public static final ReviewManager mockReviewManager = mock(ReviewManager.class); - public static final ReviewResource unit = new ReviewResource(mockReviewManager); + public static final ChallengeManager mockChallengeManager = mock(ChallengeManager.class); + public static final ReviewResource unit = new ReviewResource(mockReviewManager, mockChallengeManager); @Test public void testGetReviews() throws Exception{ diff --git a/swagger.yaml b/swagger.yaml index 79602da..8352126 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -1,977 +1,1289 @@ ---- -swagger: "2.0" -info: - version: "v3" - title: "Review Service API" - description: | - Services to enable members to review assets. - It includes functionality for scorecards, scorecard questions, reviews, - review items, scoring individual reviews, aggregating review scores. - -# during production,should point to your server machine -host: api.topcoder.com -basePath: "/v3" -# during production, should use https -schemes: -- "http" -produces: -- application/json -consumes: -- application/json - -securityDefinitions: - Bearer: - type: apiKey - name: Authorization - in: header - -paths: - /reviews/{challengeId}/aggregateScores/: - put: - tags: - - review - operationId: aggregateScores - description: | - Performs score aggragation for a given challenge. - During the review process, reviewers will create multiple reviews. Each review is - individually scored at the time that it is saved. At the end of the review phase - these individual reviews need to be processed and aggregated to generate the final - score for a given submission in a given challenge, updating the scores in the - submission and generating the project result records. - security: - - Bearer: [] - parameters: - - $ref: "#/parameters/challengeIdParam" - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '404': - description: The challenge is not found - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/GenericSuccessResponse" - - /feedbacks/copilots/{handle}: - get: - tags: - - copilot - operationId: getCopilotFeedbacks - description: Get copilot feedbacks - security: - - Bearer: [] - parameters: - - $ref: "#/parameters/handleParam" - - name: offset - description: pagination offset (ex. offset=101) - in: query - required: false - type: integer - format: int32 - - name: limit - description: number of feedback records to be retrieved. If this is greater than 50, only 50 records are returned (ex. limit=50) - in: query - required: false - type: integer - format: int32 - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '404': - description: The copilot is not found - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/CopilotFeedbackListResponse" - - /reviews/{challengeId}/assignNextReview: - put: - tags: - - review - operationId: assignNextReview - description: | - For peer review challenges, assign next review will look for the next submission that the currently - logged in user has to review, and perform the association. - The review phase of the challenge must be open; - The member must have submitted to the challenge; - There must still be submissions available for the member to review. I.e. submissions of other members that the member has not yet reviewed; - If the member has 5 or more reviews, they can only start a new review if all other reviews have been submitted (committed = 1) - The response content is the new review ID. The /reviews endpoint can be used to fetch it. - security: - - Bearer: [] - parameters: - - $ref: "#/parameters/challengeIdParam" - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '404': - description: The challenge is not found - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/AssignNextReviewResponse" - - - /reviews/: - get: - tags: - - review - operationId: getReviews - description: | - Get reviews. Users may only see reviews that they created, or submitted reviews (commited = 1) - of their own submissions done by other users. - security: - - Bearer: [] - parameters: - - name: offset - description: How many records to skip - in: query - required: false - type: integer - format: int32 - - name: limit - description: How many records to return - in: query - required: false - type: integer - format: int32 - - name: filter - required: true - type: string - in: query - description: | - key=value pairs - - challengeId id of the challenge - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/ReviewListResponse" - - /reviews/{reviewId}: - get: - tags: - - review - operationId: getReview - description: | - Get specified review. Users may only see review that they created, or submitted reviews (commited = 1) - of their own submissions done by other users. - security: - - Bearer: [] - parameters: - - $ref: "#/parameters/reviewIdParam" - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '404': - description: The review is not found - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/ReviewResponse" - put: - tags: - - review - operationId: updateReview - description: | - Update specified review. Users may only update reviews that they created and have not been submitted yet (commited = 0). - The only field that can be updated is the committed field. - parameters: - - $ref: "#/parameters/reviewIdParam" - - in: body - name: body - required: true - schema: - $ref: '#/definitions/Review' - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '404': - description: The review is not found - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/GenericSuccessResponse" - - /reviewItems/: - get: - tags: - - review - operationId: getReviewItems - description: | - Fetches review items. Users may only see reviews items related to reviews that they created, - or submitted reviews (commited = 1) of their own submissions done by other users. - security: - - Bearer: [] - parameters: - - name: offset - required: false - type: string - in: query - description: Offset to return results from (DEFAULT 0) - - name: limit - required: false - type: string - in: query - description: Maximum number of results to be returned. Only a maximun of 50 records can be retrieved at a time (DEFAULT 20) - - name: filter - required: false - type: string - in: query - description: | - the filter in key=value format - - reviewId (enum[string], optional) Id of the review for which to retrieve review items () - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/ReviewItemListResponse" - - post: - tags: - - review - operationId: createReviewItems - description: | - Creates new review items - Users may only create review items related to reviews that they created and are not submitted (committed = 0). - security: - - Bearer: [] - parameters: - - in: body - name: body - required: true - schema: - type: array - items: - $ref: '#/definitions/ReviewItem' - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/CreateReviewItemsResponse" - - put: - tags: - - review - operationId: updateReviewItems - description: | - Updates new review items - Users may only update review items related to reviews that they created and are not submitted (committed = 0). - security: - - Bearer: [] - parameters: - - in: body - name: body - required: true - schema: - type: array - items: - $ref: '#/definitions/ReviewItem' - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/GenericSuccessResponse" - - /scorecards: - get: - tags: - - review - operationId: getScorecards - description: | - Fetches scorecards - security: - - Bearer: [] - parameters: - - name: offset - required: false - type: string - in: query - description: Offset to return results from (DEFAULT 0) - - name: limit - required: false - type: string - in: query - description: Maximum number of results to be returned. Only a maximun of 50 records can be retrieved at a time (DEFAULT 20) - - name: filter - required: false - type: string - in: query - description: | - the filter in key=value format - - challengeId (number, optional) Id of the challenge - - reviewId (number, optional) id of the reivew - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/ScorecardListResponse" - - /scorecardQuestions: - get: - tags: - - review - operationId: getScorecardQuestions - description: | - Fetches scorecard questions - security: - - Bearer: [] - parameters: - - name: offset - required: false - type: string - in: query - description: Offset to return results from (DEFAULT 0) - - name: limit - required: false - type: string - in: query - description: Maximum number of results to be returned. Only a maximun of 50 records can be retrieved at a time (DEFAULT 20) - - name: filter - required: false - type: string - in: query - description: | - the filter in key=value format - - scorecardId (number, optional) Id of the scorecard - responses: - '500': - description: Internal Server Error - schema: - $ref: "#/definitions/ErrorModel" - '403': - description: No permission or wrong token - schema: - $ref: "#/definitions/ErrorModel" - '200': - description: Success - schema: - $ref: "#/definitions/ScorecardQuestionListResponse" - - -parameters: - challengeIdParam: - name: challengeId - in: path - description: challenge id - required: true - type: integer - format: int64 - reviewIdParam: - name: reviewId - in: path - description: review id - required: true - type: integer - format: int64 - handleParam: - name: handle - in: path - description: user handle - required: true - type: string - - -definitions: - ResponseMetadata: - title: Metadata object for a response - type: object - properties: - totalCount: - type: integer - format: int64 - description: Total count of the objects - - ErrorModel: - type: object - properties: - id: - type: string - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - description: http status code - type: integer - format: int32 - debug: - type: object - content: - type: object - - GenericSuccessResponse: - title: generic success response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - content: - type: string - description: success message - - CreateReviewItemsResponse: - title: success response after creating review items - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - content: - type: array - items: - type: integer - format: int64 - - - AssignNextReviewResponse: - title: assign next review response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - content: - type: string - description: next review id - - ScorecardQuestionListResponse: - title: scorecard question list response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - metadata: - $ref: "#/definitions/ResponseMetadata" - content: - type: array - items: - $ref: "#/definitions/ScorecardQuestion" - - ScorecardQuestion: - type: object - properties: - id: - type: integer - format: int64 - description: the scorecard question id - description: - type: string - description: the scorecard question description - guideline: - type: string - description: the scorecard question guideline - questionTypeId: - type: integer - format: int64 - description: the scorecard question type id - createdAt: - type: string - description: Datetime (GMT) when task was created - readOnly: true - createdBy: - type: integer - format: int64 - description: READ-ONLY. User who created this task - readOnly: true - updatedAt: - type: string - description: READ-ONLY. Datetime (GMT) when task was updated - readOnly: true - updatedBy: - type: integer - format: int64 - description: READ-ONLY. User that last updated this task - readOnly: true - - ScorecardListResponse: - title: scorecard list response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - metadata: - $ref: "#/definitions/ResponseMetadata" - content: - type: array - items: - $ref: "#/definitions/Scorecard" - - Scorecard: - type: object - properties: - id: - type: integer - format: int64 - description: the scorecard id - name: - type: string - description: the scorecard name - createdAt: - type: string - description: Datetime (GMT) when task was created - readOnly: true - createdBy: - type: integer - format: int64 - description: READ-ONLY. User who created this task - readOnly: true - updatedAt: - type: string - description: READ-ONLY. Datetime (GMT) when task was updated - readOnly: true - updatedBy: - type: integer - format: int64 - description: READ-ONLY. User that last updated this task - readOnly: true - - - ReviewItemListResponse: - title: review item list response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - metadata: - $ref: "#/definitions/ResponseMetadata" - content: - type: array - items: - $ref: "#/definitions/ReviewItem" - - ReviewItem: - type: object - properties: - id: - type: integer - format: int64 - description: the review item id - reviewId: - type: integer - format: int64 - description: the review id - scorecardQuestionId: - type: integer - format: int64 - description: the scorecard question id - uploadId: - type: integer - format: int64 - description: the upload id - answer: - type: string - description: review answer - sort: - type: integer - format: int32 - description: the sort index - reviewerUserId: - type: integer - format: int64 - description: the reviewer user id - submitterUserId: - type: integer - format: int64 - description: the submitter user id - committed: - type: boolean - description: committed or not - comments: - type: array - items: - $ref: "#/definitions/ReviewItemComment" - createdAt: - type: string - description: Datetime (GMT) when task was created - readOnly: true - createdBy: - type: integer - format: int64 - description: READ-ONLY. User who created this task - readOnly: true - updatedAt: - type: string - description: READ-ONLY. Datetime (GMT) when task was updated - readOnly: true - updatedBy: - type: integer - format: int64 - description: READ-ONLY. User that last updated this task - readOnly: true - - ReviewItemComment: - type: object - properties: - id: - type: integer - format: int64 - description: the review id - resourceId: - type: integer - format: int64 - description: the resource id - reviewItemId: - type: integer - format: int64 - description: the review item id - commentTypeId: - type: integer - format: int64 - description: the comment type id - content: - type: string - description: the content - - - ReviewListResponse: - title: review list response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - metadata: - $ref: "#/definitions/ResponseMetadata" - content: - type: array - items: - $ref: "#/definitions/Review" - - ReviewResponse: - title: review response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - metadata: - $ref: "#/definitions/ResponseMetadata" - content: - $ref: "#/definitions/Review" - - Review: - type: object - properties: - id: - type: integer - format: int64 - description: the review id - resourceId: - type: integer - format: int64 - description: the resource id - submissionId: - type: integer - format: int64 - description: the submission id - projectPhaseId: - type: integer - format: int64 - description: the project phase id - scorecardId: - type: integer - format: int64 - description: the scorecard id - uploadId: - type: integer - format: int64 - description: the upload id - committed: - type: boolean - description: comitted or not - score: - type: number - format: float - description: the final score - initialScore: - type: number - format: float - description: the initial score - reviewerUserId: - type: integer - format: int64 - description: the reviewer user id - submitterUserId: - type: integer - format: int64 - description: the submitter user id - createdAt: - type: string - description: Datetime (GMT) when task was created - readOnly: true - createdBy: - type: integer - format: int64 - description: READ-ONLY. User who created this task - readOnly: true - updatedAt: - type: string - description: READ-ONLY. Datetime (GMT) when task was updated - readOnly: true - updatedBy: - type: integer - format: int64 - description: READ-ONLY. User that last updated this task - readOnly: true - - CopilotFeedbackListResponse: - title: copilot feedback list response - type: object - properties: - id: - type: string - readOnly: true - description: unique id identifying the request - version: - type: string - result: - type: object - properties: - success: - type: boolean - status: - type: string - description: http status code - metadata: - $ref: "#/definitions/ResponseMetadata" - content: - type: array - items: - $ref: "#/definitions/CopilotFeedback" - - CopilotFeedback: - type: object - properties: - userId: - type: integer - format: int64 - description: the id of user who writes the feedback - reviewText: - type: string - description: feedback text - timeRating: - type: integer - format: int64 - description: time rating - qualityRating: - type: integer - format: int64 - description: quality rating - communicationRating: - type: integer - format: int64 - description: communication rating - managementRating: - type: integer - format: int64 - description: management rating - copilotProjectId: - type: integer - format: int64 - description: copilot project id - directProjectId: - type: integer - format: int64 - description: direct project id - directProjectName: - type: string - description: direct project name - createdAt: - type: string - description: Datetime (GMT) when task was created - readOnly: true - createdBy: - type: integer - format: int64 - description: READ-ONLY. User who created this task - readOnly: true - updatedAt: - type: string - description: READ-ONLY. Datetime (GMT) when task was updated - readOnly: true - updatedBy: - type: integer - format: int64 - description: READ-ONLY. User that last updated this task - readOnly: true - - - - \ No newline at end of file +swagger: '2.0' +info: + version: v3 + title: Review Service API + description: | + Services to enable members to review assets. + It includes functionality for scorecards, scorecard questions, reviews, + review items, scoring individual reviews, aggregating review scores, review opportunities. +host: api.topcoder.com +basePath: /v3 +schemes: + - http +produces: + - application/json +consumes: + - application/json +securityDefinitions: + Bearer: + type: apiKey + name: Authorization + in: header +paths: + '/reviews/{challengeId}/aggregateScores/': + put: + tags: + - review + operationId: aggregateScores + description: > + Performs score aggragation for a given challenge. + + During the review process, reviewers will create multiple reviews. Each + review is + + individually scored at the time that it is saved. At the end of the + review phase + + these individual reviews need to be processed and aggregated to generate + the final + + score for a given submission in a given challenge, updating the scores + in the + + submission and generating the project result records. + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/challengeIdParam' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/GenericSuccessResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The challenge is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + '/feedbacks/copilots/{handle}': + get: + tags: + - copilot + operationId: getCopilotFeedbacks + description: Get copilot feedbacks + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/handleParam' + - name: offset + description: pagination offset (ex. offset=101) + in: query + required: false + type: integer + format: int32 + - name: limit + description: >- + number of feedback records to be retrieved. If this is greater than + 50, only 50 records are returned (ex. limit=50) + in: query + required: false + type: integer + format: int32 + responses: + '200': + description: Success + schema: + $ref: '#/definitions/CopilotFeedbackListResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The copilot is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + '/reviews/{challengeId}/assignNextReview': + put: + tags: + - review + operationId: assignNextReview + description: > + For peer review challenges, assign next review will look for the next + submission that the currently + + logged in user has to review, and perform the association. + + The review phase of the challenge must be open; + + The member must have submitted to the challenge; + + There must still be submissions available for the member to review. I.e. + submissions of other members that the member has not yet reviewed; + + If the member has 5 or more reviews, they can only start a new review if + all other reviews have been submitted (committed = 1) + + The response content is the new review ID. The /reviews endpoint can be + used to fetch it. + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/challengeIdParam' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/AssignNextReviewResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The challenge is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + /reviews/: + get: + tags: + - review + operationId: getReviews + description: > + Get reviews. Users may only see reviews that they created, or submitted + reviews (commited = 1) + + of their own submissions done by other users. + security: + - Bearer: [] + parameters: + - name: offset + description: How many records to skip + in: query + required: false + type: integer + format: int32 + - name: limit + description: How many records to return + in: query + required: false + type: integer + format: int32 + - name: filter + required: true + type: string + in: query + description: | + key=value pairs + - challengeId id of the challenge + responses: + '200': + description: Success + schema: + $ref: '#/definitions/ReviewListResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + '/reviews/{reviewId}': + get: + tags: + - review + operationId: getReview + description: > + Get specified review. Users may only see review that they created, or + submitted reviews (commited = 1) + + of their own submissions done by other users. + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/reviewIdParam' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/ReviewResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The review is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + put: + tags: + - review + operationId: updateReview + description: > + Update specified review. Users may only update reviews that they created + and have not been submitted yet (commited = 0). + + The only field that can be updated is the committed field. + parameters: + - $ref: '#/parameters/reviewIdParam' + - in: body + name: body + required: true + schema: + $ref: '#/definitions/Review' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/GenericSuccessResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The review is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + delete: + tags: + - review + operationId: deleteReview + description: | + Deletes the review identified by the given review id. + parameters: + - $ref: '#/parameters/reviewIdParam' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/SuccessResponseWithId' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/ErrorModel' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The review is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + /reviewItems/: + get: + tags: + - review + operationId: getReviewItems + description: > + Fetches review items. Users may only see reviews items related to + reviews that they created, + + or submitted reviews (commited = 1) of their own submissions done by + other users. + security: + - Bearer: [] + parameters: + - name: offset + required: false + type: string + in: query + description: Offset to return results from (DEFAULT 0) + - name: limit + required: false + type: string + in: query + description: >- + Maximum number of results to be returned. Only a maximun of 50 + records can be retrieved at a time (DEFAULT 20) + - name: filter + required: false + type: string + in: query + description: > + the filter in key=value format + + - reviewId (enum[string], optional) Id of the review for which to + retrieve review items () + responses: + '200': + description: Success + schema: + $ref: '#/definitions/ReviewItemListResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + post: + tags: + - review + operationId: createReviewItems + description: > + Creates new review items + + Users may only create review items related to reviews that they created + and are not submitted (committed = 0). + security: + - Bearer: [] + parameters: + - in: body + name: body + required: true + schema: + type: array + items: + $ref: '#/definitions/ReviewItem' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/CreateReviewItemsResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + put: + tags: + - review + operationId: updateReviewItems + description: > + Updates new review items + + Users may only update review items related to reviews that they created + and are not submitted (committed = 0). + security: + - Bearer: [] + parameters: + - in: body + name: body + required: true + schema: + type: array + items: + $ref: '#/definitions/ReviewItem' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/GenericSuccessResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + '/reviews/{challengeId}/resetAggregation': + post: + tags: + - review + operationId: resetAggregation + description: | + Resets the Aggregation of the challenge identified by the given id. + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/challengeIdParam' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/SuccessResponseWithId' + '400': + description: Bad Request + schema: + $ref: '#/definitions/ErrorModel' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/ErrorModel' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The challenge is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + '/reviews/{challengeId}/resetReview': + post: + tags: + - review + operationId: resetReview + description: | + Resets the Review of the challenge identified by the given id. + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/challengeIdParam' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/SuccessResponseWithId' + '400': + description: Bad Request + schema: + $ref: '#/definitions/ErrorModel' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/ErrorModel' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The challenge is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + '/reviews/{reviewId}/reopen': + post: + tags: + - review + operationId: reopenReview + description: | + Reopens the Review identified by the given review Id. + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/reviewIdParam' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/SuccessResponseWithId' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/ErrorModel' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The challenge is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + '/reviews/{challengeId}/updateAutopilot': + post: + tags: + - autopilot + operationId: updateAutoPilot + description: > + Updates (enable/disable) the autopilot settings for the challenge + identified by the given id. + security: + - Bearer: [] + parameters: + - $ref: '#/parameters/challengeIdParam' + - in: body + name: body + required: true + schema: + $ref: '#/definitions/UpdateAutoPilotBody' + responses: + '200': + description: Success + schema: + $ref: '#/definitions/SuccessResponseWithId' + '400': + description: Bad Request + schema: + $ref: '#/definitions/ErrorModel' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/ErrorModel' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The challenge is not found + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + /scorecards: + get: + tags: + - review + operationId: getScorecards + description: | + Fetches scorecards + security: + - Bearer: [] + parameters: + - name: offset + required: false + type: string + in: query + description: Offset to return results from (DEFAULT 0) + - name: limit + required: false + type: string + in: query + description: >- + Maximum number of results to be returned. Only a maximun of 50 + records can be retrieved at a time (DEFAULT 20) + - name: filter + required: false + type: string + in: query + description: | + the filter in key=value format + - challengeId (number, optional) Id of the challenge + - reviewId (number, optional) id of the reivew + responses: + '200': + description: Success + schema: + $ref: '#/definitions/ScorecardListResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + /scorecardQuestions: + get: + tags: + - review + operationId: getScorecardQuestions + description: | + Fetches scorecard questions + security: + - Bearer: [] + parameters: + - name: offset + required: false + type: string + in: query + description: Offset to return results from (DEFAULT 0) + - name: limit + required: false + type: string + in: query + description: >- + Maximum number of results to be returned. Only a maximun of 50 + records can be retrieved at a time (DEFAULT 20) + - name: filter + required: false + type: string + in: query + description: | + the filter in key=value format + - scorecardId (number, optional) Id of the scorecard + responses: + '200': + description: Success + schema: + $ref: '#/definitions/ScorecardQuestionListResponse' + '403': + description: No permission or wrong token + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' + /reviewOpportunities: + get: + tags: + - Review Opportunities + operationId: getReviewOpportunities + description: | + Fetches review opportunities + parameters: + - name: challengeTypeId + required: false + type: integer + in: query + description: The challenge type ids for "Code", "First2Finish" etc. + enum: + - 1 Component Design + - 2 Component Development + - 5 Component Testing + - 6 Application Specification + - 7 Application Architecture + - 9 Bug Hunt + - 26 Test Scenarios + - 13 Test Suites + - 14 Application Assembly + - 23 Application Conceptualization + - 19 UI Prototype + - 24 RIA Build + - 25 RIA Component + - 29 Copilot Posting + - 35 Content Creation + - 36 Reporting + - 38 First2Finish + - 39 Code + responses: + '200': + description: Success + schema: + type: array + description: A list of Review Opportunities + items: + $ref: '#/definitions/ReviewOpportunityItem' + '400': + description: challenge type id is not positive. + schema: + $ref: '#/definitions/ErrorModel' + '404': + description: The challenge type id not supported, or challenge type id exceeds the max integer value. + schema: + $ref: '#/definitions/ErrorModel' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorModel' +parameters: + challengeIdParam: + name: challengeId + in: path + description: challenge id + required: true + type: integer + format: int64 + reviewIdParam: + name: reviewId + in: path + description: review id + required: true + type: integer + format: int64 + handleParam: + name: handle + in: path + description: user handle + required: true + type: string +definitions: + UpdateAutoPilotBody: + type: object + properties: + param: + $ref: '#/definitions/EnableAutopilotPayload' + EnableAutopilotPayload: + title: enable autopilot payload + type: object + properties: + enable: + type: boolean + description: Enable or disable the autopilot setting + ResponseMetadata: + title: Metadata object for a response + type: object + properties: + totalCount: + type: integer + format: int64 + description: Total count of the objects + ErrorModel: + type: object + properties: + id: + type: string + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + description: http status code + type: integer + format: int32 + debug: + type: object + content: + type: object + GenericSuccessResponse: + title: generic success response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + content: + type: string + description: success message + CreateReviewItemsResponse: + title: success response after creating review items + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + content: + type: array + items: + type: integer + format: int64 + SuccessResponseWithId: + title: success response which returns the id as content + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + content: + type: integer + format: int64 + AssignNextReviewResponse: + title: assign next review response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + content: + type: string + description: next review id + ScorecardQuestionListResponse: + title: scorecard question list response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + metadata: + $ref: '#/definitions/ResponseMetadata' + content: + type: array + items: + $ref: '#/definitions/ScorecardQuestion' + ReviewOpportunityItem: + title: Review Opportunity + type: object + properties: + id: + type: integer + format: int64 + description: The review opportunity id + startDate: + type: string + description: The start date of the review, in the format like "Sat Nov 18 2017 14:55:12 GMT+0200 (EET)" + submissions: + type: integer + description: Number of submissions + payments: + type: array + description: A list of ReviewPayment + items: + $ref: '#/definitions/ReviewPayment' + openPositions: + type: integer + description: Number of the open positions of reviewers for this review opportunity. + type: + type: string + description: The type of this review opportunity, will be one of "Contest Review", "Spec Review" and "Iterative Review" + challenge: + type: object + description: The challenge information of this review opportunity + properties: + id: + type: integer + format: int64 + description: The challenge ID + title: + type: string + description: The name of the challenge + version: + type: string + description: The version of the challenge + ScorecardQuestion: + type: object + properties: + id: + type: integer + format: int64 + description: the scorecard question id + description: + type: string + description: the scorecard question description + guideline: + type: string + description: the scorecard question guideline + questionTypeId: + type: integer + format: int64 + description: the scorecard question type id + createdAt: + type: string + description: Datetime (GMT) when task was created + readOnly: true + createdBy: + type: integer + format: int64 + description: READ-ONLY. User who created this task + readOnly: true + updatedAt: + type: string + description: READ-ONLY. Datetime (GMT) when task was updated + readOnly: true + updatedBy: + type: integer + format: int64 + description: READ-ONLY. User that last updated this task + readOnly: true + ScorecardListResponse: + title: scorecard list response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + metadata: + $ref: '#/definitions/ResponseMetadata' + content: + type: array + items: + $ref: '#/definitions/Scorecard' + Scorecard: + type: object + properties: + id: + type: integer + format: int64 + description: the scorecard id + name: + type: string + description: the scorecard name + createdAt: + type: string + description: Datetime (GMT) when task was created + readOnly: true + createdBy: + type: integer + format: int64 + description: READ-ONLY. User who created this task + readOnly: true + updatedAt: + type: string + description: READ-ONLY. Datetime (GMT) when task was updated + readOnly: true + updatedBy: + type: integer + format: int64 + description: READ-ONLY. User that last updated this task + readOnly: true + ReviewItemListResponse: + title: review item list response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + metadata: + $ref: '#/definitions/ResponseMetadata' + content: + type: array + items: + $ref: '#/definitions/ReviewItem' + ReviewItem: + type: object + properties: + id: + type: integer + format: int64 + description: the review item id + reviewId: + type: integer + format: int64 + description: the review id + scorecardQuestionId: + type: integer + format: int64 + description: the scorecard question id + uploadId: + type: integer + format: int64 + description: the upload id + answer: + type: string + description: review answer + sort: + type: integer + format: int32 + description: the sort index + reviewerUserId: + type: integer + format: int64 + description: the reviewer user id + submitterUserId: + type: integer + format: int64 + description: the submitter user id + committed: + type: boolean + description: committed or not + comments: + type: array + items: + $ref: '#/definitions/ReviewItemComment' + createdAt: + type: string + description: Datetime (GMT) when task was created + readOnly: true + createdBy: + type: integer + format: int64 + description: READ-ONLY. User who created this task + readOnly: true + updatedAt: + type: string + description: READ-ONLY. Datetime (GMT) when task was updated + readOnly: true + updatedBy: + type: integer + format: int64 + description: READ-ONLY. User that last updated this task + readOnly: true + ReviewItemComment: + type: object + properties: + id: + type: integer + format: int64 + description: the review id + resourceId: + type: integer + format: int64 + description: the resource id + reviewItemId: + type: integer + format: int64 + description: the review item id + commentTypeId: + type: integer + format: int64 + description: the comment type id + content: + type: string + description: the content + ReviewListResponse: + title: review list response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + metadata: + $ref: '#/definitions/ResponseMetadata' + content: + type: array + items: + $ref: '#/definitions/Review' + ReviewResponse: + title: review response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + metadata: + $ref: '#/definitions/ResponseMetadata' + content: + $ref: '#/definitions/Review' + Review: + type: object + properties: + id: + type: integer + format: int64 + description: the review id + resourceId: + type: integer + format: int64 + description: the resource id + submissionId: + type: integer + format: int64 + description: the submission id + projectPhaseId: + type: integer + format: int64 + description: the project phase id + scorecardId: + type: integer + format: int64 + description: the scorecard id + uploadId: + type: integer + format: int64 + description: the upload id + committed: + type: boolean + description: comitted or not + score: + type: number + format: float + description: the final score + initialScore: + type: number + format: float + description: the initial score + reviewerUserId: + type: integer + format: int64 + description: the reviewer user id + submitterUserId: + type: integer + format: int64 + description: the submitter user id + createdAt: + type: string + description: Datetime (GMT) when task was created + readOnly: true + createdBy: + type: integer + format: int64 + description: READ-ONLY. User who created this task + readOnly: true + updatedAt: + type: string + description: READ-ONLY. Datetime (GMT) when task was updated + readOnly: true + updatedBy: + type: integer + format: int64 + description: READ-ONLY. User that last updated this task + readOnly: true + CopilotFeedbackListResponse: + title: copilot feedback list response + type: object + properties: + id: + type: string + readOnly: true + description: unique id identifying the request + version: + type: string + result: + type: object + properties: + success: + type: boolean + status: + type: string + description: http status code + metadata: + $ref: '#/definitions/ResponseMetadata' + content: + type: array + items: + $ref: '#/definitions/CopilotFeedback' + CopilotFeedback: + type: object + properties: + userId: + type: integer + format: int64 + description: the id of user who writes the feedback + reviewText: + type: string + description: feedback text + timeRating: + type: integer + format: int64 + description: time rating + qualityRating: + type: integer + format: int64 + description: quality rating + communicationRating: + type: integer + format: int64 + description: communication rating + managementRating: + type: integer + format: int64 + description: management rating + copilotProjectId: + type: integer + format: int64 + description: copilot project id + directProjectId: + type: integer + format: int64 + description: direct project id + directProjectName: + type: string + description: direct project name + createdAt: + type: string + description: Datetime (GMT) when task was created + readOnly: true + createdBy: + type: integer + format: int64 + description: READ-ONLY. User who created this task + readOnly: true + updatedAt: + type: string + description: READ-ONLY. Datetime (GMT) when task was updated + readOnly: true + updatedBy: + type: integer + format: int64 + description: READ-ONLY. User that last updated this task + readOnly: true + ReviewPayment: + type: object + properties: + role: + type: string + description: The name of review role + roleId: + type: integer + format: int64 + description: The id of review role + payment: + type: number + format: double + description: The payment of review role