Projet

Général

Profil

0001-remove-contrib.maarch-43487.patch

Thomas Noël, 29 mai 2020 21:23

Télécharger (26,9 ko)

Voir les différences:

Subject: [PATCH 1/2] remove contrib.maarch (#43487)

 passerelle/contrib/maarch/README              |  15 -
 passerelle/contrib/maarch/__init__.py         |   0
 .../contrib/maarch/migrations/0001_initial.py |  33 ---
 .../migrations/0002_management_log_level.py   |  20 --
 .../0003_rename_maarch_model_20160624_0329.py |  19 --
 .../migrations/0004_auto_20170920_0951.py     |  19 --
 .../0005_remove_maarch_log_level.py           |  19 --
 .../contrib/maarch/migrations/__init__.py     |   0
 passerelle/contrib/maarch/models.py           |  44 ---
 passerelle/contrib/maarch/soap.py             |  61 ----
 .../passerelle/contrib/maarch/detail.html     |  21 --
 passerelle/contrib/maarch/urls.py             |  28 --
 passerelle/contrib/maarch/views.py            | 269 ------------------
 passerelle/static/css/style.css               |   4 -
 tests/settings.py                             |   1 -
 tests/test_generic_endpoint.py                |   4 +-
 16 files changed, 2 insertions(+), 555 deletions(-)
 delete mode 100644 passerelle/contrib/maarch/README
 delete mode 100644 passerelle/contrib/maarch/__init__.py
 delete mode 100644 passerelle/contrib/maarch/migrations/0001_initial.py
 delete mode 100644 passerelle/contrib/maarch/migrations/0002_management_log_level.py
 delete mode 100644 passerelle/contrib/maarch/migrations/0003_rename_maarch_model_20160624_0329.py
 delete mode 100644 passerelle/contrib/maarch/migrations/0004_auto_20170920_0951.py
 delete mode 100644 passerelle/contrib/maarch/migrations/0005_remove_maarch_log_level.py
 delete mode 100644 passerelle/contrib/maarch/migrations/__init__.py
 delete mode 100644 passerelle/contrib/maarch/models.py
 delete mode 100644 passerelle/contrib/maarch/soap.py
 delete mode 100644 passerelle/contrib/maarch/templates/passerelle/contrib/maarch/detail.html
 delete mode 100644 passerelle/contrib/maarch/urls.py
 delete mode 100644 passerelle/contrib/maarch/views.py
passerelle/contrib/maarch/README
1
Connect Publik with Maarch LetterBox
2
====================================
3

  
4
Compatible with Maarch 1.4 SOAP webservices.
5
http://maarchcourrier.com/
6

  
7
How to use
8
----------
9

  
10
1) Install python-suds
11

  
12
2) Add to your settings.py
13

  
14
# local_settings.py:
15
INSTALLED_APPS += ('passerelle.contrib.maarch',)
passerelle/contrib/maarch/migrations/0001_initial.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('base', '0001_initial'),
11
    ]
12

  
13
    operations = [
14
        migrations.CreateModel(
15
            name='Management',
16
            fields=[
17
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18
                ('title', models.CharField(verbose_name='Title', max_length=50)),
19
                ('slug', models.SlugField(verbose_name='Identifier', unique=True)),
20
                ('description', models.TextField(verbose_name='Description')),
21
                ('wsdl_url', models.CharField(help_text='Maarch WSDL URL', max_length=128, verbose_name='WSDL URL')),
22
                ('verify_cert', models.BooleanField(default=True, verbose_name='Check HTTPS Certificate validity')),
23
                ('username', models.CharField(max_length=128, verbose_name='Username', blank=True)),
24
                ('password', models.CharField(max_length=128, verbose_name='Password', blank=True)),
25
                ('keystore', models.FileField(help_text='Certificate and private key in PEM format', upload_to=b'maarch', null=True, verbose_name='Keystore', blank=True)),
26
                ('users', models.ManyToManyField(to='base.ApiUser', related_name='_management_users_+', related_query_name='+', blank=True)),
27
            ],
28
            options={
29
                'verbose_name': 'Maarch',
30
            },
31
            bases=(models.Model,),
32
        ),
33
    ]
passerelle/contrib/maarch/migrations/0002_management_log_level.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('maarch', '0001_initial'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='management',
16
            name='log_level',
17
            field=models.CharField(default=b'NOTSET', max_length=10, verbose_name='Log Level', choices=[(b'NOTSET', b'NOTSET'), (b'DEBUG', b'DEBUG'), (b'INFO', b'INFO'), (b'WARNING', b'WARNING'), (b'ERROR', b'ERROR'), (b'CRITICAL', b'CRITICAL')]),
18
            preserve_default=True,
19
        ),
20
    ]
passerelle/contrib/maarch/migrations/0003_rename_maarch_model_20160624_0329.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('base', '0002_auto_20151009_0326'),
11
        ('maarch', '0002_management_log_level'),
12
    ]
13

  
14
    operations = [
15
        migrations.RenameModel(
16
            'Management',
17
            'Maarch'
18
        ),
19
    ]
passerelle/contrib/maarch/migrations/0004_auto_20170920_0951.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import migrations, models
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('maarch', '0003_rename_maarch_model_20160624_0329'),
11
    ]
12

  
13
    operations = [
14
        migrations.AlterField(
15
            model_name='maarch',
16
            name='slug',
17
            field=models.SlugField(verbose_name='Identifier', unique=True),
18
        ),
19
    ]
passerelle/contrib/maarch/migrations/0005_remove_maarch_log_level.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.12 on 2018-11-19 13:42
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('maarch', '0004_auto_20170920_0951'),
12
    ]
13

  
14
    operations = [
15
        migrations.RemoveField(
16
            model_name='maarch',
17
            name='log_level',
18
        ),
19
    ]
passerelle/contrib/maarch/models.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.core.urlresolvers import reverse
18
from django.db import models
19
from django.utils.translation import ugettext_lazy as _
20

  
21
from passerelle.base.models import BaseResource
22

  
23
class Maarch(BaseResource):
24
    wsdl_url = models.CharField(max_length=128, blank=False,
25
            verbose_name=_('WSDL URL'),
26
            help_text=_('Maarch WSDL URL'))
27
    verify_cert = models.BooleanField(default=True,
28
            verbose_name=_('Check HTTPS Certificate validity'))
29
    username = models.CharField(max_length=128, blank=True,
30
            verbose_name=_('Username'))
31
    password = models.CharField(max_length=128, blank=True,
32
            verbose_name=_('Password'))
33
    keystore = models.FileField(upload_to='maarch', null=True, blank=True,
34
            verbose_name=_('Keystore'),
35
            help_text=_('Certificate and private key in PEM format'))
36

  
37
    category = _('Business Process Connectors')
38

  
39
    class Meta:
40
        verbose_name = _('Maarch')
41

  
42
    @classmethod
43
    def get_verbose_name(cls):
44
        return cls._meta.verbose_name
passerelle/contrib/maarch/soap.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
# borrowed from https://pypi.python.org/pypi/suds_requests
18
# and https://docs.oracle.com/cd/E50245_01/E50253/html/vmprg-soap-example-authentication-python.html
19

  
20
import requests
21

  
22
from django.conf import settings
23
from django.utils.six import StringIO
24
from suds.transport.http import HttpAuthenticated
25
from suds.transport import Reply
26
from suds.client import Client
27

  
28

  
29
class Transport(HttpAuthenticated):
30
    def __init__(self, model, **kwargs):
31
        self.model = model
32
        HttpAuthenticated.__init__(self, **kwargs) # oldstyle class...
33

  
34
    def get_requests_kwargs(self):
35
        kwargs = {}
36
        if self.model.username:
37
            kwargs['auth'] = (self.model.username, self.model.password)
38
        if self.model.keystore:
39
            kwargs['cert'] = (self.model.keystore.path, self.model.keystore.path)
40
        if not self.model.verify_cert:
41
            kwargs['verify'] = False
42
        if settings.REQUESTS_PROXIES:
43
            kwargs['proxies'] = settings.REQUESTS_PROXIES
44
        return kwargs
45

  
46
    def open(self, request):
47
        resp = requests.get(request.url, headers=request.headers,
48
                **self.get_requests_kwargs())
49
        return StringIO(resp.content)
50

  
51
    def send(self, request):
52
        self.addcredentials(request)
53
        resp = requests.post(request.url, data=request.message,
54
                headers=request.headers, **self.get_requests_kwargs())
55
        result = Reply(resp.status_code, resp.headers, resp.content)
56
        return result
57

  
58

  
59
def get_client(model):
60
    transport = Transport(model)
61
    return Client(model.wsdl_url, transport=transport, cache=None)
passerelle/contrib/maarch/templates/passerelle/contrib/maarch/detail.html
1
{% extends "passerelle/manage/service_view.html" %}
2
{% load i18n passerelle %}
3

  
4
{% block description %}
5
{% endblock %}
6

  
7
{% block endpoints %}
8
<ul>
9
<li>{% trans 'Check WSDL availability:' %} <a href="{% url 'maarch-ping' slug=object.slug %}"
10
        >{{ site_base_uri }}{% url 'maarch-ping' slug=object.slug %}</a>[?debug]</li>
11
<li>{% trans 'Store a resource:' %} POST <a href="{% url 'maarch-resource' slug=object.slug %}"
12
        >{{ site_base_uri }}{% url 'maarch-resource' slug=object.slug %}</a> (payload: JSON w.c.s. formdata)</li>
13
</ul>
14
{% endblock %}
15

  
16
{% block security %}
17
<p>
18
{% trans 'Access is limited to the following API users:' %}
19
</p>
20
{% access_rights_table resource=object permission='can_access' %}
21
{% endblock %}
passerelle/contrib/maarch/urls.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf.urls import include, url
18

  
19
from .views import *
20

  
21
urlpatterns = [
22
    url(r'^(?P<slug>[\w,-]+)/$', MaarchDetailView.as_view(),
23
        name='maarch-view'),
24
    url(r'^(?P<slug>[\w,-]+)/ping/$', PingView.as_view(),
25
        name='maarch-ping'),
26
    url(r'^(?P<slug>[\w,-]+)/resource/$', ResourceView.as_view(),
27
        name='maarch-resource'),
28
]
passerelle/contrib/maarch/views.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2015  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import requests
18
from datetime import datetime
19
import logging
20

  
21
from django.views.generic import DetailView as GenericDetailView
22
from django.utils.decorators import method_decorator
23
from django.utils.six.moves.urllib import parse as urlparse
24
from django.views.decorators.csrf import csrf_exempt
25

  
26
from passerelle.compat import json_loads
27
import passerelle.utils as utils
28
from passerelle.soap import sudsobject_to_dict, client_to_jsondict
29

  
30
from .soap import get_client
31
from .models import Maarch
32

  
33
logger = logging.getLogger('passerelle.contrib.maarch')
34

  
35
class MaarchException(Exception):
36
    pass
37

  
38
class MaarchDetailView(GenericDetailView):
39
    model = Maarch
40
    template_name = 'passerelle/contrib/maarch/detail.html'
41

  
42

  
43
class DetailView(GenericDetailView):
44
    model = Maarch
45

  
46
    def get_client(self):
47
        return get_client(self.get_object())
48

  
49
    def get_data(self, request, *args, **kwargs):
50
        raise NotImplementedError
51

  
52
    @utils.protected_api('can_access')
53
    def get(self, request, *args, **kwargs):
54
        data = self.get_data(request, *args, **kwargs)
55
        return utils.response_for_json(request, data)
56

  
57

  
58
class PingView(DetailView):
59
    def get_data(self, request, *args, **kwargs):
60
        client = self.get_client()
61
        res = {'ping': 'pong'}
62
        if 'debug' in request.GET:
63
            res['client'] = client_to_jsondict(client)
64
        return res
65

  
66

  
67
class ResourceView(DetailView):
68
    @method_decorator(csrf_exempt)
69
    def dispatch(self, *args, **kwargs):
70
        return super(ResourceView, self).dispatch(*args, **kwargs)
71

  
72
    @utils.protected_api('can_access')
73
    def post(self, request, *args, **kwargs):
74
        client = self.get_client()
75
        formdata = json_loads(request.body)
76
        extras = formdata.get('extra', {})
77

  
78
        debug = 'debug' in request.GET
79
        if debug:
80
            debug_output = {}
81

  
82
        # get formdef schema from wcs API
83
        url = formdata['url']
84
        p = urlparse.urlsplit(url)
85
        scheme, netloc, path, query, fragment = \
86
            p.scheme, p.netloc, p.path, p.query, p.fragment
87
        schema_path = path.rsplit('/', 2)[0] + '/schema'
88
        schema_url = urlparse.urlunsplit((scheme, netloc, schema_path, query, fragment))
89
        schema = requests.get(schema_url).json()
90

  
91
        # storeAttachmentResource attachments: list, build from formdata file fields
92
        attachments = []
93

  
94
        #
95
        # build document (encodedFile and fileFormat)
96
        #
97

  
98
        document = u'<html>'
99
        document += u'<head><meta charset="utf-8"></head>'
100
        document += '<body>'
101
        document += u'<h1>%s</h1>' % schema['name']
102
        page = ''
103
        empty_page = True
104
        for field in schema['fields']:
105
            if field['type'] == 'page':
106
                # add last page, if it contains values
107
                if page and not empty_page:
108
                    document += page
109
                page = u'<hr /><h2>%s</h2>' % field['label']
110
                empty_page = True
111
            elif field['type'] == 'title':
112
                page += u'<h3>%s</h3>' % field['label']
113
            elif 'varname' in field:
114
                varname = field['varname']
115
                value = formdata['fields'].get(varname)
116
                if not value:
117
                    continue
118
                if field['type'] == 'file':
119
                    # field is a file: add it to attachments list
120
                    value['fileFormat'] = value['content_type'].split('/')[1] # FIXME (how ?)
121
                    attachments.append(value)
122
                    value = '%s' % value['filename']
123
                page += u'<dl>'
124
                page += u'<dt>%s</dt>' % field['label']
125
                page += u'<dd>%s</dd>' % value
126
                page += u'</dl>'
127
                empty_page = False
128
        if page and not empty_page: # add last page, if it contains values
129
            document += page
130
        document += u'</body></html>'
131
        encodedFile = document.encode('utf-8').encode('base64')
132
        fileFormat = 'html'
133

  
134
        if debug:
135
            debug_output['document'] = document
136

  
137
        #
138
        # build metadata for storeResource and storeExtResource
139
        #
140

  
141
        # storeResource metadata
142
        metadata = {}
143
        # storeExtResource metadata
144
        ext_metadata = {}
145

  
146
        # extract metadata and ext_metadata from formdata['extra']
147
        for name, value in extras.items():
148
            if name.startswith('maarch_metadata_'):
149
                metadata[name[16:]] = value
150
            if name.startswith('maarch_ext_metadata_'):
151
                ext_metadata[name[20:]] = value
152

  
153
        # prepare metadata for SOAP call
154
        def prepare_soap_metadata(metadata):
155
            datas = []
156
            for name, value in metadata.items():
157
                data = client.factory.create('arrayOfDataContent')
158
                data.column = name
159
                if name.endswith('_date'):
160
                    data.type = 'date'
161
                    value = datetime.strptime(value[:19], '%Y-%m-%dT%H:%M:%S')
162
                    value = value.strftime('%d-%m-%Y %H:%M:%S')
163
                elif isinstance(value, basestring):
164
                    data.type = 'string'
165
                elif isinstance(value, int):
166
                    data.type = 'int'
167
                elif isinstance(value, float):
168
                    data.type = 'float'
169
                elif value is None:
170
                    data.type = 'string'
171
                    value = ''
172
                data.value = value
173
                datas.append(data)
174
            soap_metadata = client.factory.create('arrayOfData')
175
            soap_metadata.datas = datas
176
            return soap_metadata
177

  
178
        metadata = prepare_soap_metadata(metadata)
179
        ext_metadata = prepare_soap_metadata(ext_metadata)
180

  
181
        if debug:
182
            debug_output['metadata'] = '%r' % metadata
183
            debug_output['ext_metadata'] = '%r' % ext_metadata
184

  
185
        #
186
        # get other Maarch variables (letterbox configuration by default)
187
        #
188

  
189
        collId = extras.get('maarch_collId') or 'letterbox_coll'
190
        table = extras.get('maarch_table') or 'res_letterbox'
191
        status = extras.get('maarch_status') or 'ATT'
192
        ext_table = extras.get('maarch_ext_table') or 'mlb_coll_ext'
193

  
194
        if debug:
195
            debug_output['collId'] = collId
196
            debug_output['table'] = table
197
            debug_output['status'] = status
198
            debug_output['ext_table'] = ext_table
199

  
200
        # if INES ESB (Tibco) between passerelle and Maarch, add a "maarch_id"
201
        # parameter. Get value from formdata or workflow options.
202
        maarch_id = extras.get('maarch_id')
203

  
204
        if debug:
205
            debug_output['maarch_id'] = maarch_id
206

  
207
        #
208
        # call Maarch web services
209
        #
210
        logger.debug('storeResource+Ext+Attachment: start')
211

  
212
        # store the resource (storeResource)
213
        logger.debug('storeResource: encodedFile(size):%r fileFormat:%r '
214
                     'collId:%r table:%r status:%r', len(encodedFile),
215
                        fileFormat, collId, table, status)
216
        logger.debug('storeResource: metadata: %r', metadata)
217
        if maarch_id:
218
            logger.debug('storeResource: INES maarch_id: %r', maarch_id)
219
            results = client.service.storeResource(
220
                    maarch_id,
221
                    encodedFile, metadata, collId,
222
                    table, fileFormat, status)
223
        else:
224
            results = client.service.storeResource(
225
                    encodedFile, metadata, collId,
226
                    table, fileFormat, status)
227
        data = sudsobject_to_dict(results)
228
        logger.debug('storeResource result: %r', data)
229

  
230
        resId = data.get('resId')
231
        if not resId:
232
            raise MaarchException('no resId after storeResource')
233
        logger.debug('storeResource result: resId:%r', resId)
234

  
235
        logger.debug('storeExtResource: resId:%r ext_table:%r', resId,
236
                     ext_table)
237
        logger.debug('storeExtResource: ext_metadata: %r', ext_metadata)
238
        # store external metadata (storeExtResource)
239
        if maarch_id:
240
            logger.debug('storeExtResource: INES maarch_id: %r', maarch_id)
241
            results = client.service.storeExtResource(
242
                    maarch_id,
243
                    resId, ext_metadata, ext_table)
244
        else:
245
            results = client.service.storeExtResource(
246
                    resId, ext_metadata, ext_table)
247

  
248
        # store attachments
249
        for attachment in attachments:
250
            logger.debug('storeAttachmentResource: resId:%r collId:%r '
251
                         'content(size):%r fileFormat:%r filename:%r', resId,
252
                             collId, len(attachment['content']),
253
                             attachment['fileFormat'], attachment['filename'])
254
            if maarch_id:
255
                logger.debug('storeAttachmentResource: INES maarch_id: %r', maarch_id)
256
                client.service.storeAttachmentResource(
257
                        maarch_id,
258
                        resId, collId, attachment['content'],
259
                        attachment['fileFormat'], attachment['filename'])
260
            else:
261
                client.service.storeAttachmentResource(
262
                        resId, collId, attachment['content'],
263
                        attachment['fileFormat'], attachment['filename'])
264

  
265
        if debug:
266
            data['debug'] = debug_output
267

  
268
        logger.debug('storeResource+Ext+Attachment: resId:%r -- end', resId)
269
        return utils.response_for_json(request, data)
passerelle/static/css/style.css
157 157
	content: "\f073"; /* calendar */
158 158
}
159 159

  
160
li.connector.maarch a::before {
161
	content: "\f0e0"; /* envelope */
162
}
163

  
164 160
li.connector.cmisconnector a::before {
165 161
	content: "\f15b"; /* file */
166 162
}
tests/settings.py
25 25
    'passerelle.contrib.iparapheur',
26 26
    'passerelle.contrib.iws',
27 27
    'passerelle.contrib.lille_urban_card',
28
    'passerelle.contrib.maarch',
29 28
    'passerelle.contrib.mdph13',
30 29
    'passerelle.contrib.meyzieu_newsletters',
31 30
    'passerelle.contrib.nancypoll',
tests/test_generic_endpoint.py
506 506

  
507 507
def test_https_warnings(app, db, monkeypatch, httpbin_secure, relax_openssl):
508 508
    from requests.exceptions import SSLError
509
    from passerelle.contrib.maarch.models import Maarch
509
    from passerelle.apps.arcgis.models import ArcGIS
510 510

  
511
    resource = utils.make_resource(Maarch, wsdl_url='https://example.com/', slug='slug', verify_cert=True)
511
    resource = utils.make_resource(ArcGIS, base_url='https://example.com/', slug='gis', verify_cert=True)
512 512
    with pytest.raises(SSLError):
513 513
        resource.requests.get(httpbin_secure.join('/get/'))
514 514
    resource.verify_cert = False
515
-