Merge pull request #858 from spulec/dashboard

Add a dashboard
This commit is contained in:
Steve Pulec 2017-03-12 20:26:42 -04:00 committed by GitHub
commit 5807a38092
42 changed files with 389 additions and 123 deletions

View File

@ -5,7 +5,7 @@ import datetime
import requests import requests
from moto.packages.responses import responses from moto.packages.responses import responses
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_with_milliseconds
from .utils import create_id from .utils import create_id
from .exceptions import StageNotFoundException from .exceptions import StageNotFoundException
@ -13,7 +13,7 @@ from .exceptions import StageNotFoundException
STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}" STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}"
class Deployment(dict): class Deployment(BaseModel, dict):
def __init__(self, deployment_id, name, description=""): def __init__(self, deployment_id, name, description=""):
super(Deployment, self).__init__() super(Deployment, self).__init__()
@ -24,7 +24,7 @@ class Deployment(dict):
datetime.datetime.now()) datetime.datetime.now())
class IntegrationResponse(dict): class IntegrationResponse(BaseModel, dict):
def __init__(self, status_code, selection_pattern=None): def __init__(self, status_code, selection_pattern=None):
self['responseTemplates'] = {"application/json": None} self['responseTemplates'] = {"application/json": None}
@ -33,7 +33,7 @@ class IntegrationResponse(dict):
self['selectionPattern'] = selection_pattern self['selectionPattern'] = selection_pattern
class Integration(dict): class Integration(BaseModel, dict):
def __init__(self, integration_type, uri, http_method, request_templates=None): def __init__(self, integration_type, uri, http_method, request_templates=None):
super(Integration, self).__init__() super(Integration, self).__init__()
@ -58,14 +58,14 @@ class Integration(dict):
return self["integrationResponses"].pop(status_code) return self["integrationResponses"].pop(status_code)
class MethodResponse(dict): class MethodResponse(BaseModel, dict):
def __init__(self, status_code): def __init__(self, status_code):
super(MethodResponse, self).__init__() super(MethodResponse, self).__init__()
self['statusCode'] = status_code self['statusCode'] = status_code
class Method(dict): class Method(BaseModel, dict):
def __init__(self, method_type, authorization_type): def __init__(self, method_type, authorization_type):
super(Method, self).__init__() super(Method, self).__init__()
@ -92,7 +92,7 @@ class Method(dict):
return self.method_responses.pop(response_code) return self.method_responses.pop(response_code)
class Resource(object): class Resource(BaseModel):
def __init__(self, id, region_name, api_id, path_part, parent_id): def __init__(self, id, region_name, api_id, path_part, parent_id):
self.id = id self.id = id
@ -165,7 +165,7 @@ class Resource(object):
return self.resource_methods[method_type].pop('methodIntegration') return self.resource_methods[method_type].pop('methodIntegration')
class Stage(dict): class Stage(BaseModel, dict):
def __init__(self, name=None, deployment_id=None, variables=None, def __init__(self, name=None, deployment_id=None, variables=None,
description='', cacheClusterEnabled=False, cacheClusterSize=None): description='', cacheClusterEnabled=False, cacheClusterSize=None):
@ -293,7 +293,7 @@ class Stage(dict):
raise Exception('Patch operation "%s" not implemented' % op['op']) raise Exception('Patch operation "%s" not implemented' % op['op'])
class RestAPI(object): class RestAPI(BaseModel):
def __init__(self, id, region_name, name, description): def __init__(self, id, region_name, name, description):
self.id = id self.id = id

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from moto.elb import elb_backends from moto.elb import elb_backends
from moto.elb.exceptions import LoadBalancerNotFoundError from moto.elb.exceptions import LoadBalancerNotFoundError
@ -16,7 +16,7 @@ class InstanceState(object):
self.lifecycle_state = lifecycle_state self.lifecycle_state = lifecycle_state
class FakeScalingPolicy(object): class FakeScalingPolicy(BaseModel):
def __init__(self, name, policy_type, adjustment_type, as_name, scaling_adjustment, def __init__(self, name, policy_type, adjustment_type, as_name, scaling_adjustment,
cooldown, autoscaling_backend): cooldown, autoscaling_backend):
@ -43,7 +43,7 @@ class FakeScalingPolicy(object):
self.as_name, self.scaling_adjustment) self.as_name, self.scaling_adjustment)
class FakeLaunchConfiguration(object): class FakeLaunchConfiguration(BaseModel):
def __init__(self, name, image_id, key_name, ramdisk_id, kernel_id, security_groups, user_data, def __init__(self, name, image_id, key_name, ramdisk_id, kernel_id, security_groups, user_data,
instance_type, instance_monitoring, instance_profile_name, instance_type, instance_monitoring, instance_profile_name,
@ -142,7 +142,7 @@ class FakeLaunchConfiguration(object):
return block_device_map return block_device_map
class FakeAutoScalingGroup(object): class FakeAutoScalingGroup(BaseModel):
def __init__(self, name, availability_zones, desired_capacity, max_size, def __init__(self, name, availability_zones, desired_capacity, max_size,
min_size, launch_config_name, vpc_zone_identifier, min_size, launch_config_name, vpc_zone_identifier,

View File

@ -14,12 +14,12 @@ except:
from io import StringIO from io import StringIO
import boto.awslambda import boto.awslambda
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.s3.models import s3_backend from moto.s3.models import s3_backend
from moto.s3.exceptions import MissingBucket from moto.s3.exceptions import MissingBucket
class LambdaFunction(object): class LambdaFunction(BaseModel):
def __init__(self, spec): def __init__(self, spec):
# required # required

View File

@ -4,14 +4,14 @@ import json
import uuid import uuid
import boto.cloudformation import boto.cloudformation
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .parsing import ResourceMap, OutputMap from .parsing import ResourceMap, OutputMap
from .utils import generate_stack_id from .utils import generate_stack_id
from .exceptions import ValidationError from .exceptions import ValidationError
class FakeStack(object): class FakeStack(BaseModel):
def __init__(self, stack_id, name, template, parameters, region_name, notification_arns=None, tags=None, role_arn=None): def __init__(self, stack_id, name, template, parameters, region_name, notification_arns=None, tags=None, role_arn=None):
self.stack_id = stack_id self.stack_id = stack_id
@ -99,7 +99,7 @@ class FakeStack(object):
self.status = "DELETE_COMPLETE" self.status = "DELETE_COMPLETE"
class FakeEvent(object): class FakeEvent(BaseModel):
def __init__(self, stack_id, stack_name, logical_resource_id, physical_resource_id, resource_type, resource_status, resource_status_reason=None, resource_properties=None): def __init__(self, stack_id, stack_name, logical_resource_id, physical_resource_id, resource_type, resource_status, resource_status_reason=None, resource_properties=None):
self.stack_id = stack_id self.stack_id = stack_id

View File

@ -1,4 +1,4 @@
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
import boto.ec2.cloudwatch import boto.ec2.cloudwatch
import datetime import datetime
@ -10,7 +10,7 @@ class Dimension(object):
self.value = value self.value = value
class FakeAlarm(object): class FakeAlarm(BaseModel):
def __init__(self, name, namespace, metric_name, comparison_operator, evaluation_periods, def __init__(self, name, namespace, metric_name, comparison_operator, evaluation_periods,
period, threshold, statistic, description, dimensions, alarm_actions, period, threshold, statistic, description, dimensions, alarm_actions,
@ -34,7 +34,7 @@ class FakeAlarm(object):
self.configuration_updated_timestamp = datetime.datetime.utcnow() self.configuration_updated_timestamp = datetime.datetime.utcnow()
class MetricDatum(object): class MetricDatum(BaseModel):
def __init__(self, namespace, name, value, dimensions): def __init__(self, namespace, name, value, dimensions):
self.namespace = namespace self.namespace = namespace

View File

@ -1,4 +1,4 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from .models import BaseBackend, moto_api_backend # flake8: noqa from .models import BaseModel, BaseBackend, moto_api_backend # flake8: noqa
moto_api_backends = {"global": moto_api_backend} moto_api_backends = {"global": moto_api_backend}

View File

@ -1,9 +1,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from __future__ import absolute_import from __future__ import absolute_import
from collections import defaultdict
import functools import functools
import inspect import inspect
import re import re
import six
from moto import settings from moto import settings
from moto.packages.responses import responses from moto.packages.responses import responses
@ -208,9 +210,36 @@ class Model(type):
return dec return dec
model_data = defaultdict(dict)
class InstanceTrackerMeta(type):
def __new__(meta, name, bases, dct):
cls = super(InstanceTrackerMeta, meta).__new__(meta, name, bases, dct)
if name == 'BaseModel':
return cls
service = cls.__module__.split(".")[1]
if name not in model_data[service]:
model_data[service][name] = cls
cls.instances = []
return cls
@six.add_metaclass(InstanceTrackerMeta)
class BaseModel(object):
def __new__(cls, *args, **kwargs):
if six.PY2:
instance = super(BaseModel, cls).__new__(cls, *args, **kwargs)
else:
instance = super(BaseModel, cls).__new__(cls)
cls.instances.append(instance)
return instance
class BaseBackend(object): class BaseBackend(object):
def reset(self): def reset(self):
for service, models in model_data.items():
for model_name, model in models.items():
model.instances = []
self.__dict__ = {} self.__dict__ = {}
self.__init__() self.__init__()

View File

@ -12,6 +12,7 @@ from jinja2 import Environment, DictLoader, TemplateNotFound
import six import six
from six.moves.urllib.parse import parse_qs, urlparse from six.moves.urllib.parse import parse_qs, urlparse
from flask import render_template
import xmltodict import xmltodict
from pkg_resources import resource_filename from pkg_resources import resource_filename
from werkzeug.exceptions import HTTPException from werkzeug.exceptions import HTTPException
@ -350,6 +351,32 @@ class MotoAPIResponse(BaseResponse):
return 200, {}, json.dumps({"status": "ok"}) return 200, {}, json.dumps({"status": "ok"})
return 400, {}, json.dumps({"Error": "Need to POST to reset Moto"}) return 400, {}, json.dumps({"Error": "Need to POST to reset Moto"})
def model_data(self, request, full_url, headers):
from moto.core.models import model_data
results = {}
for service in sorted(model_data):
models = model_data[service]
results[service] = {}
for name in sorted(models):
model = models[name]
results[service][name] = []
for instance in model.instances:
inst_result = {}
for attr in dir(instance):
if not attr.startswith("_"):
try:
json.dumps(getattr(instance, attr))
except TypeError:
pass
else:
inst_result[attr] = getattr(instance, attr)
results[service][name].append(inst_result)
return 200, {"Content-Type": "application/javascript"}, json.dumps(results)
def dashboard(self, request, full_url, headers):
return render_template('dashboard.html')
class _RecursiveDictRef(object): class _RecursiveDictRef(object):
"""Store a recursive reference to dict.""" """Store a recursive reference to dict."""

View File

@ -8,5 +8,7 @@ url_bases = [
response_instance = MotoAPIResponse() response_instance = MotoAPIResponse()
url_paths = { url_paths = {
'{0}/moto-api/$': response_instance.dashboard,
'{0}/moto-api/data.json': response_instance.model_data,
'{0}/moto-api/reset': response_instance.reset_response, '{0}/moto-api/reset': response_instance.reset_response,
} }

View File

@ -122,7 +122,10 @@ class convert_flask_to_httpretty_response(object):
result = self.callback(request, request.url, {}) result = self.callback(request, request.url, {})
# result is a status, headers, response tuple # result is a status, headers, response tuple
status, headers, content = result if len(result) == 3:
status, headers, content = result
else:
status, headers, content = 200, {}, result
response = Response(response=content, status=status, headers=headers) response = Response(response=content, status=status, headers=headers)
if request.method == "HEAD" and 'content-length' in headers: if request.method == "HEAD" and 'content-length' in headers:

View File

@ -2,11 +2,11 @@ from __future__ import unicode_literals
import datetime import datetime
import boto.datapipeline import boto.datapipeline
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys
class PipelineObject(object): class PipelineObject(BaseModel):
def __init__(self, object_id, name, fields): def __init__(self, object_id, name, fields):
self.object_id = object_id self.object_id = object_id
@ -21,7 +21,7 @@ class PipelineObject(object):
} }
class Pipeline(object): class Pipeline(BaseModel):
def __init__(self, name, unique_id): def __init__(self, name, unique_id):
self.name = name self.name = name

View File

@ -4,7 +4,7 @@ import datetime
import json import json
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
from .comparisons import get_comparison_func from .comparisons import get_comparison_func
@ -53,7 +53,7 @@ class DynamoType(object):
return comparison_func(self.value, *range_values) return comparison_func(self.value, *range_values)
class Item(object): class Item(BaseModel):
def __init__(self, hash_key, hash_key_type, range_key, range_key_type, attrs): def __init__(self, hash_key, hash_key_type, range_key, range_key_type, attrs):
self.hash_key = hash_key self.hash_key = hash_key
@ -90,7 +90,7 @@ class Item(object):
} }
class Table(object): class Table(BaseModel):
def __init__(self, name, hash_key_attr, hash_key_type, def __init__(self, name, hash_key_attr, hash_key_type,
range_key_attr=None, range_key_type=None, read_capacity=None, range_key_attr=None, range_key_type=None, read_capacity=None,

View File

@ -5,7 +5,7 @@ import decimal
import json import json
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
from .comparisons import get_comparison_func from .comparisons import get_comparison_func
@ -76,7 +76,7 @@ class DynamoType(object):
return comparison_func(self.cast_value, *range_values) return comparison_func(self.cast_value, *range_values)
class Item(object): class Item(BaseModel):
def __init__(self, hash_key, hash_key_type, range_key, range_key_type, attrs): def __init__(self, hash_key, hash_key_type, range_key, range_key_type, attrs):
self.hash_key = hash_key self.hash_key = hash_key
@ -173,7 +173,7 @@ class Item(object):
'ADD not supported for %s' % ', '.join(update_action['Value'].keys())) 'ADD not supported for %s' % ', '.join(update_action['Value'].keys()))
class Table(object): class Table(BaseModel):
def __init__(self, table_name, schema=None, attr=None, throughput=None, indexes=None, global_indexes=None): def __init__(self, table_name, schema=None, attr=None, throughput=None, indexes=None, global_indexes=None):
self.name = table_name self.name = table_name

View File

@ -13,7 +13,7 @@ from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
from boto.ec2.launchspecification import LaunchSpecification from boto.ec2.launchspecification import LaunchSpecification
from moto.core import BaseBackend from moto.core import BaseBackend
from moto.core.models import Model from moto.core.models import Model, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds, camelcase_to_underscores from moto.core.utils import iso_8601_datetime_with_milliseconds, camelcase_to_underscores
from .exceptions import ( from .exceptions import (
EC2ClientError, EC2ClientError,
@ -129,7 +129,7 @@ class StateReason(object):
self.code = code self.code = code
class TaggedEC2Resource(object): class TaggedEC2Resource(BaseModel):
def get_tags(self, *args, **kwargs): def get_tags(self, *args, **kwargs):
tags = self.ec2_backend.describe_tags( tags = self.ec2_backend.describe_tags(
@ -2612,7 +2612,7 @@ class InternetGatewayBackend(object):
return self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0] return self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
class VPCGatewayAttachment(object): class VPCGatewayAttachment(BaseModel):
def __init__(self, gateway_id, vpc_id): def __init__(self, gateway_id, vpc_id):
self.gateway_id = gateway_id self.gateway_id = gateway_id
@ -2633,7 +2633,7 @@ class VPCGatewayAttachment(object):
@property @property
def physical_resource_id(self): def physical_resource_id(self):
return self.id return self.vpc_id
class VPCGatewayAttachmentBackend(object): class VPCGatewayAttachmentBackend(object):

View File

@ -2,12 +2,12 @@ from __future__ import unicode_literals
import uuid import uuid
from random import randint, random from random import randint, random
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from copy import copy from copy import copy
class BaseObject(object): class BaseObject(BaseModel):
def camelCase(self, key): def camelCase(self, key):
words = [] words = []

View File

@ -11,7 +11,7 @@ from boto.ec2.elb.policies import (
Policies, Policies,
OtherPolicy, OtherPolicy,
) )
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.ec2.models import ec2_backends from moto.ec2.models import ec2_backends
from .exceptions import ( from .exceptions import (
LoadBalancerNotFoundError, LoadBalancerNotFoundError,
@ -21,7 +21,7 @@ from .exceptions import (
) )
class FakeHealthCheck(object): class FakeHealthCheck(BaseModel):
def __init__(self, timeout, healthy_threshold, unhealthy_threshold, def __init__(self, timeout, healthy_threshold, unhealthy_threshold,
interval, target): interval, target):
@ -34,7 +34,7 @@ class FakeHealthCheck(object):
raise BadHealthCheckDefinition raise BadHealthCheckDefinition
class FakeListener(object): class FakeListener(BaseModel):
def __init__(self, load_balancer_port, instance_port, protocol, ssl_certificate_id): def __init__(self, load_balancer_port, instance_port, protocol, ssl_certificate_id):
self.load_balancer_port = load_balancer_port self.load_balancer_port = load_balancer_port
@ -47,7 +47,7 @@ class FakeListener(object):
return "FakeListener(lbp: %s, inp: %s, pro: %s, cid: %s, policies: %s)" % (self.load_balancer_port, self.instance_port, self.protocol, self.ssl_certificate_id, self.policy_names) return "FakeListener(lbp: %s, inp: %s, pro: %s, cid: %s, policies: %s)" % (self.load_balancer_port, self.instance_port, self.protocol, self.ssl_certificate_id, self.policy_names)
class FakeBackend(object): class FakeBackend(BaseModel):
def __init__(self, instance_port): def __init__(self, instance_port):
self.instance_port = instance_port self.instance_port = instance_port
@ -57,7 +57,7 @@ class FakeBackend(object):
return "FakeBackend(inp: %s, policies: %s)" % (self.instance_port, self.policy_names) return "FakeBackend(inp: %s, policies: %s)" % (self.instance_port, self.policy_names)
class FakeLoadBalancer(object): class FakeLoadBalancer(BaseModel):
def __init__(self, name, zones, ports, scheme='internet-facing', vpc_id=None, subnets=None): def __init__(self, name, zones, ports, scheme='internet-facing', vpc_id=None, subnets=None):
self.name = name self.name = name

View File

@ -5,12 +5,12 @@ from datetime import timedelta
import boto.emr import boto.emr
import pytz import pytz
from dateutil.parser import parse as dtparse from dateutil.parser import parse as dtparse
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .utils import random_instance_group_id, random_cluster_id, random_step_id from .utils import random_instance_group_id, random_cluster_id, random_step_id
class FakeApplication(object): class FakeApplication(BaseModel):
def __init__(self, name, version, args=None, additional_info=None): def __init__(self, name, version, args=None, additional_info=None):
self.additional_info = additional_info or {} self.additional_info = additional_info or {}
@ -19,7 +19,7 @@ class FakeApplication(object):
self.version = version self.version = version
class FakeBootstrapAction(object): class FakeBootstrapAction(BaseModel):
def __init__(self, args, name, script_path): def __init__(self, args, name, script_path):
self.args = args or [] self.args = args or []
@ -27,7 +27,7 @@ class FakeBootstrapAction(object):
self.script_path = script_path self.script_path = script_path
class FakeInstanceGroup(object): class FakeInstanceGroup(BaseModel):
def __init__(self, instance_count, instance_role, instance_type, def __init__(self, instance_count, instance_role, instance_type,
market='ON_DEMAND', name=None, id=None, bid_price=None): market='ON_DEMAND', name=None, id=None, bid_price=None):
@ -57,7 +57,7 @@ class FakeInstanceGroup(object):
self.num_instances = instance_count self.num_instances = instance_count
class FakeStep(object): class FakeStep(BaseModel):
def __init__(self, def __init__(self,
state, state,
@ -81,7 +81,7 @@ class FakeStep(object):
self.state = state self.state = state
class FakeCluster(object): class FakeCluster(BaseModel):
def __init__(self, def __init__(self,
emr_backend, emr_backend,

View File

@ -1,10 +1,10 @@
import os import os
import re import re
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
class Rule(object): class Rule(BaseModel):
def _generate_arn(self, name): def _generate_arn(self, name):
return 'arn:aws:events:us-west-2:111111111111:rule/' + name return 'arn:aws:events:us-west-2:111111111111:rule/' + name

View File

@ -3,12 +3,12 @@ from __future__ import unicode_literals
import hashlib import hashlib
import boto.glacier import boto.glacier
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .utils import get_job_id from .utils import get_job_id
class ArchiveJob(object): class ArchiveJob(BaseModel):
def __init__(self, job_id, archive_id): def __init__(self, job_id, archive_id):
self.job_id = job_id self.job_id = job_id
@ -35,7 +35,7 @@ class ArchiveJob(object):
} }
class Vault(object): class Vault(BaseModel):
def __init__(self, vault_name, region): def __init__(self, vault_name, region):
self.vault_name = vault_name self.vault_name = vault_name

View File

@ -3,13 +3,13 @@ import base64
from datetime import datetime from datetime import datetime
import pytz import pytz
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException
from .utils import random_access_key, random_alphanumeric, random_resource_id, random_policy_id from .utils import random_access_key, random_alphanumeric, random_resource_id, random_policy_id
class Policy(object): class Policy(BaseModel):
is_attachable = False is_attachable = False
@ -54,7 +54,7 @@ class InlinePolicy(Policy):
"""TODO: is this needed?""" """TODO: is this needed?"""
class Role(object): class Role(BaseModel):
def __init__(self, role_id, name, assume_role_policy_document, path): def __init__(self, role_id, name, assume_role_policy_document, path):
self.id = role_id self.id = role_id
@ -96,7 +96,7 @@ class Role(object):
raise UnformattedGetAttTemplateException() raise UnformattedGetAttTemplateException()
class InstanceProfile(object): class InstanceProfile(BaseModel):
def __init__(self, instance_profile_id, name, path, roles): def __init__(self, instance_profile_id, name, path, roles):
self.id = instance_profile_id self.id = instance_profile_id
@ -126,7 +126,7 @@ class InstanceProfile(object):
raise UnformattedGetAttTemplateException() raise UnformattedGetAttTemplateException()
class Certificate(object): class Certificate(BaseModel):
def __init__(self, cert_name, cert_body, private_key, cert_chain=None, path=None): def __init__(self, cert_name, cert_body, private_key, cert_chain=None, path=None):
self.cert_name = cert_name self.cert_name = cert_name
@ -140,7 +140,7 @@ class Certificate(object):
return self.name return self.name
class AccessKey(object): class AccessKey(BaseModel):
def __init__(self, user_name): def __init__(self, user_name):
self.user_name = user_name self.user_name = user_name
@ -159,7 +159,7 @@ class AccessKey(object):
raise UnformattedGetAttTemplateException() raise UnformattedGetAttTemplateException()
class Group(object): class Group(BaseModel):
def __init__(self, name, path='/'): def __init__(self, name, path='/'):
self.name = name self.name = name
@ -198,7 +198,7 @@ class Group(object):
return self.policies.keys() return self.policies.keys()
class User(object): class User(BaseModel):
def __init__(self, name, path=None): def __init__(self, name, path=None):
self.name = name self.name = name

View File

@ -11,13 +11,13 @@ from operator import attrgetter
from hashlib import md5 from hashlib import md5
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .exceptions import StreamNotFoundError, ShardNotFoundError, ResourceInUseError, \ from .exceptions import StreamNotFoundError, ShardNotFoundError, ResourceInUseError, \
ResourceNotFoundError, InvalidArgumentError ResourceNotFoundError, InvalidArgumentError
from .utils import compose_shard_iterator, compose_new_shard_iterator, decompose_shard_iterator from .utils import compose_shard_iterator, compose_new_shard_iterator, decompose_shard_iterator
class Record(object): class Record(BaseModel):
def __init__(self, partition_key, data, sequence_number, explicit_hash_key): def __init__(self, partition_key, data, sequence_number, explicit_hash_key):
self.partition_key = partition_key self.partition_key = partition_key
@ -33,7 +33,7 @@ class Record(object):
} }
class Shard(object): class Shard(BaseModel):
def __init__(self, shard_id, starting_hash, ending_hash): def __init__(self, shard_id, starting_hash, ending_hash):
self._shard_id = shard_id self._shard_id = shard_id
@ -94,7 +94,7 @@ class Shard(object):
} }
class Stream(object): class Stream(BaseModel):
def __init__(self, stream_name, shard_count, region): def __init__(self, stream_name, shard_count, region):
self.stream_name = stream_name self.stream_name = stream_name
@ -173,14 +173,14 @@ class Stream(object):
} }
class FirehoseRecord(object): class FirehoseRecord(BaseModel):
def __init__(self, record_data): def __init__(self, record_data):
self.record_id = 12345678 self.record_id = 12345678
self.record_data = record_data self.record_data = record_data
class DeliveryStream(object): class DeliveryStream(BaseModel):
def __init__(self, stream_name, **stream_kwargs): def __init__(self, stream_name, **stream_kwargs):
self.name = stream_name self.name = stream_name

View File

@ -1,12 +1,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import boto.kms import boto.kms
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .utils import generate_key_id from .utils import generate_key_id
from collections import defaultdict from collections import defaultdict
class Key(object): class Key(BaseModel):
def __init__(self, policy, key_usage, description, region): def __init__(self, policy, key_usage, description, region):
self.id = generate_key_id() self.id = generate_key_id()

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
import uuid import uuid
import datetime import datetime
@ -8,7 +8,7 @@ from random import choice
from .exceptions import ResourceNotFoundException, ValidationException from .exceptions import ResourceNotFoundException, ValidationException
class OpsworkInstance(object): class OpsworkInstance(BaseModel):
""" """
opsworks maintains its own set of ec2 instance metadata. opsworks maintains its own set of ec2 instance metadata.
This metadata exists before any instance reservations are made, and is This metadata exists before any instance reservations are made, and is
@ -166,7 +166,7 @@ class OpsworkInstance(object):
return d return d
class Layer(object): class Layer(BaseModel):
def __init__(self, stack_id, type, name, shortname, def __init__(self, stack_id, type, name, shortname,
attributes=None, attributes=None,
@ -292,7 +292,7 @@ class Layer(object):
return d return d
class Stack(object): class Stack(BaseModel):
def __init__(self, name, region, service_role_arn, default_instance_profile_arn, def __init__(self, name, region, service_role_arn, default_instance_profile_arn,
vpcid="vpc-1f99bf7a", vpcid="vpc-1f99bf7a",

View File

@ -6,13 +6,13 @@ import boto.rds
from jinja2 import Template from jinja2 import Template
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import get_random_hex from moto.core.utils import get_random_hex
from moto.ec2.models import ec2_backends from moto.ec2.models import ec2_backends
from moto.rds2.models import rds2_backends from moto.rds2.models import rds2_backends
class Database(object): class Database(BaseModel):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.status = "available" self.status = "available"
@ -239,7 +239,7 @@ class Database(object):
backend.delete_database(self.db_instance_identifier) backend.delete_database(self.db_instance_identifier)
class SecurityGroup(object): class SecurityGroup(BaseModel):
def __init__(self, group_name, description): def __init__(self, group_name, description):
self.group_name = group_name self.group_name = group_name
@ -317,7 +317,7 @@ class SecurityGroup(object):
backend.delete_security_group(self.group_name) backend.delete_security_group(self.group_name)
class SubnetGroup(object): class SubnetGroup(BaseModel):
def __init__(self, subnet_name, description, subnets): def __init__(self, subnet_name, description, subnets):
self.subnet_name = subnet_name self.subnet_name = subnet_name

View File

@ -7,7 +7,7 @@ import boto.rds2
from jinja2 import Template from jinja2 import Template
from re import compile as re_compile from re import compile as re_compile
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import get_random_hex from moto.core.utils import get_random_hex
from moto.ec2.models import ec2_backends from moto.ec2.models import ec2_backends
from .exceptions import (RDSClientError, from .exceptions import (RDSClientError,
@ -17,7 +17,7 @@ from .exceptions import (RDSClientError,
DBParameterGroupNotFoundError) DBParameterGroupNotFoundError)
class Database(object): class Database(BaseModel):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.status = "available" self.status = "available"
@ -372,7 +372,7 @@ class Database(object):
backend.delete_database(self.db_instance_identifier) backend.delete_database(self.db_instance_identifier)
class SecurityGroup(object): class SecurityGroup(BaseModel):
def __init__(self, group_name, description, tags): def __init__(self, group_name, description, tags):
self.group_name = group_name self.group_name = group_name
@ -481,7 +481,7 @@ class SecurityGroup(object):
backend.delete_security_group(self.group_name) backend.delete_security_group(self.group_name)
class SubnetGroup(object): class SubnetGroup(BaseModel):
def __init__(self, subnet_name, description, subnets, tags): def __init__(self, subnet_name, description, subnets, tags):
self.subnet_name = subnet_name self.subnet_name = subnet_name

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import boto.redshift import boto.redshift
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from .exceptions import ( from .exceptions import (
ClusterNotFoundError, ClusterNotFoundError,
@ -12,7 +12,7 @@ from .exceptions import (
) )
class Cluster(object): class Cluster(BaseModel):
def __init__(self, redshift_backend, cluster_identifier, node_type, master_username, def __init__(self, redshift_backend, cluster_identifier, node_type, master_username,
master_user_password, db_name, cluster_type, cluster_security_groups, master_user_password, db_name, cluster_type, cluster_security_groups,
@ -174,7 +174,7 @@ class Cluster(object):
} }
class SubnetGroup(object): class SubnetGroup(BaseModel):
def __init__(self, ec2_backend, cluster_subnet_group_name, description, subnet_ids): def __init__(self, ec2_backend, cluster_subnet_group_name, description, subnet_ids):
self.ec2_backend = ec2_backend self.ec2_backend = ec2_backend
@ -220,7 +220,7 @@ class SubnetGroup(object):
} }
class SecurityGroup(object): class SecurityGroup(BaseModel):
def __init__(self, cluster_security_group_name, description): def __init__(self, cluster_security_group_name, description):
self.cluster_security_group_name = cluster_security_group_name self.cluster_security_group_name = cluster_security_group_name
@ -235,7 +235,7 @@ class SecurityGroup(object):
} }
class ParameterGroup(object): class ParameterGroup(BaseModel):
def __init__(self, cluster_parameter_group_name, group_family, description): def __init__(self, cluster_parameter_group_name, group_family, description):
self.cluster_parameter_group_name = cluster_parameter_group_name self.cluster_parameter_group_name = cluster_parameter_group_name

View File

@ -5,11 +5,11 @@ from collections import defaultdict
import uuid import uuid
from jinja2 import Template from jinja2 import Template
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import get_random_hex from moto.core.utils import get_random_hex
class HealthCheck(object): class HealthCheck(BaseModel):
def __init__(self, health_check_id, health_check_args): def __init__(self, health_check_id, health_check_args):
self.id = health_check_id self.id = health_check_id
@ -63,7 +63,7 @@ class HealthCheck(object):
return template.render(health_check=self) return template.render(health_check=self)
class RecordSet(object): class RecordSet(BaseModel):
def __init__(self, kwargs): def __init__(self, kwargs):
self.name = kwargs.get('Name') self.name = kwargs.get('Name')
@ -154,7 +154,7 @@ class RecordSet(object):
hosted_zone.delete_rrset_by_name(self.name) hosted_zone.delete_rrset_by_name(self.name)
class FakeZone(object): class FakeZone(BaseModel):
def __init__(self, name, id_, private_zone, comment=None): def __init__(self, name, id_, private_zone, comment=None):
self.name = name self.name = name
@ -212,7 +212,7 @@ class FakeZone(object):
return hosted_zone return hosted_zone
class RecordSetGroup(object): class RecordSetGroup(BaseModel):
def __init__(self, hosted_zone_id, record_sets): def __init__(self, hosted_zone_id, record_sets):
self.hosted_zone_id = hosted_zone_id self.hosted_zone_id = hosted_zone_id

View File

@ -9,7 +9,7 @@ import codecs
import six import six
from bisect import insort from bisect import insort
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds, rfc_1123_datetime from moto.core.utils import iso_8601_datetime_with_milliseconds, rfc_1123_datetime
from .exceptions import BucketAlreadyExists, MissingBucket, MissingKey, InvalidPart, EntityTooSmall from .exceptions import BucketAlreadyExists, MissingBucket, MissingKey, InvalidPart, EntityTooSmall
from .utils import clean_key_name, _VersionedKeyStore from .utils import clean_key_name, _VersionedKeyStore
@ -18,7 +18,7 @@ UPLOAD_ID_BYTES = 43
UPLOAD_PART_MIN_SIZE = 5242880 UPLOAD_PART_MIN_SIZE = 5242880
class FakeKey(object): class FakeKey(BaseModel):
def __init__(self, name, value, storage="STANDARD", etag=None, is_versioned=False, version_id=0): def __init__(self, name, value, storage="STANDARD", etag=None, is_versioned=False, version_id=0):
self.name = name self.name = name
@ -119,7 +119,7 @@ class FakeKey(object):
return self._expiry.strftime("%a, %d %b %Y %H:%M:%S GMT") return self._expiry.strftime("%a, %d %b %Y %H:%M:%S GMT")
class FakeMultipart(object): class FakeMultipart(BaseModel):
def __init__(self, key_name, metadata): def __init__(self, key_name, metadata):
self.key_name = key_name self.key_name = key_name
@ -167,7 +167,7 @@ class FakeMultipart(object):
yield self.parts[part_id] yield self.parts[part_id]
class FakeGrantee(object): class FakeGrantee(BaseModel):
def __init__(self, id='', uri='', display_name=''): def __init__(self, id='', uri='', display_name=''):
self.id = id self.id = id
@ -193,14 +193,14 @@ PERMISSION_WRITE_ACP = 'WRITE_ACP'
PERMISSION_READ_ACP = 'READ_ACP' PERMISSION_READ_ACP = 'READ_ACP'
class FakeGrant(object): class FakeGrant(BaseModel):
def __init__(self, grantees, permissions): def __init__(self, grantees, permissions):
self.grantees = grantees self.grantees = grantees
self.permissions = permissions self.permissions = permissions
class FakeAcl(object): class FakeAcl(BaseModel):
def __init__(self, grants=[]): def __init__(self, grants=[]):
self.grants = grants self.grants = grants
@ -234,7 +234,7 @@ def get_canned_acl(acl):
return FakeAcl(grants=grants) return FakeAcl(grants=grants)
class LifecycleRule(object): class LifecycleRule(BaseModel):
def __init__(self, id=None, prefix=None, status=None, expiration_days=None, def __init__(self, id=None, prefix=None, status=None, expiration_days=None,
expiration_date=None, transition_days=None, expiration_date=None, transition_days=None,
@ -249,7 +249,7 @@ class LifecycleRule(object):
self.storage_class = storage_class self.storage_class = storage_class
class FakeBucket(object): class FakeBucket(BaseModel):
def __init__(self, name, region_name): def __init__(self, name, region_name):
self.name = name self.name = name

View File

@ -47,7 +47,7 @@ class DomainDispatcherApplication(object):
def get_application(self, environ): def get_application(self, environ):
path_info = environ.get('PATH_INFO', '') path_info = environ.get('PATH_INFO', '')
if path_info.startswith("/moto-api"): if path_info.startswith("/moto-api") or path_info == "/favicon.ico":
host = "moto_api" host = "moto_api"
elif path_info.startswith("/latest/meta-data/"): elif path_info.startswith("/latest/meta-data/"):
host = "instance_metadata" host = "instance_metadata"

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import email import email
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from .exceptions import MessageRejectedError from .exceptions import MessageRejectedError
from .utils import get_random_message_id from .utils import get_random_message_id
@ -10,19 +10,19 @@ from .utils import get_random_message_id
RECIPIENT_LIMIT = 50 RECIPIENT_LIMIT = 50
class Message(object): class Message(BaseModel):
def __init__(self, message_id): def __init__(self, message_id):
self.id = message_id self.id = message_id
class RawMessage(object): class RawMessage(BaseModel):
def __init__(self, message_id): def __init__(self, message_id):
self.id = message_id self.id = message_id
class SESQuota(object): class SESQuota(BaseModel):
def __init__(self, sent): def __init__(self, sent):
self.sent = sent self.sent = sent

View File

@ -9,7 +9,7 @@ import requests
import six import six
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.sqs import sqs_backends from moto.sqs import sqs_backends
from .exceptions import SNSNotFoundError from .exceptions import SNSNotFoundError
@ -19,7 +19,7 @@ DEFAULT_ACCOUNT_ID = 123456789012
DEFAULT_PAGE_SIZE = 100 DEFAULT_PAGE_SIZE = 100
class Topic(object): class Topic(BaseModel):
def __init__(self, name, sns_backend): def __init__(self, name, sns_backend):
self.name = name self.name = name
@ -67,7 +67,7 @@ class Topic(object):
return topic return topic
class Subscription(object): class Subscription(BaseModel):
def __init__(self, topic, endpoint, protocol): def __init__(self, topic, endpoint, protocol):
self.topic = topic self.topic = topic
@ -99,7 +99,7 @@ class Subscription(object):
} }
class PlatformApplication(object): class PlatformApplication(BaseModel):
def __init__(self, region, name, platform, attributes): def __init__(self, region, name, platform, attributes):
self.region = region self.region = region
@ -116,7 +116,7 @@ class PlatformApplication(object):
) )
class PlatformEndpoint(object): class PlatformEndpoint(BaseModel):
def __init__(self, region, application, custom_user_data, token, attributes): def __init__(self, region, application, custom_user_data, token, attributes):
self.region = region self.region = region

View File

@ -6,7 +6,7 @@ from xml.sax.saxutils import escape
import boto.sqs import boto.sqs
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import camelcase_to_underscores, get_random_message_id, unix_time, unix_time_millis from moto.core.utils import camelcase_to_underscores, get_random_message_id, unix_time, unix_time_millis
from .utils import generate_receipt_handle from .utils import generate_receipt_handle
from .exceptions import ( from .exceptions import (
@ -18,7 +18,7 @@ DEFAULT_ACCOUNT_ID = 123456789012
DEFAULT_SENDER_ID = "AIDAIT2UOQQY3AUEKVGXU" DEFAULT_SENDER_ID = "AIDAIT2UOQQY3AUEKVGXU"
class Message(object): class Message(BaseModel):
def __init__(self, message_id, body): def __init__(self, message_id, body):
self.id = message_id self.id = message_id
@ -93,7 +93,7 @@ class Message(object):
return False return False
class Queue(object): class Queue(BaseModel):
camelcase_attributes = ['ApproximateNumberOfMessages', camelcase_attributes = ['ApproximateNumberOfMessages',
'ApproximateNumberOfMessagesDelayed', 'ApproximateNumberOfMessagesDelayed',
'ApproximateNumberOfMessagesNotVisible', 'ApproximateNumberOfMessagesNotVisible',

View File

@ -1,10 +1,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime
from moto.core import BaseBackend from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_with_milliseconds
class Token(object): class Token(BaseModel):
def __init__(self, duration, name=None, policy=None): def __init__(self, duration, name=None, policy=None):
now = datetime.datetime.utcnow() now = datetime.datetime.utcnow()
@ -17,7 +17,7 @@ class Token(object):
return iso_8601_datetime_with_milliseconds(self.expiration) return iso_8601_datetime_with_milliseconds(self.expiration)
class AssumedRole(object): class AssumedRole(BaseModel):
def __init__(self, role_session_name, role_arn, policy, duration, external_id): def __init__(self, role_session_name, role_arn, policy, duration, external_id):
self.session_name = role_session_name self.session_name = role_session_name

View File

@ -2,13 +2,14 @@ from __future__ import unicode_literals
from datetime import datetime from datetime import datetime
import uuid import uuid
from moto.core import BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
from ..exceptions import SWFWorkflowExecutionClosedError from ..exceptions import SWFWorkflowExecutionClosedError
from .timeout import Timeout from .timeout import Timeout
class ActivityTask(object): class ActivityTask(BaseModel):
def __init__(self, activity_id, activity_type, scheduled_event_id, def __init__(self, activity_id, activity_type, scheduled_event_id,
workflow_execution, timeouts, input=None): workflow_execution, timeouts, input=None):

View File

@ -2,13 +2,14 @@ from __future__ import unicode_literals
from datetime import datetime from datetime import datetime
import uuid import uuid
from moto.core import BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
from ..exceptions import SWFWorkflowExecutionClosedError from ..exceptions import SWFWorkflowExecutionClosedError
from .timeout import Timeout from .timeout import Timeout
class DecisionTask(object): class DecisionTask(BaseModel):
def __init__(self, workflow_execution, scheduled_event_id): def __init__(self, workflow_execution, scheduled_event_id):
self.workflow_execution = workflow_execution self.workflow_execution = workflow_execution

View File

@ -1,13 +1,14 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import defaultdict from collections import defaultdict
from moto.core import BaseModel
from ..exceptions import ( from ..exceptions import (
SWFUnknownResourceFault, SWFUnknownResourceFault,
SWFWorkflowExecutionAlreadyStartedFault, SWFWorkflowExecutionAlreadyStartedFault,
) )
class Domain(object): class Domain(BaseModel):
def __init__(self, name, retention, description=None): def __init__(self, name, retention, description=None):
self.name = name self.name = name

View File

@ -1,9 +1,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core import BaseModel
from moto.core.utils import camelcase_to_underscores from moto.core.utils import camelcase_to_underscores
class GenericType(object): class GenericType(BaseModel):
def __init__(self, name, version, **kwargs): def __init__(self, name, version, **kwargs):
self.name = name self.name = name

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core import BaseModel
from moto.core.utils import underscores_to_camelcase, unix_time from moto.core.utils import underscores_to_camelcase, unix_time
from ..utils import decapitalize from ..utils import decapitalize
@ -27,7 +28,7 @@ SUPPORTED_HISTORY_EVENT_TYPES = (
) )
class HistoryEvent(object): class HistoryEvent(BaseModel):
def __init__(self, event_id, event_type, event_timestamp=None, **kwargs): def __init__(self, event_id, event_type, event_timestamp=None, **kwargs):
if event_type not in SUPPORTED_HISTORY_EVENT_TYPES: if event_type not in SUPPORTED_HISTORY_EVENT_TYPES:

View File

@ -1,7 +1,8 @@
from moto.core import BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
class Timeout(object): class Timeout(BaseModel):
def __init__(self, obj, timestamp, kind): def __init__(self, obj, timestamp, kind):
self.obj = obj self.obj = obj

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import uuid import uuid
from moto.core import BaseModel
from moto.core.utils import camelcase_to_underscores, unix_time from moto.core.utils import camelcase_to_underscores, unix_time
from ..constants import ( from ..constants import (
@ -20,7 +21,7 @@ from .timeout import Timeout
# TODO: extract decision related logic into a Decision class # TODO: extract decision related logic into a Decision class
class WorkflowExecution(object): class WorkflowExecution(BaseModel):
# NB: the list is ordered exactly as in SWF validation exceptions so we can # NB: the list is ordered exactly as in SWF validation exceptions so we can
# mimic error messages closely ; don't reorder it without checking SWF. # mimic error messages closely ; don't reorder it without checking SWF.

View File

@ -0,0 +1,186 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Moto</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet">
<style>
body {
padding-top: 70px;
padding-bottom: 30px;
}
.theme-dropdown .dropdown-menu {
position: static;
display: block;
margin-bottom: 20px;
}
.theme-showcase > p > .btn {
margin: 5px 0;
}
.theme-showcase .navbar .container {
width: auto;
}
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Moto</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about" data-toggle="modal" data-target="#aboutModal">About</a></li>
</ul>
</div>
</div>
</nav>
<div class="container theme-showcase" role="main" id="main">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.6/handlebars.js"></script>
{% raw %}
<script id="template" type="text/x-handlebars-template">
<ul id="myTab" class="nav nav-pills">
{{#each data}}
<li {{#if @first}}class="active"{{/if}}><a href="#{{this.name}}" data-toggle="tab">{{this.name}}</a></li>
{{/each}}
</ul>
<div id="myTabContent" class="tab-content">
{{#each data}}
<div class="tab-pane fade {{#if @first}}in active{{/if}}" id="{{this.name}}">
{{#each this}}
{{#unless @last}} <!-- Skip name key -->
<div class="page-header">
<h3>{{@key}}</h3>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-striped table-bordered table-condensed">
{{#each this}}
<tr>
{{#each this}}
<td>{{@key}}: {{this}}</td>
{{/each}}
</tr>
{{else}}
<tr><td>[]</td></tr>
{{/each}}
</table>
</div>
</div>
{{/unless}}
{{/each}}
</div>
{{/each}}
</div>
</script>
<script>
sortObject = function(obj) {
if ($.isArray(obj)) {
var result = [];
$.each(obj, function(index, array_item) {
result.push(sortObject(array_item));
})
return result;
}
if (!$.isPlainObject(obj)) {
return obj;
}
var keys = $.map(obj, function(element,index) {return index});
keys.sort();
var len = keys.length;
var result = {};
$.each(keys, function(index, key) {
var val = obj[key];
result[key] = sortObject(val);
})
return result;
}
flattenAndSortObject = function(obj) {
if (!$.isPlainObject(obj)) {
return obj;
}
var keys = $.map(obj, function(element,index) {return index});
keys.sort();
var len = keys.length;
var result = [];
$.each(keys, function(index, key) {
var val = obj[key];
val.name = key;
result.push(sortObject(val));
})
return result;
}
$(document).ready(function (){
$.getJSON("/moto-api/data.json", function(data) {
var source = $('#template').html();
var template = Handlebars.compile(source);
data = flattenAndSortObject(data);
$('#main').append(template({"data": data}));
});
})
</script>
{% endraw %}
<!-- Modal -->
<div class="modal fade" id="aboutModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">About Moto</h4>
</div>
<div class="modal-body">
<p>Moto was created by <a href="https://twitter.com/spulec">Steve Pulec</a> and <a href="https://github.com/spulec/moto/blob/master/AUTHORS.md">many other contributors</a>.</p>
<p>Please open any issues <a href="https://github.com/spulec/moto/issues">here</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -19,3 +19,15 @@ def test_reset_api():
res.content.should.equal(b'{"status": "ok"}') res.content.should.equal(b'{"status": "ok"}')
conn.list_queues().shouldnt.contain('QueueUrls') # No more queues conn.list_queues().shouldnt.contain('QueueUrls') # No more queues
@mock_sqs
def test_data_api():
conn = boto3.client("sqs", region_name='us-west-1')
conn.create_queue(QueueName="queue1")
res = requests.post("{base_url}/moto-api/data.json".format(base_url=base_url))
queues = res.json()['sqs']['Queue']
len(queues).should.equal(1)
queue = queues[0]
queue['name'].should.equal("queue1")