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  }