V2 API using Pecan and WSME
This change removes the v1 API completely and adds v2 using the Pecan framework and WSME for (de)serialization. Also add a small hack where we subclass RestController for now to support PATCH blueprint v2-pecan * Not added (Needs be determined) Invoice Lines * Added resources Currency Language InvoiceStates PGProvider Merchant Customer PaymentMethods PaymentGatewayConfiguration Plan Product Subscription Invoice Usage Change-Id: I3d252b05d5d49664e3038fa0b1fa8a6e210d03cd
This commit is contained in:
parent
5897c3cf5a
commit
b0f909200f
@ -17,25 +17,15 @@
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
import flask
|
||||
from billingstack.openstack.common import jsonutils as json
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
API_SERVICE_OPTS = [
|
||||
cfg.IntOpt('api_port', default=9091,
|
||||
help='The port for the billing API server'),
|
||||
cfg.IntOpt('api_listen', default='0.0.0.0', help='Bind to address'),
|
||||
cfg.IntOpt('workers', default=None,
|
||||
help='Number of worker processes to spawn'),
|
||||
cfg.StrOpt('api_paste_config', default='api-paste.ini',
|
||||
help='File name for the paste.deploy config for the api'),
|
||||
cfg.StrOpt('auth_strategy', default='noauth',
|
||||
help='The strategy to use for auth. Supports noauth or '
|
||||
'keystone'),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(API_SERVICE_OPTS, 'service:api')
|
||||
|
||||
# Allows us to serialize datetime's etc
|
||||
flask.helpers.json = json
|
||||
|
91
billingstack/api/app.py
Normal file
91
billingstack/api/app.py
Normal file
@ -0,0 +1,91 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import logging
|
||||
import os
|
||||
import pecan
|
||||
from oslo.config import cfg
|
||||
from wsgiref import simple_server
|
||||
|
||||
from billingstack import service
|
||||
from billingstack.api import hooks
|
||||
from billingstack.openstack.common import log
|
||||
|
||||
cfg.CONF.import_opt('state_path', 'billingstack.paths')
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def get_config():
|
||||
conf = {
|
||||
'app': {
|
||||
'root': 'billingstack.api.v2.controllers.root.RootController',
|
||||
'modules': ['designate.api.v2'],
|
||||
}
|
||||
}
|
||||
return pecan.configuration.conf_from_dict(conf)
|
||||
|
||||
|
||||
def setup_app(pecan_config=None, extra_hooks=None):
|
||||
app_hooks = [
|
||||
hooks.NoAuthHook()
|
||||
]
|
||||
|
||||
if extra_hooks:
|
||||
app_hooks.extend(extra_hooks)
|
||||
|
||||
pecan_config = pecan_config or get_config()
|
||||
|
||||
pecan.configuration.set_config(dict(pecan_config), overwrite=True)
|
||||
|
||||
app = pecan.make_app(
|
||||
pecan_config.app.root,
|
||||
debug=cfg.CONF.debug,
|
||||
hooks=app_hooks,
|
||||
force_canonical=getattr(pecan_config.app, 'force_canonical', True)
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
class VersionSelectorApplication(object):
|
||||
def __init__(self):
|
||||
self.v2 = setup_app()
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
return self.v2(environ, start_response)
|
||||
|
||||
|
||||
def start():
|
||||
service.prepare_service()
|
||||
|
||||
root = VersionSelectorApplication()
|
||||
|
||||
host = cfg.CONF['service:api'].api_listen
|
||||
port = cfg.CONF['service:api'].api_port
|
||||
|
||||
srv = simple_server.make_server(host, port, root)
|
||||
|
||||
LOG.info('Starting server in PID %s' % os.getpid())
|
||||
LOG.info("Configuration:")
|
||||
cfg.CONF.log_opt_values(LOG, logging.INFO)
|
||||
|
||||
if host == '0.0.0.0':
|
||||
LOG.info('serving on 0.0.0.0:%s, view at http://127.0.0.1:%s' %
|
||||
(port, port))
|
||||
else:
|
||||
LOG.info("serving on http://%s:%s" % (host, port))
|
||||
|
||||
srv.serve_forever()
|
@ -1,62 +0,0 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from billingstack.openstack.common import local
|
||||
from billingstack.openstack.common import log as logging
|
||||
from billingstack.openstack.common.context import RequestContext
|
||||
from billingstack import wsgi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def pipeline_factory(loader, global_conf, **local_conf):
|
||||
"""
|
||||
A paste pipeline replica that keys off of auth_strategy.
|
||||
|
||||
Code nabbed from cinder.
|
||||
"""
|
||||
pipeline = local_conf[cfg.CONF['service:api'].auth_strategy]
|
||||
pipeline = pipeline.split()
|
||||
filters = [loader.get_filter(n) for n in pipeline[:-1]]
|
||||
app = loader.get_app(pipeline[-1])
|
||||
filters.reverse()
|
||||
for filter in filters:
|
||||
app = filter(app)
|
||||
return app
|
||||
|
||||
|
||||
class NoAuthContextMiddleware(wsgi.Middleware):
|
||||
def merchant_id(self, request):
|
||||
parts = [p for p in request.path_info.split('/') if p]
|
||||
if parts[0] == 'merchants' and len(parts) >= 2:
|
||||
return parts[1]
|
||||
|
||||
def process_request(self, request):
|
||||
merchant_id = self.merchant_id(request)
|
||||
|
||||
# NOTE(kiall): This makes the assumption that disabling authentication
|
||||
# means you wish to allow full access to everyone.
|
||||
context = RequestContext(is_admin=True, tenant=merchant_id)
|
||||
|
||||
# Store the context where oslo-log exepcts to find it.
|
||||
local.store.context = context
|
||||
|
||||
# Attach the context to the request environment
|
||||
request.environ['context'] = context
|
@ -14,15 +14,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import functools
|
||||
import mimetypes
|
||||
import pecan.rest
|
||||
|
||||
from flask import request, Blueprint
|
||||
from wsme.types import Base, Enum, UserType, text, Unset, wsproperty
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from billingstack.api import utils
|
||||
from billingstack.openstack.common import log
|
||||
|
||||
|
||||
@ -43,6 +40,11 @@ CORS_ALLOW_HEADERS = [
|
||||
]
|
||||
|
||||
|
||||
class RestController(pecan.rest.RestController):
|
||||
def _handle_patch(self, method, remainder):
|
||||
return self._handle_post(method, remainder)
|
||||
|
||||
|
||||
class Property(UserType):
|
||||
"""
|
||||
A Property that just passes the value around...
|
||||
@ -57,6 +59,27 @@ class Property(UserType):
|
||||
property_type = Property()
|
||||
|
||||
|
||||
def _query_to_criterion(query, storage_func=None, **kw):
|
||||
"""
|
||||
Iterate over the query checking against the valid signatures (later).
|
||||
|
||||
:param query: A list of queries.
|
||||
:param storage_func: The name of the storage function to very against.
|
||||
"""
|
||||
translation = {
|
||||
'customer': 'customer_id'
|
||||
}
|
||||
|
||||
criterion = {}
|
||||
for q in query:
|
||||
key = translation.get(q.field, q.field)
|
||||
criterion[key] = q.as_dict()
|
||||
|
||||
criterion.update(kw)
|
||||
|
||||
return criterion
|
||||
|
||||
|
||||
operation_kind = Enum(str, 'lt', 'le', 'eq', 'ne', 'ge', 'gt')
|
||||
|
||||
|
||||
@ -133,78 +156,3 @@ class ModelBase(Base):
|
||||
Return a class of this object from values in the from_db
|
||||
"""
|
||||
return cls(**values)
|
||||
|
||||
|
||||
class Rest(Blueprint):
|
||||
"""
|
||||
Helper to do stuff
|
||||
"""
|
||||
def get(self, rule, status_code=200, **kw):
|
||||
return self._mroute('GET', rule, status_code, **kw)
|
||||
|
||||
def post(self, rule, status_code=202, **kw):
|
||||
return self._mroute('POST', rule, status_code, **kw)
|
||||
|
||||
def patch(self, rule, status_code=202, **kw):
|
||||
return self._mroute('PATCH', rule, status_code, **kw)
|
||||
|
||||
def put(self, rule, status_code=202, **kw):
|
||||
return self._mroute('PUT', rule, status_code, **kw)
|
||||
|
||||
def delete(self, rule, status_code=204, **kw):
|
||||
return self._mroute('DELETE', rule, status_code, **kw)
|
||||
|
||||
def _mroute(self, methods, rule, status_code=None, **kw):
|
||||
if type(methods) is str:
|
||||
methods = [methods]
|
||||
|
||||
return self.route(rule, methods=methods, status_code=status_code,
|
||||
**kw)
|
||||
|
||||
def guess_response_type(self, type_suffix=None):
|
||||
"""
|
||||
Get the MIME type based on keywords / request
|
||||
"""
|
||||
if type_suffix:
|
||||
response_type = mimetypes.guess_type("res." + type_suffix)[0]
|
||||
request.response_type = response_type
|
||||
|
||||
def route(self, rule, sig_args=[], sig_kw={}, **options):
|
||||
"""
|
||||
Helper function that sets up the route as well as adding CORS..
|
||||
"""
|
||||
status = options.pop('status_code', None)
|
||||
|
||||
def decorator(func):
|
||||
endpoint = options.pop('endpoint', func.__name__)
|
||||
|
||||
if 'body' in options and 'body' not in sig_kw:
|
||||
sig_kw['body'] = options['body']
|
||||
|
||||
# NOTE: Wrap the function with CORS support.
|
||||
@utils.crossdomain(origin=cfg.CONF.cors_allowed_origin,
|
||||
max_age=cfg.CONF.cors_max_age,
|
||||
headers=",".join(CORS_ALLOW_HEADERS))
|
||||
@functools.wraps(func)
|
||||
def handler(**kw):
|
||||
# extract response content type
|
||||
self.guess_response_type(kw.pop('response_type', None))
|
||||
|
||||
# NOTE: Extract fields (column selection)
|
||||
fields = list(set(request.args.getlist('fields')))
|
||||
fields.sort()
|
||||
request.fields_selector = fields
|
||||
|
||||
if hasattr(func, '_wsme_definition'):
|
||||
func._wsme_definition.status_code = status
|
||||
|
||||
return func(**kw)
|
||||
|
||||
#_rule = "/<tenant_id>" + rule
|
||||
# NOTE: Add 2 set of rules, 1 with response content type and one wo
|
||||
self.add_url_rule(rule, endpoint, handler, **options)
|
||||
rtype_rule = rule + '.<response_type>'
|
||||
self.add_url_rule(rtype_rule, endpoint, handler, **options)
|
||||
|
||||
return func
|
||||
return decorator
|
||||
|
40
billingstack/api/hooks.py
Normal file
40
billingstack/api/hooks.py
Normal file
@ -0,0 +1,40 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import hooks
|
||||
|
||||
from billingstack.openstack.common.context import RequestContext
|
||||
|
||||
|
||||
class NoAuthHook(hooks.PecanHook):
|
||||
"""
|
||||
Simple auth - all requests will be is_admin=True
|
||||
"""
|
||||
def merchant_id(self, path):
|
||||
"""
|
||||
Get merchant id from url
|
||||
"""
|
||||
parts = [p for p in path.split('/') if p]
|
||||
try:
|
||||
index = parts.index('merchants') + 1
|
||||
return parts[index]
|
||||
except ValueError:
|
||||
return
|
||||
except IndexError:
|
||||
return
|
||||
|
||||
def before(self, state):
|
||||
merchant_id = self.merchant_id(state.request.path_url)
|
||||
state.request.ctxt = RequestContext(tenant=merchant_id, is_admin=True)
|
@ -1,80 +0,0 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
import flask
|
||||
import webob.dec
|
||||
from billingstack import exceptions
|
||||
from billingstack import wsgi
|
||||
from billingstack.openstack.common.rpc import common as rpc_common
|
||||
from billingstack.openstack.common import log
|
||||
from billingstack.openstack.common import jsonutils as json
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class FaultWrapperMiddleware(wsgi.Middleware):
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, request):
|
||||
try:
|
||||
return request.get_response(self.application)
|
||||
except exceptions.Base, e:
|
||||
# Handle Moniker Exceptions
|
||||
status = e.error_code if hasattr(e, 'error_code') else 500
|
||||
|
||||
# Start building up a response
|
||||
response = {
|
||||
'code': status
|
||||
}
|
||||
|
||||
if hasattr(e, 'error_type'):
|
||||
response['type'] = e.error_type
|
||||
|
||||
if hasattr(e, 'errors'):
|
||||
response['errors'] = e.errors
|
||||
|
||||
response['message'] = e.get_message()
|
||||
|
||||
return self._handle_exception(request, e, status, response)
|
||||
except rpc_common.Timeout, e:
|
||||
# Special case for RPC timeout's
|
||||
response = {
|
||||
'code': 504,
|
||||
'type': 'timeout',
|
||||
}
|
||||
|
||||
return self._handle_exception(request, e, 504, response)
|
||||
except Exception, e:
|
||||
# Handle all other exception types
|
||||
return self._handle_exception(request, e)
|
||||
|
||||
def _handle_exception(self, request, e, status=500, response={}):
|
||||
# Log the exception ASAP
|
||||
LOG.exception(e)
|
||||
|
||||
headers = [
|
||||
('Content-Type', 'application/json'),
|
||||
]
|
||||
|
||||
# Set a response code, if one is missing.
|
||||
if 'code' not in response:
|
||||
response['code'] = status
|
||||
|
||||
# TODO(kiall): Send a fault notification
|
||||
|
||||
# Return the new response
|
||||
return flask.Response(status=status, headers=headers,
|
||||
response=json.dumps(response))
|
@ -1,55 +0,0 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
from oslo.config import cfg
|
||||
from paste import deploy
|
||||
|
||||
from billingstack.openstack.common import log as logging
|
||||
from billingstack.openstack.common import wsgi
|
||||
from billingstack import exceptions
|
||||
from billingstack import utils
|
||||
#from billingstack import policy
|
||||
|
||||
|
||||
cfg.CONF.import_opt('state_path', 'billingstack.paths')
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Service(wsgi.Service):
|
||||
def __init__(self, backlog=128, threads=1000):
|
||||
|
||||
api_paste_config = cfg.CONF['service:api'].api_paste_config
|
||||
config_paths = utils.find_config(api_paste_config)
|
||||
|
||||
if len(config_paths) == 0:
|
||||
msg = 'Unable to determine appropriate api-paste-config file'
|
||||
raise exceptions.ConfigurationError(msg)
|
||||
|
||||
LOG.info('Using api-paste-config found at: %s' % config_paths[0])
|
||||
|
||||
#policy.init_policy()
|
||||
|
||||
application = deploy.loadapp("config:%s" % config_paths[0],
|
||||
name='bs_api')
|
||||
|
||||
super(Service, self).__init__(application=application,
|
||||
host=cfg.CONF['service:api'].api_listen,
|
||||
port=cfg.CONF['service:api'].api_port,
|
||||
backlog=backlog,
|
||||
threads=threads)
|
@ -1,55 +0,0 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
import flask
|
||||
from oslo.config import cfg
|
||||
from stevedore import named
|
||||
from billingstack.openstack.common import log as logging
|
||||
from billingstack.api.v1.resources import bp as v1_bp
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
cfg.CONF.register_opts([
|
||||
cfg.ListOpt('enabled-extensions-v1', default=[],
|
||||
help='Enabled API Extensions'),
|
||||
], group='service:api')
|
||||
|
||||
|
||||
def factory(global_config, **local_conf):
|
||||
app = flask.Flask('billingstack.api.v1')
|
||||
|
||||
app.config.update(
|
||||
PROPAGATE_EXCEPTIONS=True
|
||||
)
|
||||
|
||||
app.register_blueprint(v1_bp)
|
||||
|
||||
# TODO(kiall): Ideally, we want to make use of the Plugin class here.
|
||||
# This works for the moment though.
|
||||
def _register_blueprint(ext):
|
||||
app.register_blueprint(ext.plugin)
|
||||
|
||||
# Add any (enabled) optional extensions
|
||||
extensions = cfg.CONF['service:api'].enabled_extensions_v1
|
||||
|
||||
if len(extensions) > 0:
|
||||
extmgr = named.NamedExtensionManager('billingstack.api.v1.extensions',
|
||||
names=extensions)
|
||||
extmgr.map(_register_blueprint)
|
||||
|
||||
return app
|
@ -1,728 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from flask import request
|
||||
from flask import Response
|
||||
|
||||
from billingstack.api.base import Rest, Query
|
||||
from billingstack.api.v1 import models
|
||||
from billingstack.biller.rpcapi import biller_api
|
||||
from billingstack.central.rpcapi import central_api
|
||||
from billingstack.rater.rpcapi import rater_api
|
||||
|
||||
from wsmeext.flask import signature
|
||||
|
||||
|
||||
bp = Rest('v1', __name__)
|
||||
|
||||
|
||||
def _query_to_criterion(query, storage_func=None, **kw):
|
||||
"""
|
||||
Iterate over the query checking against the valid signatures (later).
|
||||
|
||||
:param query: A list of queries.
|
||||
:param storage_func: The name of the storage function to very against.
|
||||
"""
|
||||
translation = {
|
||||
'customer': 'customer_id'
|
||||
}
|
||||
|
||||
criterion = {}
|
||||
for q in query:
|
||||
key = translation.get(q.field, q.field)
|
||||
criterion[key] = q.as_dict()
|
||||
|
||||
criterion.update(kw)
|
||||
|
||||
return criterion
|
||||
|
||||
|
||||
# Currencies
|
||||
@bp.post('/currencies')
|
||||
@signature(models.Currency, body=models.Currency)
|
||||
def create_currency(body):
|
||||
row = central_api.create_currency(
|
||||
request.environ['context'], body.to_db())
|
||||
return models.Currency.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/currencies')
|
||||
@signature([models.Currency], [Query])
|
||||
def list_currencies(q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_currencies(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Currency.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/currencies/<currency_id>')
|
||||
@signature(models.Currency, str)
|
||||
def get_currency(currency_id):
|
||||
row = central_api.get_currency(request.environ['context'],
|
||||
currency_id)
|
||||
|
||||
return models.Currency.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/currencies/<currency_id>')
|
||||
@signature(models.Currency, str, body=models.Currency)
|
||||
def update_currency(currency_id, body):
|
||||
row = central_api.update_currency(
|
||||
request.environ['context'],
|
||||
currency_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Currency.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/currencies/<currency_id>')
|
||||
def delete_currency(currency_id):
|
||||
central_api.delete_currency(request.environ['context'], currency_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Language
|
||||
@bp.post('/languages')
|
||||
@signature(models.Language, body=models.Language)
|
||||
def create_language(body):
|
||||
row = central_api.create_language(request.environ['context'],
|
||||
body.to_db())
|
||||
|
||||
return models.Language.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/languages')
|
||||
@signature([models.Language], [Query])
|
||||
def list_languages(q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_languages(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Language.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/languages/<language_id>')
|
||||
@signature(models.Language, str)
|
||||
def get_language(language_id):
|
||||
row = central_api.get_language(request.environ['context'],
|
||||
language_id)
|
||||
|
||||
return models.Language.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/languages/<language_id>')
|
||||
@signature(models.Language, str, body=models.Language)
|
||||
def update_language(language_id, body):
|
||||
row = central_api.update_language(
|
||||
request.environ['context'],
|
||||
language_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Language.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/languages/<language_id>')
|
||||
def delete_language(language_id):
|
||||
central_api.delete_language(request.environ['context'], language_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# PGP / PGM
|
||||
@bp.get('/payment-gateway-providers')
|
||||
@signature([models.PGProvider], [Query])
|
||||
def list_pg_providers(q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_pg_providers(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.PGProvider.from_db, rows)
|
||||
|
||||
|
||||
# invoice_states
|
||||
@bp.post('/invoice-states')
|
||||
@signature(models.InvoiceState, body=models.InvoiceState)
|
||||
def create_invoice_state(body):
|
||||
row = biller_api.create_invoice_state(
|
||||
request.environ['context'], body.to_db())
|
||||
|
||||
return models.InvoiceState.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/invoice-states')
|
||||
@signature([models.InvoiceState], [Query])
|
||||
def list_invoice_states(q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = biller_api.list_invoice_states(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.InvoiceState.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/invoice-states/<state_id>')
|
||||
@signature(models.InvoiceState, str,)
|
||||
def get_invoice_state(state_id):
|
||||
row = biller_api.get_invoice_state(request.environ['context'],
|
||||
state_id)
|
||||
|
||||
return models.InvoiceState.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/invoice-states/<state_id>')
|
||||
@signature(models.InvoiceState, str, body=models.InvoiceState)
|
||||
def update_invoice_state(state_id, body):
|
||||
row = biller_api.update_invoice_state(
|
||||
request.environ['context'],
|
||||
state_id,
|
||||
body.to_db())
|
||||
|
||||
return models.InvoiceState.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/invoice-states/<state_id>')
|
||||
def delete_invoice_state(state_id):
|
||||
biller_api.delete_invoice_state(
|
||||
request.environ['context'],
|
||||
state_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# merchants
|
||||
@bp.post('/merchants')
|
||||
@signature(models.Merchant, body=models.Merchant)
|
||||
def create_merchant(body):
|
||||
row = central_api.create_merchant(request.environ['context'],
|
||||
body.to_db())
|
||||
|
||||
return models.Merchant.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants')
|
||||
@signature([models.Merchant], [Query])
|
||||
def list_merchants(q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_merchants(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Merchant.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>')
|
||||
@signature(models.Merchant, str)
|
||||
def get_merchant(merchant_id):
|
||||
row = central_api.get_merchant(request.environ['context'],
|
||||
merchant_id)
|
||||
|
||||
return models.Merchant.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>')
|
||||
@signature(models.Merchant, str, body=models.Merchant)
|
||||
def update_merchant(merchant_id, body):
|
||||
row = central_api.update_merchant(
|
||||
request.environ['context'],
|
||||
merchant_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Merchant.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>')
|
||||
def delete_merchant(merchant_id):
|
||||
central_api.delete_merchant(request.environ['context'], merchant_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Invoices
|
||||
@bp.post('/merchants/<merchant_id>/payment-gateways')
|
||||
@signature(models.PGConfig, str, body=models.PGConfig)
|
||||
def create_payment_gateway(merchant_id, body):
|
||||
row = central_api.create_pg_config(
|
||||
request.environ['context'],
|
||||
merchant_id,
|
||||
body.to_db())
|
||||
|
||||
return models.PGConfig.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/payment-gateways')
|
||||
@signature([models.PGConfig], str, [Query])
|
||||
def list_payment_gateways(merchant_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id)
|
||||
|
||||
rows = central_api.list_pg_configs(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.PGConfig.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/payment-gateways/<pg_config_id>')
|
||||
@signature(models.PGConfig, str, str)
|
||||
def get_payment_gateway(merchant_id, pg_config_id):
|
||||
row = central_api.get_pg_config(request.environ['context'], pg_config_id)
|
||||
|
||||
return models.PGConfig.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/payment-gateways/<pg_config_id>')
|
||||
@signature(models.PGConfig, str, str, body=models.PGConfig)
|
||||
def update_payment_gateway(merchant_id, pg_config_id, body):
|
||||
row = central_api.update_pg_config(
|
||||
request.environ['context'],
|
||||
pg_config_id,
|
||||
body.to_db())
|
||||
|
||||
return models.PGConfig.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/payment-gateways/<pg_config_id>')
|
||||
def delete_pg_config(merchant_id, pg_config_id):
|
||||
central_api.delete_pg_config(
|
||||
request.environ['context'],
|
||||
pg_config_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# customers
|
||||
@bp.post('/merchants/<merchant_id>/customers')
|
||||
@signature(models.Customer, str, body=models.Customer)
|
||||
def create_customer(merchant_id, body):
|
||||
row = central_api.create_customer(
|
||||
request.environ['context'],
|
||||
merchant_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Customer.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/customers')
|
||||
@signature([models.Customer], str, [Query])
|
||||
def list_customers(merchant_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id)
|
||||
|
||||
rows = central_api.list_customers(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Customer.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/customers/<customer_id>')
|
||||
@signature(models.Customer, str, str)
|
||||
def get_customer(merchant_id, customer_id):
|
||||
row = central_api.get_customer(request.environ['context'],
|
||||
customer_id)
|
||||
|
||||
return models.Customer.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/customers/<customer_id>')
|
||||
@signature(models.Customer, str, str, body=models.Customer)
|
||||
def update_customer(merchant_id, customer_id, body):
|
||||
row = central_api.update_customer(
|
||||
request.environ['context'],
|
||||
customer_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Customer.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/customers/<customer_id>')
|
||||
def delete_customer(merchant_id, customer_id):
|
||||
central_api.delete_customer(request.environ['context'], customer_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# PaymentMethods
|
||||
@bp.post('/merchants/<merchant_id>/customers/<customer_id>/payment-methods')
|
||||
@signature(models.PaymentMethod, str, str, body=models.PaymentMethod)
|
||||
def create_payment_method(merchant_id, customer_id, body):
|
||||
row = central_api.create_payment_method(
|
||||
request.environ['context'],
|
||||
customer_id,
|
||||
body.to_db())
|
||||
|
||||
return models.PaymentMethod.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/customers/<customer_id>/payment-methods')
|
||||
@signature([models.PaymentMethod], str, str, [Query])
|
||||
def list_payment_methods(merchant_id, customer_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id,
|
||||
customer_id=customer_id)
|
||||
|
||||
rows = central_api.list_payment_methods(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.PaymentMethod.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/customers/<customer_id>/payment-methods/'
|
||||
'<pm_id>')
|
||||
@signature(models.PaymentMethod, str, str, str)
|
||||
def get_payment_method(merchant_id, customer_id, pm_id):
|
||||
row = central_api.get_payment_method(request.environ['context'], pm_id)
|
||||
|
||||
return models.PaymentMethod.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/customers/<customer_id>/payment-methods/'
|
||||
'<pm_id>')
|
||||
@signature(models.PaymentMethod, str, str, str, body=models.PaymentMethod)
|
||||
def update_payment_method(merchant_id, customer_id, pm_id, body):
|
||||
row = central_api.update_payment_method(request.environ['context'], pm_id,
|
||||
body.to_db())
|
||||
|
||||
return models.PaymentMethod.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/customers/<customer_id>/payment-methods/'
|
||||
'<pm_id>')
|
||||
def delete_payment_method(merchant_id, customer_id, pm_id):
|
||||
central_api.delete_payment_method(request.environ['context'], pm_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Plans
|
||||
@bp.post('/merchants/<merchant_id>/plans')
|
||||
@signature(models.Plan, str, body=models.Plan)
|
||||
def create_plan(merchant_id, body):
|
||||
row = central_api.create_plan(
|
||||
request.environ['context'],
|
||||
merchant_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Plan.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/plans')
|
||||
@signature([models.Plan], str, [Query])
|
||||
def list_plans(merchant_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id)
|
||||
|
||||
rows = central_api.list_plans(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Plan.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/plans/<plan_id>')
|
||||
@signature(models.Plan, str, str)
|
||||
def get_plan(merchant_id, plan_id):
|
||||
row = central_api.get_plan(request.environ['context'],
|
||||
plan_id)
|
||||
|
||||
return models.Plan.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/plans/<plan_id>')
|
||||
@signature(models.Plan, str, str, body=models.Plan)
|
||||
def update_plan(merchant_id, plan_id, body):
|
||||
row = central_api.update_plan(
|
||||
request.environ['context'],
|
||||
plan_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Plan.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/plans/<plan_id>')
|
||||
def delete_plan(merchant_id, plan_id):
|
||||
central_api.delete_plan(request.environ['context'], plan_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Plan Item
|
||||
@bp.put('/merchants/<merchant_id>/plans/<plan_id>/items/<product_id>')
|
||||
@signature(models.PlanItem, str, str, str)
|
||||
def add_plan_item(merchant_id, plan_id, product_id):
|
||||
values = {
|
||||
'plan_id': plan_id,
|
||||
'product_id': product_id
|
||||
}
|
||||
|
||||
row = central_api.create_plan_item(request.environ['context'], values)
|
||||
|
||||
return models.PlanItem.from_db(row)
|
||||
|
||||
|
||||
@bp.patch('/merchants/<merchant_id>/plans/<plan_id>/items/<product_id>')
|
||||
@signature(models.PlanItem, str, str, str, body=models.PlanItem)
|
||||
def update_plan_item(merchant_id, plan_id, product_id, body):
|
||||
row = central_api.update_plan_item(
|
||||
request.environ['context'], plan_id, product_id, body.to_db())
|
||||
|
||||
return models.PlanItem.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/plans/<plan_id>/items/<product_id>')
|
||||
def delete_plan_item(merchant_id, plan_id, product_id):
|
||||
central_api.delete_plan_item(request.environ['context'],
|
||||
plan_id, product_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Products
|
||||
@bp.post('/merchants/<merchant_id>/products')
|
||||
@signature(models.Product, str, body=models.Product)
|
||||
def create_product(merchant_id, body):
|
||||
row = central_api.create_product(
|
||||
request.environ['context'],
|
||||
merchant_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/products')
|
||||
@signature([models.Product], str, [Query])
|
||||
def list_products(merchant_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id)
|
||||
|
||||
rows = central_api.list_products(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Product.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/products/<product_id>')
|
||||
@signature(models.Product, str, str)
|
||||
def get_product(merchant_id, product_id):
|
||||
row = central_api.get_product(request.environ['context'],
|
||||
product_id)
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/products/<product_id>')
|
||||
@signature(models.Product, str, str, body=models.Product)
|
||||
def update_product(merchant_id, product_id, body):
|
||||
row = central_api.update_product(
|
||||
request.environ['context'],
|
||||
product_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/products/<product_id>')
|
||||
def delete_product(merchant_id, product_id):
|
||||
central_api.delete_product(request.environ['context'], product_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Invoices
|
||||
@bp.post('/merchants/<merchant_id>/invoices')
|
||||
@signature(models.Invoice, str, body=models.Invoice)
|
||||
def create_invoice(merchant_id, body):
|
||||
row = biller_api.create_invoice(
|
||||
request.environ['context'],
|
||||
merchant_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Invoice.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/invoices')
|
||||
@signature([models.InvoiceState], str, [Query])
|
||||
def list_invoices(merchant_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id)
|
||||
|
||||
rows = biller_api.list_invoices(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Invoice.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/invoices/<invoice_id>')
|
||||
@signature(models.Invoice, str, str)
|
||||
def get_invoice(merchant_id, invoice_id):
|
||||
row = biller_api.get_invoice(request.environ['context'],
|
||||
invoice_id)
|
||||
|
||||
return models.Invoice.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/invoices/<invoice_id>')
|
||||
@signature(models.Invoice, str, str, body=models.Invoice)
|
||||
def update_invoice(merchant_id, invoice_id, body):
|
||||
row = biller_api.update_invoice(
|
||||
request.environ['context'],
|
||||
invoice_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Invoice.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/invoices/<invoice_id>')
|
||||
def delete_invoice(merchant_id, invoice_id):
|
||||
biller_api.delete_invoice(request.environ['context'], invoice_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Products
|
||||
@bp.post('/merchants/<merchant_id>/invoices/<invoice_id>/lines')
|
||||
@signature(models.InvoiceLine, str, str, body=models.InvoiceLine)
|
||||
def create_invoice_line(merchant_id, invoice_id, body):
|
||||
row = biller_api.create_invoice_line(
|
||||
request.environ['context'],
|
||||
invoice_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/invoices/<invoice_id>/lines')
|
||||
@signature([models.InvoiceLine], str, str, [Query])
|
||||
def list_invoice_lines(merchant_id, invoice_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id,
|
||||
invoice_id=invoice_id)
|
||||
|
||||
rows = biller_api.list_invoice_lines(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Product.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/invoices/<invoice_id>/lines/<line_id>')
|
||||
@signature(models.InvoiceLine, str, str, str)
|
||||
def get_invoice_line(merchant_id, invoice_id, line_id):
|
||||
row = biller_api.get_invoice_line(request.environ['context'],
|
||||
line_id)
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/invoices/<invoice_id>/lines/<line_id>')
|
||||
@signature(models.InvoiceLine, str, str, str, body=models.InvoiceLine)
|
||||
def update_invoice_line(merchant_id, invoice_id, line_id, body):
|
||||
row = biller_api.update_invoice_line(
|
||||
request.environ['context'],
|
||||
line_id,
|
||||
body.as_dict())
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/invoices/<invoice_id>/lines/<line_id>')
|
||||
def delete_invoice_line(merchant_id, invoice_id, line_id):
|
||||
biller_api.delete_invoice_line(request.environ['context'], line_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Subscription
|
||||
@bp.post('/merchants/<merchant_id>/subscriptions')
|
||||
@signature(models.Subscription, str, body=models.Subscription)
|
||||
def create_subscription(merchant_id, body):
|
||||
row = central_api.create_subscription(
|
||||
request.environ['context'],
|
||||
body.to_db())
|
||||
|
||||
return models.Subscription.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/subscriptions')
|
||||
@signature([models.Subscription], str, [Query])
|
||||
def list_subscriptions(merchant_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id)
|
||||
|
||||
rows = central_api.list_subscriptions(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Subscription.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/subscriptions/<subscription_id>')
|
||||
@signature(models.Subscription, str, str)
|
||||
def get_subscription(merchant_id, subscription_id):
|
||||
row = central_api.get_subscription(request.environ['context'],
|
||||
subscription_id)
|
||||
|
||||
return models.Subscription.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/subscriptions/<subscription_id>')
|
||||
@signature(models.Subscription, str, str, body=models.Subscription)
|
||||
def update_subscription(merchant_id, subscription_id, body):
|
||||
row = central_api.update_subscription(
|
||||
request.environ['context'],
|
||||
subscription_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Subscription.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/subscriptions/<subscription_id>')
|
||||
def delete_subscription(merchant_id, subscription_id):
|
||||
central_api.delete_subscription(
|
||||
request.environ['context'],
|
||||
subscription_id)
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
# Usage
|
||||
@bp.post('/merchants/<merchant_id>/usage')
|
||||
@signature(models.Usage, str, body=models.Usage)
|
||||
def create_usage(merchant_id, body):
|
||||
values = body.to_db()
|
||||
|
||||
values['merchant_id'] = merchant_id
|
||||
row = rater_api.create_usage(request.environ['context'], values)
|
||||
|
||||
return models.Usage.from_db(row)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/usage')
|
||||
@signature([models.Usage], str, [Query])
|
||||
def list_usages(merchant_id, q=[]):
|
||||
criterion = _query_to_criterion(q, merchant_id=merchant_id)
|
||||
|
||||
rows = rater_api.list_usages(
|
||||
request.environ['context'], criterion=criterion)
|
||||
|
||||
return map(models.Usage.from_db, rows)
|
||||
|
||||
|
||||
@bp.get('/merchants/<merchant_id>/usage/<usage_id>')
|
||||
@signature([models.Usage], str, str)
|
||||
def get_usage(merchant_id, usage_id):
|
||||
row = rater_api.get_usage(request.environ['context'],
|
||||
usage_id)
|
||||
|
||||
return models.Usage.from_db(row)
|
||||
|
||||
|
||||
@bp.put('/merchants/<merchant_id>/usage/<usage_id>')
|
||||
@signature(models.Usage, str, str, body=models.Usage)
|
||||
def update_usage(merchant_id, usage_id, body):
|
||||
row = rater_api.update_usage(
|
||||
request.environ['context'],
|
||||
usage_id,
|
||||
body.to_db())
|
||||
|
||||
return models.Usage.from_db(row)
|
||||
|
||||
|
||||
@bp.delete('/merchants/<merchant_id>/usage/<usage_id>')
|
||||
def delete_usage(merchant_id, usage_id):
|
||||
rater_api.delete_usage(
|
||||
request.environ['context'],
|
||||
usage_id)
|
||||
return Response(status=204)
|
@ -1,6 +1,6 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
@ -13,17 +13,6 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
from billingstack.openstack.common import wsgi
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
class Middleware(wsgi.Middleware):
|
||||
@classmethod
|
||||
def factory(cls, global_config, **local_conf):
|
||||
""" Used for paste app factories in paste.deploy config files """
|
||||
|
||||
def _factory(app):
|
||||
return cls(app, **local_conf)
|
||||
|
||||
return _factory
|
||||
cfg.CONF.import_opt('state_path', 'billingstack.paths')
|
@ -1,6 +1,6 @@
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P. All Rights Reserved.
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@hp.com>
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
@ -13,21 +13,3 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
import flask
|
||||
|
||||
|
||||
def factory(global_config, **local_conf):
|
||||
app = flask.Flask('billingstack.api.versions')
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
def version_list():
|
||||
return flask.jsonify({
|
||||
"versions": [{
|
||||
"id": "v1",
|
||||
"status": "CURRENT"
|
||||
}]
|
||||
})
|
||||
|
||||
return app
|
67
billingstack/api/v2/controllers/currency.py
Normal file
67
billingstack/api/v2/controllers/currency.py
Normal file
@ -0,0 +1,67 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.central.rpcapi import central_api
|
||||
|
||||
|
||||
class CurrencyController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Currency)
|
||||
def get_all(self):
|
||||
row = central_api.get_currency(request.ctxt, self.id_)
|
||||
|
||||
return models.Currency.from_db(row)
|
||||
|
||||
@wsme.validate(models.Currency)
|
||||
@wsme_pecan.wsexpose(models.Currency, body=models.Currency)
|
||||
def patch(self, body):
|
||||
row = central_api.update_currency(request.ctxt, self.id_, body.to_db())
|
||||
return models.Currency.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_currency(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class CurrenciesController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, currency_id, *remainder):
|
||||
return CurrencyController(currency_id), remainder
|
||||
|
||||
@wsme.validate(models.Currency)
|
||||
@wsme_pecan.wsexpose(models.Currency, body=models.Currency,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_currency(request.ctxt, body.to_db())
|
||||
|
||||
return models.Currency.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Currency], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_currencies(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Currency.from_db, rows)
|
74
billingstack/api/v2/controllers/customer.py
Normal file
74
billingstack/api/v2/controllers/customer.py
Normal file
@ -0,0 +1,74 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.api.v2.controllers.payment import PaymentMethodsController
|
||||
from billingstack.central.rpcapi import central_api
|
||||
|
||||
|
||||
class CustomerController(RestController):
|
||||
payment_methods = PaymentMethodsController()
|
||||
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['customer_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Customer)
|
||||
def get_all(self):
|
||||
row = central_api.get_customer(request.ctxt, self.id_)
|
||||
|
||||
return models.Customer.from_db(row)
|
||||
|
||||
@wsme.validate(models.Customer)
|
||||
@wsme_pecan.wsexpose(models.Customer, body=models.Customer)
|
||||
def patch(self, body):
|
||||
row = central_api.update_customer(request.ctxt, self.id_, body.to_db())
|
||||
return models.Customer.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_customer(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class CustomersController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, customer_id, *remainder):
|
||||
return CustomerController(customer_id), remainder
|
||||
|
||||
@wsme.validate(models.Customer)
|
||||
@wsme_pecan.wsexpose(models.Customer, body=models.Customer,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_customer(
|
||||
request.ctxt,
|
||||
request.context['merchant_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.Customer.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Customer], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_customers(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Customer.from_db, rows)
|
73
billingstack/api/v2/controllers/invoice.py
Normal file
73
billingstack/api/v2/controllers/invoice.py
Normal file
@ -0,0 +1,73 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.biller.rpcapi import biller_api
|
||||
|
||||
|
||||
class InvoiceController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['invoice_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Invoice)
|
||||
def get_all(self):
|
||||
row = biller_api.get_invoice(request.ctxt, self.id_)
|
||||
|
||||
return models.Invoice.from_db(row)
|
||||
|
||||
@wsme.validate(models.Invoice)
|
||||
@wsme_pecan.wsexpose(models.Invoice, body=models.Invoice)
|
||||
def patch(self, body):
|
||||
row = biller_api.update_invoice(request.ctxt, self.id_, body.to_db())
|
||||
|
||||
return models.Invoice.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
biller_api.delete_invoice(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class InvoicesController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, invoice_id, *remainder):
|
||||
return InvoiceController(invoice_id), remainder
|
||||
|
||||
@wsme.validate(models.Invoice)
|
||||
@wsme_pecan.wsexpose(models.Invoice, body=models.Invoice, status_code=202)
|
||||
def post(self, body):
|
||||
row = biller_api.create_invoice(
|
||||
request.ctxt,
|
||||
request.context['merchant_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.Invoice.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Invoice], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(
|
||||
q,
|
||||
merchant_id=request.context['merchant_id'])
|
||||
|
||||
rows = biller_api.list_invoices(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Invoice.from_db, rows)
|
68
billingstack/api/v2/controllers/invoice_state.py
Normal file
68
billingstack/api/v2/controllers/invoice_state.py
Normal file
@ -0,0 +1,68 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.biller.rpcapi import biller_api
|
||||
|
||||
|
||||
class InvoiceStateController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.InvoiceState)
|
||||
def get_all(self):
|
||||
row = biller_api.get_invoice_state(request.ctxt, self.id_)
|
||||
|
||||
return models.InvoiceState.from_db(row)
|
||||
|
||||
@wsme.validate(models.InvoiceState)
|
||||
@wsme_pecan.wsexpose(models.InvoiceState, body=models.InvoiceState)
|
||||
def patch(self, body):
|
||||
row = biller_api.update_invoice_state(
|
||||
request.ctxt, self.id_, body.to_db())
|
||||
return models.InvoiceState.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
biller_api.delete_invoice_state(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class InvoiceStatesController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, invoice_state_id, *remainder):
|
||||
return InvoiceStateController(invoice_state_id), remainder
|
||||
|
||||
@wsme.validate(models.InvoiceState)
|
||||
@wsme_pecan.wsexpose(models.InvoiceState, body=models.InvoiceState,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = biller_api.create_invoice_state(request.ctxt, body.to_db())
|
||||
|
||||
return models.InvoiceState.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.InvoiceState], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = biller_api.list_invoice_states(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.InvoiceState.from_db, rows)
|
67
billingstack/api/v2/controllers/language.py
Normal file
67
billingstack/api/v2/controllers/language.py
Normal file
@ -0,0 +1,67 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.central.rpcapi import central_api
|
||||
|
||||
|
||||
class LanguageController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Language)
|
||||
def get_all(self):
|
||||
row = central_api.get_language(request.ctxt, self.id_)
|
||||
|
||||
return models.Language.from_db(row)
|
||||
|
||||
@wsme.validate(models.InvoiceState)
|
||||
@wsme_pecan.wsexpose(models.Language, body=models.Language)
|
||||
def patch(self, body):
|
||||
row = central_api.update_language(request.ctxt, self.id_, body.to_db())
|
||||
return models.Language.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_language(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class LanguagesController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, language_id, *remainder):
|
||||
return LanguageController(language_id), remainder
|
||||
|
||||
@wsme.validate(models.InvoiceState)
|
||||
@wsme_pecan.wsexpose(models.Language, body=models.Language,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_language(request.ctxt, body.to_db())
|
||||
|
||||
return models.Language.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Language], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_languages(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Language.from_db, rows)
|
85
billingstack/api/v2/controllers/merchant.py
Normal file
85
billingstack/api/v2/controllers/merchant.py
Normal file
@ -0,0 +1,85 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.central.rpcapi import central_api
|
||||
from billingstack.api.v2.controllers.customer import CustomersController
|
||||
from billingstack.api.v2.controllers.payment import PGConfigsController
|
||||
from billingstack.api.v2.controllers.plan import PlansController
|
||||
from billingstack.api.v2.controllers.product import ProductsController
|
||||
from billingstack.api.v2.controllers.subscription import \
|
||||
SubscriptionsController
|
||||
from billingstack.api.v2.controllers.invoice import InvoicesController
|
||||
from billingstack.api.v2.controllers.usage import UsagesController
|
||||
|
||||
|
||||
class MerchantController(RestController):
|
||||
customers = CustomersController()
|
||||
payment_gateway_configurations = PGConfigsController()
|
||||
plans = PlansController()
|
||||
products = ProductsController()
|
||||
subscriptions = SubscriptionsController()
|
||||
|
||||
invoices = InvoicesController()
|
||||
usage = UsagesController()
|
||||
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['merchant_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Merchant)
|
||||
def get_all(self):
|
||||
row = central_api.get_merchant(request.ctxt, self.id_)
|
||||
|
||||
return models.Merchant.from_db(row)
|
||||
|
||||
@wsme.validate(models.InvoiceState)
|
||||
@wsme_pecan.wsexpose(models.Merchant, body=models.Merchant)
|
||||
def patch(self, body):
|
||||
row = central_api.update_merchant(request.ctxt, self.id_, body.to_db())
|
||||
return models.Merchant.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_merchant(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class MerchantsController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, merchant_id, *remainder):
|
||||
return MerchantController(merchant_id), remainder
|
||||
|
||||
@wsme.validate(models.Merchant)
|
||||
@wsme_pecan.wsexpose(models.Merchant, body=models.Merchant,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_merchant(request.ctxt, body.to_db())
|
||||
|
||||
return models.Merchant.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Merchant], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_merchants(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Merchant.from_db, rows)
|
139
billingstack/api/v2/controllers/payment.py
Normal file
139
billingstack/api/v2/controllers/payment.py
Normal file
@ -0,0 +1,139 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.central.rpcapi import central_api
|
||||
|
||||
|
||||
class PGProviders(RestController):
|
||||
@wsme_pecan.wsexpose([models.PGProvider], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(q)
|
||||
|
||||
rows = central_api.list_pg_providers(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.PGProvider.from_db, rows)
|
||||
|
||||
|
||||
class PGConfigController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.PGConfig)
|
||||
def get_all(self):
|
||||
row = central_api.get_pg_config(request.ctxt, self.id_)
|
||||
|
||||
return models.PGConfig.from_db(row)
|
||||
|
||||
@wsme.validate(models.PGConfig)
|
||||
@wsme_pecan.wsexpose(models.PGConfig, body=models.PGConfig)
|
||||
def patch(self, body):
|
||||
row = central_api.update_pg_config(
|
||||
request.ctxt,
|
||||
self.id_,
|
||||
body.to_db())
|
||||
|
||||
return models.PGConfig.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_pg_config(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class PGConfigsController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, method_id, *remainder):
|
||||
return PGConfigController(method_id), remainder
|
||||
|
||||
@wsme.validate(models.PGConfig)
|
||||
@wsme_pecan.wsexpose(models.PGConfig, body=models.PGConfig,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_pg_config(
|
||||
request.ctxt,
|
||||
request.context['merchant_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.PGConfig.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.PGConfig], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(
|
||||
q, merchant_id=request.context['merchant_id'])
|
||||
|
||||
rows = central_api.list_pg_configs(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.PGConfig.from_db, rows)
|
||||
|
||||
|
||||
class PaymentMethodController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['payment_method_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.PaymentMethod)
|
||||
def get_all(self):
|
||||
row = central_api.get_payment_method(request.ctxt, self.id_)
|
||||
|
||||
return models.PaymentMethod.from_db(row)
|
||||
|
||||
@wsme.validate(models.PaymentMethod)
|
||||
@wsme_pecan.wsexpose(models.PaymentMethod, body=models.PaymentMethod)
|
||||
def patch(self, body):
|
||||
row = central_api.update_payment_method(
|
||||
request.ctxt,
|
||||
self.id_,
|
||||
body.to_db())
|
||||
|
||||
return models.PaymentMethod.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_payment_method(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class PaymentMethodsController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, method_id, *remainder):
|
||||
return PaymentMethodController(method_id), remainder
|
||||
|
||||
@wsme.validate(models.PaymentMethod)
|
||||
@wsme_pecan.wsexpose(models.PaymentMethod, body=models.PaymentMethod,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_payment_method(
|
||||
request.ctxt,
|
||||
request.context['customer_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.PaymentMethod.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.PaymentMethod], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(
|
||||
q, merchant_id=request.context['merchant_id'],
|
||||
customer_id=request.context['customer_id'])
|
||||
|
||||
rows = central_api.list_payment_methods(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.PaymentMethod.from_db, rows)
|
116
billingstack/api/v2/controllers/plan.py
Normal file
116
billingstack/api/v2/controllers/plan.py
Normal file
@ -0,0 +1,116 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.central.rpcapi import central_api
|
||||
|
||||
|
||||
class ItemController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
|
||||
@wsme.validate(models.PlanItem)
|
||||
@wsme_pecan.wsexpose(models.PlanItem, body=models.PlanItem)
|
||||
def put(self, body):
|
||||
values = {
|
||||
'plan_id': request.context['plan_id'],
|
||||
'product_id': self.id_
|
||||
}
|
||||
|
||||
row = central_api.create_plan_item(request.ctxt, values)
|
||||
|
||||
return models.PlanItem.from_db(row)
|
||||
|
||||
@wsme.validate(models.PlanItem)
|
||||
@wsme_pecan.wsexpose(models.PlanItem, body=models.PlanItem)
|
||||
def patch(self, body):
|
||||
row = central_api.update_plan_item(
|
||||
request.ctxt,
|
||||
request.context['plan_id'],
|
||||
self.id_,
|
||||
body.to_db())
|
||||
|
||||
return models.PlanItem.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self, id_):
|
||||
central_api.delete_plan_item(
|
||||
request.ctxt,
|
||||
request.context['plan_id'],
|
||||
id_)
|
||||
|
||||
|
||||
class ItemsController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, id_, *remainder):
|
||||
return ItemController(id_), remainder
|
||||
|
||||
|
||||
class PlanController(RestController):
|
||||
items = ItemsController()
|
||||
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['plan_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Plan)
|
||||
def get_all(self):
|
||||
row = central_api.get_plan(request.ctxt, self.id_)
|
||||
|
||||
return models.Plan.from_db(row)
|
||||
|
||||
@wsme.validate(models.Plan)
|
||||
@wsme_pecan.wsexpose(models.Plan, body=models.Plan)
|
||||
def patch(self, body):
|
||||
row = central_api.update_plan(request.ctxt, self.id_, body.to_db())
|
||||
|
||||
return models.Plan.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_plan(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class PlansController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, plan_id, *remainder):
|
||||
return PlanController(plan_id), remainder
|
||||
|
||||
@wsme.validate(models.Plan)
|
||||
@wsme_pecan.wsexpose(models.Plan, body=models.Plan, status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_plan(
|
||||
request.ctxt,
|
||||
request.context['merchant_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.Plan.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Plan], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(
|
||||
q,
|
||||
merchant_id=request.context['merchant_id'])
|
||||
|
||||
rows = central_api.list_plans(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Plan.from_db, rows)
|
74
billingstack/api/v2/controllers/product.py
Normal file
74
billingstack/api/v2/controllers/product.py
Normal file
@ -0,0 +1,74 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.central.rpcapi import central_api
|
||||
|
||||
|
||||
class ProductController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['product_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Product)
|
||||
def get_all(self):
|
||||
row = central_api.get_product(request.ctxt, self.id_)
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
@wsme.validate(models.Product)
|
||||
@wsme_pecan.wsexpose(models.Product, body=models.Product)
|
||||
def patch(self, body):
|
||||
row = central_api.update_product(request.ctxt, self.id_, body.to_db())
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_product(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class ProductsController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, product_id, *remainder):
|
||||
return ProductController(product_id), remainder
|
||||
|
||||
@wsme.validate(models.Product)
|
||||
@wsme_pecan.wsexpose(models.Product, body=models.Product,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_product(
|
||||
request.ctxt,
|
||||
request.context['merchant_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.Product.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Product], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(
|
||||
q,
|
||||
merchant_id=request.context['merchant_id'])
|
||||
|
||||
rows = central_api.list_products(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Product.from_db, rows)
|
42
billingstack/api/v2/controllers/root.py
Normal file
42
billingstack/api/v2/controllers/root.py
Normal file
@ -0,0 +1,42 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from billingstack.openstack.common import log
|
||||
from billingstack.api.v2.controllers.currency import CurrenciesController
|
||||
from billingstack.api.v2.controllers.language import LanguagesController
|
||||
from billingstack.api.v2.controllers.merchant import MerchantsController
|
||||
from billingstack.api.v2.controllers.invoice_state import \
|
||||
InvoiceStatesController
|
||||
from billingstack.api.v2.controllers.payment import PGProviders
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class V2Controller(object):
|
||||
# Central
|
||||
currencies = CurrenciesController()
|
||||
languages = LanguagesController()
|
||||
merchants = MerchantsController()
|
||||
|
||||
# Biller
|
||||
invoice_states = InvoiceStatesController()
|
||||
|
||||
# Collector
|
||||
payment_gateway_providers = PGProviders()
|
||||
|
||||
|
||||
class RootController(object):
|
||||
v2 = V2Controller()
|
75
billingstack/api/v2/controllers/subscription.py
Normal file
75
billingstack/api/v2/controllers/subscription.py
Normal file
@ -0,0 +1,75 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.central.rpcapi import central_api
|
||||
|
||||
|
||||
class SubscriptionController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['subscription_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Subscription)
|
||||
def get_all(self):
|
||||
row = central_api.get_subscription(request.ctxt, self.id_)
|
||||
|
||||
return models.Subscription.from_db(row)
|
||||
|
||||
@wsme.validate(models.Subscription)
|
||||
@wsme_pecan.wsexpose(models.Subscription, body=models.Subscription)
|
||||
def patch(self, body):
|
||||
row = central_api.update_subscription(request.ctxt, self.id_,
|
||||
body.to_db())
|
||||
|
||||
return models.Subscription.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
central_api.delete_subscription(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class SubscriptionsController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, subscription_id, *remainder):
|
||||
return SubscriptionController(subscription_id), remainder
|
||||
|
||||
@wsme.validate(models.Subscription)
|
||||
@wsme_pecan.wsexpose(models.Subscription, body=models.Subscription,
|
||||
status_code=202)
|
||||
def post(self, body):
|
||||
row = central_api.create_subscription(
|
||||
request.ctxt,
|
||||
request.context['merchant_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.Subscription.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Subscription], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(
|
||||
q,
|
||||
merchant_id=request.context['merchant_id'])
|
||||
|
||||
rows = central_api.list_subscriptions(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Subscription.from_db, rows)
|
73
billingstack/api/v2/controllers/usage.py
Normal file
73
billingstack/api/v2/controllers/usage.py
Normal file
@ -0,0 +1,73 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from pecan import expose, request
|
||||
import wsme
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
|
||||
from billingstack.api.base import Query, _query_to_criterion, RestController
|
||||
from billingstack.api.v2 import models
|
||||
from billingstack.rater.rpcapi import rater_api
|
||||
|
||||
|
||||
class UsageController(RestController):
|
||||
def __init__(self, id_):
|
||||
self.id_ = id_
|
||||
request.context['usage_id'] = id_
|
||||
|
||||
@wsme_pecan.wsexpose(models.Usage)
|
||||
def get_all(self):
|
||||
row = rater_api.get_usage(request.ctxt, self.id_)
|
||||
|
||||
return models.Usage.from_db(row)
|
||||
|
||||
@wsme.validate(models.Usage)
|
||||
@wsme_pecan.wsexpose(models.Usage, body=models.Usage)
|
||||
def patch(self, body):
|
||||
row = rater_api.update_usage(request.ctxt, self.id_, body.to_db())
|
||||
|
||||
return models.Usage.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose(None, status_code=204)
|
||||
def delete(self):
|
||||
rater_api.delete_usage(request.ctxt, self.id_)
|
||||
|
||||
|
||||
class UsagesController(RestController):
|
||||
@expose()
|
||||
def _lookup(self, usage_id, *remainder):
|
||||
return UsageController(usage_id), remainder
|
||||
|
||||
@wsme.validate(models.Usage)
|
||||
@wsme_pecan.wsexpose(models.Usage, body=models.Usage, status_code=202)
|
||||
def post(self, body):
|
||||
row = rater_api.create_usage(
|
||||
request.ctxt,
|
||||
request.context['merchant_id'],
|
||||
body.to_db())
|
||||
|
||||
return models.Usage.from_db(row)
|
||||
|
||||
@wsme_pecan.wsexpose([models.Usage], [Query])
|
||||
def get_all(self, q=[]):
|
||||
criterion = _query_to_criterion(
|
||||
q,
|
||||
merchant_id=request.context['merchant_id'])
|
||||
|
||||
rows = rater_api.list_usages(
|
||||
request.ctxt, criterion=criterion)
|
||||
|
||||
return map(models.Usage.from_db, rows)
|
@ -16,9 +16,8 @@
|
||||
"""
|
||||
Base classes for API tests.
|
||||
"""
|
||||
from billingstack.api.v1 import factory
|
||||
from billingstack.api.middleware.errors import FaultWrapperMiddleware
|
||||
from billingstack.api.auth import NoAuthContextMiddleware
|
||||
import pecan.testing
|
||||
|
||||
from billingstack.openstack.common import jsonutils as json
|
||||
from billingstack.openstack.common import log
|
||||
from billingstack.tests.base import ServiceTestCase
|
||||
@ -44,15 +43,9 @@ class APITestMixin(object):
|
||||
def make_path(self, path):
|
||||
path = self._ensure_slash(path)
|
||||
if self.PATH_PREFIX:
|
||||
path = path + self._ensure_slash(self.PATH_PREFIX)
|
||||
path = self._ensure_slash(self.PATH_PREFIX) + path
|
||||
return path
|
||||
|
||||
def load_content(self, response):
|
||||
try:
|
||||
response.json = json.loads(response.data)
|
||||
except ValueError:
|
||||
response.json = None
|
||||
|
||||
def _query(self, queries):
|
||||
query_params = {'q.field': [],
|
||||
'q.value': [],
|
||||
@ -77,17 +70,15 @@ class APITestMixin(object):
|
||||
|
||||
LOG.debug('GET: %s %r', path, all_params)
|
||||
|
||||
response = self.client.get(path,
|
||||
content_type=content_type,
|
||||
query_string=all_params,
|
||||
headers=headers)
|
||||
response = self.app.get(
|
||||
path,
|
||||
params=all_params,
|
||||
headers=headers)
|
||||
|
||||
LOG.debug('GOT RESPONSE: %s', response.data)
|
||||
LOG.debug('GOT RESPONSE: %s', response.body)
|
||||
|
||||
self.assertEqual(response.status_code, status_code)
|
||||
|
||||
self.load_content(response)
|
||||
|
||||
return response
|
||||
|
||||
def post(self, path, data, headers=None, content_type="application/json",
|
||||
@ -97,18 +88,16 @@ class APITestMixin(object):
|
||||
LOG.debug('POST: %s %s', path, data)
|
||||
|
||||
content = json.dumps(data)
|
||||
response = self.client.post(
|
||||
response = self.app.post(
|
||||
path,
|
||||
data=content,
|
||||
content,
|
||||
content_type=content_type,
|
||||
headers=headers)
|
||||
|
||||
LOG.debug('POST RESPONSE: %r' % response.data)
|
||||
LOG.debug('POST RESPONSE: %r' % response.body)
|
||||
|
||||
self.assertEqual(response.status_code, status_code)
|
||||
|
||||
self.load_content(response)
|
||||
|
||||
return response
|
||||
|
||||
def put(self, path, data, headers=None, content_type="application/json",
|
||||
@ -118,17 +107,34 @@ class APITestMixin(object):
|
||||
LOG.debug('PUT: %s %s', path, data)
|
||||
|
||||
content = json.dumps(data)
|
||||
response = self.client.put(
|
||||
response = self.app.put(
|
||||
path,
|
||||
data=content,
|
||||
content,
|
||||
content_type=content_type,
|
||||
headers=headers)
|
||||
|
||||
LOG.debug('PUT RESPONSE: %r' % response.data)
|
||||
LOG.debug('PUT RESPONSE: %r' % response.body)
|
||||
|
||||
self.assertEqual(response.status_code, status_code)
|
||||
|
||||
self.load_content(response)
|
||||
return response
|
||||
|
||||
def patch_(self, path, data, headers=None, content_type="application/json",
|
||||
q=[], status_code=200, **params):
|
||||
path = self.make_path(path)
|
||||
|
||||
LOG.debug('PUT: %s %s', path, data)
|
||||
|
||||
content = json.dumps(data)
|
||||
response = self.app.patch(
|
||||
path,
|
||||
content,
|
||||
content_type=content_type,
|
||||
headers=headers)
|
||||
|
||||
LOG.debug('PATCH RESPONSE: %r', response.body)
|
||||
|
||||
self.assertEqual(response.status_code, status_code)
|
||||
|
||||
return response
|
||||
|
||||
@ -138,9 +144,7 @@ class APITestMixin(object):
|
||||
|
||||
LOG.debug('DELETE: %s %r', path, all_params)
|
||||
|
||||
response = self.client.delete(path, query_string=all_params)
|
||||
|
||||
#LOG.debug('DELETE RESPONSE: %r' % response.body)
|
||||
response = self.app.delete(path, params=all_params)
|
||||
|
||||
self.assertEqual(response.status_code, status_code)
|
||||
|
||||
@ -151,6 +155,7 @@ class FunctionalTest(ServiceTestCase, APITestMixin):
|
||||
"""
|
||||
billingstack.api base test
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(FunctionalTest, self).setUp()
|
||||
|
||||
@ -159,7 +164,13 @@ class FunctionalTest(ServiceTestCase, APITestMixin):
|
||||
self.start_service('central')
|
||||
self.setSamples()
|
||||
|
||||
self.app = factory({})
|
||||
self.app.wsgi_app = FaultWrapperMiddleware(self.app.wsgi_app)
|
||||
self.app.wsgi_app = NoAuthContextMiddleware(self.app.wsgi_app)
|
||||
self.client = self.app.test_client()
|
||||
self.app = self.make_app()
|
||||
|
||||
def make_app(self):
|
||||
self.config = {
|
||||
'app': {
|
||||
'root': 'billingstack.api.v2.controllers.root.RootController',
|
||||
'modules': ['billingstack.api'],
|
||||
}
|
||||
}
|
||||
return pecan.testing.load_test_app(self.config)
|
||||
|
5
billingstack/tests/api/v2/__init__.py
Normal file
5
billingstack/tests/api/v2/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
|
||||
|
||||
class V2Test(FunctionalTest):
|
||||
PATH_PREFIX = '/v2'
|
@ -19,12 +19,12 @@ Test Currency
|
||||
|
||||
import logging
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestCurrency(FunctionalTest):
|
||||
class TestCurrency(V2Test):
|
||||
__test__ = True
|
||||
path = "currencies"
|
||||
|
||||
@ -53,7 +53,7 @@ class TestCurrency(FunctionalTest):
|
||||
_, currency = self.create_currency(fixture=1)
|
||||
|
||||
url = self.item_path(currency['name'])
|
||||
resp = self.put(url, currency)
|
||||
resp = self.patch_(url, currency)
|
||||
|
||||
self.assertData(resp.json, currency)
|
||||
|
@ -17,11 +17,11 @@
|
||||
Test Customers.
|
||||
"""
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.api.v1.models import Customer
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
from billingstack.api.v2.models import Customer
|
||||
|
||||
|
||||
class TestCustomer(FunctionalTest):
|
||||
class TestCustomer(V2Test):
|
||||
__test__ = True
|
||||
path = "merchants/%s/customers"
|
||||
|
||||
@ -69,7 +69,7 @@ class TestCustomer(FunctionalTest):
|
||||
expected['name'] = 'test'
|
||||
|
||||
url = self.item_path(self.merchant['id'], customer['id'])
|
||||
resp = self.put(url, customer)
|
||||
resp = self.patch_(url, customer)
|
||||
|
||||
self.assertData(resp.json, customer)
|
||||
|
@ -19,14 +19,14 @@ Test InvoiceState
|
||||
|
||||
import logging
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestInvoiceState(FunctionalTest):
|
||||
class TestInvoiceState(V2Test):
|
||||
__test__ = True
|
||||
path = "invoice-states"
|
||||
path = "invoice_states"
|
||||
|
||||
def setUp(self):
|
||||
super(TestInvoiceState, self).setUp()
|
||||
@ -59,7 +59,7 @@ class TestInvoiceState(FunctionalTest):
|
||||
_, state = self.create_invoice_state()
|
||||
|
||||
url = self.item_path(state['name'])
|
||||
resp = self.put(url, state)
|
||||
resp = self.patch_(url, state)
|
||||
|
||||
self.assertData(resp.json, state)
|
||||
|
@ -19,12 +19,12 @@ Test Language
|
||||
|
||||
import logging
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestLanguage(FunctionalTest):
|
||||
class TestLanguage(V2Test):
|
||||
__test__ = True
|
||||
path = "languages"
|
||||
|
||||
@ -53,7 +53,7 @@ class TestLanguage(FunctionalTest):
|
||||
_, language = self.create_language(fixture=1)
|
||||
|
||||
url = self.item_path(language['name'])
|
||||
resp = self.put(url, language)
|
||||
resp = self.patch_(url, language)
|
||||
|
||||
self.assertData(resp.json, language)
|
||||
|
@ -17,11 +17,11 @@
|
||||
Test Merchants
|
||||
"""
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.api.v1.models import Merchant
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
from billingstack.api.v2.models import Merchant
|
||||
|
||||
|
||||
class TestMerchant(FunctionalTest):
|
||||
class TestMerchant(V2Test):
|
||||
__test__ = True
|
||||
|
||||
def fixture(self):
|
||||
@ -51,7 +51,7 @@ class TestMerchant(FunctionalTest):
|
||||
def test_update_merchant(self):
|
||||
expected = Merchant.from_db(self.merchant).as_dict()
|
||||
|
||||
resp = self.put('merchants/' + self.merchant['id'], expected)
|
||||
resp = self.patch_('merchants/' + self.merchant['id'], expected)
|
||||
|
||||
self.assertData(expected, resp.json)
|
||||
|
@ -19,14 +19,14 @@ Test Products
|
||||
|
||||
import logging
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestPaymentMethod(FunctionalTest):
|
||||
class TestPaymentMethod(V2Test):
|
||||
__test__ = True
|
||||
path = "merchants/%s/customers/%s/payment-methods"
|
||||
path = "merchants/%s/customers/%s/payment_methods"
|
||||
|
||||
def setUp(self):
|
||||
super(TestPaymentMethod, self).setUp()
|
||||
@ -82,7 +82,7 @@ class TestPaymentMethod(FunctionalTest):
|
||||
self.customer['id'], method['id'])
|
||||
|
||||
expected = dict(fixture, name='test2')
|
||||
resp = self.put(url, expected)
|
||||
resp = self.patch_(url, expected)
|
||||
self.assertData(expected, resp.json)
|
||||
|
||||
def test_delete_payment_method(self):
|
@ -17,10 +17,10 @@
|
||||
Test Plans
|
||||
"""
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
|
||||
|
||||
class TestPlan(FunctionalTest):
|
||||
class TestPlan(V2Test):
|
||||
__test__ = True
|
||||
path = "merchants/%s/plans"
|
||||
|
||||
@ -54,7 +54,7 @@ class TestPlan(FunctionalTest):
|
||||
plan['name'] = 'test'
|
||||
|
||||
url = self.item_path(self.merchant['id'], plan['id'])
|
||||
resp = self.put(url, plan)
|
||||
resp = self.patch_(url, plan)
|
||||
|
||||
self.assertData(resp.json, plan)
|
||||
|
@ -19,12 +19,12 @@ Test Products
|
||||
|
||||
import logging
|
||||
|
||||
from billingstack.tests.api.base import FunctionalTest
|
||||
from billingstack.tests.api.v2 import V2Test
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestProduct(FunctionalTest):
|
||||
class TestProduct(V2Test):
|
||||
__test__ = True
|
||||
path = "merchants/%s/products"
|
||||
|
||||
@ -57,7 +57,7 @@ class TestProduct(FunctionalTest):
|
||||
product['name'] = 'test'
|
||||
|
||||
url = self.item_path(self.merchant['id'], product['id'])
|
||||
resp = self.put(url, product)
|
||||
resp = self.patch_(url, product)
|
||||
|
||||
self.assertData(resp.json, product)
|
||||
|
@ -305,6 +305,17 @@ class BaseTestCase(testtools.TestCase, AssertMixin):
|
||||
_values.update(values)
|
||||
return _values
|
||||
|
||||
def path_get(self, project_file=None):
|
||||
root = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'..',
|
||||
'..',
|
||||
)
|
||||
)
|
||||
if project_file:
|
||||
return os.path.join(root, project_file)
|
||||
else:
|
||||
return root
|
||||
|
||||
|
||||
class Services(dict):
|
||||
def __getattr__(self, name):
|
||||
|
@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied: Moniker
|
||||
import sys
|
||||
import eventlet
|
||||
|
||||
from oslo.config import cfg
|
||||
from billingstack.openstack.common import log as logging
|
||||
from billingstack.openstack.common import service
|
||||
from billingstack import utils
|
||||
from billingstack.api import service as api_service
|
||||
from billingstack.service import prepare_service
|
||||
|
||||
|
||||
prepare_service(sys.argv)
|
||||
|
||||
logging.setup('wsme')
|
||||
|
||||
launcher = service.launch(api_service.Service(),
|
||||
cfg.CONF['service:api'].workers)
|
||||
launcher.wait()
|
@ -1,36 +0,0 @@
|
||||
[composite:bs_api]
|
||||
use = egg:Paste#urlmap
|
||||
/: bs_api_versions
|
||||
/v1: bs_core_api_v1
|
||||
|
||||
[app:bs_api_versions]
|
||||
paste.app_factory = billingstack.api.versions:factory
|
||||
|
||||
[composite:bs_core_api_v1]
|
||||
use = call:billingstack.api.auth:pipeline_factory
|
||||
noauth = noauthcontext faultwrapper bs_core_app_v1
|
||||
keystone = authtoken keystonecontext faultwrapper bs_core_app_v1
|
||||
|
||||
[app:bs_core_app_v1]
|
||||
paste.app_factory = billingstack.api.v1:factory
|
||||
|
||||
[filter:faultwrapper]
|
||||
paste.filter_factory = billingstack.api.middleware.errors:FaultWrapperMiddleware.factory
|
||||
|
||||
[filter:noauthcontext]
|
||||
paste.filter_factory = billingstack.api.auth:NoAuthContextMiddleware.factory
|
||||
|
||||
#[filter:keystonecontext]
|
||||
#paste.filter_factory = billingstack.api.auth:KeystoneContextMiddleware.factory
|
||||
|
||||
[filter:authtoken]
|
||||
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
|
||||
service_protocol = http
|
||||
service_host = 127.0.0.1
|
||||
service_port = 5000
|
||||
auth_host = 127.0.0.1
|
||||
auth_port = 35357
|
||||
auth_protocol = http
|
||||
admin_tenant_name = %SERVICE_TENANT_NAME%
|
||||
admin_user = %SERVICE_USER%
|
||||
admin_password = %SERVICE_PASSWORD%
|
@ -27,10 +27,10 @@ packages =
|
||||
scripts =
|
||||
bin/billingstack-db-manage
|
||||
bin/billingstack-manage
|
||||
bin/billingstack-api
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
billingstack-api = billingstack.api.app:start
|
||||
billingstack-biller = billingstack.biller.service:launch
|
||||
billingstack-central = billingstack.central.service:launch
|
||||
billingstack-collector = billingstack.collector.service:launch
|
||||
|
@ -5,7 +5,7 @@ argparse
|
||||
cliff>=1.4
|
||||
eventlet>=0.12.0
|
||||
extras
|
||||
flask==0.9
|
||||
pecan
|
||||
iso8601>=0.1.4
|
||||
netaddr
|
||||
oslo.config>=1.1.0
|
||||
@ -15,4 +15,4 @@ pycountry
|
||||
routes>=1.12.3
|
||||
stevedore>=0.9
|
||||
webob>=1.2.3,<1.3
|
||||
wsme>=0.5b2
|
||||
https://bitbucket.org/cdevienne/wsme/get/tip.zip#egg=WSME
|
||||
|
Loading…
x
Reference in New Issue
Block a user