MythTV master
mythwsdl.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 COPYING for details
10//
12
13#include <QFileInfo>
14
15#include "mythwsdl.h"
16
21
22
24//
26
27
29{
30 m_typesToInclude.clear();
31
32 if (!Request->m_queries.contains("raw"))
33 {
34 appendChild( createProcessingInstruction( "xml-stylesheet",
35 R"(type="text/xsl" href="/xslt/service.xslt")" ));
36 }
37
38 QDomElement oNode;
39
40 QString sClassName = m_pMetaService->m_name;
41 QString sTargetNamespace = "http://mythtv.org";
42
43 m_oRoot = createElementNS( "http://schemas.xmlsoap.org/wsdl/", "definitions");
44
45 m_oRoot.setAttribute( "targetNamespace", sTargetNamespace );
46
47 m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
48 m_oRoot.setAttribute( "xmlns:xs" , "http://www.w3.org/2001/XMLSchema" );
49 m_oRoot.setAttribute( "xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/" );
50 m_oRoot.setAttribute( "xmlns:tns" , sTargetNamespace );
51 m_oRoot.setAttribute( "xmlns:wsaw", "http://www.w3.org/2006/05/addressing/wsdl" );
52
53 m_oRoot.setAttribute( "name", QString( "%1Services" ).arg( sClassName ) );
54
55 m_oTypes = createElement( "types" );
57 m_oPortType = createElement( "portType" );
58 m_oBindings = createElement( "binding" );
59 m_oService = createElement( "service" );
60
61 appendChild( m_oRoot );
62 m_oRoot.appendChild( m_oTypes );
63 m_oRoot.appendChild( m_oPortType );
64 m_oRoot.appendChild( m_oBindings );
65 m_oRoot.appendChild( m_oService );
66
67 m_oPortType.setAttribute( "name", sClassName );
68
69 // ----------------------------------------------------------------------
70
71 QDomElement oImportNode = createElement( "xs:schema" );
72 oImportNode.setAttribute( "targetNamespace" , "http://MythTV.org/Imports" );
73
74 m_oTypes.appendChild( oImportNode );
75
76 // ----------------------------------------------------------------------
77
78 oNode = createElement( "xs:schema" );
79 oNode.setAttribute( "targetNamespace" , sTargetNamespace );
80 oNode.setAttribute( "elementFormDefault", "qualified" );
81
82 m_oTypes.appendChild( oNode );
83 m_oTypes = oNode;
84
85 // ----------------------------------------------------------------------
86 // Add Bindings...
87 // ----------------------------------------------------------------------
88
89 m_oBindings.setAttribute( "name", QString("BasicHttpBinding_%1").arg( sClassName ));
90 m_oBindings.setAttribute( "type", QString( "tns:%1" ).arg( sClassName ));
91
92 oNode = createElement( "soap:binding" );
93 oNode.setAttribute( "transport", "http://schemas.xmlsoap.org/soap/http" );
94
95 m_oBindings.appendChild( oNode );
96
97 // ----------------------------------------------------------------------
98 // Loop for each method in class
99 // ----------------------------------------------------------------------
100
101 for (auto & [path, handler] : m_pMetaService->m_slots)
102 {
103 QString sRequestTypeName = path;
104 QString sResponseTypeName = path + "Response";
105
106 QString sInputMsgName = QString( "%1_%2_InputMessage" )
107 .arg( sClassName,
108 path );
109 QString sOutputMsgName = QString( "%1_%2_OutputMessage" )
110 .arg( sClassName,
111 path );
112
113 // ------------------------------------------------------------------
114 // Create PortType Operations
115 // ------------------------------------------------------------------
116
117 QDomElement oOp = createElement( "operation" );
118
119 oOp.setAttribute( "name", path );
120
121 // ------------------------------------------------------------------
122 // Add Operation Description
123 // ------------------------------------------------------------------
124
125 QString sDescription;
126
127 if (handler->m_requestTypes & HTTPGet)
128 sDescription = "GET ";
129 else
130 sDescription = "POST ";
131
132 oNode = createElement( "documentation" );
133 oNode.appendChild( createTextNode( sDescription ));
134
135 oOp.appendChild( oNode );
136
137 // ------------------------------------------------------------------
138 // Create PortType input element
139 // ------------------------------------------------------------------
140
141 oNode = createElement( "input" );
142 oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3" )
143 .arg( sTargetNamespace,
144 sClassName,
145 path ));
146 oNode.setAttribute( "message" , "tns:" + sInputMsgName );
147
148 oOp.appendChild( oNode );
149
150 // ------------------------------------------------------------------
151 // Create PortType output element
152 // ------------------------------------------------------------------
153
154 oNode = createElement( "output" );
155 oNode.setAttribute( "wsaw:Action", QString( "%1/%2/%3Response" )
156 .arg( sTargetNamespace,
157 sClassName,
158 path ));
159 oNode.setAttribute( "message", "tns:" + sOutputMsgName );
160
161 oOp.appendChild( oNode );
162
163 m_oPortType.appendChild( oOp );
164
165 // ------------------------------------------------------------------
166 // Create Messages
167 // ------------------------------------------------------------------
168
169 QDomElement oMsg = CreateMessage( sInputMsgName, sRequestTypeName );
170
171 m_oRoot.insertAfter( oMsg, m_oLastMsg );
172 m_oLastMsg = oMsg;
173
174 // ------------------------------------------------------------------
175 // Create Request Type
176 // ------------------------------------------------------------------
177
178 m_oTypes.appendChild( CreateMethodType( handler, sRequestTypeName ) );
179
180 // ------------------------------------------------------------------
181 // Create Response message
182 // ------------------------------------------------------------------
183
184 oMsg = CreateMessage( sOutputMsgName, sResponseTypeName );
185
186 m_oRoot.insertAfter( oMsg, m_oLastMsg );
187 m_oLastMsg = oMsg;
188
189 // ------------------------------------------------------------------
190 // Create Response Type
191 // ------------------------------------------------------------------
192
193 m_oTypes.appendChild( CreateMethodType( handler, sResponseTypeName, true ) );
194
195 // ------------------------------------------------------------------
196 // Create Soap Binding Operations
197 // ------------------------------------------------------------------
198 m_oBindings.appendChild( CreateBindingOperation( path, handler, 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_pMetaService->m_meta,
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 Request->m_headers->value("host") + "/" +
238 oPort.appendChild( oNode );
239 m_oService.appendChild( oPort );
240
241 // ----------------------------------------------------------------------
242 //
243 // ----------------------------------------------------------------------
244 if (m_typesToInclude.count() > 0)
245 {
246 // ------------------------------------------------------------------
247 // Create all needed includes
248 //
249 // <xs:import schemaLocation="<path to dependant schema" namespace="http://mythtv.org"/>
250 // ------------------------------------------------------------------
251
252 QString sBaseUri = "http://" + Request->m_headers->value("host") +"/" + m_pMetaService->m_name + "/xsd";
253
254 QMap<QString, TypeInfo>::const_iterator it2 = m_typesToInclude.constBegin();
255 while( it2 != m_typesToInclude.constEnd())
256 {
257 QDomElement oIncNode = createElement( "xs:import" );
258 QString sType = it2.key();
259 TypeInfo info = it2.value();
260
261 // sType.remove( "DTC::" );
262 if (sType.startsWith("V2"))
263 sType.remove(0,2);
264 sType.remove( "*" );
265
266 QString sValue = QString( "%1?%2=%3" ).arg( sBaseUri,
267 info.sAttrName,
268 sType);
269
270 if (!info.sContentType.isEmpty())
271 sValue += "&name=" + info.sContentType;
272
273 oIncNode.setAttribute( "schemaLocation", sValue );
274 oIncNode.setAttribute( "namespace" , "http://mythtv.org" );
275
276 oImportNode.appendChild( oIncNode );
277 ++it2;
278 }
279 }
280
281 // ----------------------------------------------------------------------
282 // Return wsdl doc to caller
283 // ----------------------------------------------------------------------
284
285 // Create the XML result
286 auto data = MythHTTPData::Create(toByteArray());
287 data->m_mimeType = MythMimeDatabase::MimeTypeForName("application/xml");
288 data->m_cacheType = HTTPETag | HTTPShortLife;
290}
291
293//
295
296QDomElement MythWSDL::CreateBindingOperation( const QString& path,
297 const HTTPMethodPtr& handler,
298 const QString &sClassName)
299{
300 // ------------------------------------------------------------------
301 // Create PortType Operation
302 // ------------------------------------------------------------------
303
304 QDomElement oOp = createElement( "operation" );
305
306 oOp.setAttribute( "name", path );
307
308 QDomElement oNode = createElement( "soap:operation" );
309 oNode.setAttribute( "soapAction", QString( "http://mythtv.org/%1/%2" )
310 .arg( sClassName,
311 path ));
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 QT_VERSION < QT_VERSION_CHECK(6,0,0)
328 const char * typeName = QMetaType::typeName(handler->m_types[0]);
329#else
330 const char * typeName = QMetaType(handler->m_types[0]).name();
331#endif
332
333 if (QString::compare(typeName, "void", Qt::CaseInsensitive ) != 0)
334 {
335 // Create output element
336
337 oDirection = createElement( "output" );
338
339 oNode = createElement( "soap:body" );
340 oNode.setAttribute( "use", "literal" );
341
342 oDirection.appendChild( oNode );
343 oOp.appendChild( oDirection );
344
345 }
346
347 return oOp;
348}
349
351//
353
354QDomElement MythWSDL::CreateMessage( const QString& sMsgName,
355 const QString& sTypeName )
356{
357 QDomElement oMsg = createElement( "message" );
358
359 oMsg.setAttribute( "name", sMsgName );
360
361 QDomElement oNode = createElement( "part" );
362
363 oNode.setAttribute( "name" , "parameters" );
364 oNode.setAttribute( "element", "tns:" + sTypeName );
365
366 oMsg.appendChild( oNode );
367
368 return oMsg;
369}
370
372//
374
375QDomElement MythWSDL::CreateMethodType( const HTTPMethodPtr& handler,
376 QString sTypeName,
377 bool bReturnType /* = false */)
378{
379 QDomElement oElementNode = createElement( "xs:element" );
380
381 oElementNode.setAttribute( "name", sTypeName );
382
383 QDomElement oTypeNode = createElement( "xs:complexType" );
384 QDomElement oSeqNode = createElement( "xs:sequence" );
385
386 oElementNode.appendChild( oTypeNode );
387 oTypeNode .appendChild( oSeqNode );
388
389 // ------------------------------------------------------------------
390 // Create message for parameters
391 // ------------------------------------------------------------------
392
393 if (bReturnType)
394 {
395 QDomElement oNode = createElement( "xs:element" );
396
397 // QString sType = oInfo.m_oMethod.typeName();
398#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
399 const char * typeName = QMetaType::typeName(handler->m_types[0]);
400#else
401 const char * typeName = QMetaType(handler->m_types[0]).name();
402#endif
403
404 QString sType(typeName);
405 // if (sType.startsWith("V2"))
406 // sType.remove(0,2);
407 // sType.remove( "*" );
408
409 sTypeName.remove( "Response" );
410
411 oNode.setAttribute( "minOccurs", 0 );
412 oNode.setAttribute( "name" , sTypeName + "Result" );
413 oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
414
415 bool bCustomType = IsCustomType( sType );
416 sType = MythXSD::ConvertTypeToXSD( sType, bCustomType );
417 QString sPrefix = "xs:";
418
419 if (sType.startsWith("V2"))
420 {
421 sType.remove(0,2);
422 sType.remove( "*" );
423 }
424
425 if (bCustomType)
426 {
427 sPrefix = "tns:";
428
429 sType = AddTypeInfo( sType );
430 }
431
432 oNode.setAttribute( "type", sPrefix + sType );
433
434 oSeqNode.appendChild( oNode );
435 }
436 else
437 {
438
439 size_t typecount = std::min(handler->m_types.size(), static_cast<size_t>(100));
440
441 // Parameters
442 // Iterate over the method's parameters
443 size_t count = 1;
444 QList<QByteArray> paramNames;
445 QList<QByteArray> paramTypes;
446 for (count=1; count < typecount; count++)
447 {
448 auto name = handler->m_names[count];
449 paramNames.append(name.toUtf8());
450#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
451 const char * typeName = QMetaType::typeName(handler->m_types[count]);
452#else
453 const char * typeName = QMetaType(handler->m_types[count]).name();
454#endif
455 paramTypes.append(QByteArray(typeName));
456 }
457
458 for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ )
459 {
460 QString sName = paramNames[ nIdx ];
461 QString sParamType = paramTypes[ nIdx ];
462
463 bool bCustomType = IsCustomType( sParamType );
464 sParamType = MythXSD::ConvertTypeToXSD( sParamType, bCustomType );
465 QString sPrefix = "xs:";
466
467 if (bCustomType)
468 {
469 sPrefix = "tns:";
470
471 sParamType = AddTypeInfo( sParamType );
472 }
473
474 QDomElement oNode = createElement( "xs:element" );
475
476 oNode.setAttribute( "minOccurs", 0 );
477 oNode.setAttribute( "name" , sName );
478 oNode.setAttribute( "nillable" , static_cast<int>(true) ); //-=>TODO: This may need to be determined by sParamType
479 oNode.setAttribute( "type" , sPrefix + sParamType );
480
481 oSeqNode.appendChild( oNode );
482 }
483 }
484
485 return oElementNode;
486}
487
489//
491
492bool MythWSDL::IsCustomType( const QString &sTypeName )
493{
494#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
495 int id = QMetaType::type( sTypeName.toUtf8() );
496#else
497 int id = QMetaType::fromName( sTypeName.toUtf8() ).id();
498#endif
499
500 switch( id )
501 {
502 case QMetaType::QStringList:
503 case QMetaType::QVariantList:
504 case QMetaType::QVariantMap:
505 return true;
506
507 default:
508 // for now, treat QFileInfo as a string. Need to turn into MTOM later.
509 if (id == qMetaTypeId<QFileInfo>())
510 return false;
511 if (sTypeName == "std::chrono::seconds")
512 return false;
513 break;
514 }
515
516 return !(id < QMetaType::User);
517}
518
520//
522
523QString MythWSDL::AddTypeInfo( QString sType )
524{
525 QString sCustomAttr = "type";
526
527 // if sParamType contains "::", then assume it's a enum
528 // (this needs to be looked at again for a better approach)
529
530 if (sType.contains( "::" ))
531 {
532 sType = sType.replace( "::", "." );
533 sCustomAttr = "enum";
534 }
535
536 TypeInfo info = { sCustomAttr, QString() };
537
538 m_typesToInclude.insert( sType, info );
539
540 return sType;
541}
542
544//
546
547QString MythWSDL::ReadClassInfo( const QMetaObject *pMeta, const QString &sKey )
548{
549 int nIdx = -1;
550
551 if (pMeta)
552 nIdx = pMeta->indexOfClassInfo( sKey.toUtf8() );
553
554 if (nIdx >=0)
555 return pMeta->classInfo( nIdx ).value();
556
557 return {};
558}
static HTTPData Create()
Definition: mythhttpdata.cpp:4
const QMetaObject & m_meta
static HTTPResponse DataResponse(const HTTPRequest2 &Request, const HTTPData &Data)
static MythMimeType MimeTypeForName(const QString &Name)
Return a mime type that matches the given name.
QDomElement m_oLastMsg
Definition: mythwsdl.h:43
static QString ReadClassInfo(const QMetaObject *pMeta, const QString &sKey)
Definition: mythwsdl.cpp:547
QString AddTypeInfo(QString sType)
Definition: mythwsdl.cpp:523
QDomElement m_oRoot
Definition: mythwsdl.h:41
QDomElement CreateBindingOperation(const QString &path, const HTTPMethodPtr &handler, const QString &sClassName)
Definition: mythwsdl.cpp:296
QDomElement CreateMethodType(const HTTPMethodPtr &handler, QString sTypeName, bool bReturnType=false)
Definition: mythwsdl.cpp:375
QDomElement m_oService
Definition: mythwsdl.h:46
QDomElement m_oPortType
Definition: mythwsdl.h:44
static bool IsCustomType(const QString &sTypeName)
Definition: mythwsdl.cpp:492
QMap< QString, TypeInfo > m_typesToInclude
Definition: mythwsdl.h:39
HTTPResponse GetWSDL(const HTTPRequest2 &Request)
Definition: mythwsdl.cpp:28
QDomElement m_oTypes
Definition: mythwsdl.h:42
QDomElement m_oBindings
Definition: mythwsdl.h:45
QDomElement CreateMessage(const QString &sMsgName, const QString &sTypeName)
Definition: mythwsdl.cpp:354
MythHTTPMetaService * m_pMetaService
Definition: mythwsdl.h:38
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
Definition: mythxsd.cpp:899
std::shared_ptr< MythHTTPMetaMethod > HTTPMethodPtr
std::shared_ptr< MythHTTPRequest > HTTPRequest2
Definition: mythhttptypes.h:39
@ HTTPGet
Definition: mythhttptypes.h:94
std::shared_ptr< MythHTTPResponse > HTTPResponse
Definition: mythhttptypes.h:40
@ HTTPETag
@ HTTPShortLife
dictionary info
Definition: azlyrics.py:7