diff --git a/README b/README index 2cfc1d870643f740d4ee1647160c1cde5c0e088d..a735a0dbbe6d9fb3d44c47a044ffbedfaec6d4a2 100644 --- a/README +++ b/README @@ -204,7 +204,7 @@ The web and SOAP interfaces depend on the following packages: - apache2 >= 2.2.11 - libapache2-mod-wsgi >= 2.8 - python-webpy >= 0.33 -- python-soaplib >= 2.0.0-alpha1 +- python-rpclib >= 2.8.0-beta - python-simpletal >= 4.1-6 Automatic remote deployment depends on Fabric: @@ -230,7 +230,7 @@ source code (excluding the standard library imports): pyparsing setuptools simpletal - soaplib + rpclib suds web webtest diff --git a/bin/mutalyzer-json-service.wsgi b/bin/mutalyzer-json-service.wsgi new file mode 100755 index 0000000000000000000000000000000000000000..052dbbb36111927d5c7ef3853af9610a1181b00f --- /dev/null +++ b/bin/mutalyzer-json-service.wsgi @@ -0,0 +1,45 @@ +#!/usr/bin/env python +""" +WSGI interface to the Mutalyzer HTTP/RPC+JSON webservice. + +The WSGI interface is exposed through the module variable 'application'. + +Example Apache/mod_wsgi configuration: + + WSGIScriptAlias /json /usr/local/bin/mutalyzer-json-service.wsgi + +Be sure to have this line first if you also define a / alias, like this: + + WSGIScriptAlias /services /usr/local/bin/mutalyzer-json-service.wsgi + WSGIScriptAlias / /usr/local/bin/mutalyzer-website.wsgi + +You can also use the built-in HTTP server by running this file directly. + +To start the built-in HTTP server on port 8082: + + /usr/local/bin/mutalyzer-json-service.wsgi 8082 +""" + + +import sys +from wsgiref.simple_server import make_server +from rpclib.server.wsgi import WsgiApplication +from mutalyzer.services import json + + +DEFAULT_PORT = 8082 + + +application = WsgiApplication(json.application) + + +if __name__ == '__main__': + port = DEFAULT_PORT + if len(sys.argv) > 1: + try: + port = int(sys.argv[1]) + except ValueError: + print 'Not a valid port number: %s' % sys.argv[1] + sys.exit(1) + print 'Listening at http://localhost:%d/' % port + make_server('localhost', port, application).serve_forever() diff --git a/bin/mutalyzer-webservice.wsgi b/bin/mutalyzer-soap-service.wsgi similarity index 57% rename from bin/mutalyzer-webservice.wsgi rename to bin/mutalyzer-soap-service.wsgi index 5a01545e2e705cf9b04bc26b14da521c24e8c3d9..79fe02e17b3a59e40af2a6ad93d5a36721b96973 100755 --- a/bin/mutalyzer-webservice.wsgi +++ b/bin/mutalyzer-soap-service.wsgi @@ -6,39 +6,32 @@ The WSGI interface is exposed through the module variable 'application'. Example Apache/mod_wsgi configuration: - WSGIScriptAlias /services /usr/local/bin/mutalyzer-webservice.wsgi + WSGIScriptAlias /services /usr/local/bin/mutalyzer-soap-service.wsgi Be sure to have this line first if you also define a / alias, like this: - WSGIScriptAlias /services /usr/local/bin/mutalyzer-webservice.wsgi + WSGIScriptAlias /services /usr/local/bin/mutalyzer-soap-service.wsgi WSGIScriptAlias / /usr/local/bin/mutalyzer-website.wsgi You can also use the built-in HTTP server by running this file directly. To start the built-in HTTP server on port 8081: - /usr/local/bin/mutalyzer-webservice.wsgi 8081 - -@todo: Do we really use namespaces correctly? -@todo: For some reason, the server exposes its location including ?wsdl. -@todo: More thourough input checking. The @soap decorator does not do any - kind of strictness checks on the input. For example, in - transcriptInfo, the build argument must really be present. (Hint: - use __checkBuild.) + /usr/local/bin/mutalyzer-soap-service.wsgi 8081 """ import sys from wsgiref.simple_server import make_server -from mutalyzer import webservice +from mutalyzer.services import soap DEFAULT_PORT = 8081 # Unfortunately we cannot instantiate wsgi.Application here, see the note -# near the bottom of mutalyzer/webservice.py. -application = webservice.application +# near the bottom of mutalyzer/services/soap.py. +application = soap.wsgi_application if __name__ == '__main__': @@ -49,6 +42,6 @@ if __name__ == '__main__': except ValueError: print 'Not a valid port number: %s' % sys.argv[1] sys.exit(1) - print 'Listening to http://localhost:%d/' % port + print 'Listening at http://localhost:%d/' % port print 'WDSL file is at http://localhost:%d/?wsdl' % port make_server('localhost', port, application).serve_forever() diff --git a/extras/apache/mutalyzer.conf b/extras/apache/mutalyzer.conf index 67b8fd172b8db0b7c501204449369ddd5e6e3c5d..0142a57666db70f5cbc57ca2c625c2cd0092deee 100644 --- a/extras/apache/mutalyzer.conf +++ b/extras/apache/mutalyzer.conf @@ -11,14 +11,22 @@ Alias /mutalyzer/base /var/www/mutalyzer/base WSGIDaemonProcess mutalyzer processes=2 threads=15 maximum-requests=10000 WSGIProcessGroup mutalyzer -# Webservice -WSGIScriptAlias /mutalyzer/services <MUTALYZER_BIN_WEBSERVICE> +# SOAP/1.1 webservice +WSGIScriptAlias /mutalyzer/services <MUTALYZER_BIN_SOAP_SERVICE> <Directory /mutalyzer/services> Order deny,allow Allow from all Options -Indexes </Directory> +# HTTP/RPC+JSON webservice +WSGIScriptAlias /mutalyzer/json <MUTALYZER_BIN_JSON_SERVICE> +<Directory /mutalyzer/json> + Order deny,allow + Allow from all + Options -Indexes +</Directory> + # Website WSGIScriptAlias /mutalyzer <MUTALYZER_BIN_WEBSITE> <Directory /mutalyzer> diff --git a/extras/migrations/007-apache-json-webservice.migration b/extras/migrations/007-apache-json-webservice.migration new file mode 100755 index 0000000000000000000000000000000000000000..cd9b82784e59c2b3db54ffce99d252f3312342da --- /dev/null +++ b/extras/migrations/007-apache-json-webservice.migration @@ -0,0 +1,46 @@ +#!/bin/bash + +# Add HTTP/RPC+JSON webservice entry to Apache configuration. +# +# Usage: +# ./007-apache-json-webservice.migration [migrate] + +COLOR_INFO='\033[32m' +COLOR_WARNING='\033[33m' +COLOR_ERROR='\033[31m' +COLOR_END='\033[0m' + +BIN_SOAP_SERVICE=$(which mutalyzer-soap-service.wsgi) +BIN_JSON_SERVICE=$(which mutalyzer-json-service.wsgi) + +BIN_OLD_SOAP_SERVICE=$(echo $BIN_SOAP_SERVICE | sed 's/mutalyzer-soap-service/mutalyzer-webservice/') + +CONF=/etc/apache2/conf.d/mutalyzer.conf + +if [ -e $CONF ] && ! $(grep -q "${BIN_JSON_SERVICE}" $CONF) && $(grep -q "${BIN_OLD_SOAP_SERVICE}" $CONF); then + echo -e "${COLOR_WARNING}This migration is needed.${COLOR_END}" + if [ "$1" = 'migrate' ]; then + echo 'Performing migration.' + if $(grep -q '^# Website' $CONF); then + sed -i 's!^# Webservice!# SOAP/1.1 webservice!' $CONF + sed -i "s!$BIN_OLD_SOAP_SERVICE!$BIN_SOAP_SERVICE!g" $CONF + echo -e "${COLOR_INFO}Changed ${BIN_OLD_SOAP_SERVICE} to ${BIN_SOAP_SERVICE} in ${CONF}${COLOR_END}" + # Insert before '# Website' line + sed -i "/^# Website/ i\\ +# HTTP/RPC+JSON webservice\\ +WSGIScriptAlias /mutalyzer/json $BIN_JSON_SERVICE\\ +<Directory /mutalyzer/json>\\ + Order deny,allow\\ + Allow from all\\ + Options -Indexes\\ +</Directory>\\ +" $CONF + echo -e "${COLOR_INFO}Added HTTP/RPC+JSON webservice at /mutalyzer/json to ${CONF}${COLOR_END}" + echo 'Performed migration.' + else + echo -e "${COLOR_ERROR}Could not perform migration.${COLOR_END}" + fi + fi +else + echo -e "${COLOR_INFO}This migration is not needed.${COLOR_END}" +fi diff --git a/extras/post-install.sh b/extras/post-install.sh index 3a001756912f3a4c177760ba4515e5392c635411..34a4213761a29c1d704bb67d65d8633809357b50 100644 --- a/extras/post-install.sh +++ b/extras/post-install.sh @@ -31,7 +31,8 @@ BIN_BATCHD=$(which mutalyzer-batchd) BIN_CACHE_SYNC=$(which mutalyzer-cache-sync) BIN_MAPPING_UPDATE=$(which mutalyzer-mapping-update) BIN_WEBSITE=$(which mutalyzer-website.wsgi) -BIN_WEBSERVICE=$(which mutalyzer-webservice.wsgi) +BIN_SOAP_SERVICE=$(which mutalyzer-soap-service.wsgi) +BIN_JSON_SERVICE=$(which mutalyzer-json-service.wsgi) if [ ! -e /etc/mutalyzer/config ]; then echo -e "${COLOR_INFO}Creating /etc/mutalyzer/config${COLOR_END}" @@ -83,7 +84,7 @@ sed -i -e "s@<MUTALYZER_BIN_MAPPING_UPDATE>@${BIN_MAPPING_UPDATE}@g" /etc/cron.d echo -e "${COLOR_INFO}Creating /etc/apache2/conf.d/mutalyzer.conf${COLOR_END}" cp extras/apache/mutalyzer.conf /etc/apache2/conf.d/mutalyzer.conf -sed -i -e "s@<MUTALYZER_BIN_WEBSITE>@${BIN_WEBSITE}@g" -e "s@<MUTALYZER_BIN_WEBSERVICE>@${BIN_WEBSERVICE}@g" -e "s@<MUTALYZER_BIN_BATCHD>@${BIN_BATCHD}@g" /etc/apache2/conf.d/mutalyzer.conf +sed -i -e "s@<MUTALYZER_BIN_WEBSITE>@${BIN_WEBSITE}@g" -e "s@<MUTALYZER_BIN_SOAP_SERVICE>@${BIN_SOAP_SERVICE}@g" -e "s@<MUTALYZER_BIN_JSON_SERVICE>@${BIN_JSON_SERVICE}@g" -e "s@<MUTALYZER_BIN_BATCHD>@${BIN_BATCHD}@g" /etc/apache2/conf.d/mutalyzer.conf chmod u=rw,go=r /etc/apache2/conf.d/mutalyzer.conf echo "You will now be asked for the MySQL root password" diff --git a/extras/post-upgrade.sh b/extras/post-upgrade.sh index b6224d7619607400404abbf3c027df5e29d75ec9..16178068cbac5b545ab32fce89152e1f401dee19 100644 --- a/extras/post-upgrade.sh +++ b/extras/post-upgrade.sh @@ -23,7 +23,8 @@ COLOR_END='\033[0m' # directory to be used. PACKAGE_ROOT=$(cd / && python -c 'import mutalyzer; print mutalyzer.package_root()') BIN_WEBSITE=$(which mutalyzer-website.wsgi) -BIN_WEBSERVICE=$(which mutalyzer-webservice.wsgi) +BIN_SOAP_SERVICE=$(which mutalyzer-soap-service.wsgi) +BIN_JSON_SERVICE=$(which mutalyzer-json-service.wsgi) if [ ! -e /var/www/mutalyzer ]; then mkdir -p /var/www/mutalyzer @@ -48,7 +49,8 @@ echo -e "${COLOR_INFO}Assuming mod_wsgi daemon mode, not restarting Apache${COLO echo "Touching WSGI entry to reload application" touch $BIN_WEBSITE -touch $BIN_WEBSERVICE +touch $BIN_SOAP_SERVICE +touch $BIN_JSON_SERVICE echo "Restarting Mutalyzer batch daemon" /etc/init.d/mutalyzer-batchd restart diff --git a/extras/pre-install.sh b/extras/pre-install.sh index e9b096c9a5c3e87d09970edcf0dbcf3928e43ca1..7994868d4fa4e2ad10b2c003b26f9cb2642a17fe 100644 --- a/extras/pre-install.sh +++ b/extras/pre-install.sh @@ -44,10 +44,10 @@ apt-get install -y \ python-setuptools \ git-core -echo -e "${COLOR_INFO}Installing latest soaplib from mirrored git master${COLOR_END}" +echo -e "${COLOR_INFO}Installing latest rpclib from git master${COLOR_END}" pushd $(mktemp -d) -git clone https://github.com/martijnvermaat/soaplib.git . +git clone https://github.com/arskom/rpclib.git . python setup.py install popd diff --git a/mutalyzer/models.py b/mutalyzer/models.py index 41c3afd82ad45a256e1d6c49b6660d5cef7cf37e..0dcc07ec440cc716e679611f825cce7c2e331ca8 100644 --- a/mutalyzer/models.py +++ b/mutalyzer/models.py @@ -1,13 +1,13 @@ """ Collection of serilizable objects used by the SOAP webservice. They extend -from the soaplib ClassModel. +from the rpclib ClassModel. -Default attributes for the soaplib ClassModel: +Default attributes for the rpclib ClassModel: - nillable = True - min_occurs = 0 - max_occurs = 1 -Additional attributes values for the soaplib String model: +Additional attributes values for the rpclib String model: - min_len = 0 - max_len = 'unbounded' - pattern = None @@ -17,15 +17,15 @@ Additional attributes values for the soaplib String model: """ -from soaplib.core.model.primitive import String, Integer, Boolean, DateTime -from soaplib.core.model.clazz import ClassModel, Array +from rpclib.model.primitive import String, Integer, Boolean, DateTime +from rpclib.model.complex import ComplexModel, Array from mutalyzer import SOAP_NAMESPACE class Mandatory(object): """ - This is soaplib.model.primitive.Mandatory, but without min_length=1 for + This is rpclib.model.primitive.Mandatory, but without min_length=1 for the String model. """ String = String(min_occurs=1, nillable=False) @@ -35,7 +35,7 @@ class Mandatory(object): #Mandatory -class SoapMessage(ClassModel): +class SoapMessage(ComplexModel): """ Type of messages used in SOAP method return values. """ @@ -46,7 +46,7 @@ class SoapMessage(ClassModel): #SoapMessage -class Mapping(ClassModel): +class Mapping(ComplexModel): """ Return type of SOAP method mappingInfo. """ @@ -64,7 +64,7 @@ class Mapping(ClassModel): #Mapping -class Transcript(ClassModel): +class Transcript(ComplexModel): """ Return type of SOAP method transcriptInfo. """ @@ -76,7 +76,7 @@ class Transcript(ClassModel): #Transcript -class RawVariant(ClassModel): +class RawVariant(ComplexModel): """ Used in MutalyzerOutput data type. """ @@ -87,7 +87,7 @@ class RawVariant(ClassModel): #RawVariant -class MutalyzerOutput(ClassModel): +class MutalyzerOutput(ComplexModel): """ Return type of SOAP method runMutalyzer. """ @@ -128,7 +128,7 @@ class MutalyzerOutput(ClassModel): #MutalyzerOutput -class TranscriptNameInfo(ClassModel): +class TranscriptNameInfo(ComplexModel): """ Return type of SOAP method getGeneAndTranscript. """ @@ -139,7 +139,7 @@ class TranscriptNameInfo(ClassModel): #TranscriptNameInfo -class ExonInfo(ClassModel): +class ExonInfo(ComplexModel): """ Used in TranscriptInfo data type. """ @@ -154,7 +154,7 @@ class ExonInfo(ClassModel): #ExonInfo -class ProteinTranscript(ClassModel): +class ProteinTranscript(ComplexModel): """ Used in TranscriptInfo data type. """ @@ -166,7 +166,7 @@ class ProteinTranscript(ClassModel): #ProteinTranscript -class TranscriptInfo(ClassModel): +class TranscriptInfo(ComplexModel): """ Used in return type of SOAP method getTranscriptsAndInfo. @@ -206,7 +206,7 @@ class TranscriptInfo(ClassModel): #TranscriptInfo -class TranscriptMappingInfo(ClassModel): +class TranscriptMappingInfo(ComplexModel): """ Used in return type of SOAP method getTranscriptsRange. """ @@ -226,7 +226,7 @@ class TranscriptMappingInfo(ClassModel): #TranscriptMappingInfo -class CheckSyntaxOutput(ClassModel): +class CheckSyntaxOutput(ComplexModel): """ Return type of SOAP method checkSyntax. """ @@ -237,7 +237,7 @@ class CheckSyntaxOutput(ClassModel): #CheckSyntaxOutput -class InfoOutput(ClassModel): +class InfoOutput(ComplexModel): """ Return type of SOAP method info. """ @@ -253,7 +253,7 @@ class InfoOutput(ClassModel): #InfoOutput -class CacheEntry(ClassModel): +class CacheEntry(ComplexModel): """ Used in getCache SOAP method. """ diff --git a/mutalyzer/services/__init__.py b/mutalyzer/services/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..05b3d031865b91b2a3ebd2ead081592a52a119e2 --- /dev/null +++ b/mutalyzer/services/__init__.py @@ -0,0 +1,3 @@ +""" +Services (RPC) for Mutalyzer. +""" diff --git a/mutalyzer/services/json.py b/mutalyzer/services/json.py new file mode 100644 index 0000000000000000000000000000000000000000..b1f46c313d6724a9f0b8f81d723fe692f1f9ad63 --- /dev/null +++ b/mutalyzer/services/json.py @@ -0,0 +1,17 @@ +""" +Mutalyzer webservice HTTP/RPC with JSON response payloads. +""" + + +from rpclib.application import Application +from rpclib.protocol.http import HttpRpc +from rpclib.protocol.json import JsonObject + +import mutalyzer +from mutalyzer.services import rpc + + +# HTTP/RPC application. +application = Application([rpc.MutalyzerService], tns=mutalyzer.SOAP_NAMESPACE, + in_protocol=HttpRpc(), out_protocol=JsonObject(skip_depth=2), + name='Mutalyzer') diff --git a/mutalyzer/webservice.py b/mutalyzer/services/rpc.py similarity index 83% rename from mutalyzer/webservice.py rename to mutalyzer/services/rpc.py index 71c3447b6991e488ca54ed4f866383b231674aea..1cb4a7ac79c17a26b2bdc1449426c15ba687372f 100644 --- a/mutalyzer/webservice.py +++ b/mutalyzer/services/rpc.py @@ -1,8 +1,6 @@ """ -Mutalyzer webservices. +Mutalyzer RPC services. -@todo: Do we really use namespaces correctly? -@todo: For some reason, the server exposes its location including ?wsdl. @todo: More thourough input checking. The @soap decorator does not do any kind of strictness checks on the input. For example, in transcriptInfo, the build argument must really be present. (Hint: @@ -10,23 +8,11 @@ Mutalyzer webservices. """ -# WSGI applications should never print anything to stdout. We redirect to -# stderr, but eventually Mutalyzer should be fixed to never just 'print' -# anything. -# http://code.google.com/p/modwsgi/wiki/DebuggingTechniques -import sys -sys.stdout = sys.stderr - -# Log exceptions to stdout -import logging; logging.basicConfig() - -from soaplib.core import Application -from soaplib.core.service import soap -from soaplib.core.service import DefinitionBase -from soaplib.core.model.primitive import String, Integer, Boolean, DateTime -from soaplib.core.model.clazz import Array -from soaplib.core.model.exception import Fault -from soaplib.core.server import wsgi +from rpclib.decorator import srpc +from rpclib.service import ServiceBase +from rpclib.model.primitive import String, Integer, Boolean, DateTime +from rpclib.model.complex import Array +from rpclib.model.fault import Fault import os import socket from operator import itemgetter, attrgetter @@ -44,102 +30,106 @@ from mutalyzer import GenRecord from mutalyzer.models import * -class MutalyzerService(DefinitionBase): +def _checkBuild(L, build) : """ - Mutalyzer webservices. + Check if the build is supported (hg18 or hg19). - These methods are made public via a SOAP interface. + Returns: + - Nothing (but raises an EARG exception). + + @arg L: An output object for logging. + @type L: object + @arg build: The human genome build name that needs to be checked. + @type build: string """ - def __init__(self, environ=None): - super(MutalyzerService, self).__init__(environ) - #__init__ - def __checkBuild(self, L, build) : - """ - Check if the build is supported (hg18 or hg19). + if not build in config.get('dbNames'): + L.addMessage(__file__, 4, "EARG", "EARG %s" % build) + raise Fault("EARG", + "The build argument (%s) was not a valid " \ + "build name." % build) + #if +#_checkBuild - Returns: - - Nothing (but raises an EARG exception). - @arg L: An output object for logging. - @type L: object - @arg build: The human genome build name that needs to be checked. - @type build: string - """ +def _checkChrom(L, D, chrom) : + """ + Check if the chromosome is in our database. - if not build in config.get('dbNames'): - L.addMessage(__file__, 4, "EARG", "EARG %s" % build) - raise Fault("EARG", - "The build argument (%s) was not a valid " \ - "build name." % build) - #if - #__checkBuild + Returns: + - Nothing (but raises an EARG exception). - def __checkChrom(self, L, D, chrom) : - """ - Check if the chromosome is in our database. + @arg L: An output object for logging. + @type L: object + @arg D: A handle to the database. + @type D: object + @arg chrom: The name of the chromosome. + @type chrom: string + """ - Returns: - - Nothing (but raises an EARG exception). + if not D.isChrom(chrom) : + L.addMessage(__file__, 4, "EARG", "EARG %s" % chrom) + raise Fault("EARG", + "The chrom argument (%s) was not a valid " \ + "chromosome name." % chrom) + #if +#_checkChrom - @arg L: An output object for logging. - @type L: object - @arg D: A handle to the database. - @type D: object - @arg chrom: The name of the chromosome. - @type chrom: string - """ - if not D.isChrom(chrom) : - L.addMessage(__file__, 4, "EARG", "EARG %s" % chrom) - raise Fault("EARG", - "The chrom argument (%s) was not a valid " \ - "chromosome name." % chrom) - #if - #__checkChrom +def _checkPos(L, pos) : + """ + Check if the position is valid. + + Returns: + - Nothing (but raises an ERANGE exception). - def __checkPos(self, L, pos) : - """ - Check if the position is valid. + @arg L: An output object for logging. + @type L: object + @arg pos: The position. + @type pos: integer + """ - Returns: - - Nothing (but raises an ERANGE exception). + if pos < 1 : + L.addMessage(__file__, 4, "ERANGE", "ERANGE %i" % pos) + raise Fault("ERANGE", + "The pos argument (%i) is out of range." % pos) + #if +#_checkPos - @arg L: An output object for logging. - @type L: object - @arg pos: The position. - @type pos: integer - """ - if pos < 1 : - L.addMessage(__file__, 4, "ERANGE", "ERANGE %i" % pos) - raise Fault("ERANGE", - "The pos argument (%i) is out of range." % pos) - #if - #__checkPos +def _checkVariant(L, variant) : + """ + Check if a variant is provided. - def __checkVariant(self, L, variant) : - """ - Check if a variant is provided. + Returns: + - Nothing (but raises an EARG exception). - Returns: - - Nothing (but raises an EARG exception). + @arg L: An output object for logging. + @type L: object + @arg variant: The variant. + @type variant: string + """ - @arg L: An output object for logging. - @type L: object - @arg variant: The variant. - @type variant: string - """ + if not variant : + L.addMessage(__file__, 4, "EARG", "EARG no variant") + raise Fault("EARG", "The variant argument is not provided.") + #if +#_checkVariant - if not variant : - L.addMessage(__file__, 4, "EARG", "EARG no variant") - raise Fault("EARG", "The variant argument is not provided.") - #if - #__checkVariant - @soap(Mandatory.String, Mandatory.String, Mandatory.Integer, Boolean, +class MutalyzerService(ServiceBase): + """ + Mutalyzer webservices. + + These methods are made public via a SOAP interface. + """ + def __init__(self, environ=None): + super(MutalyzerService, self).__init__(environ) + #__init__ + + @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer, Boolean, _returns = Array(Mandatory.String)) - def getTranscripts(self, build, chrom, pos, versions=False) : + def getTranscripts(build, chrom, pos, versions=False) : """ Get all the transcripts that overlap with a chromosomal position. @@ -167,11 +157,11 @@ class MutalyzerService(DefinitionBase): "Received request getTranscripts(%s %s %s %s)" % (build, chrom, pos, versions)) - self.__checkBuild(L, build) + _checkBuild(L, build) D = Db.Mapping(build) - self.__checkChrom(L, D, chrom) - self.__checkPos(L, pos) + _checkChrom(L, D, chrom) + _checkPos(L, pos) ret = D.get_Transcripts(chrom, pos, pos, True) @@ -192,8 +182,8 @@ class MutalyzerService(DefinitionBase): return ret #getTranscripts - @soap(Mandatory.String, Mandatory.String, _returns = Array(Mandatory.String)) - def getTranscriptsByGeneName(self, build, name): + @srpc(Mandatory.String, Mandatory.String, _returns = Array(Mandatory.String)) + def getTranscriptsByGeneName(build, name): """ Todo: documentation. """ @@ -203,7 +193,7 @@ class MutalyzerService(DefinitionBase): "Received request getTranscriptsByGene(%s %s)" % (build, name)) - self.__checkBuild(L, build) + _checkBuild(L, build) D = Db.Mapping(build) ret = D.get_TranscriptsByGeneName(name) @@ -221,9 +211,9 @@ class MutalyzerService(DefinitionBase): return [] #getTranscriptsByGene - @soap(Mandatory.String, Mandatory.String, Mandatory.Integer, + @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer, Mandatory.Integer, Mandatory.Integer, _returns = Array(Mandatory.String)) - def getTranscriptsRange(self, build, chrom, pos1, pos2, method) : + def getTranscriptsRange(build, chrom, pos1, pos2, method) : """ Get all the transcripts that overlap with a range on a chromosome. @@ -250,7 +240,7 @@ class MutalyzerService(DefinitionBase): chrom, pos1, pos2, method)) D = Db.Mapping(build) - self.__checkBuild(L, build) + _checkBuild(L, build) ret = D.get_Transcripts(chrom, pos1, pos2, method) @@ -265,9 +255,9 @@ class MutalyzerService(DefinitionBase): return ret #getTranscriptsRange - @soap(Mandatory.String, Mandatory.String, Mandatory.Integer, + @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer, Mandatory.Integer, Mandatory.Integer, _returns = Array(TranscriptMappingInfo)) - def getTranscriptsMapping(self, build, chrom, pos1, pos2, method): + def getTranscriptsMapping(build, chrom, pos1, pos2, method): """ Get all the transcripts and their info that overlap with a range on a chromosome. @@ -301,7 +291,7 @@ class MutalyzerService(DefinitionBase): 'getTranscriptsRange(%s %s %s %s %s)' % \ (build, chrom, pos1, pos2, method)) - self.__checkBuild(output, build) + _checkBuild(output, build) database = Db.Mapping(build) transcripts = [] @@ -332,8 +322,8 @@ class MutalyzerService(DefinitionBase): return transcripts #getTranscriptsMapping - @soap(Mandatory.String, Mandatory.String, _returns = Mandatory.String) - def getGeneName(self, build, accno) : + @srpc(Mandatory.String, Mandatory.String, _returns = Mandatory.String) + def getGeneName(build, accno) : """ Find the gene name associated with a transcript. @@ -351,7 +341,7 @@ class MutalyzerService(DefinitionBase): "Received request getGeneName(%s %s)" % (build, accno)) D = Db.Mapping(build) - self.__checkBuild(L, build) + _checkBuild(L, build) ret = D.get_GeneName(accno.split('.')[0]) @@ -363,9 +353,9 @@ class MutalyzerService(DefinitionBase): #getGeneName - @soap(Mandatory.String, Mandatory.String, Mandatory.String, + @srpc(Mandatory.String, Mandatory.String, Mandatory.String, Mandatory.String, _returns = Mapping) - def mappingInfo(self, LOVD_ver, build, accNo, variant) : + def mappingInfo(LOVD_ver, build, accNo, variant) : """ Search for an NM number in the MySQL database, if the version number matches, get the start and end positions in a variant and @@ -421,9 +411,9 @@ class MutalyzerService(DefinitionBase): return result #mappingInfo - @soap(Mandatory.String, Mandatory.String, Mandatory.String, + @srpc(Mandatory.String, Mandatory.String, Mandatory.String, _returns = Transcript) - def transcriptInfo(self, LOVD_ver, build, accNo) : + def transcriptInfo(LOVD_ver, build, accNo) : """ Search for an NM number in the MySQL database, if the version number matches, the transcription start and end and CDS end @@ -457,8 +447,8 @@ class MutalyzerService(DefinitionBase): return T #transcriptInfo - @soap(Mandatory.String, Mandatory.String, _returns = Mandatory.String) - def chromAccession(self, build, name) : + @srpc(Mandatory.String, Mandatory.String, _returns = Mandatory.String) + def chromAccession(build, name) : """ Get the accession number of a chromosome, given a name. @@ -476,8 +466,8 @@ class MutalyzerService(DefinitionBase): L.addMessage(__file__, -1, "INFO", "Received request chromAccession(%s %s)" % (build, name)) - self.__checkBuild(L, build) - self.__checkChrom(L, D, name) + _checkBuild(L, build) + _checkChrom(L, D, name) result = D.chromAcc(name) @@ -489,8 +479,8 @@ class MutalyzerService(DefinitionBase): return result #chromAccession - @soap(Mandatory.String, Mandatory.String, _returns = Mandatory.String) - def chromosomeName(self, build, accNo) : + @srpc(Mandatory.String, Mandatory.String, _returns = Mandatory.String) + def chromosomeName(build, accNo) : """ Get the name of a chromosome, given a chromosome accession number. @@ -508,8 +498,8 @@ class MutalyzerService(DefinitionBase): L.addMessage(__file__, -1, "INFO", "Received request chromName(%s %s)" % (build, accNo)) - self.__checkBuild(L, build) -# self.__checkChrom(L, D, name) + _checkBuild(L, build) +# self._checkChrom(L, D, name) result = D.chromName(accNo) @@ -521,8 +511,8 @@ class MutalyzerService(DefinitionBase): return result #chromosomeName - @soap(Mandatory.String, Mandatory.String, _returns = Mandatory.String) - def getchromName(self, build, acc) : + @srpc(Mandatory.String, Mandatory.String, _returns = Mandatory.String) + def getchromName(build, acc) : """ Get the chromosome name, given a transcript identifier (NM number). @@ -540,8 +530,8 @@ class MutalyzerService(DefinitionBase): L.addMessage(__file__, -1, "INFO", "Received request getchromName(%s %s)" % (build, acc)) - self.__checkBuild(L, build) -# self.__checkChrom(L, D, name) + _checkBuild(L, build) +# self._checkChrom(L, D, name) result = D.get_chromName(acc) @@ -553,8 +543,8 @@ class MutalyzerService(DefinitionBase): return result #chromosomeName - @soap(Mandatory.String, Mandatory.String, String, _returns = Array(Mandatory.String)) - def numberConversion(self, build, variant, gene=None): + @srpc(Mandatory.String, Mandatory.String, String, _returns = Array(Mandatory.String)) + def numberConversion(build, variant, gene=None): """ Converts I{c.} to I{g.} notation or vice versa @@ -591,8 +581,8 @@ class MutalyzerService(DefinitionBase): return result #numberConversion - @soap(Mandatory.String, _returns = CheckSyntaxOutput) - def checkSyntax(self, variant): + @srpc(Mandatory.String, _returns = CheckSyntaxOutput) + def checkSyntax(variant): """ Checks the syntax of a variant. @@ -611,7 +601,7 @@ class MutalyzerService(DefinitionBase): result = CheckSyntaxOutput() - self.__checkVariant(output, variant) + _checkVariant(output, variant) grammar = Grammar(output) parsetree = grammar.parse(variant) @@ -630,8 +620,8 @@ class MutalyzerService(DefinitionBase): return result #checkSyntax - @soap(Mandatory.String, _returns = MutalyzerOutput) - def runMutalyzer(self, variant) : + @srpc(Mandatory.String, _returns = MutalyzerOutput) + def runMutalyzer(variant) : """ Run the Mutalyzer name checker. @@ -687,7 +677,7 @@ class MutalyzerService(DefinitionBase): result.molecule = O.getIndexedOutput('molecule', 0) # We force the results to strings here, because some results - # may be of type Bio.Seq.Seq which soaplib doesn't like. + # may be of type Bio.Seq.Seq which rpclib doesn't like. # # todo: We might have to also do this elsewhere. @@ -735,8 +725,8 @@ class MutalyzerService(DefinitionBase): return result #runMutalyzer - @soap(Mandatory.String, Mandatory.String, _returns = TranscriptNameInfo) - def getGeneAndTranscript(self, genomicReference, transcriptReference) : + @srpc(Mandatory.String, Mandatory.String, _returns = TranscriptNameInfo) + def getGeneAndTranscript(genomicReference, transcriptReference) : """ Todo: documentation. """ @@ -768,8 +758,8 @@ class MutalyzerService(DefinitionBase): return ret #getGeneAndTranscript - @soap(Mandatory.String, String, _returns = Array(TranscriptInfo)) - def getTranscriptsAndInfo(self, genomicReference, geneName=None): + @srpc(Mandatory.String, String, _returns = Array(TranscriptInfo)) + def getTranscriptsAndInfo(genomicReference, geneName=None): """ Given a genomic reference, return all its transcripts with their transcription/cds start/end sites and exons. @@ -816,7 +806,7 @@ class MutalyzerService(DefinitionBase): D = Db.Cache() O.addMessage(__file__, -1, "INFO", - "Received request getTranscriptsAndInfo(%s)" % genomicReference) + "Received request getTranscriptsAndInfo(%s, %s)" % (genomicReference, geneName)) retriever = Retriever.GenBankRetriever(O, D) record = retriever.loadrecord(genomicReference) @@ -909,25 +899,25 @@ class MutalyzerService(DefinitionBase): return transcripts #getTranscriptsAndInfo - @soap(Mandatory.String, _returns = Mandatory.String) - def upLoadGenBankLocalFile(self, content) : + @srpc(Mandatory.String, _returns = Mandatory.String) + def upLoadGenBankLocalFile(content) : """ Not implemented yet. """ raise Fault('ENOTIMPLEMENTED', 'Not implemented yet') #upLoadGenBankLocalFile - @soap(Mandatory.String, _returns = Mandatory.String) - def upLoadGenBankRemoteFile(self, url) : + @srpc(Mandatory.String, _returns = Mandatory.String) + def upLoadGenBankRemoteFile(url) : """ Not implemented yet. """ raise Fault('ENOTIMPLEMENTED', 'Not implemented yet') #upLoadGenBankRemoteFile - @soap(Mandatory.String, Mandatory.String, Mandatory.Integer, + @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer, Mandatory.Integer, _returns = Mandatory.String) - def sliceChromosomeByGene(self, geneSymbol, organism, upStream, + def sliceChromosomeByGene(geneSymbol, organism, upStream, downStream) : """ Todo: documentation, error handling, argument checking, tests. @@ -955,9 +945,9 @@ class MutalyzerService(DefinitionBase): return UD #sliceChromosomeByGene - @soap(Mandatory.String, Mandatory.Integer, Mandatory.Integer, + @srpc(Mandatory.String, Mandatory.Integer, Mandatory.Integer, Mandatory.Integer, _returns = Mandatory.String) - def sliceChromosome(self, chromAccNo, start, end, orientation) : + def sliceChromosome(chromAccNo, start, end, orientation) : """ Todo: documentation, error handling, argument checking, tests. @@ -982,8 +972,8 @@ class MutalyzerService(DefinitionBase): return UD #sliceChromosome - @soap(_returns = InfoOutput) - def info(self): + @srpc(_returns = InfoOutput) + def info(): """ Gives some static application information, such as the current running version. @@ -1020,8 +1010,8 @@ class MutalyzerService(DefinitionBase): return result #info - @soap(_returns = Mandatory.String) - def ping(self): + @srpc(_returns = Mandatory.String) + def ping(): """ Simple function to test the interface. @@ -1031,8 +1021,8 @@ class MutalyzerService(DefinitionBase): return 'pong' #ping - @soap(DateTime, _returns = Array(CacheEntry)) - def getCache(self, created_since=None): + @srpc(DateTime, _returns = Array(CacheEntry)) + def getCache(created_since=None): """ Get a list of entries from the local cache created since given date. @@ -1063,8 +1053,8 @@ class MutalyzerService(DefinitionBase): return map(cache_entry_to_soap, cache) #getCache - @soap(Mandatory.String, _returns = Array(Mandatory.String)) - def getdbSNPDescriptions(self, rs_id): + @srpc(Mandatory.String, _returns = Array(Mandatory.String)) + def getdbSNPDescriptions(rs_id): """ Lookup HGVS descriptions for a dbSNP rs identifier. @@ -1095,13 +1085,3 @@ class MutalyzerService(DefinitionBase): return descriptions #getdbSNPDescriptions #MutalyzerService - - -# WSGI application for use with e.g. Apache/mod_wsgi -soap_application = Application([MutalyzerService], mutalyzer.SOAP_NAMESPACE, - 'Mutalyzer') -# Note: We would like to create the wsgi.Application instance only in the -# bin/mutalyzer-webservice.wsgi script, but unfortunately this breaks the -# get_wsdl method of soap_application which we use to generate API -# documentation in website.py. -application = wsgi.Application(soap_application) diff --git a/mutalyzer/services/soap.py b/mutalyzer/services/soap.py new file mode 100644 index 0000000000000000000000000000000000000000..31bb4e67d99376e8504c26af1a694dce1f19976d --- /dev/null +++ b/mutalyzer/services/soap.py @@ -0,0 +1,25 @@ +""" +Mutalyzer SOAP/1.1 webservice. +""" + + +from rpclib.application import Application +from rpclib.protocol.soap import Soap11 +from rpclib.server.wsgi import WsgiApplication + +import mutalyzer +from mutalyzer.services import rpc + + +# SOAP/1.1 application. +application = Application([rpc.MutalyzerService], tns=mutalyzer.SOAP_NAMESPACE, + in_protocol=Soap11(), out_protocol=Soap11(), + name='Mutalyzer') + + +# Below we define WSGI applications for use with e.g. Apache/mod_wsgi. +# Note: We would like to create the wsgi.Application instance only in the +# bin/mutalyzer-webservice.wsgi script, but unfortunately this breaks the +# get_interface_document method of rpclib which we use to generate API +# documentation in website.py. +wsgi_application = WsgiApplication(application) diff --git a/mutalyzer/templates/webservdoc.html b/mutalyzer/templates/webservdoc.html deleted file mode 100644 index 03a27cdf264533d4a954fca8e293f63a96198974..0000000000000000000000000000000000000000 --- a/mutalyzer/templates/webservdoc.html +++ /dev/null @@ -1,26 +0,0 @@ -<html> - <head> - <title></title> - </head> - <body> - <div metal:define-macro="content"> - <script type="text/javascript"> - function setHeight() { - parent.document.getElementById('docframe').height = - document['body'].scrollHeight * 32; - } - </script> - <center> - <h3>Webservices documentation</h3> - </center> - <iframe - id = "docframe" - src = "documentation" - width="100%" - frameborder = "0" - scrolling = "no" - onload = "setHeight();"> - </iframe> - </div> - </body> -</html> diff --git a/mutalyzer/templates/webservices.html b/mutalyzer/templates/webservices.html index f1a7463d7191a3c913ef89ab5d148e81747953a9..c9dbd3040e54ba6496e2780586343f0098b63225 100644 --- a/mutalyzer/templates/webservices.html +++ b/mutalyzer/templates/webservices.html @@ -5,14 +5,19 @@ <body> <div metal:define-macro="content"> <center> - <h3>Webservices page</h3> + <h3>Webservices</h3> </center> <br> - Most Mutalyzer functionality is available trough a SOAP webservice. + Most Mutalyzer functionality is programmatically available trough two + interfaces: a SOAP webservice and a HTTP/RPC+JSON webservice. + <br> + <h3>SOAP webservice</h3> A <a href="services/?wsdl">WSDL description</a> is available - for easy generation of client programs in many languages. - <h3>Example clients</h3> - The following are some example client programs for the webservice. They + for easy generation of client programs in many languages. See the + <a href = "soap-api">annotated API</a> for detailed documentation. + <br> + <br> + The following are some example client programs for SOAP webservice. They use the <a href="documentation#op.checkSyntax">checkSyntax</a> method to determine if a variant description adheres to the <span class="helper" title="Human Genome Variation Society standard variant nomenclature"> @@ -32,9 +37,17 @@ Here is an example that could be used for <a href="download/textmining.py">text mining</a> on a <a href="downloads/textmining_sample.txt">sample</a> input file. - <h3>Documentation</h3> - Also see the <a href = "documentation">documentation</a> page for a full - description of the webservice. + <h3>HTTP/RPC+JSON webservice</h3> + The HTTP/RPC+JSON webservice is experimental and currently undocumented. + It can be called using HTTP GET requests on <code tal:content = "structure string:${location}${serviceJsonLocation}/method?param=value"></code> where <code>method</code> is the name of the method to be called and method parameters are expected as <code>param=value</code> query string parameters. Responses are JSON-encoded. + <br> + <br> + Example: <a tal:attributes="href string:${location}${serviceJsonLocation}/checkSyntax?variant=AB026906.1:c.274del" tal:content="structure string:checkSyntax?variant=AB026906.1:c.274del"></a> + <br> + <br> + For now, you can work from this example using the above mentioned + <a href="soap-api">annotated SOAP API</a>, since the HTTP/RPC+JSON + webservice mirrors the functionality of the SOAP webservice. </div> </body> </html> diff --git a/mutalyzer/website.py b/mutalyzer/website.py index 2c956243546e6475d8ede439cd7f9516b8edb84e..15f5dcf41e477dc1e9bc04317e2e83e83979f9da 100644 --- a/mutalyzer/website.py +++ b/mutalyzer/website.py @@ -3,7 +3,8 @@ General Mutalyzer website interface. """ -WEBSERVICE_LOCATION = '/services' +SERVICE_SOAP_LOCATION = '/services' +SERVICE_JSON_LOCATION = '/json' WSDL_VIEWER = 'templates/wsdl-viewer.xsl' GENOME_BROWSER_URL = 'http://genome.ucsc.edu/cgi-bin/hgTracks?db=hg19&position={chromosome}:{start}-{stop}&hgt.customText={bed_file}' @@ -28,12 +29,13 @@ from lxml import etree from cStringIO import StringIO from simpletal import simpleTALES from simpletal import simpleTAL +from rpclib.interface.wsdl import Wsdl11 import mutalyzer from mutalyzer import util from mutalyzer import config from mutalyzer.grammar import Grammar -from mutalyzer import webservice +from mutalyzer.services import soap from mutalyzer import variantchecker from mutalyzer.output import Output from mutalyzer.mapping import Converter @@ -59,7 +61,6 @@ urls = ( '/(disclaimer)', 'Static', '/(nameGenerator)', 'Static', '/(webservices)', 'Static', - '/(webservdoc)', 'Static', '/checkForward', 'CheckForward', '/check', 'Check', '/bed', 'Bed', @@ -71,7 +72,7 @@ urls = ( '/batch([a-zA-Z]+)?', 'BatchChecker', '/progress', 'BatchProgress', '/Results_(\d+)\.txt', 'BatchResult', - '/documentation', 'Documentation', + '/soap-api', 'SoapApi', '/Variant_info', 'VariantInfo', '/getGS', 'GetGS', '/download/([a-zA-Z-]+\.(?:py|cs|php|rb))', 'Download', @@ -134,6 +135,8 @@ class render_tal: context.addGlobal('interactive', not standalone) + context.addGlobal('location', web.ctx.homedomain + web.ctx.homepath) + for name, value in self.globals.items(): context.addGlobal(name, value) @@ -177,7 +180,10 @@ render = render_tal(os.path.join(mutalyzer.package_root(), 'templates'), 'releaseDate' : mutalyzer.__date__, 'release' : mutalyzer.RELEASE, 'copyrightYears' : mutalyzer.COPYRIGHT_YEARS, - 'contactEmail' : config.get('email')}) + 'contactEmail' : config.get('email'), + 'serviceSoapLocation' : SERVICE_SOAP_LOCATION, + 'serviceJsonLocation' : SERVICE_JSON_LOCATION +}) # web.py application app = web.application(urls, globals(), autoreload = False) @@ -1415,7 +1421,7 @@ class Uploader: #Uploader -class Documentation: +class SoapApi: """ SOAP webservice documentation. """ @@ -1442,8 +1448,10 @@ class Documentation: @todo: Use configuration value for .xsl location. @todo: Cache this transformation. """ - url = web.ctx.homedomain + web.ctx.homepath + WEBSERVICE_LOCATION - wsdl_handle = StringIO(webservice.soap_application.get_wsdl(url)) + url = web.ctx.homedomain + web.ctx.homepath + SERVICE_SOAP_LOCATION + wsdl = Wsdl11(soap.application.interface) + wsdl.build_interface_document(url) + wsdl_handle = StringIO(wsdl.get_interface_document()) xsl_handle = open(os.path.join(mutalyzer.package_root(), WSDL_VIEWER), 'r') wsdl_doc = etree.parse(wsdl_handle) @@ -1453,7 +1461,7 @@ class Documentation: web.header('Content-Type', 'text/html') return str(transform(wsdl_doc)) #GET -#Documentation +#SoapApi class Static: diff --git a/setup.py b/setup.py index 2899ae3d0673eb0b2cfae85305df8e294270a744..e27929607de526dee671fcdb23b557ef72283f3c 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,8 @@ setup( 'bin/mutalyzer-batchd', 'bin/mutalyzer-cache-sync', 'bin/mutalyzer-mapping-update', - 'bin/mutalyzer-webservice.wsgi', + 'bin/mutalyzer-soap-service.wsgi', + 'bin/mutalyzer-json-service.wsgi', 'bin/mutalyzer-website.wsgi'], zip_safe=False ) diff --git a/tests/test_services_json.py b/tests/test_services_json.py new file mode 100644 index 0000000000000000000000000000000000000000..6055a185aee5447062c14f7050b5f3ad907b4f76 --- /dev/null +++ b/tests/test_services_json.py @@ -0,0 +1,68 @@ +""" +Tests for the SOAP interface to Mutalyzer. +""" + + +from urllib import urlencode +import simplejson as json +import requests +from nose.tools import * +import mutalyzer + + +SERVICE_ROOT = 'http://localhost/mutalyzer/json/' + + +def call(method, **kwargs): + """ + Do a HTTP/RPC request and decode the JSON response. + """ + r = requests.get(SERVICE_ROOT + method + '?' + urlencode(kwargs)) + return json.loads(r.content) + + +class TestServicesJson(): + """ + Test the Mutalyzer HTTP/RPC+JSON interface. + """ + def test_checksyntax_valid(self): + """ + Running checkSyntax with a valid variant name should return True. + """ + r = call('checkSyntax', variant='AB026906.1:c.274G>T') + assert_equal(r['CheckSyntaxOutput']['valid'], True) + + def test_checksyntax_invalid(self): + """ + Running checkSyntax with an invalid variant name should return False + and give at least one error message. + """ + r = call('checkSyntax', variant='0:abcd') + assert_equal(r['CheckSyntaxOutput']['valid'], False) + assert len(r['CheckSyntaxOutput']['messages']['SoapMessage']) >= 1 + + def test_checksyntax_empty(self): + """ + Running checkSyntax with no variant name should raise exception. + """ + r = call('checkSyntax') + assert r['Fault'] + + def test_transcriptinfo_valid(self): + """ + Running transcriptInfo with valid arguments should get us a Transcript + object. + """ + r = call('transcriptInfo', LOVD_ver='123', build='hg19', + accNo='NM_002001.2') + assert_equal(r['Transcript']['trans_start'], -99) + assert_equal(r['Transcript']['trans_stop'], 1066) + assert_equal(r['Transcript']['CDS_stop'], 774) + + def test_info(self): + """ + Running the info method should give us some version information. + """ + r = call('info') + assert_equal(type(r['InfoOutput']['versionParts']['string']), list) + assert_equal(r['InfoOutput']['version'], mutalyzer.__version__) diff --git a/tests/test_webservice.py b/tests/test_services_soap.py similarity index 97% rename from tests/test_webservice.py rename to tests/test_services_soap.py index 07e96ba983f5d1f9785c73c5f8996363bf6763aa..0fdbec9a7be4227c6976adcd9ab0d6407d37a3b8 100644 --- a/tests/test_webservice.py +++ b/tests/test_services_soap.py @@ -26,7 +26,7 @@ logging.basicConfig(level=logging.INFO) for logger in ('suds.metrics', 'suds.wsdl', 'suds.xsd.schema', 'suds.xsd.sxbasic', 'suds.xsd.sxbase', 'suds.xsd.query', 'suds.transport.http', 'suds.xsd.deplist', 'suds.mx.core', - 'suds.mx.literal', 'suds.resolver'): + 'suds.mx.literal', 'suds.resolver', 'suds.client'): logging.getLogger(logger).setLevel(logging.ERROR) @@ -46,11 +46,10 @@ class TestWSDL(): assert 'name="Mutalyzer"' in wsdl -class TestWebservice(): +class TestServicesSoap(): """ Test the Mutalyzer SOAP interface. """ - def setUp(self): """ Initialize webservice entrypoint. @@ -367,23 +366,23 @@ class TestWebservice(): r = self.client.service.runMutalyzer('NG_012772:g.18964del') assert_equal(r.errors, 0) assert_equal(r.referenceId, 'NG_012772') - assert_equal(r.sourceId, 'NG_012772.1') + assert_equal(r.sourceId, 'NG_012772.3') assert_equal(r.sourceAccession, 'NG_012772') - assert_equal(r.sourceVersion, '1') - assert_equal(r.sourceGi, '256574794') + assert_equal(r.sourceVersion, '3') + assert_equal(r.sourceGi, '388428999') assert_equal(r.molecule, 'g') def test_runmutalyzer_reference_info_ng_version(self): """ Get reference info for an NG variant with version. """ - r = self.client.service.runMutalyzer('NG_012772:g.18964del') + r = self.client.service.runMutalyzer('NG_012772.3:g.18964del') assert_equal(r.errors, 0) - assert_equal(r.referenceId, 'NG_012772') - assert_equal(r.sourceId, 'NG_012772.1') + assert_equal(r.referenceId, 'NG_012772.3') + assert_equal(r.sourceId, 'NG_012772.3') assert_equal(r.sourceAccession, 'NG_012772') - assert_equal(r.sourceVersion, '1') - assert_equal(r.sourceGi, '256574794') + assert_equal(r.sourceVersion, '3') + assert_equal(r.sourceGi, '388428999') assert_equal(r.molecule, 'g') def test_runmutalyzer_reference_info_gi(self): diff --git a/tests/test_website.py b/tests/test_website.py index 898074d01698008ebd9e99b9e5be28d1f0a342c4..7589230cd9fc7fc31413397e92c8acc034db6c86 100644 --- a/tests/test_website.py +++ b/tests/test_website.py @@ -630,11 +630,11 @@ facilisi.""" assert_equal(r.content_type, 'text/plain') r.mustcontain('NM_003002.1:c.3_4insG') - def test_soap_documentation(self): + def test_annotated_soap_api(self): """ Test the SOAP documentation generated from the WSDL. """ - r = self.app.get('/documentation') + r = self.app.get('/soap-api') assert_equal(r.content_type, 'text/html') r.mustcontain('Web Service: Mutalyzer')