MythTV  master
mythwsdl.cpp
Go to the documentation of this file.
1 // 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 
17 #include "http/mythmimedatabase.h"
19 #include "http/mythhttprequest.h"
20 #include "http/mythhttpresponse.h"
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 
296 QDomElement 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 
354 QDomElement 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 
375 QDomElement 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 
492 bool 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 
523 QString 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 
547 QString 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 }
MythWSDL::CreateBindingOperation
QDomElement CreateBindingOperation(const QString &path, const HTTPMethodPtr &handler, const QString &sClassName)
Definition: mythwsdl.cpp:296
MythWSDL::GetWSDL
HTTPResponse GetWSDL(const HTTPRequest2 &Request)
Definition: mythwsdl.cpp:28
MythWSDL::IsCustomType
static bool IsCustomType(const QString &sTypeName)
Definition: mythwsdl.cpp:492
MythHTTPResponse::DataResponse
static HTTPResponse DataResponse(const HTTPRequest2 &Request, const HTTPData &Data)
Definition: mythhttpresponse.cpp:249
HTTPGet
@ HTTPGet
Definition: mythhttptypes.h:92
TypeInfo
Definition: mythxsd.h:72
MythWSDL::CreateMethodType
QDomElement CreateMethodType(const HTTPMethodPtr &handler, QString sTypeName, bool bReturnType=false)
Definition: mythwsdl.cpp:375
mythhttpmetaservice.h
HTTPETag
@ HTTPETag
Definition: mythhttptypes.h:144
MythWSDL::ReadClassInfo
static QString ReadClassInfo(const QMetaObject *pMeta, const QString &sKey)
Definition: mythwsdl.cpp:547
MythWSDL::m_typesToInclude
QMap< QString, TypeInfo > m_typesToInclude
Definition: mythwsdl.h:39
MythWSDL::m_oRoot
QDomElement m_oRoot
Definition: mythwsdl.h:41
MythWSDL::m_oPortType
QDomElement m_oPortType
Definition: mythwsdl.h:44
MythWSDL::AddTypeInfo
QString AddTypeInfo(QString sType)
Definition: mythwsdl.cpp:523
MythXSD::ConvertTypeToXSD
static QString ConvertTypeToXSD(const QString &sType, bool bCustomType=false)
Definition: mythxsd.cpp:897
TypeInfo::sAttrName
QString sAttrName
Definition: mythxsd.h:72
MythHTTPMetaService::m_meta
const QMetaObject & m_meta
Definition: mythhttpmetaservice.h:20
MythHTTPData::Create
static HTTPData Create()
Definition: mythhttpdata.cpp:4
mythwsdl.h
MythWSDL::m_oService
QDomElement m_oService
Definition: mythwsdl.h:46
MythHTTPMetaService::m_name
QString m_name
Definition: mythhttpmetaservice.h:21
mythhttpresponse.h
mythmimedatabase.h
MythMimeDatabase::MimeTypeForName
static MythMimeType MimeTypeForName(const QString &Name)
Return a mime type that matches the given name.
Definition: mythmimedatabase.cpp:201
HTTPResponse
std::shared_ptr< MythHTTPResponse > HTTPResponse
Definition: mythhttptypes.h:39
hardwareprofile.distros.mythtv_data.request.Request
def Request(url=None)
Definition: distros/mythtv_data/request.py:64
MythHTTPMetaService::m_slots
HTTPMethods m_slots
Definition: mythhttpmetaservice.h:24
HTTPRequest2
std::shared_ptr< MythHTTPRequest > HTTPRequest2
Definition: mythhttptypes.h:38
MythWSDL::m_oLastMsg
QDomElement m_oLastMsg
Definition: mythwsdl.h:43
MythWSDL::m_oBindings
QDomElement m_oBindings
Definition: mythwsdl.h:45
mythhttprequest.h
TypeInfo::sContentType
QString sContentType
Definition: mythxsd.h:72
MythWSDL::CreateMessage
QDomElement CreateMessage(const QString &sMsgName, const QString &sTypeName)
Definition: mythwsdl.cpp:354
MythWSDL::m_pMetaService
MythHTTPMetaService * m_pMetaService
Definition: mythwsdl.h:38
HTTPShortLife
@ HTTPShortLife
Definition: mythhttptypes.h:146
HTTPMethodPtr
std::shared_ptr< MythHTTPMetaMethod > HTTPMethodPtr
Definition: mythhttpmetamethod.h:14
MythWSDL::m_oTypes
QDomElement m_oTypes
Definition: mythwsdl.h:42