Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evols #10

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function MockNewCtrl($scope, $location, $routeParams, Mock) {
$scope.mock.timeoutms = 0;
$scope.mock.httpStatus = 200;
$scope.mock.response = "";
$scope.mock.wsdl = "";
$scope.mock.criteria = "*";
$scope.mock.mockGroupName = $routeParams.mockGroupName;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function ServicesCtrl($scope, $rootScope, $routeParams, ServicesService, $location, ngTableParams, $filter) {
function ServicesCtrl($scope, $rootScope, $routeParams, ServicesService, $location, ngTableParams, $filter, MockGroupsService) {

$scope.groups = $routeParams.groups;
$scope.environmentName = $routeParams.environmentName;
Expand All @@ -7,6 +7,19 @@ function ServicesCtrl($scope, $rootScope, $routeParams, ServicesService, $locati
success(function (data) {
$scope.services = data.services;

$scope.services.forEach(function(element) {
if(element.useMockGroup){
MockGroupsService.get(element.mockGroupId)
.success(function (mockgroupResponse){
element.mockName = mockgroupResponse.name;
})
.error(function (error){
console.log("Erreur lors de la récupération du Groupe "+element.mockGroupId+" : "+error);
});
}
});


$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10, // count per page
Expand Down
6 changes: 6 additions & 0 deletions app/assets/javascripts/app/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ spApp.filter('capitalize', function() {
return input.substring(0,1).toUpperCase()+input.substring(1);
}
});

spApp.filter('arrayToList', function(){
return function(arr) {
return arr.join(', ');
}
});
6 changes: 5 additions & 1 deletion app/assets/javascripts/app/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ spApp.factory('MockGroup', function ($resource) {
{mockgroupId: '@_id.$oid'},
{
update: {method: 'PUT'},
create: {method: 'POST'}
create: {method: 'POST'},
get: {method: 'GET'}
}
);
});
Expand All @@ -125,6 +126,9 @@ spApp.factory("MockGroupsService", function ($http) {
return {
findAll: function (group) {
return $http.get('/mockgroups/' + group + '/findall');
},
get: function (mockGroupId) {
return $http.get('/mockgroups/'+mockGroupId);
}
}
});
Expand Down
1 change: 0 additions & 1 deletion app/assets/javascripts/soapower.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,3 @@ spApp.run(['$location', '$rootScope', function ($location, $rootScope) {
$rootScope.$broadcast("showGroupsFilter", false, "Soapower");
});
}]);

113 changes: 97 additions & 16 deletions app/controllers/Soap.scala
Original file line number Diff line number Diff line change
@@ -1,29 +1,88 @@
package controllers

import java.net.{InetAddress, NetworkInterface}

import models.RequestData._
import models._
import org.jboss.netty.handler.codec.http.HttpMethod
import play.Logger
import play.api.http.HeaderNames
import play.api.mvc._
import play.api.libs.iteratee._
import models._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.mvc._
import reactivemongo.bson.{BSONDocument, BSONObjectID}

import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import reactivemongo.bson.{BSONDocument, BSONObjectID}
import models.RequestData._
import org.jboss.netty.handler.codec.http.HttpMethod


object Soap extends Controller {

def index(environment: String, localTarget: String) = Action.async(parse.xml) {
def getWSDL(environment: String, localTarget: String) = Action.async {
implicit request =>
Logger.debug("Request on environment:" + environment + " localTarget:" + localTarget)
val requestContentType = request.contentType.get
val sender = request.remoteAddress
val content = request.body.toString()
val headers = request.headers.toSimpleMap
forwardRequest(environment, localTarget, sender, content, headers, requestContentType)

Logger.debug("Request WSDL on environment:" + environment + " localTarget:" + localTarget)


val service = Service.findByLocalTargetAndEnvironmentName(Service.SOAP, localTarget, environment, HttpMethod.POST)
service.map(svc =>
if (svc.isDefined && svc.get != null) {
if (svc.get.useMockGroup && svc.get.mockGroupId.isDefined) {

val fmock = Mock.findByMockGroupAndContent(BSONObjectID(svc.get.mockGroupId.get), "")
val mock = Await.result(fmock, 1.second)


Ok(mock.wsdl)
} else {
// Call remote service to get WSDL back
val client = new Client(svc.get, environment, null, null, null, null, null)
client.sendGetWSDLRequest
var wsdl: String = client.wsdlResponse.body

// Modify the location to call Soapower instead of remote target in the next call
wsdl = rewriteTargetLocation(wsdl, environment, localTarget)

Ok(wsdl)
}
} else {
val err = "environment " + environment + " with localTarget " + localTarget + " unknown"
Logger.error(err)
BadRequest(err)
}
)
}


def index(environment: String, localTarget: String) = Action.async(parse.anyContent) {
implicit request =>

Logger.debug("Request on environment:" + environment + " localTarget:" + localTarget)

// On determine si le flux est du XML ou du MTOM
var content : String = null
if (request.body.isInstanceOf[AnyContentAsXml]) {

content = request.body.asInstanceOf[AnyContentAsXml].xml.toString

} else {

// Cas MTOM
val buffer: RawBuffer = request.body.asRaw.get
content = buffer.asBytes() match {
case Some(x) => new String(x, "UTF-8")
case None => scala.io.Source.fromFile(buffer.asFile).mkString
}

}

val requestContentType = request.contentType.get
val sender = request.remoteAddress
val headers = request.headers.toSimpleMap

forwardRequest(environment, localTarget, sender, content, headers, requestContentType)

}

/**
* Automatically detect new services. If the given parameters interpolates an existing service, then nothing is created otherwise a new service is created.
* The new service takes the given parameters and theses defaults parameters :
Expand Down Expand Up @@ -108,7 +167,8 @@ object Soap extends Controller {

/**
* Replay a given request.
* @param requestId request to replay, content is post by user
*
* @param requestId request to replay, content is post by user
*/
def replay(requestId: String) = Action.async(parse.xml) {
implicit request =>
Expand Down Expand Up @@ -136,7 +196,7 @@ object Soap extends Controller {
private def forwardRequest(environmentName: String, localTarget: String, sender: String, content: String, headers: Map[String, String], requestContentType: String): Future[Result] = {
val service = Service.findByLocalTargetAndEnvironmentName(Service.SOAP, localTarget, environmentName, HttpMethod.POST)

service.map(svc =>
service.map(svc =>
if (svc.isDefined && svc.get != null) {
val client = new Client(svc.get, environmentName, sender, content, headers, Service.SOAP, requestContentType)
if (svc.get.useMockGroup && svc.get.mockGroupId.isDefined) {
Expand All @@ -146,7 +206,11 @@ object Soap extends Controller {
val sr = new Results.Status(mock.httpStatus).apply(mock.response.getBytes())
.withHeaders("ProxyVia" -> "soapower")
.withHeaders(UtilConvert.headersFromString(mock.httpHeaders).toArray: _*)
.as(XML)

// Define Content-Type only if not defined in the Mock
if (!mock.httpHeaders.contains("Content-Type")) {
sr.as(XML)
}

val timeoutFuture = play.api.libs.concurrent.Promise.timeout(sr, mock.timeoutms.milliseconds)
Await.result(timeoutFuture, 10.second) // 10 seconds (10000 ms) is the maximum allowed.
Expand All @@ -168,4 +232,21 @@ object Soap extends Controller {
}
)
}

/**
* Redefine host to call when get WSDL back, to avoid calling the remote target directly
*
* @param wsdl wsdl to modify
* @param environment
* @param localTarget
* @return wsdl with location on Soapower
*/
private def rewriteTargetLocation(wsdl: String, environment: String, localTarget: String) = {

val hostname = InetAddress.getLocalHost.getHostName
val port = System.getProperty("http.port", "9000")

wsdl.replaceAll("location=\".+\"", "location=\"http://" + hostname + ":" + port + "/soap/" + environment + "/" + localTarget +"\"")

}
}
36 changes: 33 additions & 3 deletions app/models/Client.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package models

import com.ning.http.client.{AsyncHttpClient, FluentCaseInsensitiveStringsMap}
import com.ning.http.client.{FluentCaseInsensitiveStringsMap}
import com.ning.http.client.providers.netty.NettyResponse
import scala.concurrent.Future
import scala.concurrent.Await
import scala.concurrent.duration._
import play.api.libs.ws._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.concurrent._
import play.core.utils.CaseInsensitiveOrdered
import play.Logger
import collection.immutable.TreeMap
Expand All @@ -16,6 +15,7 @@ import java.io.StringWriter
import java.io.PrintWriter
import play.api.Play.current
import org.jboss.netty.handler.codec.http.HttpMethod
import java.net.{HttpURLConnection, URL}

object Client {
private val DEFAULT_NO_SOAPACTION = "Soapower_NoSoapAction"
Expand Down Expand Up @@ -73,6 +73,8 @@ class Client(pService: Service, environmentName: String, sender: String, content

var response: ClientResponse = null

var wsdlResponse: WSDLClientResponse = null

private var futureResponse: Future[WSResponse] = null
private var requestTimeInMillis: Long = -1

Expand Down Expand Up @@ -100,6 +102,7 @@ class Client(pService: Service, environmentName: String, sender: String, content

/**
* Send a request to a REST service
*
* @param correctUrl
* @param query
*/
Expand Down Expand Up @@ -159,11 +162,12 @@ class Client(pService: Service, environmentName: String, sender: String, content
* Send a request to a SOAP service
*/
def sendSoapRequestAndWaitForResponse() {

if (Logger.isDebugEnabled) {
Logger.debug("RemoteTarget (soap)" + service.remoteTarget)
}

requestTimeInMillis = System.currentTimeMillis
requestTimeInMillis = System.currentTimeMillis

// prepare request
var wsRequestHolder = WS.url(service.remoteTarget).withRequestTimeout(service.timeoutms.toInt)
Expand All @@ -188,6 +192,25 @@ class Client(pService: Service, environmentName: String, sender: String, content
saveData(content)
}

/**
* Permet d'envoyer une demande de WSDL.
* Pour le moment ce type de trame n'est pas persiste
*/
def sendGetWSDLRequest() {

requestTimeInMillis = System.currentTimeMillis

val connection = new URL(service.remoteTarget + "?wsdl").openConnection.asInstanceOf[HttpURLConnection]
connection.setConnectTimeout(5000)
connection.setRequestMethod("GET")
val inputStream = connection.getInputStream
val content: String = scala.io.Source.fromInputStream(inputStream).mkString
if (inputStream != null) inputStream.close

wsdlResponse = new WSDLClientResponse(content, (System.currentTimeMillis - requestTimeInMillis))

}

private def waitForResponse(headers: Map[String, String]) = {
try {
val wsResponse: WSResponse = Await.result(futureResponse, service.timeoutms.millis * 1000000)
Expand Down Expand Up @@ -230,6 +253,7 @@ class Client(pService: Service, environmentName: String, sender: String, content

/**
* If content is null or empty, return "[null or empty]"
*
* @param content a string
* @return [null or empty] or the content if not null (or empty!)
*/
Expand Down Expand Up @@ -313,4 +337,10 @@ class ClientResponse(wsResponse: WSResponse = null, val responseTimeInMillis: Lo
}
}

class WSDLClientResponse(wsdl : String = null, val responseTimeInMillis: Long) {

var body: String = if (wsdl != null) wsdl else ""

}


2 changes: 1 addition & 1 deletion app/models/Environment.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ object Environment {

private val keyCacheAllOptions = "environment-options"
private val keyCacheByName = "environment-name-"
private val ENVIRONMENT_NAME_PATTERN = "[a-zA-Z0-9]{1,200}"
private val ENVIRONMENT_NAME_PATTERN = "[a-zA-Z0-9_]{1,200}"

/**
* Identity key for CSV file
Expand Down
7 changes: 5 additions & 2 deletions app/models/Mock.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ case class Mock(_id: Option[BSONObjectID],
httpHeaders: String,
criteria: String,
response: String,
wsdl: String,
mockGroupName: Option[String]) {
def this(mockDoc: BSONDocument, mockGroupName: Option[String]) =
this(
Expand All @@ -31,6 +32,7 @@ case class Mock(_id: Option[BSONObjectID],
mockDoc.getAs[String]("httpHeaders").get,
mockDoc.getAs[String]("criteria").get,
mockDoc.getAs[String]("response").get,
mockDoc.getAs[String]("wsdl").get,
mockGroupName)
}

Expand Down Expand Up @@ -73,7 +75,8 @@ object Mock {
"httpStatus" -> BSONInteger(mock.httpStatus),
"httpHeaders" -> BSONString(mock.httpHeaders),
"criteria" -> BSONString(mock.criteria),
"response" -> BSONString(mock.response))
"response" -> BSONString(mock.response),
"wsdl" -> BSONString(mock.wsdl))
}

/**
Expand Down Expand Up @@ -151,7 +154,7 @@ object Mock {
mocksGroup.map(
omocks => {
val noMockFound: Mock = new Mock(Some(BSONObjectID.generate), "mockNotFoundName", "mockNotFoundDescription", -1,
0, HttpStatus.SC_INTERNAL_SERVER_ERROR.toString, "noCriteria", "no mock found in soapower",
0, HttpStatus.SC_INTERNAL_SERVER_ERROR.toString, "noCriteria", "no mock found in soapower", "no mock found",
Some("Error getting Mock with mockGroupId " + mockGroupId)
)

Expand Down
2 changes: 1 addition & 1 deletion app/models/MockGroup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ object MockGroup {
}

private val keyCacheAllOptions = "mockGroup-options"
private val MOCKGROUP_NAME_PATTERN = "[a-zA-Z0-9]{1,200}"
private val MOCKGROUP_NAME_PATTERN = "[a-zA-Z0-9_]{1,200}"


/**
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import play.PlayScala

name := "soapower"

version := "2.1.7"
version := "2.1.8"

lazy val root = (project in file(".")).enablePlugins(PlayScala,SbtWeb)

Expand Down
1 change: 1 addition & 0 deletions conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ PUT /autorest/:group/:environment/*remoteTarget
POST /autorest/:group/:environment/*remoteTarget controllers.Rest.autoIndex(group, environment, remoteTarget)

# Soap
GET /soap/:environment/*localTarget controllers.Soap.getWSDL(environment, localTarget)
POST /soap/:environment/*localTarget controllers.Soap.index(environment, localTarget)
POST /autosoap/:group/:environment/*remoteTarget controllers.Soap.autoIndex(group, environment, remoteTarget)
POST /replay/soap/:id controllers.Soap.replay(id:String)
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.13.5
sbt.version=0.13.7
Loading