github.com/getgauge/gauge@v1.6.9/api/apiMessageHandler.go (about) 1 /*---------------------------------------------------------------- 2 * Copyright (c) ThoughtWorks, Inc. 3 * Licensed under the Apache License, Version 2.0 4 * See LICENSE in the project root for license information. 5 *----------------------------------------------------------------*/ 6 7 package api 8 9 import ( 10 "net" 11 "path/filepath" 12 13 "github.com/getgauge/common" 14 "github.com/getgauge/gauge-proto/go/gauge_messages" 15 "github.com/getgauge/gauge/api/infoGatherer" 16 "github.com/getgauge/gauge/conceptExtractor" 17 "github.com/getgauge/gauge/config" 18 "github.com/getgauge/gauge/conn" 19 "github.com/getgauge/gauge/formatter" 20 "github.com/getgauge/gauge/gauge" 21 "github.com/getgauge/gauge/logger" 22 "github.com/getgauge/gauge/parser" 23 "github.com/getgauge/gauge/plugin" 24 "github.com/getgauge/gauge/refactor" 25 "github.com/getgauge/gauge/runner" 26 "github.com/getgauge/gauge/util" 27 "google.golang.org/protobuf/proto" 28 ) 29 30 type gaugeAPIMessageHandler struct { 31 specInfoGatherer *infoGatherer.SpecInfoGatherer 32 Runner runner.Runner 33 } 34 35 func (handler *gaugeAPIMessageHandler) MessageBytesReceived(bytesRead []byte, connection net.Conn) { 36 apiMessage := &gauge_messages.APIMessage{} 37 var responseMessage *gauge_messages.APIMessage 38 err := proto.Unmarshal(bytesRead, apiMessage) 39 if err != nil { 40 logger.Errorf(false, "Failed to read API proto message: %s\n", err.Error()) 41 responseMessage = handler.getErrorMessage(err) 42 } else { 43 logger.Debugf(false, "Api Request Received: %s", apiMessage) 44 messageType := apiMessage.GetMessageType() 45 switch messageType { 46 case gauge_messages.APIMessage_GetProjectRootRequest: 47 responseMessage = handler.projectRootRequestResponse(apiMessage) 48 case gauge_messages.APIMessage_GetInstallationRootRequest: 49 responseMessage = handler.installationRootRequestResponse(apiMessage) 50 case gauge_messages.APIMessage_GetAllStepsRequest: 51 responseMessage = handler.getAllStepsRequestResponse(apiMessage) 52 case gauge_messages.APIMessage_SpecsRequest: 53 responseMessage = handler.getSpecsRequestResponse(apiMessage) 54 case gauge_messages.APIMessage_GetStepValueRequest: 55 responseMessage = handler.getStepValueRequestResponse(apiMessage) 56 case gauge_messages.APIMessage_GetLanguagePluginLibPathRequest: 57 responseMessage = handler.getLanguagePluginLibPath(apiMessage) 58 case gauge_messages.APIMessage_GetAllConceptsRequest: 59 responseMessage = handler.getAllConceptsRequestResponse(apiMessage) 60 case gauge_messages.APIMessage_PerformRefactoringRequest: 61 responseMessage = handler.performRefactoring(apiMessage) 62 handler.performRefresh(responseMessage.PerformRefactoringResponse.FilesChanged) 63 case gauge_messages.APIMessage_ExtractConceptRequest: 64 responseMessage = handler.extractConcept(apiMessage) 65 case gauge_messages.APIMessage_FormatSpecsRequest: 66 responseMessage = handler.formatSpecs(apiMessage) 67 default: 68 responseMessage = handler.createUnsupportedAPIMessageResponse(apiMessage) 69 } 70 } 71 handler.sendMessage(responseMessage, connection) 72 } 73 74 func (handler *gaugeAPIMessageHandler) sendMessage(message *gauge_messages.APIMessage, connection net.Conn) { 75 logger.Debugf(false, "Sending API response: %s", message) 76 dataBytes, err := proto.Marshal(message) 77 if err != nil { 78 logger.Errorf(false, "Failed to respond to API request. Could not Marshal response %s\n", err.Error()) 79 } 80 if err := conn.Write(connection, dataBytes); err != nil { 81 logger.Errorf(false, "Failed to respond to API request. Could not write response %s\n", err.Error()) 82 } 83 } 84 85 func (handler *gaugeAPIMessageHandler) projectRootRequestResponse(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 86 projectRootResponse := &gauge_messages.GetProjectRootResponse{ProjectRoot: config.ProjectRoot} 87 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_GetProjectRootResponse, MessageId: message.MessageId, ProjectRootResponse: projectRootResponse} 88 } 89 90 func (handler *gaugeAPIMessageHandler) installationRootRequestResponse(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 91 root, err := common.GetInstallationPrefix() 92 if err != nil { 93 logger.Errorf(false, "Failed to find installation root while responding to API request. %s\n", err.Error()) 94 root = "" 95 } 96 installationRootResponse := &gauge_messages.GetInstallationRootResponse{InstallationRoot: root} 97 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_GetInstallationRootResponse, MessageId: message.MessageId, InstallationRootResponse: installationRootResponse} 98 } 99 100 func (handler *gaugeAPIMessageHandler) getAllStepsRequestResponse(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 101 steps := handler.specInfoGatherer.Steps(true) 102 var stepValueResponses []*gauge_messages.ProtoStepValue 103 for _, step := range steps { 104 stepValue := parser.CreateStepValue(step) 105 stepValueResponses = append(stepValueResponses, gauge.ConvertToProtoStepValue(&stepValue)) 106 } 107 getAllStepsResponse := &gauge_messages.GetAllStepsResponse{AllSteps: stepValueResponses} 108 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_GetAllStepResponse, MessageId: message.MessageId, AllStepsResponse: getAllStepsResponse} 109 } 110 111 func (handler *gaugeAPIMessageHandler) getSpecsRequestResponse(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 112 getAllSpecsResponse := handler.createSpecsResponseMessageFor(handler.specInfoGatherer.GetAvailableSpecDetails(message.SpecsRequest.Specs)) 113 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_SpecsResponse, MessageId: message.MessageId, SpecsResponse: getAllSpecsResponse} 114 } 115 116 func (handler *gaugeAPIMessageHandler) getStepValueRequestResponse(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 117 request := message.GetStepValueRequest() 118 stepText := request.GetStepText() 119 hasInlineTable := request.GetHasInlineTable() 120 stepValue, err := parser.ExtractStepValueAndParams(stepText, hasInlineTable) 121 122 if err != nil { 123 return handler.getErrorResponse(message, err) 124 } 125 stepValueResponse := &gauge_messages.GetStepValueResponse{StepValue: gauge.ConvertToProtoStepValue(stepValue)} 126 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_GetStepValueResponse, MessageId: message.MessageId, StepValueResponse: stepValueResponse} 127 128 } 129 130 func (handler *gaugeAPIMessageHandler) getAllConceptsRequestResponse(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 131 allConceptsResponse := handler.createGetAllConceptsResponseMessageFor(handler.specInfoGatherer.Concepts()) 132 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_GetAllConceptsResponse, MessageId: message.MessageId, AllConceptsResponse: allConceptsResponse} 133 } 134 135 func (handler *gaugeAPIMessageHandler) getLanguagePluginLibPath(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 136 libPathRequest := message.GetLibPathRequest() 137 language := libPathRequest.GetLanguage() 138 languageInstallDir, err := plugin.GetInstallDir(language, "") 139 if err != nil { 140 return handler.getErrorMessage(err) 141 } 142 runnerInfo, err := runner.GetRunnerInfo(language) 143 if err != nil { 144 return handler.getErrorMessage(err) 145 } 146 relativeLibPath := runnerInfo.Lib 147 libPath := filepath.Join(languageInstallDir, relativeLibPath) 148 response := &gauge_messages.GetLanguagePluginLibPathResponse{Path: libPath} 149 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_GetLanguagePluginLibPathResponse, MessageId: message.MessageId, LibPathResponse: response} 150 } 151 152 func (handler *gaugeAPIMessageHandler) getErrorResponse(message *gauge_messages.APIMessage, err error) *gauge_messages.APIMessage { 153 errorResponse := &gauge_messages.ErrorResponse{Error: err.Error()} 154 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_ErrorResponse, MessageId: message.MessageId, Error: errorResponse} 155 156 } 157 158 func (handler *gaugeAPIMessageHandler) getErrorMessage(err error) *gauge_messages.APIMessage { 159 id := common.GetUniqueID() 160 errorResponse := &gauge_messages.ErrorResponse{Error: err.Error()} 161 return &gauge_messages.APIMessage{MessageType: gauge_messages.APIMessage_ErrorResponse, MessageId: id, Error: errorResponse} 162 } 163 164 func (handler *gaugeAPIMessageHandler) createSpecsResponseMessageFor(details []*infoGatherer.SpecDetail) *gauge_messages.SpecsResponse { 165 specDetails := make([]*gauge_messages.SpecsResponse_SpecDetail, 0) 166 for _, d := range details { 167 detail := &gauge_messages.SpecsResponse_SpecDetail{} 168 if d.HasSpec() { 169 detail.Spec = gauge.ConvertToProtoSpec(d.Spec) 170 } 171 for _, e := range d.Errs { 172 detail.ParseErrors = append(detail.ParseErrors, &gauge_messages.Error{Type: gauge_messages.Error_PARSE_ERROR, Filename: e.FileName, Message: e.Message, LineNumber: int32(e.LineNo)}) 173 } 174 specDetails = append(specDetails, detail) 175 } 176 return &gauge_messages.SpecsResponse{Details: specDetails} 177 } 178 179 func (handler *gaugeAPIMessageHandler) createGetAllConceptsResponseMessageFor(conceptInfos []*gauge_messages.ConceptInfo) *gauge_messages.GetAllConceptsResponse { 180 return &gauge_messages.GetAllConceptsResponse{Concepts: conceptInfos} 181 } 182 183 func (handler *gaugeAPIMessageHandler) performRefactoring(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 184 refactoringRequest := message.PerformRefactoringRequest 185 response := &gauge_messages.PerformRefactoringResponse{} 186 c := make(chan bool) 187 runner, err := ConnectToRunner(c, false) 188 defer func() { 189 err := runner.Kill() 190 if err != nil { 191 logger.Errorf(true, "failed to kill runner with pid: %d", runner.Pid()) 192 } 193 }() 194 if err != nil { 195 response.Success = false 196 response.Errors = []string{err.Error()} 197 return &gauge_messages.APIMessage{MessageId: message.MessageId, MessageType: gauge_messages.APIMessage_PerformRefactoringResponse, PerformRefactoringResponse: response} 198 } 199 refactoringResult := refactor.GetRefactoringChanges(refactoringRequest.GetOldStep(), refactoringRequest.GetNewStep(), runner, handler.specInfoGatherer.SpecDirs, true) 200 refactoringResult.WriteToDisk() 201 if refactoringResult.Success { 202 logger.Infof(false, "%s", refactoringResult.String()) 203 } else { 204 logger.Errorf(false, "Refactoring response from gauge. Errors : %s", refactoringResult.Errors) 205 } 206 response.Success = refactoringResult.Success 207 response.Errors = refactoringResult.Errors 208 response.FilesChanged = refactoringResult.AllFilesChanged() 209 return &gauge_messages.APIMessage{MessageId: message.MessageId, MessageType: gauge_messages.APIMessage_PerformRefactoringResponse, PerformRefactoringResponse: response} 210 } 211 212 func (handler *gaugeAPIMessageHandler) performRefresh(files []string) { 213 for _, file := range files { 214 if util.IsConcept(file) { 215 handler.specInfoGatherer.OnConceptFileModify(file) 216 } 217 } 218 for _, file := range files { 219 if util.IsSpec(file) { 220 handler.specInfoGatherer.OnSpecFileModify(file) 221 } 222 } 223 } 224 225 func (handler *gaugeAPIMessageHandler) extractConcept(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 226 request := message.GetExtractConceptRequest() 227 success, err, filesChanged := conceptExtractor.ExtractConcept(request.GetConceptName(), request.GetSteps(), request.GetConceptFileName(), request.GetChangeAcrossProject(), request.GetSelectedTextInfo()) 228 response := &gauge_messages.ExtractConceptResponse{IsSuccess: success, Error: err.Error(), FilesChanged: filesChanged} 229 return &gauge_messages.APIMessage{MessageId: message.MessageId, MessageType: gauge_messages.APIMessage_ExtractConceptResponse, ExtractConceptResponse: response} 230 } 231 232 func (handler *gaugeAPIMessageHandler) formatSpecs(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 233 request := message.GetFormatSpecsRequest() 234 results := formatter.FormatSpecFiles(request.GetSpecs()...) 235 var warnings []string 236 var errors []string 237 for _, result := range results { 238 if result.ParseErrors != nil { 239 for _, err := range result.ParseErrors { 240 errors = append(errors, err.Error()) 241 } 242 } 243 if result.Warnings != nil { 244 var warningTexts []string 245 for _, warning := range result.Warnings { 246 warningTexts = append(warningTexts, warning.String()) 247 } 248 warnings = append(warnings, warningTexts...) 249 } 250 } 251 formatResponse := &gauge_messages.FormatSpecsResponse{Errors: errors, Warnings: warnings} 252 return &gauge_messages.APIMessage{MessageId: message.MessageId, MessageType: gauge_messages.APIMessage_FormatSpecsResponse, FormatSpecsResponse: formatResponse} 253 } 254 255 func (handler *gaugeAPIMessageHandler) createUnsupportedAPIMessageResponse(message *gauge_messages.APIMessage) *gauge_messages.APIMessage { 256 return &gauge_messages.APIMessage{MessageId: message.MessageId, 257 MessageType: gauge_messages.APIMessage_UnsupportedApiMessageResponse, 258 UnsupportedApiMessageResponse: &gauge_messages.UnsupportedApiMessageResponse{}} 259 }