예제 #1
0
class InvitationSerializer(DynamicFieldsMixin, HyperlinkedMixin,
                           serializers.ModelSerializer):
    hyperlinks = (
        ('self', 'invitation-detail', ('instance.name', 'pk')),
        ('resend', 'invitation-resend', ('instance.name', 'pk')),
    )
    role = DisplayedChoiceField(source='role_id',
                                choices=Role.ROLE_CHOICES.as_choices(),
                                default=Role.ROLE_CHOICES.FULL)
    inviter = serializers.CharField(source='inviter.email', read_only=True)
    state = DisplayedChoiceField(choices=Invitation.STATE_CHOICES.as_choices(),
                                 read_only=True)

    class Meta:
        model = Invitation
        fields = (
            'id',
            'email',
            'role',
            'key',
            'inviter',
            'created_at',
            'updated_at',
            'state',
        )
        extra_kwargs = {'key': {'read_only': True}}
예제 #2
0
class AdminInstanceRoleSerializer(DynamicFieldsMixin, HyperlinkedMixin,
                                  serializers.HyperlinkedModelSerializer):
    hyperlinks = (('self', 'instance-admin-detail', (
        'instance.name',
        'admin_id',
    )), )

    id = serializers.ReadOnlyField(source='admin_id')
    email = serializers.ReadOnlyField(source='admin.email')
    first_name = serializers.ReadOnlyField(source='admin.first_name')
    last_name = serializers.ReadOnlyField(source='admin.last_name')

    role = DisplayedChoiceField(source='role_id',
                                choices=Role.ROLE_CHOICES.as_choices(),
                                default=Role.ROLE_CHOICES.FULL)

    class Meta:
        model = AdminInstanceRole
        fields = (
            'id',
            'role',
            'email',
            'first_name',
            'last_name',
        )
예제 #3
0
class RestoreSerializer(HyperlinkedMixin, ModelSerializer):
    hyperlinks = (('self', 'restores-detail', ('instance.name', 'id')), )
    backup = PrimaryKeyRelatedField(required=False,
                                    allow_null=True,
                                    queryset=Backup.objects.none())
    status = DisplayedChoiceField(Backup.STATUSES.as_choices(), read_only=True)
    author = AdminFullSerializer(read_only=True, source="owner")

    class Meta:
        model = Restore
        fields = ('id', 'backup', 'created_at', 'updated_at', 'status',
                  'archive', 'status_info', 'author')
        read_only_fields = ('created_at', 'id', 'status', 'status_info',
                            'author')

    def get_fields(self):
        fields = super().get_fields()
        if 'request' in self.context:
            fields['backup'].queryset = Backup.objects.filter(
                owner=self.context['view'].request.user,
                status=Backup.STATUSES.SUCCESS,
                location=settings.LOCATION,
            )
        return fields

    def validate(self, attrs):
        has_archive = bool(attrs.get('archive', False))
        has_backup = bool(attrs.get('backup', False))
        if has_backup and has_archive or (not has_backup and not has_archive):
            raise ValidationError(
                'You have to provide either backup or archive.')
        if has_archive and not self.context['request'].user.is_staff:
            raise PermissionDenied()
        return super().validate(attrs)
예제 #4
0
class ChannelSerializer(RevalidateMixin, DynamicFieldsMixin, HyperlinkedMixin,
                        HStoreSerializer):
    hyperlinks = (
        ('self', 'channel-detail', (
            'instance.name',
            'name',
        )),
        ('group', 'group-detail', (
            'instance.name',
            'group_id',
        )),
        ('poll', 'channel-poll', (
            'instance.name',
            'name',
        )),
        ('publish', 'channel-publish', (
            'instance.name',
            'name',
        )),
        ('history', 'change-list', (
            'instance.name',
            'name',
        )),
    )

    name = LowercaseCharField(validators=[
        UniqueValidator(queryset=Channel.objects.all()),
        DjangoValidator()
    ])
    type = DisplayedChoiceField(choices=Channel.TYPES.as_choices(),
                                default=Channel.TYPES.DEFAULT)
    group_permissions = DisplayedChoiceField(
        choices=Channel.PERMISSIONS.as_choices(),
        default=Channel.PERMISSIONS.NONE)
    other_permissions = DisplayedChoiceField(
        choices=Channel.PERMISSIONS.as_choices(),
        default=Channel.PERMISSIONS.NONE)

    class Meta:
        model = Channel
        fields = ('name', 'description', 'type', 'group', 'group_permissions',
                  'other_permissions', 'created_at', 'updated_at',
                  'custom_publish')
예제 #5
0
class SocketSerializer(RevalidateMixin, MetadataMixin, DynamicFieldsMixin, HyperlinkedMixin, ModelSerializer):
    hyperlinks = (
        ('self', 'socket-detail', (
            'instance.name',
            'name',
        )),
        ('update', 'socket-update', (
            'instance.name',
            'name',
        )),
        ('endpoints', 'socket-endpoint-endpoint', (
            'instance.name',
            'name',
        )),
        ('handlers', 'socket-handler-list', (
            'instance.name',
            'name',
        )),
        ('zip_file', 'socket-zip-file', (
            'instance.name',
            'name',
        )),
    )

    name = LowercaseCharField(validators=[
        UniqueValidator(queryset=Socket.objects.all()),
        DjangoValidator()
    ])
    zip_file = FileField(write_only=True)
    zip_file_list = JSONField(validators=[FileListValidator()], default=None, write_only=True)
    config = JSONField(validators=[validate_config], default={})
    install_config = JSONField(write_only=True, default={})
    status = DisplayedChoiceField(Socket.STATUSES.as_choices(), read_only=True)
    status_info = JSONField(read_only=True)
    installed = JSONField(read_only=True)
    files = serializers.SerializerMethodField()
    environment = SlugRelatedField(slug_field='name',
                                   queryset=SocketEnvironment.objects.all(),
                                   default=None, allow_null=True)

    class Meta:
        model = Socket
        fields = ('name', 'description', 'created_at', 'updated_at', 'version',
                  'status', 'status_info', 'install_url', 'metadata', 'config',
                  'zip_file', 'zip_file_list', 'installed', 'files', 'environment',
                  'install_config', )
        read_only_fields = ('install_url', 'version')

    def get_files(self, obj):
        file_list = copy.deepcopy(obj.file_list)

        for val in file_list.values():
            if not val['file'].startswith('<'):
                val['file'] = default_storage.url(val['file'])
        return file_list
예제 #6
0
class AdminInvitationSerializer(HyperlinkedMixin, serializers.ModelSerializer):
    hyperlinks = (('self', 'admin-invitation-detail', ('pk', )),
                  ('instance', 'instance-detail', ('instance.name', )))
    role = DisplayedChoiceField(source='role.name',
                                choices=Role.ROLE_CHOICES.as_choices())
    state = DisplayedChoiceField(choices=Invitation.STATE_CHOICES.as_choices(),
                                 default=Invitation.STATE_CHOICES.NEW)
    inviter = serializers.CharField(source='inviter.email')
    instance = serializers.CharField(source='instance.name')

    class Meta:
        model = Invitation
        fields = (
            'id',
            'email',
            'role',
            'key',
            'instance',
            'inviter',
            'created_at',
            'updated_at',
            'state',
        )
예제 #7
0
class ChangeSerializer(DynamicFieldsMixin, HyperlinkedMixin,
                       serializers.Serializer):
    hyperlinks = (('self', 'change-detail', (
        'instance.name',
        'channel.name',
        'id',
    )), )

    id = serializers.IntegerField()
    created_at = serializers.DateTimeField()
    action = DisplayedChoiceField(choices=Change.ACTIONS.as_choices())
    author = JSONField()
    metadata = JSONField()
    payload = JSONField()
예제 #8
0
class InvoiceSerializer(DynamicFieldsMixin, HyperlinkedMixin,
                        serializers.ModelSerializer):
    hyperlinks = (
        ('self', 'invoice-detail', ('pk', )),
        ('pdf', 'invoice-pdf', ('pk', )),
        ('retry-payment', 'invoice-retry-payment', ('pk', )),
    )

    period = serializers.CharField(source='formatted_period')
    amount = serializers.CharField(source='formatted_amount')
    items = InvoiceItemSerializer(many=True)
    status = DisplayedChoiceField(choices=Invoice.STATUS_CHOICES.as_choices())

    class Meta:
        model = Invoice
        fields = ('id', 'period', 'amount', 'status', 'created_at',
                  'updated_at', 'items')
예제 #9
0
class SocketEnvironmentSerializer(RevalidateMixin, MetadataMixin, DynamicFieldsMixin, HyperlinkedMixin,
                                  ModelSerializer):
    hyperlinks = (
        ('self', 'socket-environment-detail', (
            'instance.name',
            'name',
        )),
    )

    name = LowercaseCharField(validators=[
        UniqueValidator(queryset=SocketEnvironment.objects.all()),
        DjangoValidator()
    ])
    zip_file = FileField(write_only=True)
    status = DisplayedChoiceField(SocketEnvironment.STATUSES.as_choices(), read_only=True)
    status_info = JSONField(read_only=True)
    checksum = CharField(read_only=True)

    class Meta:
        model = SocketEnvironment
        fields = ('name', 'description', 'created_at', 'updated_at',
                  'status', 'status_info', 'metadata', 'zip_file', 'checksum')
예제 #10
0
class BackupSerializer(MetadataMixin, ModelSerializer):
    instance = SlugRelatedField(slug_field='name',
                                required=False,
                                read_only=True,
                                allow_null=False)
    status = DisplayedChoiceField(Backup.STATUSES.as_choices(), read_only=True)
    author = AdminFullSerializer(read_only=True, source="owner")
    details = JSONField(read_only=True)

    class Meta:
        model = Backup
        read_only_fields = ('id', 'instance', 'created_at', 'updated_at',
                            'archive', 'size', 'status', 'status_info',
                            'author', 'details')
        fields = read_only_fields + ('description', 'label', 'query_args',
                                     'metadata')
        extra_kwargs = {
            'description': {
                'required': False
            },
            'label': {
                'required': False
            }
        }
예제 #11
0
class BalanceItemSerializer(serializers.Serializer):
    amount = serializers.CharField(source='formatted_amount')
    quantity = serializers.IntegerField()
    source = DisplayedChoiceField(choices=InvoiceItem.SOURCES.as_choices())
예제 #12
0
class HostingSerializer(DynamicFieldsMixin, HyperlinkedMixin, ModelSerializer):
    hyperlinks = (
        ('self', 'hosting-detail', (
            'instance.name',
            'pk',
        )),
        ('files', 'hosting-file-list', (
            'instance.name',
            'pk',
        )),
        ('set_default', 'hosting-set-default', (
            'instance.name',
            'pk',
        )),
        ('enable_ssl', 'hosting-enable-ssl', (
            'instance.name',
            'pk',
        )),
    )

    name = serializers.CharField(
        max_length=253,
        validators=[
            UniqueValidator(queryset=Hosting.objects.all()),
            HostingNameValidator()
        ])
    domains = serializers.ListField(child=LowercaseCharField(
        max_length=253, validators=[DomainValidator()]), )
    ssl_status = DisplayedChoiceField(
        choices=Hosting.SSL_STATUSES.as_choices(), read_only=True)

    class Meta:
        model = Hosting
        read_only_fields = ('is_default', )
        fields = ('id', 'name', 'is_default', 'description', 'created_at',
                  'updated_at', 'domains', 'is_active', 'ssl_status')

    def validate_domains(self, value):
        value_set = set(value)
        only_domains = [
            v for v in value_set if re.match(VALID_DOMAIN_REGEX, v)
        ]
        # Only 1 domain per hosting allowed so that we can process SSL properly
        if len(only_domains) > 1:
            raise OnlyOneDomainAllowed()

        # this checks the global domains;
        if Instance.objects.exclude(pk=get_current_instance().pk).filter(
                domains__overlap=only_domains).exists():
            raise DomainAlreadyUsed()

        # prevent for creating hosting with the same domain;
        # but allow to update the same object with the same domains;
        validate_queryset = Hosting.objects.all()
        # update case; if empty - create case;
        current_hosting = self.instance
        if current_hosting:
            validate_queryset = validate_queryset.exclude(
                pk=current_hosting.pk)

        # use a all values here - this will check if no two hosting objects exists with the same instance name
        # and suffix combination;
        if validate_queryset.filter(domains__overlap=list(value_set)).exists():
            raise DomainAlreadyUsed()

        return list(value_set)

    def validate(self, data):
        if self.instance and self.instance.is_locked:
            raise HostingLocked()
        return super().validate(data)

    def to_internal_value(self, data):
        reverted_data = super().to_internal_value(data)
        # Automatically add name to domains
        if reverted_data:
            if 'name' in reverted_data:
                name = reverted_data['name']
            else:
                name = self.instance.name

            if 'domains' in reverted_data:
                domains = reverted_data['domains']
            else:
                domains = self.instance

            if isinstance(domains, list):
                if name not in domains:
                    domains.append(name)
            else:
                reverted_data['domains'] = [name]
        return reverted_data
예제 #13
0
class APNSMessageSerializer(HyperlinkedMixin, ModelSerializer):
    hyperlinks = (
        ('self', 'apns-messages-detail', ('instance.name', 'pk')),
    )

    SCHEMA = {
        'type': 'object',
        'maxItems': 128,
        'required': [
            'registration_ids',
            'environment',
            'aps'
        ],
        'properties': {
            # Targets
            'registration_ids': {
                'type': 'array',
                'uniqueItems': True,
                'maxItems': 1000,
                'items': {
                    'type': 'string'
                }
            },
            'environment': {
                'type': 'string',
                'enum': [
                    'development',
                    'production',
                ]
            },
            'aps': {
                'type': 'object',
                'required': ['alert'],
                'properties': {
                    'alert': {
                        'oneOf': [
                            {'type': 'string'},
                            {
                                'type': 'object',
                                'required': ['title', 'body'],
                                'properties': {
                                    'title': {
                                        'type': 'string'
                                    },
                                    'body': {
                                        'type': 'string'
                                    },
                                    'title-loc-key': {
                                        'type': 'string'
                                    },
                                    'title-loc-args': {
                                        'type': 'array',
                                        'items': {
                                            'type': 'string'
                                        }
                                    },
                                    'action-loc-key': {
                                        'type': 'string'
                                    },
                                    'loc-key': {
                                        'type': 'string'
                                    },
                                    'loc-args': {
                                        'type': 'array',
                                        'items': {
                                            'type': 'string'
                                        }
                                    },
                                    'launch-image': {
                                        'type': 'string'
                                    }
                                }
                            },
                        ]
                    },
                    'badge': {
                        'type': 'integer'
                    },
                    'sound': {
                        'type': 'string'
                    },
                    'content-available': {
                        'type': 'integer'
                    },
                    'category': {
                        'type': 'string'
                    }
                }
            },
        }
    }

    status = DisplayedChoiceField(choices=APNSMessage.STATUSES.as_choices(), read_only=True)
    content = JSONField(required=True, schema=SCHEMA)
    result = JSONField(default={}, read_only=True)

    class Meta:
        model = APNSMessage
        read_only_fields = (
            'created_at',
            'updated_at',
            'status',
            'result',
        )
        fields = '__all__'

    def validate(self, data):
        config = Cached(APNSConfig, kwargs={'id': 1}).get()
        if 'content' in data:
            environment = data['content']['environment']
        else:
            environment = self.instance.content['environment']
        certificate = getattr(config, '{}_certificate'.format(environment))
        bundle_identifier = getattr(config, '{}_bundle_identifier'.format(environment))

        if not certificate:
            raise ValidationError('APNS certificate for "{}" environment is required.'.format(environment))

        if not bundle_identifier:
            raise ValidationError('APNS bundle identifier for "{}" environment is required.'.format(environment))

        return data
예제 #14
0
class GCMMessageSerializer(HyperlinkedMixin, ModelSerializer):
    hyperlinks = (
        ('self', 'gcm-messages-detail', ('instance.name', 'pk')),
    )

    # More info: https://developers.google.com/cloud-messaging/http-server-ref
    SCHEMA = {
        'type': 'object',
        'additionalProperties': False,
        'required': [
            'registration_ids',
            'environment',
        ],
        'properties': {
            # Targets
            'registration_ids': {
                'type': 'array',
                'uniqueItems': True,
                'maxItems': 1000,
                'items': {
                    'type': 'string'
                }
            },
            'environment': {
                'type': 'string',
                'enum': [
                    'development',
                    'production',
                ]
            },

            # Payload
            'data': {
                'type': 'object',
                'maxProperties': 256
            },
            'notification': {
                'type': 'object',
                'maxProperties': 256
            },

            # Options
            'collapse_key': {
                'type': 'string'
            },
            'priority': {
                'type': 'string',
                'enum': ['normal', 'high']
            },
            'content_available': {
                'type': 'boolean'
            },
            'delay_while_idle': {
                'type': 'boolean'
            },
            'time_to_live': {
                'type': 'integer',
                'maximum': 3600 * 24 * 28,  # 4 weeks
                'minimum': 1
            },
            'restricted_package_name': {
                'type': 'string'
            },
            'dry_run': {
                'type': 'boolean'
            }
        }
    }

    status = DisplayedChoiceField(choices=GCMMessage.STATUSES.as_choices(), read_only=True)
    content = JSONField(required=True, schema=SCHEMA)
    result = JSONField(default={}, read_only=True)

    class Meta:
        model = GCMMessage
        read_only_fields = (
            'created_at',
            'updated_at',
            'status',
            'result',
        )
        fields = '__all__'

    def validate(self, data):
        config = Cached(GCMConfig, kwargs={'id': 1}).get()
        if 'content' in data:
            environment = data['content']['environment']
        else:
            environment = self.instance.content['environment']
        api_key = getattr(config, '{}_api_key'.format(environment))

        if not api_key:
            raise ValidationError('GCM api key for "{}" environment is required.'.format(environment))

        return data