MythTV master
wsdl.cpp
Go to the documentation of this file.
1
2// Program Name: wsdl.cpp
3// Created : Jan. 19, 2010
4//
5// Purpose : WSDL XML Generation Class
6//
7// Copyright (c) 2010 David Blain <dblain@mythtv.org>
8//
9// Licensed under the GPL v2 or later, see LICENSE for details
10//
12
13#include "wsdl.h"
14#include "xsd.h"
15
16#include "servicehost.h"
17
19//
21
22bool Wsdl::GetWSDL( HTTPRequest *pRequest )
23{
24 m_typesToInclude.clear();
25
26 if (!pRequest->m_mapParams.contains( "raw" ))
27 {
28 appendChild( createProcessingInstruction( "xml-stylesheet",
29 R"(type="text/xsl" href="/xslt/service.xslt")" ));
30 }
31
32 QDomElement oNode;
33
34 QString sClassName = m_pServiceHost->GetServiceMetaObject().className();
35 QString sTargetNamespace = "http://mythtv.org";
36
37 m_oRoot = createElementNS( "http://schemas.xmlsoap.org/wsdl/", "definitions");
38
39 m_oRoot.setAttribute( "targetNamespace", sTargetNamespace );
40
41 m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
42 m_oRoot.setAttribute( "xmlns:xs" , "http://www.w3.org/2001/XMLSchema" );
43 m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
44 m_oRoot.setAttribute( "xmlns:tns" , sTargetNamespace );
45 m_oRoot.setAttribute( "xmlns:wsaw", "http://www.w3.org/2006/05/addressing/wsdl" );
46
47 m_oRoot.setAttribute( "name", QString( "%1Services" ).arg( sClassName ) );
48
49 m_oTypes = createElement( "types" );
51 m_oPortType = createElement( "portType" );
52 m_oBindings = createElement( "binding" );
53 m_oService = createElement( "service" );
54
55 appendChild( m_oRoot );
56 m_oRoot.appendChild( m_oTypes );
57 m_oRoot.appendChild( m_oPortType );
58 m_oRoot.appendChild( m_oBindings );
59 m_oRoot.appendChild( m_oService );
60
61 m_oPortType.setAttribute( "name", sClassName );
62
63 // ----------------------------------------------------------------------
64
65 QDomElement oImportNode = createElement( "xs:schema" );
66 oImportNode.setAttribute( "targetNamespace" , "http://MythTV.org/Imports" );
67
68 m_oTypes.appendChild( oImportNode );
69
70 // ----------------------------------------------------------------------
71
72 oNode = createElement( "xs:schema" );
73 oNode.setAttribute( "targetNamespace" , sTargetNamespace );
74 oNode.setAttribute( "elementFormDefault", "qualified" );
75
76 m_oTypes.appendChild( oNode );
77 m_oTypes = oNode;
78
79 // ----------------------------------------------------------------------
80 // Add Bindings...
81 // ----------------------------------------------------------------------
82
83 m_oBindings.setAttribute( "name", QString("BasicHttpBinding_%1").arg( sClassName ));
84 m_oBindings.setAttribute( "type", QString( "tns:%1" ).arg( sClassName ));
85
86 oNode = createElement( "soap:binding" );
87 //oNode.setAttribute( "style" , "document" );
88 oNode.setAttribute( "transport", "http://schemas.xmlsoap.org/soap/http" );
89
90 m_oBindings.appendChild( oNode );
91
92 // ----------------------------------------------------------------------
93 // Loop for each method in class
94 // ----------------------------------------------------------------------
95
96
97 for (const auto& oInfo : m_pServiceHost->GetMethods() )
98 {
99 QString sRequestTypeName = oInfo.m_sName;
100 QString sResponseTypeName = oInfo.m_sName + "Response";
101
102 QString sInputMsgName = QString( "%1_%2_InputMessage" )
103 .arg( sClassName,
104 oInfo.m_sName );
105 QString sOutputMsgName = QString( "%1_%2_OutputMessage" )
106 .arg( sClassName,
107 oInfo.m_sName );
108
109 // ------------------------------------------------------------------
110 // Create PortType Operations
111 // ------------------------------------------------------------------
112
113 QDomElement oOp = createElement( "operation" );
114
115 oOp.setAttribute( "name", oInfo.m_sName );
116
117 // ------------------------------------------------------------------
118 // Add Operation Description
119 // ------------------------------------------------------------------
120
121 QString sDescription;
122
123 if ( oInfo.m_eRequestType == RequestTypePost )
124 sDescription = "POST ";
125 else
126 sDescription = "GET ";
127
129 "description" );
130
131 oNode = createElement( "documentation" );
132 oNode.appendChild( createTextNode( sDescription ));
133
134 oOp.appendChild( oNode );
135
136 // ------------------------------------------------------------------
137 // Create PortType input element
138 // ------------------------------------------------------------------
139
140 oNode = createElement( "input" );
141 oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3" )
142 .arg( sTargetNamespace,
143 sClassName,
144 oInfo.m_sName ));
145 oNode.setAttribute( "message" , "tns:" + sInputMsgName );
146
147 oOp.appendChild( oNode );
148
149 // ------------------------------------------------------------------
150 // Create PortType output element
151 // ------------------------------------------------------------------
152
153 oNode = createElement( "output" );
154 oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3Response" )
155 .arg( sTargetNamespace,
156 sClassName,
157 oInfo.m_sName ));
158 oNode.setAttribute( "message", "tns:" + sOutputMsgName );
159
160 oOp.appendChild( oNode );
161
162 m_oPortType.appendChild( oOp );
163
164 // ------------------------------------------------------------------
165 // Create Messages
166 // ------------------------------------------------------------------
167
168 QDomElement oMsg = CreateMessage( sInputMsgName, sRequestTypeName );
169
170 m_oRoot.insertAfter( oMsg, m_oLastMsg );
171 m_oLastMsg = oMsg;
172
173 // ------------------------------------------------------------------
174 // Create Request Type
175 // ------------------------------------------------------------------
176
177 m_oTypes.appendChild( CreateMethodType( oInfo, sRequestTypeName ) );
178
179 // ------------------------------------------------------------------
180 // Create Response message
181 // ------------------------------------------------------------------
182
183 oMsg = CreateMessage( sOutputMsgName, sResponseTypeName );
184
185 m_oRoot.insertAfter( oMsg, m_oLastMsg );
186 m_oLastMsg = oMsg;
187
188 // ------------------------------------------------------------------
189 // Create Response Type
190 // ------------------------------------------------------------------
191
192 m_oTypes.appendChild( CreateMethodType( oInfo, sResponseTypeName, true ) );
193
194 // ------------------------------------------------------------------
195 // Create Soap Binding Operations
196 // ------------------------------------------------------------------
197
198 m_oBindings.appendChild( CreateBindingOperation( oInfo, sClassName ));
199 }
200
201 // ----------------------------------------------------------------------
202 // Add Service Details
203 // ----------------------------------------------------------------------
204
205 QString sServiceName = QString( "%1Services" ).arg( sClassName );
206
207 m_oService.setAttribute( "name", sServiceName );
208
209 // ------------------------------------------------------------------
210 // Add Service Description
211 // ------------------------------------------------------------------
212
213 QString sDescription = "Interface Version " +
215 "version" );
216
217 sDescription += " - " + ReadClassInfo( &m_pServiceHost->GetServiceMetaObject(),
218 "description" );
219
220 oNode = createElement( "documentation" );
221 oNode.appendChild( createTextNode( sDescription ));
222
223 m_oService.appendChild( oNode );
224
225 // ------------------------------------------------------------------
226 // Add Service Port
227 // ------------------------------------------------------------------
228
229 QDomElement oPort = createElement( "port" );
230
231 oPort.setAttribute( "name" , QString("BasicHttpBinding_%1" ).arg( sClassName ));
232 oPort.setAttribute( "binding", QString("tns:BasicHttpBinding_%1").arg( sClassName ));
233
234 oNode = createElement( "soap:address" );
235 oNode.setAttribute( "location", "http://" +
236 pRequest->GetLastHeader( "host" ) + "/" +
238
239 oPort.appendChild( oNode );
240 m_oService.appendChild( oPort );
241
242 // ----------------------------------------------------------------------
243 //
244 // ----------------------------------------------------------------------
245
246 if (m_typesToInclude.count() > 0)
247 {
248 // ------------------------------------------------------------------
249 // Create all needed includes
250 //
251 // <xs:import schemaLocation="<path to dependant schema" namespace="http://mythtv.org"/>
252 // ------------------------------------------------------------------
253
254 QString sBaseUri = "http://" + pRequest->GetLastHeader( "host" ) + pRequest->m_sBaseUrl + "/xsd";
255
256 QMap<QString, TypeInfo>::const_iterator it2 = m_typesToInclude.constBegin();
257 while( it2 != m_typesToInclude.constEnd())
258 {
259 QDomElement oIncNode = createElement( "xs:import" );
260 QString sType = it2.key();
261 TypeInfo info = it2.value();
262
263 sType.remove( "DTC::" );
264
265 QString sValue = QString( "%1?%2=%3" ).arg( sBaseUri,
266 info.sAttrName,
267 sType);
268
269 if (!info.sContentType.isEmpty())
270 sValue += "&name=" + info.sContentType;
271
272 oIncNode.setAttribute( "schemaLocation", sValue );
273 oIncNode.setAttribute( "namespace" , "http://mythtv.org" );
274
275 oImportNode.appendChild( oIncNode );
276 ++it2;
277 }
278 }
279
280
281 // ----------------------------------------------------------------------
282 // Return wsdl doc to caller
283 // ----------------------------------------------------------------------
284
285 QTextStream os( &(pRequest->m_response) );
286
288
289 save( os, 0 );
290 return true;
291}
292
294//
296
297QDomElement Wsdl::CreateBindingOperation( const MethodInfo &oInfo,
298 const QString &sClassName)
299{
300 // ------------------------------------------------------------------
301 // Create PortType Operation
302 // ------------------------------------------------------------------
303
304 QDomElement oOp = createElement( "operation" );
305
306 oOp.setAttribute( "name", oInfo.m_sName );
307
308 QDomElement oNode = createElement( "soap:operation" );
309 oNode.setAttribute( "soapAction", QString( "http://mythtv.org/%1/%2" )
310 .arg( sClassName,
311 oInfo.m_sName ));
312 oNode.setAttribute( "style" , "document" );
313
314 oOp.appendChild( oNode );
315
316 // ------------------------------------------------------------------
317 // Create PortType input element
318 // ------------------------------------------------------------------
319
320 QDomElement oDirection = createElement( "input" );
321 oNode = createElement( "soap:body" );
322 oNode.setAttribute( "use", "literal" );
323
324 oDirection.appendChild( oNode );
325 oOp.appendChild( oDirection );
326
327 if (QString::compare( oInfo.m_oMethod.typeName(), "void", Qt::CaseInsensitive ) != 0)
328 {
329 // Create output element
330
331 oDirection = createElement( "output" );
332
333 oNode = createElement( "soap:body" );
334 oNode.setAttribute( "use", "literal" );
335
336 oDirection.appendChild( oNode );
337 oOp.appendChild( oDirection );
338
339 }
340
341 return oOp;
342}
343
345//
347
348QDomElement Wsdl::CreateMessage( const QString& sMsgName,
349 const QString& sTypeName )
350{
351 QDomElement oMsg = createElement( "message" );
352
353 oMsg.setAttribute( "name", sMsgName );
354
355 QDomElement oNode = createElement( "part" );
356
357 oNode.setAttribute( "name" , "parameters" );
358 oNode.setAttribute( "element", "tns:" + sTypeName );
359
360 oMsg.appendChild( oNode );
361
362 return oMsg;
363}
364
366//
368
369QDomElement Wsdl::CreateMethodType( const MethodInfo &oInfo,
370 QString sTypeName,
371 bool bReturnType /* = false */)
372{
373 QDomElement oElementNode = createElement( "xs:element" );
374
375 oElementNode.setAttribute( "name", sTypeName );
376
377 QDomElement oTypeNode = createElement( "xs:complexType" );
378 QDomElement oSeqNode = createElement( "xs:sequence" );
379
380 oElementNode.appendChild( oTypeNode );
381 oTypeNode .appendChild( oSeqNode );
382
383 // ------------------------------------------------------------------
384 // Create message for parameters
385 // ------------------------------------------------------------------
386
387 if (bReturnType)
388 {
389 QDomElement oNode = createElement( "xs:element" );
390
391 QString sType = oInfo.m_oMethod.typeName();
392
393 sTypeName.remove( "Response" );
394
395 oNode.setAttribute( "minOccurs", 0 );
396 oNode.setAttribute( "name" , sTypeName + "Result" );
397 oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
398
399 bool bCustomType = IsCustomType( sType );
400
401 sType = Xsd::ConvertTypeToXSD( sType, bCustomType );
402
403 QString sPrefix = "xs:";
404
405 if (bCustomType)
406 {
407 sPrefix = "tns:";
408
409 sType = AddTypeInfo( sType );
410 }
411
412 oNode.setAttribute( "type", sPrefix + sType );
413
414 oSeqNode.appendChild( oNode );
415 }
416 else
417 {
418 QList<QByteArray> paramNames = oInfo.m_oMethod.parameterNames();
419 QList<QByteArray> paramTypes = oInfo.m_oMethod.parameterTypes();
420
421 for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ )
422 {
423 QString sName = paramNames[ nIdx ];
424 QString sParamType = paramTypes[ nIdx ];
425
426 bool bCustomType = IsCustomType( sParamType );
427
428 sParamType = Xsd::ConvertTypeToXSD( sParamType, bCustomType );
429
430 QString sPrefix = "xs:";
431
432 if (bCustomType)
433 {
434 sPrefix = "tns:";
435
436 sParamType = AddTypeInfo( sParamType );
437 }
438
439 QDomElement oNode = createElement( "xs:element" );
440
441 oNode.setAttribute( "minOccurs", 0 );
442 oNode.setAttribute( "name" , sName );
443 oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
444 oNode.setAttribute( "type" , sPrefix + sParamType );
445
446 oSeqNode.appendChild( oNode );
447 }
448 }
449
450 return oElementNode;
451}
452
454//
456
457bool Wsdl::IsCustomType( const QString &sTypeName )
458{
459#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
460 int id = QMetaType::type( sTypeName.toUtf8() );
461#else
462 int id = QMetaType::fromName( sTypeName.toUtf8() ).id();
463#endif
464
465 switch( id )
466 {
467 case QMetaType::QStringList:
468 case QMetaType::QVariantList:
469 case QMetaType::QVariantMap:
470 return true;
471
472 default:
473 // for now, treat QFileInfo as a string. Need to turn into MTOM later.
474 if (id == qMetaTypeId<QFileInfo>())
475 return false;
476 break;
477 }
478
479 return !(id < QMetaType::User);
480}
481
483//
485
486QString Wsdl::AddTypeInfo( QString sType )
487{
488 QString sCustomAttr = "type";
489
490 // if sParamType contains "::", then assume it's a enum
491 // (this needs to be looked at again for a better approach)
492
493 if (sType.contains( "::" ))
494 {
495 sType = sType.replace( "::", "." );
496 sCustomAttr = "enum";
497 }
498
499 TypeInfo info = { sCustomAttr, QString() };
500
501 m_typesToInclude.insert( sType, info );
502
503 return sType;
504}
505
507//
509
510QString Wsdl::ReadClassInfo( const QMetaObject *pMeta, const QString &sKey )
511{
512 int nIdx = -1;
513
514 if (pMeta)
515 nIdx = pMeta->indexOfClassInfo( sKey.toUtf8().constData() );
516
517 if (nIdx >=0)
518 return pMeta->classInfo( nIdx ).value();
519
520 return {};
521}
HttpResponseType m_eResponseType
Definition: httprequest.h:150
QString GetLastHeader(const QString &sType) const
QString m_sBaseUrl
Definition: httprequest.h:128
QStringMap m_mapParams
Definition: httprequest.h:132
QBuffer m_response
Definition: httprequest.h:158
QString m_sName
Definition: servicehost.h:40
QMetaMethod m_oMethod
Definition: servicehost.h:41
const MetaInfoMap & GetMethods()
Definition: servicehost.h:94
const QMetaObject & GetServiceMetaObject()
Definition: servicehost.h:93
virtual QString GetServiceControlURL()
Definition: servicehost.h:91
QString AddTypeInfo(QString sType)
Definition: wsdl.cpp:486
QDomElement CreateMessage(const QString &sMsgName, const QString &sTypeName)
Definition: wsdl.cpp:348
QMap< QString, TypeInfo > m_typesToInclude
Definition: wsdl.h:37
bool GetWSDL(HTTPRequest *pRequest)
Definition: wsdl.cpp:22
QDomElement m_oPortType
Definition: wsdl.h:42
QDomElement CreateMethodType(const MethodInfo &oInfo, QString sTypeName, bool bReturnType=false)
Definition: wsdl.cpp:369
static QString ReadClassInfo(const QMetaObject *pMeta, const QString &sKey)
Definition: wsdl.cpp:510
static bool IsCustomType(const QString &sTypeName)
Definition: wsdl.cpp:457
QDomElement m_oTypes
Definition: wsdl.h:40
ServiceHost * m_pServiceHost
Definition: wsdl.h:36
QDomElement m_oLastMsg
Definition: wsdl.h:41
QDomElement m_oRoot
Definition: wsdl.h:39
QDomElement CreateBindingOperation(const MethodInfo &oInfo, const QString &sClassName)
Definition: wsdl.cpp:297
QDomElement m_oBindings
Definition: wsdl.h:43
QDomElement m_oService
Definition: wsdl.h:44
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
Definition: xsd.cpp:871
@ ResponseTypeXML
Definition: httprequest.h:79
@ RequestTypePost
Definition: httprequest.h:51
dictionary info
Definition: azlyrics.py:7