Выполнила:
Мелякова Екатерина Валерьевна
Подпись______________
Руководитель:
старший преподаватель
Кетов Дмитрий Владимирович
Подпись______________
Санкт-Петербург 2016
СОДЕРЖАНИЕ
ВВЕДЕНИЕ .................................................................................................................. 3
3
1 ОБЗОР ПРЕДМЕТНОЙ ОБЛАСТИ
4
веб-службы разработчик клиента может получить с помощью реестра UDDI
(Universal Description, Discovery and Integration). [30]
Для RESTful веб-служб в настоящее время невозможно выделить один
общепринятый язык описания. Более того, зачастую разработчики
предоставляют только литературное описание веб-службы и параметров,
необходимых для разработки клиента. Для составления машиночитаемого
описания RESTful веб-службы в настоящее время используются такие языки
описания, как WSDL, упомянутый выше, WADL (Web Application Description
Language), RAML (RESTful API Modeling Language), OpenAPI Specification
(ранее называвшийся Swagger), Blueprint, Google Discovery Document, I/O Docs,
Hypermedia и др. [7, 8, 9, 14] Основные сведения о языках описания RESTful
веб-служб приведены в таблице 1.1. [6, 12, 13, 15, 19, 27, 28]
Таблица 1.1
Основные сведения о языках описания RESTful веб-служб
WSDL WADL OpenAPI Blueprint RAML
Дата первого
2000 2009 2011 2013 2013
релиза
Текущая версия 2.0 20090831 2.0 1A9 1.0
Формат
представления XML XML JSON Markdown YAML
данных
IBM,
Sun
Разработчик Microsoft, Reverb Apiary Mulesoft
Microsystems
Ariba
5
– Методы, используемые для доступа к данным ресурсам (GET,
POST, PUT и т.д.);
– Параметры, которые должны быть применены для использования
данных методов (Query, Template, HTTP Header и т.д.);
– Формат входящих и исходящих сообщений и представлений
данных (JSON Schema, XML Schema, Relax NG и т.д.);
– Коды статусов и сообщений об ошибках;
– Информацию обо всех вышеперечисленных элементах (их
описание) [10, 11, 16, 20, 21].
Однако основной сферой применения документов, составленных на
языке описания веб-службы, является использование их для генерации кода
клиентских приложений и настройки имеющихся клиентов. Наличие широких
возможностей по автоматической генерации кода с помощью готовых утилит
можно считать одним из ключевых факторов широкого применения веб-служб,
основанных на использовании протокола SOAP. Различные среды разработки
(Integrated Development Environment, IDE) позволяют генерировать как сам
WSDL-файл для уже разработанной веб-службы, так и код клиента веб-службы
на основе соответствующего WSDL-файла. [5] Был произведен обзор
имеющихся утилит для генерации кода на основе языков описания RESTful веб-
служб, результаты приведены в таблице 1.2. [15, 19, 22, 24, 25]
Таблица 1.2
Наличие генераторов кода на основе языков описания веб-служб
6
Продолжение таблицы 1.2
А 1 2 3 4
Генерация кода Java, PHP, Java, Java, Java,
клиента Ruby, Python, JavaScript, JavaScript, JavaScript,
C# Ruby, C# Go, Python, Scala,
PHP, Ruby Objective-C,
Android, C#,
Flash, Python,
PHP, Ruby,
Dart, Go, Perl
Автоматическая Java ASP.NET Java Java, Node.js
генерация (go, python)
файла описания
Несмотря на то, что WSDL версии 2.0 был дополнен поддержкой всех
запросов HTTP (версия 1.1 поддерживала только методы GET и POST), что
позиционируется как улучшенная поддержка REST [28], на практике он редко
применяется для описания RESTful веб-служб. В то же время WADL
изначально разрабатывался сотрудниками компании Sun Microsystems для
RESTful веб-служб. [26]
7
переменную в строке адреса, которая будет подставлена во время выполнения
запроса – за счет значения template у атрибута style. [23, 27]
<resources base="http://www.thomas-bayer.com">
<resource path="/CUSTOMER/{CustomerId}">
<param name="CustomerId" style="template" type="xsd:int"/>
...
</resource>
</resources>
Каждый ресурс может содержать элемент method, который содержит
дочерний элемент request, описывающий запрос к веб-службе, и может
содержать элементы response, описывающие ответы веб-службы. Формат
запросов и ответов может быть определен с помощью элемента representation c
атрибутом mediaType.
<resource path="/namesincountry.groovy">
<method name="GET">
<request>
<param name="country" style="query" type="xs:string"/>
</request>
<response status="200">
<representation mediaType="application/xml/>
</response>
<response status="400">
<representation mediaType="application/xml/>
</response>
</method>
</resource>
8
<option value="all"/>
<option value="any"/>
<option value="phrase"/>
</param>
<param name="RespSize" style="query" type="xsd:string" repeating="true">
<option value="Small"/>
<option value="Medium"/>
<option value="Large"/>
<option value="Images"/>
</param>
</request>
<response status="200">
<representation mediaType="application/xml/>
</response>
</method>
10
* При необходимости генерации кода во время сборки Java проекта с
помощью Maven. Возможно использование напрямую без Maven.
** Выявлена несовместимость с браузером Mozilla Firefox.
Рассмотрим один из генераторов кода клиента на языке Java – wadl2java,
разработанный в рамках проекта Glassfish. [25]
12
Рисунок 1.3 – Веб-интерфейс кодогенератора Rest Describe & Compile
При генерации кода по фрагменту WADL, представленному выше, был
получен следующий фрагмент кода клиента.
class RestRequest:
def __init__(self, ): ...
def do_get_call(self, uri): ...
def do_post_call(self, uri, post_args): ...
class NameGroovy(RestRequest):
def __init__(self, name = None):
RestRequest.__init__(self, )
self.name = name
def prepare_params(self):
import urllib
params = {}
if self.name:
params['name'] = self.name
return urllib.urlencode(params)
def submit(self):
request_uri = 'http://www.thomas-bayer.com//restnames//name.groovy'
return self.do_get_call(request_uri + '?' + self.prepare_params())
13
Сгенерированный код представляет собой один суперкласс RestRequest,
содержащий функции do_get_call и do_post_call, ответственные за генерацию
запроса методами GET и POST соответственно. Код данного класса
генерируется для любых исходных файлов описания WADL, независимо от
того, какие методы поддерживает ресурс. Данная особенность объясняется
использованием шаблона для генерации кода.
Также генерируются подклассы, соответствующие элементам method.
Имя подкласса соответствует атрибуту path родительского элемента resource,
поэтому если у элемента resource несколько дочерних элементов method –
классы будут сгенерированы с одинаковым именем. Элементы param
используются в качестве аргументов конструктора класса, при этом
анализируется только наличие у параметра атрибута required.
Работа данной программы была проверена на имеющихся файлах
описания WADL, при этом 32 файла не были проанализированы из-за
использования тэгов, которые программа воспринимала как некорректные,
несмотря на то, что они относились к указанному в документе пространству
имен, и 2 файла были проанализированы, но не был сгенерирован код (вероятно
по причине того, что файлы были объемом более 10 тысяч строк). Результаты
проверки приведены в Приложении 2.
Таким образом, количество генераторов кода клиента веб-службы на
основе файла описания WADL невелико. Обзор существующих разработок
показал, что генераторы кода для языка Java более проработаны, нежели для
других языков. Рассмотренный кодогенератор для Python обладает рядом
недостатков:
– ограничение использования тэгов в файле описания;
– недостаточная гибкость ввиду использования шаблона (генерация
кода, который не будет использован);
– ограниченная поддержка методов, отличных от GET и POST;
– генерация классов с одинаковым именем;
– не учитываются свойства параметров.
14
2 ЦЕЛЬ И ЗАДАЧИ РАБОТЫ
15
– Обработка свойств параметров запроса к службе (обязательность,
повторяемость, наличие вариантов значений и др.).
4. Реализация записи полученного кода в указанный пользователем
файл;
5. Проведение испытаний работы кодогенератора;
6. Анализ результатов, полученных в ходе испытаний.
16
3 РАЗРАБОТКА КОДОГЕНЕРАТОРА ДЛЯ PYTHON
17
неверно указан путь к файлу WADL), выдается сообщение об ошибке и справка
об используемых параметрах.
19
3.2 Реализация синтаксического анализа файла WADL
20
Обработка элемента функцией
processing node
Нет
У resourse или resource_type естьт
дочерние элементы method?
Да
Нет
Есть дочерние
элементы resource?
Да
21
Обработка элемента функцией
processing class
24
Обработка элемента функцией
processing method
Да
Да
Есть дочерние
элементы response?
25
WADL), после чего производится поиск дочернего элемента request (согласно
документации WADL, элемент method может содержать только один дочерний
элемент request и ноль и более элементов response). [27] У элемента request
анализируются дочерние элементы param и присваивается значение атрибута
request_params.
Параметры запроса могут быть повторяемыми, обязательными, иметь
варианты значений, значение по умолчанию или фиксированное значение. Для
хранения и обработки информации о свойствах параметров была выбрана
структура словаря, в которой генерируемому имени параметра соответствует
список свойств в следующем порядке:
[Original_name, Required=True/False, Repeating=True/False,
Default=Value/None, Fixed=Value/None, Option_values=[value1, value2]/None]
26
элементов representation. У найденных дочерних элементов representation
анализируется наличие атрибута mediaType. Кроме того, может присутствовать
атрибут href, ссылающийся на другой объект representation с соответствующим
значением атрибута id, и значение атрибута mediaType может быть присвоено
там. Значения атрибутов mediaType собираются в список types, и, если он не
окажется пустым, то формат представления ответа от службы рассматривается
как необязательный параметр, с фиксированными вариантами значений и
значением по умолчанию, и к словарю request_params добавляется элемент с
ключом «_format_», при этом полученный список types добавляется в качестве
вариантов значений параметра:
{‘_format_’ : [‘_format_’, False, False, 'application/json', None,
['application/json', 'application/vnd.sun.wadl+xml']]}
28
4 ИСПЫТАНИЯ И ТЕСТИРОВАНИЕ
29
– указание в качестве входного или выходного файлов с запретом на
чтение и запись.
По результатам тестирования было выявлено возникновение
исключений TypeError, NameError и PermissionError, и была добавлена
обработка исключений посредством конструкций try – except.
Была проверена работа кодогенератора при подаче на вход
некорректного файла описания WADL. В данном случае возникает ошибка
синтаксического анализа – исключение xml.etree.ElementTree.ParseError. Была
добавлена обработка данного исключения с выдачей пользователю информации
о строке и позиции, на которой возникла ошибка синтаксического анализа.
Работа кодогенератора была проверена на 36 файлах WADL, полученных
из репозитория PayPal на сайте веб-сервиса для хостинга IT-проектов GitHub, а
также непосредственно с сайтов разработчиков веб-служб. Сбой работы
кодогенератора возник при обработке 2 файлов, которые были составлены с
синтаксическими ошибками (например, у некоторых элементов отсутствовали
закрывающие тэги). Результаты проверки приведены в Приложении 2.
30
Ресурс sqlrest располагается по относительному адресу /sqlrest и имеет
ряд дочерних ресурсов: CUSTOMER, PRODUCT, INVOICE, ITEM,
предоставляющих информацию о заказчиках, продуктах, счетах и заказах
соответственно (с помощью метода GET). Можно добавлять новые записи,
отправляя соответствующие запросы с корректными данными c помощью
метода POST. Кроме того, можно получать и удалять о конкретных заказчиках,
продуктах, счетах и заказах по относительным адресам вида /resource/{id} с
помощью методов GET и DELETE. Фрагмент WADL документа,
соответствующего ресурсу CUSTOMER и дочернему CustomerId приведен
ниже.
<resource path="/sqlrest">
<resource path="/CUSTOMER">
<method name="POST">
<request>
<representation mediaType="application/xml"/>
</request>
<response status="201"/>
</method>
<resource path="/{CustomerId}">
<param name="CustomerId" style="template" type="xs:int"/>
<method name="GET">
<request/>
<response status="200"/>
</method>
<method name="DELETE">
<request/>
<response status="200"/>
</method>
...
</resource>
31
def SEI_sqlrest_CUSTOMER_POST (self, _data_):
data = _data_
response = interact(self.connection, self.baseurl, 'POST', self.requrl,
data)
return response
class SEI_sqlrest_CUSTOMER_CustomerId:
def __init__(self, CustomerId):
self.baseurl = 'http://www.thomas-bayer.com'
self.requrl = '/sqlrest/CUSTOMER/{CustomerId}'
self.param_names = ['CustomerId']
self.param_values = [CustomerId]
for i in range(len(self.param_names)):
self.requrl = re.sub('{'+self.param_names[i]+'}', str(self.param_values[i]),
self.requrl, flags=re.I)
self.connection = setconnection(self.baseurl)
32
print(result.SEI_sqlrest_CUSTOMER_CustomerId_DELETE())
34
if (set(param_value).issubset(query_params_with_options[param])):
for p in param_value:
if (param == '_format_'): header = {'Accept':p}
else: params += query_param_names[param]+'='+str(p)+'&'
else:
print('Parameter '+param+' has incorrect value')
exit(1)
else:
for p in param_value:
params += query_param_names[param]+'='+str(p)+'&'
requrl = self.requrl if (params == '') else self.requrl
+'?'+parse.quote(params[:-1], safe='=&')
response = interact(self.connection, self.baseurl, 'GET', requrl,
headers=header)
return response
35
4.3 Тестирование сгенерированного кода клиента с веб-службой AppDirect
36
<param name="max" style="query" type="xs:long"/>
<param name="domainAvailable" style="query" type="xs:boolean"/>
</request>
<response status="204">
</response>
</method>
</resource>
</resource>
37
указывают, что для некоторых целей она может не подходить или быть
избыточной, поэтому для возможности написания собственных клиентских
модулей предоставляется файл описания WADL.
Однако предоставляемый файл описания WADL отличается тем, что,
несмотря на объем в почти 50 тысяч строк, не содержит информации о
конкретных адресах расположения ресурсов. Элемент resources указывает URI
веб-службы, а единственный дочерний элемент resource содержит пустой
относительный путь, то есть является корневым. Однако файл содержит
элементы resource_type, которые описывают типы ресурсов и содержат
дочерние элементы method, характеризующие поддерживаемые данным типом
ресурсов методы.
Таким образом, если добавить к данному файлу WADL описание
необходимых элементов resource с указанием соответствующих им типов
(элементов resource_type), то можно сгенерировать код классов для отправки
запросов к данным ресурсам.
В результате анализа элементов resource_type был реализован поиск
дочерних элементов method не только у элементов resource, но и, при наличии у
элемента resource ссылки на тип ресурса, у соответствующего элемента
resource_type. Кроме того, было обнаружено, что методы могут носить
одинаковые значения атрибута name (то есть названия, например ресурс может
поддерживать несколько разных методов GET), но иметь разные id, в связи с
чем была изменена процедура присвоения имени для функции,
соответствующей методу.
Был составлен фрагмент документа WADL для описания ресурсов,
относящихся к данным о зарегистрированных пользователях и их проектах.
<wadl:resource path="/people" type="#people"/>
<wadl:resource path="/~{Person}" type="#person">
<wadl:param name="Person" style="template" type="xs:string"/>
<wadl:resource path="/{Project}/+git/{Repository}/+ref/{Path}" type="#git_ref">
<wadl:param name="Project" style="template" type="xs:string"/>
<wadl:param name="Repository" style="template" type="xs:string"/>
<wadl:param name="Path" style="template" type="xs:string"/>
38
</wadl:resource>
</wadl:resource>
</wadl:resources>
39
Таким образом, проведенное тестирование на трех работающих веб-
службах выявило несколько нестандартных ситуаций, потребовавших внести
изменения в генерируемый код. После того, как особенности ресурсов,
используемых для тестирования, были учтены, генерируемый код позволил
подключаться, отправлять корректные запросы и получать ожидаемые
корректные ответы от веб-служб.
40
ЗАКЛЮЧЕНИЕ
41
В третьем разделе была разработана общая схема работы
кодогенератора, а также разработаны основные функции, обеспечивающие
синтаксический анализ входного файла на языке WADL, выделение и обработку
необходимой информации для установления соединения, отправки запроса и
получения ответа от веб-службы в необходимом поддерживаемом формате.
В четвертом разделе отражено проведенное тестирование
кодогенератора, в том числе испытания вариантов некорректного запуска
самого кодогенератора или подачи на вход некорректных данных, а также
результаты тестирования генерации кода для корректных файлов WADL. Кроме
того, отражены результаты тестирования самого сгенерированного кода для
трех реально-работающих сторонних веб-служб.
По результатам проверки работоспособности кодогенератора, а также
генерируемого кода, можно сделать вывод о том, что поставленные задачи по
разработке кодогенератора, а также требования к генерируемому коду были
выполнены.
42
СПИСОК ИСПОЛЬЗОВАННОЙ ЛИТЕРАТУРЫ
43
12. OpenAPI Specification [Электронный документ]. –
(http://swagger.io/specification). Проверено 18.05.2016.
13. OpenAPI Specification [Электронный документ]. –
(https://en.wikipedia.org/wiki/OpenAPI_Specification). Проверено
18.05.2016.
14. Overview of RESTful API Description Languages [Электронный документ].
– (https://en.wikipedia.org/wiki/Overview_of_RESTful_API_Description_
Languages). Проверено 18.05.2016.
15. RAML Specification [Электронный документ]. – (http://raml.org/spec.html).
Проверено 18.05.2016.
16. Representational state transfer [Электронный документ]. –
(https://en.wikipedia.org/wiki/Representational_state_transfer). Проверено
18.05.2016.
17. REST Client Generator [Электронный документ]. –
(http://catalogue.fiware.org/enablers/rest-client-generator/documentation).
Проверено 18.05.2016.
18. REST Describe & Compile Documentation [Электронный документ]. –
(http://tomayac.de/rest-describe/latest/doc). Проверено 18.05.2016.
19. RESTful API Modeling Language [Электронный документ]. –
(https://en.wikipedia.org/wiki/RAML_(software)). Проверено 18.05.2016.
20. Roger L. Costello. Building Web Services the REST Way [Электронный
документ]. – (http://www.xfront.com/REST-Web-Services.html). Проверено
18.05.2016.
21. Roy Fielding. Representational State Transfer (REST) [Электронный
документ]. – (http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_
style.htm). Проверено 18.05.2016.
22. Swagger-Codegen [Электронный документ]. – (https://github.com/swagger-
api/swagger-codegen). Проверено 18.05.2016.
44
23. Understanding WADL Documents [Электронный документ]. –
(http://docs.oracle.com/cd/E41633_01/pt853pbh1/eng/pt/tibr/concept_Understa
ndingWADLDocuments-7b7fd7.html). Проверено 18.05.2016.
24. wadl2java command line tool [Электронный документ]. –
(http://cxf.apache.org/docs/jaxrs-services-description.html#JAXRSServices
Description-wadl2javacommandlinetool). Проверено 18.05.2016.
25. wadl2java Tool Documentation [Электронный документ]. –
(https://wadl.java.net/wadl2java.html). Проверено 18.05.2016.
26. Web Application Description Language [Электронный документ]. –
(https://en.wikipedia.org/wiki/Web_Application_Description_Language).
Проверено 18.05.2016.
27. Web Application Description Language [Электронный документ]. –
(https://www.w3.org/Submission/wadl). Проверено 18.05.2016.
28. Web Services Description Language (WSDL) Version 2.0 [Электронный
документ]. – (http://www.w3.org/TR/wsdl20). Проверено 18.05.2016.
29. Web Services Glossary [Электронный документ]. –
(https://www.w3.org/TR/2004/NOTE-ws-gloss-20040211). Проверено
18.05.2016.
30. Web services protocol stack [Электронный документ]. –
(https://en.wikipedia.org/wiki/Web_services_protocol_stack). Проверено
18.05.2016.
31. Writing a REST Client in Python [Электронный документ]. –
(https://developer.atlassian.com/fecrudev/integration-tutorials/writing-a-rest-
client-in-python). Проверено 18.05.2016.
32. XML vulnerabilities [Электронный документ]. –
(https://docs.python.org/3.4/library/xml.html#xml-vulnerabilities). Проверено
18.05.2016.
45
Приложение 1
#!/usr/bin/python3
import argparse
import os
import re
import sys
from defusedxml import ElementTree
#from xml.etree import ElementTree
#If you are sure in the safety of processing WADL and do not want to install
defusedxml, uncomment the line above and comment the one two lines above.
Please, keep in mind the XML vulnerabilities in xml.etree module.
generated_names = []
global outputfile
46
output = generating_string('class '+self.name+':')
if (self.doc != ''): output += generating_string('\'\'\''+self.doc[:-
2]+'\'\'\'', 1)
if (self.param_names != []): temp = ', '+', '.join(self.param_names)
else: temp = ''
output += generating_string('def __init__(self'+temp+'):', 1)
output += generating_string('self.baseurl = \''+self.baseurl+'\'', 2)
output += generating_string('self.requrl = \''+self.requrl+'\'', 2)
if (temp != ''):
temp = '[\''+'\', \''.join(self.param_names)+'\']'
output += generating_string('self.addr_param_names = '+temp, 2)
output += generating_string('self.addr_param_values =
'+re.sub('\'','',temp), 2)
output += generating_string('for i in
range(len(self.addr_param_names)):\n\t\t\tself.requrl =
re.sub(\'{\'+self.addr_param_names[i]+\'}\', str(self.addr_param_values[i]),
self.requrl, flags=re.I)', 2)
output += generating_string('self.connection =
setconnection(self.baseurl)\n', 2)
return output
47
valuesdict_line = 'query_param_values = {'
optiondict_line += 'query_params_with_options = {'
repeatlist_line += 'query_params_repeating = ['
for i in [0, 1]:
for param in self.request_params.keys():
if (i == 0 and self.request_params[param][1] == False or i == 1 and
self.request_params[param][1] == True): continue
if (namesdict_line[-1:] != '{'): namesdict_line += ', '
namesdict_line += '\''+param+'\' : \''+self.request_params[param][0]
+'\''
if (self.request_params[param][4] != None):
params_line += '\''+self.request_params[param][0]+'='+
self.request_params[param][4]+'&\''
else:
if (self.request_params[param][1] == True):
if (valuesdict_line[-1:] != '{'): valuesdict_line += ', '
valuesdict_line += '\''+param+'\' : '+param
def_line += ', '+param
else:
if (self.request_params[param][3] != None):
if (valuesdict_line[-1:] != '{'): valuesdict_line += ', '
valuesdict_line += '\''+param+'\' : '+param
def_line += ', '+param+'=\''+self.request_params[param][3]+'\''
else:
if (valuesdict_line[-1:] != '{'): valuesdict_line += ', '
def_line += ', '+param+'=None'
valuesdict_line += '\''+param+'\' : '+param
if (self.request_params[param][5] != None):
if (optiondict_line[-1:] != '{'): optiondict_line += ', '
optiondict_line += '\''+param+'\': [\''+'\',
\''.join(self.request_params[param][5])+'\']'
if (self.request_params[param][2] == True and
self.request_params[param][4] == None):
if (repeatlist_line[-1:] != '['): repeatlist_line += ', '
repeatlist_line += '\''+param+'\''
namesdict_line += '}'
valuesdict_line += '}'
optiondict_line += '}'
repeatlist_line += ']'
if (params_line == 'params = '): params_line += '\'\''
else: response_line += ', self.requrl'
title = self.id if (self.id != '') else self.name
48
output = generating_string('def '+classname+'_'+title+'
(self'+def_line+'):',1)
if (self.doc != ''): output += generating_string('\'\'\''+self.doc[:-
2]+'\'\'\'', 2)
if (self.name in ['POST', 'PATCH', 'PUT']):
output += generating_string('data = _data_',2)
response_line += ', data'
if (self.request_params != {}):
output += generating_string('global param_value',2)
output += generating_string(namesdict_line,2)
output += generating_string(valuesdict_line,2)
output += generating_string(repeatlist_line,2)
if (optiondict_line != 'query_params_with_options = {}'): output +=
generating_string(optiondict_line+'\n\t\theader = \'\'',2)
output += generating_string(params_line,2)
output += generating_string('for param in query_param_values.keys():',2)
output += generating_string('exec(\'global param_value\\r\\nparam_value =
\'+param)',3)
output += generating_string('if (param_value is None): continue',3)
output += generating_string('if (param not in query_params_repeating and
isinstance(param_value, list)):',3)
output += generating_string('print(\'Parameter \'+param+\' cannot take
multiple values.\')',4)
output += generating_string('exit(1)',4)
output += generating_string('if (not isinstance(param_value, list)):
param_value = [param_value]',3)
if (optiondict_line != 'query_params_with_options = {}'):
response_line += ', headers=header'
output += generating_string('if (param in
query_params_with_options.keys()):',3)
output += generating_string('if
(set(param_value).issubset(query_params_with_options[param])):',4)
output += generating_string('for p in param_value:',5)
output += generating_string('if (param == \'_format_\'): header =
{\'Accept\':p}',6)
output += generating_string('else: params +=
query_param_names[param]+\'=\'+str(p)+\'&\'',6)
output += generating_string('else:',4)
output += generating_string('print(\'Parameter \'+param+\' has incorrect
value\')',5)
output += generating_string('exit(1)',5)
output += generating_string('else:',3)
49
t=1
else: t=0
output += generating_string('for p in param_value:',3+t)
output += generating_string('params += query_param_names[param]+\'=\'+
str(p)+\'&\'',4+t)
output += generating_string('requrl = self.requrl if (params == \'\') else
self.requrl +\'?\'+parse.quote(params[:-1], safe=\'=&\')',2)
response_line += ')'
output += generating_string(response_line,2)
output += generating_string('return response\n',2)
return output
50
newresourceclass.doc += newresourceclass.doc+d.text.lstrip()+'\n'
params = resource.findall(namespace+'param')
if(params != []):
for i in params:
newresourceclass.param_names.append(i.attrib['name'])
outputfile.write(newresourceclass.generate_class_code())
methods = resource.findall(namespace+'method')
if (typeobj != None): methods.extend(typeobj.findall(namespace+'method'))
if (methods != []):
for m in methods:
if 'name' in m.attrib:
processing_method(root, m, newresourceclass.name)
elif 'href' in m.attrib:
commonmethod = root.find('.//*[@id=\''+m.attrib['href'].split('#',
1)[1]+'\']')
if 'name' in commonmethod.attrib:
processing_method(root, commonmethod, newresourceclass.name)
51
else: newmethodclass.request_params[normname].append(False)
if('default' in p.attrib):
newmethodclass.request_params[normname].append(p.attrib['default'])
else: newmethodclass.request_params[normname].append(None)
if('fixed' in p.attrib):
newmethodclass.request_params[normname].append(p.attrib['fixed'])
else: newmethodclass.request_params[normname].append(None)
options = p.findall(namespace+'option')
if(options != []):
optionlist = []
for o in options:
optionlist.append(o.attrib['value'])
newmethodclass.request_params[normname].append(optionlist)
else: newmethodclass.request_params[normname].append(None)
responses = element.findall(namespace+'response')
if(responses != []):
for r in responses:
mediatypes = r.findall(namespace+'representation')
types = []
if (mediatypes != []):
for t in mediatypes:
if ('mediaType' in t.attrib):
types.append(t.attrib['mediaType'])
if ('href' in t.attrib):
typeobj =
root.find('.//*[@id=\''+t.attrib['href'].split('#',1)[1]+'\']')
if ('mediaType' in typeobj.attrib):
types.append(typeobj.attrib['mediaType'])
if (types != []):
newmethodclass.request_params['_format_'] =
['_format_',False,False,types[0],None]
newmethodclass.request_params['_format_'].append(types)
outputfile.write(newmethodclass.generate_method_code(parentname))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-i", dest='input', help="path to input WADL file")
parser.add_argument("-o", dest='output', help="path to output file")
arguments = parser.parse_args()
if arguments.input is None:
print ('No input file path provided.')
52
exit(1)
if not os.path.exists(arguments.input):
print ('Please, check provided input file path.')
exit(1)
if arguments.output is None:
print ('No output file path provided.')
exit(1)
try:
common = open('common.py')
except:
print('Unable to read file common.py')
exit(1)
try:
outputfile = open(arguments.output, 'w')
outputfile.write('\'\'\'This file is generated and the author of
codegenerator disclaims all warranties with regard to this software\'\'\'\n')
outputfile.write('import re\n')
outputfile.write(common.read())
common.close()
except:
print('Unable to write to output file')
exit(1)
try:
inputfile = open(arguments.input, encoding='utf-8', mode='r')
data = inputfile.read()
except UnicodeDecodeError:
try:
inputfile.close()
inputfile = open(arguments.input, mode='r')
data = inputfile.read()
except:
print('Unable to read input file')
exit(1)
try:
root = ElementTree.fromstring(data)
inputfile.close()
except ElementTree.ParseError:
type, value, traceback = sys.exc_info()
print('Please, check to syntax of the provided WADL file: string
'+str(value.position[0])+ ' position '+str(value.position[1]))
exit(1)
namespace = root.tag[0:-11]
53
resources_elements = root.findall(namespace+'resources')
if resources_elements != []:
for base in resources_elements:
processing_node(root, base, namespace, '', base.attrib['base'])
outputfile.close()
exit()
else:
print('No resources elements found. Check provided WADL file.')
exit(1)
54
Приложение 2
class SEI_integration_v1_dummy_domain_suggestions:
def __init__(self):
self.baseurl = 'https://www.appdirect.com/api'
self.requrl = '/integration/v1/dummy/domain/suggestions'
self.connection = setconnection(self.baseurl)
56
query_param_names = {'p_domainAvailable' : 'domainAvailable', 'p_sleep' :
'sleep', 'p_currency' : 'currency', 'p_max' : 'max', 'p_q' : 'q'}
query_param_values = {'p_domainAvailable' : p_domainAvailable, 'p_sleep' :
p_sleep, 'p_currency' : p_currency, 'p_max' : p_max, 'p_q' : p_q}
query_params_repeating = []
params = ''
for param in query_param_values.keys():
exec('global param_value\r\nparam_value = '+param)
if (param_value is None): continue
if (param not in query_params_repeating and isinstance(param_value,
list)):
print('Parameter '+param+' cannot take multiple values.')
exit(1)
if (not isinstance(param_value, list)): param_value = [param_value]
for p in param_value:
params += query_param_names[param]+'='+str(p)+'&'
requrl = self.requrl if (params == '') else self.requrl
+'?'+parse.quote(params[:-1], safe='=&')
response = interact(self.connection, self.baseurl, 'GET', requrl)
return response
57
Приложение 4
class SEI_people:
def __init__(self):
self.baseurl = 'https://api.launchpad.net/1.0'
self.requrl = '/people'
self.connection = setconnection(self.baseurl)
58
query_param_names = {'p_text' : 'text', 'p_wsop' : 'ws.op', 'p_createdafter'
: 'created_after', 'p_createdbefore' : 'created_before', '_format_' :
'_format_'}
query_param_values = {'p_text' : p_text, 'p_createdafter' : p_createdafter,
'p_createdbefore' : p_createdbefore, '_format_' : _format_}
query_params_repeating = []
query_params_with_options = {'_format_': ['application/json']}
header = ''
params = 'ws.op=findPerson&'
for param in query_param_values.keys():
exec('global param_value\r\nparam_value = '+param)
if (param_value is None): continue
if (param not in query_params_repeating and isinstance(param_value,
list)):
print('Parameter '+param+' cannot take multiple values.')
exit(1)
if (not isinstance(param_value, list)): param_value = [param_value]
if (param in query_params_with_options.keys()):
if (set(param_value).issubset(query_params_with_options[param])):
for p in param_value:
if (param == '_format_'): header = {'Accept':p}
else: params += query_param_names[param]+'='+str(p)+'&'
else:
print('Parameter '+param+' has incorrect value')
exit(1)
else:
for p in param_value:
params += query_param_names[param]+'='+str(p)+'&'
requrl = self.requrl if (params == '') else self.requrl
+'?'+parse.quote(params[:-1], safe='=&')
response = interact(self.connection, self.baseurl, 'GET', requrl,
headers=header)
return response
59