/* * Copyright 2014 Ranjan Kumar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.restfiddle.controller.rest; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Resource; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URIBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import com.google.api.client.auth.oauth2.BrowserClientRequestUrl; import com.restfiddle.constant.NodeType; import com.restfiddle.dao.ActivityLogRepository; import com.restfiddle.dao.ConversationRepository; import com.restfiddle.dao.NodeRepository; import com.restfiddle.dao.RfRequestRepository; import com.restfiddle.dao.RfResponseRepository; import com.restfiddle.dao.util.ConversationConverter; import com.restfiddle.dto.BodyAssertDTO; import com.restfiddle.dto.ConversationDTO; import com.restfiddle.dto.NodeStatusResponseDTO; import com.restfiddle.dto.OAuth2RequestDTO; import com.restfiddle.dto.RfRequestDTO; import com.restfiddle.dto.RfResponseDTO; import com.restfiddle.dto.RunnerLogDTO; import com.restfiddle.entity.ActivityLog; import com.restfiddle.entity.BaseEntity; import com.restfiddle.entity.BaseNode; import com.restfiddle.entity.Conversation; import com.restfiddle.entity.Environment; import com.restfiddle.entity.RfRequest; import com.restfiddle.entity.RunnerLog; import com.restfiddle.entity.User; import com.restfiddle.exceptions.ApiException; import com.restfiddle.handler.AssertHandler; import com.restfiddle.handler.http.GenericHandler; import com.restfiddle.util.EntityToDTO; @RestController @EnableAutoConfiguration @ComponentScan @Transactional public class ApiController { private static final String HTTP_LOCALHOST_8080_OAUTH_RESPONSE = "http://localhost:8080/oauth/response"; private static final String RESTFIDDLE = "restfiddle"; Logger logger = LoggerFactory.getLogger(ApiController.class); @Autowired GenericHandler genericHandler; @Autowired private NodeRepository nodeRepository; @Autowired private ConversationRepository conversationRepository; @Autowired private RfRequestRepository rfRequestRepository; @Autowired private RfResponseRepository rfResponseRepository; @Autowired private AssertHandler assertHandler; @Autowired private RunnerLogController runnerLogController; @Autowired private EnvironmentController environmentController; @Resource private ActivityLogRepository activityLogRepository; @RequestMapping(value = "/api/processor", method = RequestMethod.POST, headers = "Accept=application/json") ConversationDTO requestProcessor(@RequestParam(value = "runnerLogId", required = false) String runnerLogId, @RequestBody RfRequestDTO rfRequestDTO) { Conversation existingConversation = null; Conversation currentConversation; // TODO : Get RfRequest Id if present as part of this request and update the existing conversation entity. // Note : New conversation entity which is getting created below is still required for logging purpose. if (rfRequestDTO == null) { return null; } else if (rfRequestDTO.getId() != null && !rfRequestDTO.getId().isEmpty()) { RfRequest rfRequest = rfRequestRepository.findOne(rfRequestDTO.getId()); String conversationId = rfRequest != null ? rfRequest.getConversationId() : null; existingConversation = conversationId != null ? conversationRepository.findOne(conversationId) : null; // finding updated existing conversation existingConversation = existingConversation != null ? nodeRepository.findOne(existingConversation.getNodeId()).getConversation() : null; if(existingConversation != null) { rfRequestDTO.setAssertionDTO(EntityToDTO.toDTO(existingConversation.getRfRequest().getAssertion())); } } long startTime = System.currentTimeMillis(); RfResponseDTO result = genericHandler.processHttpRequest(rfRequestDTO); long endTime = System.currentTimeMillis(); long duration = endTime - startTime; if (result != null) { String nodeId = null; if (existingConversation != null && existingConversation.getNodeId() != null) { nodeId = existingConversation.getNodeId(); } assertHandler.runAssert(result, nodeId); } currentConversation = ConversationConverter.convertToEntity(rfRequestDTO, result); // This is used to get project-runner/folder-runner logs currentConversation.setRunnerLogId(runnerLogId); if (existingConversation != null) { currentConversation.getRfRequest().setAssertion(existingConversation.getRfRequest().getAssertion()); } rfRequestRepository.save(currentConversation.getRfRequest()); rfResponseRepository.save(currentConversation.getRfResponse()); currentConversation.setDuration(duration); Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof User) { currentConversation.setLastModifiedBy((User) principal); } Date currentDate = new Date(); currentConversation.setCreatedDate(currentDate); currentConversation.setLastModifiedDate(currentDate); currentConversation.setLastRunDate(currentDate); currentConversation.setName(currentConversation.getRfRequest().getApiUrl()); try { currentConversation = conversationRepository.save(currentConversation); currentConversation.getRfRequest().setConversationId(currentConversation.getId()); rfRequestRepository.save(currentConversation.getRfRequest()); ActivityLog activityLog = null; // Note : existingConversation will be null if the request was not saved previously. if (existingConversation != null && existingConversation.getNodeId() != null) { BaseNode node = nodeRepository.findOne(existingConversation.getNodeId()); currentConversation.setNodeId(node.getId()); currentConversation.setName(node.getName()); activityLog = activityLogRepository.findActivityLogByDataId(node.getId()); } if (principal instanceof User) { currentConversation.setLastModifiedBy((User) principal); } conversationRepository.save(currentConversation); if (activityLog == null) { activityLog = new ActivityLog(); if(existingConversation != null) { activityLog.setDataId(existingConversation.getNodeId()); } activityLog.setType("CONVERSATION"); } activityLog.setName(currentConversation.getName()); activityLog.setWorkspaceId(currentConversation.getWorkspaceId()); activityLog.setLastModifiedDate(currentDate); List<BaseEntity> logData = activityLog.getData(); logData.add(0, currentConversation); activityLogRepository.save(activityLog); } catch (InvalidDataAccessResourceUsageException e) { throw new ApiException("Please use sql as datasource, some of features are not supported by hsql", e); } ConversationDTO conversationDTO = new ConversationDTO(); conversationDTO.setWorkspaceId(rfRequestDTO.getWorkspaceId()); conversationDTO.setDuration(duration); conversationDTO.setRfResponseDTO(result); if (result != null) { result.setItemDTO(conversationDTO); } return conversationDTO; } @RequestMapping(value = "/api/processor/projects/{id}", method = RequestMethod.GET) public @ResponseBody List<NodeStatusResponseDTO> runProjectById(@PathVariable("id") String id, @RequestParam(value = "envId", required = false) String envId) { logger.debug("Running all requests inside project : " + id); RunnerLogDTO runnerLogDTO = new RunnerLogDTO(); // TODO : Set project node id // runnerLogDTO.setNodeId(nodeId); RunnerLog log = runnerLogController.create(runnerLogDTO); List<BaseNode> listOfNodes = nodeRepository.findNodesFromAProject(id); List<NodeStatusResponseDTO> nodeStatuses = runNodes(listOfNodes, envId, log.getId()); return nodeStatuses; } // Handle environment passing here as above. Passing null as of now @RequestMapping(value = "/api/processor/folders/{id}", method = RequestMethod.GET) public @ResponseBody List<NodeStatusResponseDTO> runFolderById(@PathVariable("id") String id, @RequestParam(value = "envId", required = false) String envId) { logger.debug("Running all requests inside folder : " + id); RunnerLogDTO runnerLogDTO = new RunnerLogDTO(); // TODO : Set project node id // runnerLogDTO.setNodeId(nodeId); RunnerLog log = runnerLogController.create(runnerLogDTO); List<BaseNode> listOfNodes = nodeRepository.getChildren(id); List<NodeStatusResponseDTO> nodeStatuses = runNodes(listOfNodes, envId, log.getId()); return nodeStatuses; } private List<NodeStatusResponseDTO> runNodes(List<BaseNode> listOfNodes, String envId, String runnerLogId) { // TODO : Regex is hard-coded for now. User will have the option to choose different regular expressions. // TODO : Need to add the option to change regex in settings (UI). List<NodeStatusResponseDTO> nodeStatuses = new ArrayList<NodeStatusResponseDTO>(); NodeStatusResponseDTO nodeStatus; for (BaseNode baseNode : listOfNodes) { String nodeType = baseNode.getNodeType(); if (nodeType != null && (NodeType.PROJECT.name().equals(nodeType) || NodeType.FOLDER.name().equals(nodeType))) { continue; } else { Conversation conversation = baseNode.getConversation(); if (conversation != null && conversation.getRfRequest() != null) { RfRequest rfRequest = conversation.getRfRequest(); String methodType = rfRequest.getMethodType(); String apiUrl = rfRequest.getApiUrl(); String apiBody = rfRequest.getApiBody(); if (methodType != null && !methodType.isEmpty() && apiUrl != null && !apiUrl.isEmpty()) { String evaulatedApiUrl = evaluateApiUrl(envId, apiUrl); RfRequestDTO rfRequestDTO = new RfRequestDTO(); rfRequestDTO.setMethodType(methodType); rfRequestDTO.setApiUrl(evaulatedApiUrl); rfRequestDTO.setApiBody(apiBody); rfRequestDTO.setAssertionDTO(EntityToDTO.toDTO(rfRequest.getAssertion())); RfResponseDTO rfResponseDTO = requestProcessor(runnerLogId, rfRequestDTO).getRfResponseDTO(); nodeStatus = new NodeStatusResponseDTO(); nodeStatus.setId(baseNode.getId()); nodeStatus.setName(baseNode.getName()); nodeStatus.setDescription(baseNode.getDescription()); nodeStatus.setApiUrl(evaulatedApiUrl); nodeStatus.setMethodType(methodType); int successCount = 0; int failureCount = 0; if (rfResponseDTO != null) { logger.debug(baseNode.getName() + " ran with status : " + rfResponseDTO.getStatus()); nodeStatus.setStatusCode(rfResponseDTO.getStatus()); nodeStatus.setDuration(rfResponseDTO.getItemDTO().getDuration()); // TODO: get AssertionDTO from rfResponseDTO. Get bodyAssertsDTOs and loop through the list to count no. of success List<BodyAssertDTO> bodyAssertDTOs = rfResponseDTO.getAssertionDTO().getBodyAssertDTOs(); if (bodyAssertDTOs != null) { for (BodyAssertDTO bodyAssertDTO : bodyAssertDTOs) { if (bodyAssertDTO.isSuccess()) { successCount++; } else { failureCount++; } } } } nodeStatus.setSuccessAsserts(successCount); nodeStatus.setFailureAsserts(failureCount); nodeStatuses.add(nodeStatus); // TODO : Create ProjectRunnerLog and store nodeId as well as loggedConversationId. } } } } return nodeStatuses; } private String evaluateApiUrl(String envId, String apiUrl) { String regex = "\\{\\{([^\\}\\}]*)\\}\\}"; Environment env = null; if (envId != null && !envId.isEmpty()) { env = environmentController.findById(envId); } if (env != null) { Pattern p = Pattern.compile(regex); Matcher m = p.matcher(apiUrl); String tempUrl; while (m.find()) { String exprVar = m.group(1); String propVal = env.getPropertyValueByName(exprVar); tempUrl = apiUrl.replaceFirst(regex, propVal); apiUrl = tempUrl; } } return apiUrl; } @RequestMapping(value = "/api/oauth/form", method = RequestMethod.POST) public ModelAndView oauthFormRedirect(@ModelAttribute OAuth2RequestDTO oAuth2RequestDTO) throws URISyntaxException { List<String> scopes = oAuth2RequestDTO.getScopes(); String authorizationUrl = oAuth2RequestDTO.getAuthorizationUrl(); if (authorizationUrl == null || authorizationUrl.isEmpty()) { return null; } URIBuilder uriBuilder = new URIBuilder(authorizationUrl); List<NameValuePair> queryParams = uriBuilder.getQueryParams(); List<String> responseTypes = new ArrayList<String>(); if (queryParams != null && !queryParams.isEmpty()) { for (NameValuePair nameValuePair : queryParams) { if ("response_type".equals(nameValuePair.getName())) { responseTypes.add(nameValuePair.getValue()); break; } } } BrowserClientRequestUrl browserClientRequestUrl = new BrowserClientRequestUrl(authorizationUrl, oAuth2RequestDTO.getClientId()); if (!responseTypes.isEmpty()) { browserClientRequestUrl = browserClientRequestUrl.setResponseTypes(responseTypes); } String url = browserClientRequestUrl.setState(RESTFIDDLE).setScopes(scopes).setRedirectUri(HTTP_LOCALHOST_8080_OAUTH_RESPONSE).build(); return new ModelAndView("redirect:" + url); } @RequestMapping(value = "/oauth/demo-redirect", method = RequestMethod.GET) @Deprecated public ModelAndView oauthRedirect() { List<String> scopes = new ArrayList<String>(); scopes.add("https://www.googleapis.com/auth/userinfo.profile"); String url = new BrowserClientRequestUrl("https://accounts.google.com/o/oauth2/auth", "82089573969-nocs1ulh96n5kfut1bh5cq7n1enlfoe8.apps.googleusercontent.com").setState(RESTFIDDLE).setScopes(scopes) .setRedirectUri(HTTP_LOCALHOST_8080_OAUTH_RESPONSE).build(); return new ModelAndView("redirect:" + url); } @RequestMapping(value = "/api/oauth/redirect", method = RequestMethod.POST, headers = "Accept=application/json") public @ResponseBody @Deprecated String oauth2Redirect(@RequestBody OAuth2RequestDTO oAuth2RequestDTO) { List<String> scopes = oAuth2RequestDTO.getScopes(); String url = new BrowserClientRequestUrl(oAuth2RequestDTO.getAuthorizationUrl(), oAuth2RequestDTO.getClientId()).setState(RESTFIDDLE) .setScopes(scopes).setRedirectUri(HTTP_LOCALHOST_8080_OAUTH_RESPONSE).build(); return url; } }