class BaseExtractionJSONParams(CoercingType): """Common extraction parameters validator""" domains = validators.Array( items=DomainValidator(enum=list(TAG_DOMAINS.keys())), unique_items=True, description= 'List of domains to search, if not provided ALL domains will be included', allow_null=True, ) offset = validators.Integer(minimum=0, allow_null=True, description='The paging offset') limit = validators.Integer( minimum=1, maximum=10000, default=10, allow_null=True, description='The paging limit.', ) min_score = validators.Number( minimum=0.1, allow_null=True, description= 'The minimum search score required. No default is applied when not provided. ' 'Ignored when `constant_score` is on.', ) constant_score = validators.Boolean( default=True, allow_null=False, description= 'Disables relevance scoring when `True`. All results will have score `1`.', )
class Car(types.Type): # validators comes from apistar id = validators.Integer(allow_null=True) # assign in POST manufacturer = validators.String(enum=list(VALID_MANUFACTURERS)) model = validators.String(max_length=50) year = validators.Integer(minimum=1900, maximum=2050) vin = validators.String(max_length=50, default='')
def generate_fields(self, url, method, handler): fields = [] path_names = [ item.strip("{}").lstrip("+") for item in re.findall("{[^}]*}", url) ] parameters = inspect.signature(handler).parameters for name, param in parameters.items(): if name in path_names: schema = { param.empty: None, int: validators.Integer(), float: validators.Number(), str: validators.String(), }[param.annotation] field = Field(name=name, location="path", schema=schema) fields.append(field) elif param.annotation in ( param.empty, int, float, bool, str, http.QueryParam, ): if param.default is param.empty: kwargs = {} elif param.default is None: kwargs = {"default": None, "allow_null": True} else: kwargs = {"default": param.default} schema = { param.empty: None, int: validators.Integer(**kwargs), float: validators.Number(**kwargs), bool: validators.Boolean(**kwargs), str: validators.String(**kwargs), http.QueryParam: validators.String(**kwargs), }[param.annotation] field = Field(name=name, location="query", schema=schema) fields.append(field) elif issubclass(param.annotation, types.Type): if method in ("GET", "DELETE"): for ( name, validator, ) in param.annotation.validator.properties.items(): field = Field(name=name, location="query", schema=validator) fields.append(field) else: field = Field(name=name, location="body", schema=param.annotation.validator) fields.append(field) return fields
class LimitOffsetMeta(types.Type): limit = validators.Integer(title="limit", description="Number of retrieved items") offset = validators.Integer(title="offset", description="Collection offset") count = validators.Integer(title="count", description="Total number of items", allow_null=True)
class Car(types.Type): """Simple Car class.""" # allow_null required to assign id manually. id = validators.Integer(allow_null=True) make = validators.String(enum=VALID_MAKES) model = validators.String(min_length=1, max_length=50) year = validators.Integer(minimum=1900, maximum=2050) vin = validators.String(max_length=50, default="")
class Movie(types.Type): id = validators.Integer(allow_null=True) # assign in POST genre = validators.Array(items=validators.String(enum=list(VALID_GENRES))) director_name = validators.String(max_length=100) year = validators.Integer(minimum=1900, maximum=2050) language = validators.String(max_length=100, enum=list(VALID_LANGUAGES), allow_null=True) title = validators.String(max_length=200) rating = validators.Number(minimum=0, maximum=10)
class Anime(types.Type): id = validators.Integer(allow_null=True) title = validators.String(max_length=2500) year = validators.Integer(minimum=1900, maximum=2050) episodes = validators.Integer(maximum=9999) status = validators.String(enum=list(ANIME_VALID_STATUS)) type = validators.String(allow_null=True) animeSeason = validators.Object(allow_null=True) picture = validators.String(allow_null=True) sources = validators.Array(allow_null=True)
class Block(PickleType): height = validators.Integer() timestamp = validators.DateTime() txns = validators.Array(items=Transaction, allow_null=True, default=[]) mined_hash = validators.String(max_length=100) previous_hash = validators.String(max_length=100, allow_null=True) nonce = validators.Integer() @property def previous_block(self): return get_block(self.previous_hash)
def generate_fields(self, url, method, handler): fields = [] path_names = [ item.strip('{}').lstrip('+') for item in re.findall('{[^}]*}', url) ] parameters = inspect.signature(handler).parameters for name, param in parameters.items(): if name in path_names: schema = { param.empty: None, int: validators.Integer(), float: validators.Number(), str: validators.String() }[param.annotation] field = Field(name=name, location='path', schema=schema) fields.append(field) elif param.annotation in (param.empty, int, float, bool, str, http.QueryParam): if param.default is param.empty: kwargs = {} elif param.default is None: kwargs = {'default': None, 'allow_null': True} else: kwargs = {'default': param.default} schema = { param.empty: None, int: validators.Integer(**kwargs), float: validators.Number(**kwargs), bool: validators.Boolean(**kwargs), str: validators.String(**kwargs), http.QueryParam: validators.String(**kwargs), }[param.annotation] field = Field(name=name, location='query', schema=schema) fields.append(field) elif issubclass(param.annotation, types.Type): if method in ('GET', 'DELETE'): for name, validator in param.annotation.validator.properties.items( ): field = Field(name=name, location='query', schema=validator) fields.append(field) else: field = Field(name=name, location='body', schema=param.annotation.validator) fields.append(field) return fields
class AuthenticatedUserData(types.Type): id = validators.Integer() email = validators.String(description="Email address", pattern="[^@]+@[^@]+\.[^@]+", allow_null=True) phone = validators.String(description="Phone number", pattern="\D*", allow_null=True) first_name = validators.String() last_name = validators.String() language = validators.Integer() groups = validators.Array() location = validators.Integer() picture = validators.String(allow_null=True)
def resolve(self, parameter: inspect.Parameter, path_params: ValidatedPathParams, query_params: ValidatedQueryParams): params = path_params if (parameter.name in path_params) else query_params has_default = parameter.default is not parameter.empty allow_null = parameter.default is None param_validator = { parameter.empty: validators.Any(), str: validators.String(allow_null=allow_null), int: validators.Integer(allow_null=allow_null), float: validators.Number(allow_null=allow_null), bool: validators.Boolean(allow_null=allow_null) }[parameter.annotation] validator = validators.Object( properties=[(parameter.name, param_validator)], required=[] if has_default else [parameter.name]) try: params = validator.validate(params, allow_coerce=True) except validators.ValidationError as exc: raise exceptions.NotFound(exc.detail) return params.get(parameter.name, parameter.default)
class Person(types.Type): id = validators.Integer(allow_null=True) department = validators.String(enum=list(VALID_DEPARTMENT)) first_name = validators.String(max_length=50) last_name = validators.String(max_length=50) email = validators.String(max_length=100) gender = validators.String(enum=['Male', 'Female'])
class Product(types.Type): name = validators.String(max_length=10) rating = validators.Integer(allow_null=True, default=None, minimum=0, maximum=100) created = validators.DateTime()
class GetResponse(types.Type): """ default type for get request responses """ count = validators.Integer(description="The number of objects matching this query.", allow_null=True) next = validators.String(description="The url for the next page of data.", allow_null=True) previous = validators.String(description="The url for the previous page of data.", allow_null=True) results = validators.Array(description="The list of objects returned by the query.", allow_null=True)
class SessionSettings(types.Type): cookie_name = validators.String(default='session_id') cookie_age = validators.Integer(allow_null=True) cookie_domain = validators.String(allow_null=True) cookie_path = validators.String(default='/') cookie_secure = validators.Boolean(default=False) cookie_httponly = validators.Boolean(default=False)
class Ip(types.Type): # validators comes from apistar id = validators.Integer(allow_null=True) # assign in POST gender = validators.String(enum=list(VALID_GENDERS)) first_name = validators.String(max_length=50) last_name = validators.String(max_length=50) email = validators.String(max_length=50, default='') ip = validators.String(max_length=15, default="0.0.0.0")
class Business(types.Type): id = validators.Integer(allow_null=True) # assign in POST company = validators.String(max_length=100) address = validators.String(max_length=200) city = validators.String(max_length=50) country = validators.String(enum=list(VALID_COUNTRIES)) # Thought would be enough to set default to '', but got error, "May not be null". # So added "allow_null = True" post_code = validators.String(max_length=20, default='', allow_null=True)
class User(types.Type): """Simple User class.""" userid = validators.Integer(minimum=1, allow_null=True) userhash = validators.String(min_length=32, max_length=32) username = validators.String(max_length=50) fullname = validators.String(max_length=100) joined = validators.Date(allow_null=True) timezone = validators.String(enum=VALID_TIMEZONES)
class PuppyOutputType(types.Type): id = validators.Number(title="id", description="ID") name = validators.String(title="name", description="Name") number = validators.Integer(title="number", description="Number") time = validators.DateTime(title="time", description="Time") float = validators.Number(title="float", description="Float", allow_null=True) bool = validators.Boolean(title="bool", description="Boolean")
class CsrfSettings(types.Type): CSRF_COOKIE_NAME = validators.String(default='csrftoken') CSRF_COOKIE_AGE = validators.Integer(default=60 * 60 * 24 * 7 * 52) CSRF_COOKIE_DOMAIN = validators.String(allow_null=True) CSRF_COOKIE_PATH = validators.String(default='/') CSRF_COOKIE_SECURE = validators.Boolean(default=False) CSRF_COOKIE_HTTPONLY = validators.Boolean(default=False) CSRF_HEADER_NAME = validators.String(default='HTTP_X_CSRFTOKEN') CSRF_TOKEN_FIELD_NAME = validators.String(default='csrf_token') CSRF_TRUSTED_ORIGINS = validators.Array(default=[])
class FeatureUpdate(types.Type): version = validators.String(description="The feature number version.") enabled = validators.Boolean() deny = validators.Boolean() services = validators.Array( unique_items=True, items=validators.Integer(), allow_null=True, description="Array with services allowed to access the feature.", )
class PuppyInputType(types.Type): name = validators.String(title="name", description="Name") number = validators.Integer(title="number", description="Number", default=0) time = validators.DateTime(title="time", description="Time") float = validators.Number(title="float", description="Float", allow_null=True) bool = validators.Boolean(title="bool", description="Boolean", default=False)
class Device(types.Type): id = validators.Integer(allow_null=True, minimum=1, maximum=999) device_model = validators.String(enum=list(DEVICE_MODELS)) ip_address = validators.String(default='127.0.0.1') remote_access_type = validators.String( enum=['ssh', 'telnet', 'http', 'https'], default='ssh') login = validators.String(default='cisco') password = validators.String(default='cisco', min_length=5) secret = validators.String(default='cisco', min_length=5) apc = validators.Boolean(default=False) free_access_to_site = validators.Boolean(default=True) start_date = validators.Date(default=DEFAULT_DATE) company_name = validators.String(allow_null=True) city = validators.String(allow_null=True) city_address = validators.String(allow_null=True) owner = validators.String(allow_null=True) phone = validators.String(min_length=12, max_length=12) email = validators.String(allow_null=True)
class GradesBlock(BaseBlock): BLOCK_TYPE = "grades" total_grades_count = validators.Integer() global_grade = validators.Number() url = validators.String(allow_null=True) @classmethod def from_es(cls, es_poi, lang): raw_grades = es_poi.get_raw_grades() or {} total_grades_count = raw_grades.get('total_grades_count', None) global_grade = raw_grades.get('global_grade', None) if total_grades_count is None or global_grade is None: return None return cls(total_grades_count=total_grades_count, global_grade=global_grade, url=es_poi.get_reviews_url() or None)
class UserBaseType(types.Type): id = validators.Integer(allow_null=True) username = validators.String(min_length=1) role = validators.String(enum=list(UserRole.__members__.keys()), allow_null=True, default='user') fullname = validators.String(min_length=1) def __init__(self, *args, **kwargs): patched = [] for arg in args: if isinstance(arg, User): # Convert enum name to string. role = arg.role.name arg = { key: getattr(arg, key) for key in attribute_names(arg.__class__) } arg['role'] = role patched.append(arg) super().__init__(*patched, **kwargs)
class AirQuality(BaseBlock): BLOCK_TYPE = 'air_quality' CO = ParticleType PM10 = ParticleType O3 = ParticleType NO2 = ParticleType SO2 = ParticleType PM2_5 = ParticleType quality_index = validators.Integer(minimum=1, maximum=5) date = validators.DateTime() source = validators.String() source_url = validators.String() measurements_unit = validators.String(allow_null=True) @classmethod def from_es(cls, place, lang): if not settings['BLOCK_AIR_QUALITY_ENABLED']: return None if place.PLACE_TYPE != 'admin': return None if place.get('zone_type') not in ('city', 'city_district', 'suburb'): return None bbox = place.get_bbox() if not bbox: return None try: air_quality = get_air_quality(bbox) except Exception: logger.warning('Failed to fetch air quality for %s', place.get_id(), exc_info=True) return None if not air_quality: return None return cls(**air_quality)
class HappyHourBlock(BaseBlock): BLOCK_TYPE = 'happy_hours' STATUSES = ['yes', 'no'] status = validators.String(enum=STATUSES) next_transition_datetime = validators.String(allow_null=True) seconds_before_next_transition = validators.Integer(allow_null=True) raw = validators.String() days = validators.Array(items=DaysType) @classmethod def init_class(cls, status, next_transition_datetime, time_before_next, oh, poi_dt, raw): return cls( status=status, next_transition_datetime=next_transition_datetime, seconds_before_next_transition=time_before_next, raw=oh.field, days=get_days(cls, oh, poi_dt) ) @classmethod def from_es(cls, es_poi, lang): return parse_time_block(cls, es_poi, lang, es_poi.get_raw_happy_hours())
class PatientUpdateSchema(types.Type): nom = validators.String(max_length=MAX_LENGTH["nom"], default="") prenom = validators.String(max_length=MAX_LENGTH["prenom"], default="") ddn = validators.Date(default="") sexe = validators.String(enum=SEXE, default=None, allow_null=True) rue = validators.String(description="rue", max_length=MAX_LENGTH["rue"], default="") cp = validators.Integer(description="Code Postal", default=None, allow_null=True) ville = validators.String(description="Ville", max_length=MAX_LENGTH["ville"], default="") tel = validators.String(description="Numéro de Téléphone", max_length=MAX_LENGTH["tel"], default="") email = validators.String(description="email", max_length=MAX_LENGTH["email"], default="") alive = validators.Boolean(description="vivant ?", default=None, allow_null=True)
class OpeningHourBlock(BaseBlock): BLOCK_TYPE = 'opening_hours' STATUSES = ['open', 'closed'] status = validators.String(enum=STATUSES) next_transition_datetime = validators.String(allow_null=True) seconds_before_next_transition = validators.Integer(allow_null=True) is_24_7 = validators.Boolean() raw = validators.String() days = validators.Array(items=DaysType) @classmethod def init_class(cls, status, next_transition_datetime, time_before_next, oh, poi_dt, raw): if raw == '24/7' or oh.is_24_7: return cls(status=status, next_transition_datetime=None, seconds_before_next_transition=None, is_24_7=True, raw=oh.field, days=get_days(cls, oh, poi_dt)) if all(r.status == 'closed' for r in oh.rules): # Ignore opening_hours such as "Apr 1-Sep 30: off", causing overflow return None return cls(status=status, next_transition_datetime=next_transition_datetime, seconds_before_next_transition=time_before_next, is_24_7=oh.is_24_7, raw=oh.field, days=get_days(cls, oh, poi_dt)) @classmethod def from_es(cls, es_poi, lang): return parse_time_block(cls, es_poi, lang, es_poi.get_raw_opening_hours())
import pytest from apistar import validators from apistar.exceptions import (ErrorMessage, Marker, ParseError, ValidationError) from apistar.parse import parse_json VALIDATOR = validators.Object(properties={'a': validators.Integer()}, required=['a'], additional_properties=False) def test_empty_string(): with pytest.raises(ParseError) as exc: parse_json(b'', VALIDATOR) error_messages = exc.value.get_error_messages() expecting = [ErrorMessage('No content.', Marker(0))] assert error_messages == expecting def test_object_missing_property_name(): with pytest.raises(ParseError) as exc: parse_json('{', VALIDATOR) error_messages = exc.value.get_error_messages() expecting = [ ErrorMessage('Expecting property name enclosed in double quotes.', Marker(1)) ] assert error_messages == expecting