diff --git a/languages/python/templates/model_utils.mustache b/languages/python/templates/model_utils.mustache new file mode 100644 index 0000000..cd2f0ce --- /dev/null +++ b/languages/python/templates/model_utils.mustache @@ -0,0 +1,1606 @@ +{{>partial_header}} + +from datetime import date, datetime # noqa: F401 +import inspect +import io +import os +import pprint +import re +import tempfile + +from dateutil.parser import parse + +from {{packageName}}.exceptions import ( + ApiKeyError, + ApiAttributeError, + ApiTypeError, + ApiValueError, +) + +none_type = type(None) +file_type = io.IOBase + + +class cached_property(object): + # this caches the result of the function call for fn with no inputs + # use this as a decorator on fuction methods that you want converted + # into cached properties + result_key = '_results' + + def __init__(self, fn): + self._fn = fn + + def __get__(self, instance, cls=None): + if self.result_key in vars(self): + return vars(self)[self.result_key] + else: + result = self._fn() + setattr(self, self.result_key, result) + return result + + +PRIMITIVE_TYPES = (list, float, int, bool, datetime, date, str, file_type) + +def allows_single_value_input(cls): + """ + This function returns True if the input composed schema model or any + descendant model allows a value only input + This is true for cases where oneOf contains items like: + oneOf: + - float + - NumberWithValidation + - StringEnum + - ArrayModel + - null + TODO: lru_cache this + """ + if ( + issubclass(cls, ModelSimple) or + cls in PRIMITIVE_TYPES + ): + return True + elif issubclass(cls, ModelComposed): + if not cls._composed_schemas['oneOf']: + return False + return any(allows_single_value_input(c) for c in cls._composed_schemas['oneOf']) + return False + +def composed_model_input_classes(cls): + """ + This function returns a list of the possible models that can be accepted as + inputs. + TODO: lru_cache this + """ + if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: + return [cls] + elif issubclass(cls, ModelNormal): + if cls.discriminator is None: + return [cls] + else: + return get_discriminated_classes(cls) + elif issubclass(cls, ModelComposed): + if not cls._composed_schemas['oneOf']: + return [] + if cls.discriminator is None: + input_classes = [] + for c in cls._composed_schemas['oneOf']: + input_classes.extend(composed_model_input_classes(c)) + return input_classes + else: + return get_discriminated_classes(cls) + return [] + + +class OpenApiModel(object): + """The base class for all OpenAPIModels""" + +{{> model_templates/method_set_attribute }} + +{{> model_templates/methods_shared }} + + def __new__(cls, *args, **kwargs): + # this function uses the discriminator to + # pick a new schema/class to instantiate because a discriminator + # propertyName value was passed in + + if len(args) == 1: + arg = args[0] + if arg is None and is_type_nullable(cls): + # The input data is the 'null' value and the type is nullable. + return None + + if issubclass(cls, ModelComposed) and allows_single_value_input(cls): + model_kwargs = {} + oneof_instance = get_oneof_instance(cls, model_kwargs, kwargs, model_arg=arg) + return oneof_instance + + + visited_composed_classes = kwargs.get('_visited_composed_classes', ()) + if ( + cls.discriminator is None or + cls in visited_composed_classes + ): + # Use case 1: this openapi schema (cls) does not have a discriminator + # Use case 2: we have already visited this class before and are sure that we + # want to instantiate it this time. We have visited this class deserializing + # a payload with a discriminator. During that process we traveled through + # this class but did not make an instance of it. Now we are making an + # instance of a composed class which contains cls in it, so this time make an instance of cls. + # + # Here's an example of use case 2: If Animal has a discriminator + # petType and we pass in "Dog", and the class Dog + # allOf includes Animal, we move through Animal + # once using the discriminator, and pick Dog. + # Then in the composed schema dog Dog, we will make an instance of the + # Animal class (because Dal has allOf: Animal) but this time we won't travel + # through Animal's discriminator because we passed in + # _visited_composed_classes = (Animal,) + + return super(OpenApiModel, cls).__new__(cls) + + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get('_path_to_item', ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" % + (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class( + cls, discr_propertyname_py, discr_value, []) + if new_cls is None: + path_to_item = kwargs.get('_path_to_item', ()) + disc_prop_value = kwargs.get( + discr_propertyname_js, kwargs.get(discr_propertyname_py)) + raise ApiValueError( + "Cannot deserialize input data due to invalid discriminator " + "value. The OpenAPI document has no mapping for discriminator " + "property '%s'='%s' at path: %s" % + (discr_propertyname_js, disc_prop_value, path_to_item) + ) + + if new_cls in visited_composed_classes: + # if we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here + return super(OpenApiModel, cls).__new__(cls) + + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = ( + cls._composed_schemas.get('oneOf', ()) + + cls._composed_schemas.get('anyOf', ())) + oneof_anyof_child = new_cls in oneof_anyof_classes + kwargs['_visited_composed_classes'] = visited_composed_classes + (cls,) + + if cls._composed_schemas.get('allOf') and oneof_anyof_child: + # Validate that we can make self because when we make the + # new_cls it will not include the allOf validations in self + self_inst = super(OpenApiModel, cls).__new__(cls) + self_inst.__init__(*args, **kwargs) + + new_inst = new_cls.__new__(new_cls, *args, **kwargs) + new_inst.__init__(*args, **kwargs) + return new_inst + + +class ModelSimple(OpenApiModel): + """the parent class of models whose type != object in their + swagger/openapi""" + +{{> model_templates/methods_setattr_getattr_normal }} + +{{> model_templates/methods_tostr_eq_simple }} + + +class ModelNormal(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi""" + +{{> model_templates/methods_setattr_getattr_normal }} + +{{> model_templates/methods_todict_tostr_eq_shared}} + + +class ModelComposed(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi and have oneOf/allOf/anyOf + + When one sets a property we use var_name_to_model_instances to store the value in + the correct class instances + run any type checking + validation code. + When one gets a property we use var_name_to_model_instances to get the value + from the correct class instances. + This allows multiple composed schemas to contain the same property with additive + constraints on the value. + + _composed_schemas (dict) stores the anyOf/allOf/oneOf classes + key (str): allOf/oneOf/anyOf + value (list): the classes in the XOf definition. + Note: none_type can be included when the openapi document version >= 3.1.0 + _composed_instances (list): stores a list of instances of the composed schemas + defined in _composed_schemas. When properties are accessed in the self instance, + they are returned from the self._data_store or the data stores in the instances + in self._composed_schemas + _var_name_to_model_instances (dict): maps between a variable name on self and + the composed instances (self included) which contain that data + key (str): property name + value (list): list of class instances, self or instances in _composed_instances + which contain the value that the key is referring to. + """ + +{{> model_templates/methods_setattr_getattr_composed }} + +{{> model_templates/methods_todict_tostr_eq_shared}} + + +COERCION_INDEX_BY_TYPE = { + ModelComposed: 0, + ModelNormal: 1, + ModelSimple: 2, + none_type: 3, # The type of 'None'. + list: 4, + dict: 5, + float: 6, + int: 7, + bool: 8, + datetime: 9, + date: 10, + str: 11, + file_type: 12, # 'file_type' is an alias for the built-in 'file' or 'io.IOBase' type. +} + +# these are used to limit what type conversions we try to do +# when we have a valid type already and we want to try converting +# to another type +UPCONVERSION_TYPE_PAIRS = ( + (str, datetime), + (str, date), + (int, float), # A float may be serialized as an integer, e.g. '3' is a valid serialized float. + (list, ModelComposed), + (dict, ModelComposed), + (str, ModelComposed), + (int, ModelComposed), + (float, ModelComposed), + (list, ModelComposed), + (list, ModelNormal), + (dict, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), +) + +COERCIBLE_TYPE_PAIRS = { + False: ( # client instantiation of a model with client data + # (dict, ModelComposed), + # (list, ModelComposed), + # (dict, ModelNormal), + # (list, ModelNormal), + # (str, ModelSimple), + # (int, ModelSimple), + # (float, ModelSimple), + # (list, ModelSimple), + # (str, int), + # (str, float), + # (str, datetime), + # (str, date), + # (int, str), + # (float, str), + ), + True: ( # server -> client data + (dict, ModelComposed), + (list, ModelComposed), + (dict, ModelNormal), + (list, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), + # (str, int), + # (str, float), + (str, datetime), + (str, date), + # (int, str), + # (float, str), + (str, file_type) + ), +} + + +def get_simple_class(input_value): + """Returns an input_value's simple class that we will use for type checking + Python2: + float and int will return int, where int is the python3 int backport + str and unicode will return str, where str is the python3 str backport + Note: float and int ARE both instances of int backport + Note: str_py2 and unicode_py2 are NOT both instances of str backport + + Args: + input_value (class/class_instance): the item for which we will return + the simple class + """ + if isinstance(input_value, type): + # input_value is a class + return input_value + elif isinstance(input_value, tuple): + return tuple + elif isinstance(input_value, list): + return list + elif isinstance(input_value, dict): + return dict + elif isinstance(input_value, none_type): + return none_type + elif isinstance(input_value, file_type): + return file_type + elif isinstance(input_value, bool): + # this must be higher than the int check because + # isinstance(True, int) == True + return bool + elif isinstance(input_value, int): + return int + elif isinstance(input_value, datetime): + # this must be higher than the date check because + # isinstance(datetime_instance, date) == True + return datetime + elif isinstance(input_value, date): + return date + elif isinstance(input_value, str): + return str + return type(input_value) + + +def check_allowed_values(allowed_values, input_variable_path, input_values): + """Raises an exception if the input_values are not allowed + + Args: + allowed_values (dict): the allowed_values dict + input_variable_path (tuple): the path to the input variable + input_values (list/str/int/float/date/datetime): the values that we + are checking to see if they are in allowed_values + """ + these_allowed_values = list(allowed_values[input_variable_path].values()) + if (isinstance(input_values, list) + and not set(input_values).issubset( + set(these_allowed_values))): + invalid_values = ", ".join( + map(str, set(input_values) - set(these_allowed_values))), + raise ApiValueError( + "Invalid values for `%s` [%s], must be a subset of [%s]" % + ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)) + ) + ) + elif (isinstance(input_values, dict) + and not set( + input_values.keys()).issubset(set(these_allowed_values))): + invalid_values = ", ".join( + map(str, set(input_values.keys()) - set(these_allowed_values))) + raise ApiValueError( + "Invalid keys in `%s` [%s], must be a subset of [%s]" % + ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)) + ) + ) + elif (not isinstance(input_values, (list, dict)) + and input_values not in these_allowed_values): + raise ApiValueError( + "Invalid value for `%s` (%s), must be one of %s" % + ( + input_variable_path[0], + input_values, + these_allowed_values + ) + ) + + +def is_json_validation_enabled(schema_keyword, configuration=None): + """Returns true if JSON schema validation is enabled for the specified + validation keyword. This can be used to skip JSON schema structural validation + as requested in the configuration. + + Args: + schema_keyword (string): the name of a JSON schema validation keyword. + configuration (Configuration): the configuration class. + """ + + return (configuration is None or + not hasattr(configuration, '_disabled_client_side_validations') or + schema_keyword not in configuration._disabled_client_side_validations) + + +def check_validations( + validations, input_variable_path, input_values, + configuration=None): + """Raises an exception if the input_values are invalid + + Args: + validations (dict): the validation dictionary. + input_variable_path (tuple): the path to the input variable. + input_values (list/str/int/float/date/datetime): the values that we + are checking. + configuration (Configuration): the configuration class. + """ + + if input_values is None: + return + + current_validations = validations[input_variable_path] + if (is_json_validation_enabled('multipleOf', configuration) and + 'multiple_of' in current_validations and + isinstance(input_values, (int, float)) and + not (float(input_values) / current_validations['multiple_of']).is_integer()): + # Note 'multipleOf' will be as good as the floating point arithmetic. + raise ApiValueError( + "Invalid value for `%s`, value must be a multiple of " + "`%s`" % ( + input_variable_path[0], + current_validations['multiple_of'] + ) + ) + + if (is_json_validation_enabled('maxLength', configuration) and + 'max_length' in current_validations and + len(input_values) > current_validations['max_length']): + raise ApiValueError( + "Invalid value for `%s`, length must be less than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['max_length'] + ) + ) + + if (is_json_validation_enabled('minLength', configuration) and + 'min_length' in current_validations and + len(input_values) < current_validations['min_length']): + raise ApiValueError( + "Invalid value for `%s`, length must be greater than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['min_length'] + ) + ) + + if (is_json_validation_enabled('maxItems', configuration) and + 'max_items' in current_validations and + len(input_values) > current_validations['max_items']): + raise ApiValueError( + "Invalid value for `%s`, number of items must be less than or " + "equal to `%s`" % ( + input_variable_path[0], + current_validations['max_items'] + ) + ) + + if (is_json_validation_enabled('minItems', configuration) and + 'min_items' in current_validations and + len(input_values) < current_validations['min_items']): + raise ValueError( + "Invalid value for `%s`, number of items must be greater than or " + "equal to `%s`" % ( + input_variable_path[0], + current_validations['min_items'] + ) + ) + + items = ('exclusive_maximum', 'inclusive_maximum', 'exclusive_minimum', + 'inclusive_minimum') + if (any(item in current_validations for item in items)): + if isinstance(input_values, list): + max_val = max(input_values) + min_val = min(input_values) + elif isinstance(input_values, dict): + max_val = max(input_values.values()) + min_val = min(input_values.values()) + else: + max_val = input_values + min_val = input_values + + if (is_json_validation_enabled('exclusiveMaximum', configuration) and + 'exclusive_maximum' in current_validations and + max_val >= current_validations['exclusive_maximum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than `%s`" % ( + input_variable_path[0], + current_validations['exclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('maximum', configuration) and + 'inclusive_maximum' in current_validations and + max_val > current_validations['inclusive_maximum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['inclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('exclusiveMinimum', configuration) and + 'exclusive_minimum' in current_validations and + min_val <= current_validations['exclusive_minimum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than `%s`" % + ( + input_variable_path[0], + current_validations['exclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('minimum', configuration) and + 'inclusive_minimum' in current_validations and + min_val < current_validations['inclusive_minimum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than or equal " + "to `%s`" % ( + input_variable_path[0], + current_validations['inclusive_minimum'] + ) + ) + flags = current_validations.get('regex', {}).get('flags', 0) + if (is_json_validation_enabled('pattern', configuration) and + 'regex' in current_validations and + not re.search(current_validations['regex']['pattern'], + input_values, flags=flags)): + err_msg = r"Invalid value for `%s`, must match regular expression `%s`" % ( + input_variable_path[0], + current_validations['regex']['pattern'] + ) + if flags != 0: + # Don't print the regex flags if the flags are not + # specified in the OAS document. + err_msg = r"%s with flags=`%s`" % (err_msg, flags) + raise ApiValueError(err_msg) + + +def order_response_types(required_types): + """Returns the required types sorted in coercion order + + Args: + required_types (list/tuple): collection of classes or instance of + list or dict with class information inside it. + + Returns: + (list): coercion order sorted collection of classes or instance + of list or dict with class information inside it. + """ + + def index_getter(class_or_instance): + if isinstance(class_or_instance, list): + return COERCION_INDEX_BY_TYPE[list] + elif isinstance(class_or_instance, dict): + return COERCION_INDEX_BY_TYPE[dict] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelComposed)): + return COERCION_INDEX_BY_TYPE[ModelComposed] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelNormal)): + return COERCION_INDEX_BY_TYPE[ModelNormal] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelSimple)): + return COERCION_INDEX_BY_TYPE[ModelSimple] + elif class_or_instance in COERCION_INDEX_BY_TYPE: + return COERCION_INDEX_BY_TYPE[class_or_instance] + raise ApiValueError("Unsupported type: %s" % class_or_instance) + + sorted_types = sorted( + required_types, + key=lambda class_or_instance: index_getter(class_or_instance) + ) + return sorted_types + + +def remove_uncoercible(required_types_classes, current_item, spec_property_naming, + must_convert=True): + """Only keeps the type conversions that are possible + + Args: + required_types_classes (tuple): tuple of classes that are required + these should be ordered by COERCION_INDEX_BY_TYPE + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + current_item (any): the current item (input data) to be converted + + Keyword Args: + must_convert (bool): if True the item to convert is of the wrong + type and we want a big list of coercibles + if False, we want a limited list of coercibles + + Returns: + (list): the remaining coercible required types, classes only + """ + current_type_simple = get_simple_class(current_item) + + results_classes = [] + for required_type_class in required_types_classes: + # convert our models to OpenApiModel + required_type_class_simplified = required_type_class + if isinstance(required_type_class_simplified, type): + if issubclass(required_type_class_simplified, ModelComposed): + required_type_class_simplified = ModelComposed + elif issubclass(required_type_class_simplified, ModelNormal): + required_type_class_simplified = ModelNormal + elif issubclass(required_type_class_simplified, ModelSimple): + required_type_class_simplified = ModelSimple + + if required_type_class_simplified == current_type_simple: + # don't consider converting to one's own class + continue + + class_pair = (current_type_simple, required_type_class_simplified) + if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]: + results_classes.append(required_type_class) + elif class_pair in UPCONVERSION_TYPE_PAIRS: + results_classes.append(required_type_class) + return results_classes + +def get_discriminated_classes(cls): + """ + Returns all the classes that a discriminator converts to + TODO: lru_cache this + """ + possible_classes = [] + key = list(cls.discriminator.keys())[0] + if is_type_nullable(cls): + possible_classes.append(cls) + for discr_cls in cls.discriminator[key].values(): + if hasattr(discr_cls, 'discriminator') and discr_cls.discriminator is not None: + possible_classes.extend(get_discriminated_classes(discr_cls)) + else: + possible_classes.append(discr_cls) + return possible_classes + + +def get_possible_classes(cls, from_server_context): + # TODO: lru_cache this + possible_classes = [cls] + if from_server_context: + return possible_classes + if hasattr(cls, 'discriminator') and cls.discriminator is not None: + possible_classes = [] + possible_classes.extend(get_discriminated_classes(cls)) + elif issubclass(cls, ModelComposed): + possible_classes.extend(composed_model_input_classes(cls)) + return possible_classes + + +def get_required_type_classes(required_types_mixed, spec_property_naming): + """Converts the tuple required_types into a tuple and a dict described + below + + Args: + required_types_mixed (tuple/list): will contain either classes or + instance of list or dict + spec_property_naming (bool): if True these values came from the + server, and we use the data types in our endpoints. + If False, we are client side and we need to include + oneOf and discriminator classes inside the data types in our endpoints + + Returns: + (valid_classes, dict_valid_class_to_child_types_mixed): + valid_classes (tuple): the valid classes that the current item + should be + dict_valid_class_to_child_types_mixed (dict): + valid_class (class): this is the key + child_types_mixed (list/dict/tuple): describes the valid child + types + """ + valid_classes = [] + child_req_types_by_current_type = {} + for required_type in required_types_mixed: + if isinstance(required_type, list): + valid_classes.append(list) + child_req_types_by_current_type[list] = required_type + elif isinstance(required_type, tuple): + valid_classes.append(tuple) + child_req_types_by_current_type[tuple] = required_type + elif isinstance(required_type, dict): + valid_classes.append(dict) + child_req_types_by_current_type[dict] = required_type[str] + else: + valid_classes.extend(get_possible_classes(required_type, spec_property_naming)) + return tuple(valid_classes), child_req_types_by_current_type + + +def change_keys_js_to_python(input_dict, model_class): + """ + Converts from javascript_key keys in the input_dict to python_keys in + the output dict using the mapping in model_class. + If the input_dict contains a key which does not declared in the model_class, + the key is added to the output dict as is. The assumption is the model_class + may have undeclared properties (additionalProperties attribute in the OAS + document). + """ + + if getattr(model_class, 'attribute_map', None) is None: + return input_dict + output_dict = {} + reversed_attr_map = {value: key for key, value in + model_class.attribute_map.items()} + for javascript_key, value in input_dict.items(): + python_key = reversed_attr_map.get(javascript_key) + if python_key is None: + # if the key is unknown, it is in error or it is an + # additionalProperties variable + python_key = javascript_key + output_dict[python_key] = value + return output_dict + + +def get_type_error(var_value, path_to_item, valid_classes, key_type=False): + error_msg = type_error_message( + var_name=path_to_item[-1], + var_value=var_value, + valid_classes=valid_classes, + key_type=key_type + ) + return ApiTypeError( + error_msg, + path_to_item=path_to_item, + valid_classes=valid_classes, + key_type=key_type + ) + + +def deserialize_primitive(data, klass, path_to_item): + """Deserializes string to primitive type. + + :param data: str/int/float + :param klass: str/class the class to convert to + + :return: int, float, str, bool, date, datetime + """ + additional_message = "" + try: + if klass in {datetime, date}: + additional_message = ( + "If you need your parameter to have a fallback " + "string value, please set its type as `type: {}` in your " + "spec. That allows the value to be any type. " + ) + if klass == datetime: + if len(data) < 8: + raise ValueError("This is not a datetime") + # The string should be in iso8601 datetime format. + parsed_datetime = parse(data) + date_only = ( + parsed_datetime.hour == 0 and + parsed_datetime.minute == 0 and + parsed_datetime.second == 0 and + parsed_datetime.tzinfo is None and + 8 <= len(data) <= 10 + ) + if date_only: + raise ValueError("This is a date, not a datetime") + return parsed_datetime + elif klass == date: + if len(data) < 8: + raise ValueError("This is not a date") + return parse(data).date() + else: + converted_value = klass(data) + if isinstance(data, str) and klass == float: + if str(converted_value) != data: + # '7' -> 7.0 -> '7.0' != '7' + raise ValueError('This is not a float') + return converted_value + except (OverflowError, ValueError) as ex: + # parse can raise OverflowError + raise ApiValueError( + "{0}Failed to parse {1} as {2}".format( + additional_message, repr(data), klass.__name__ + ), + path_to_item=path_to_item + ) from ex + + +def get_discriminator_class(model_class, + discr_name, + discr_value, cls_visited): + """Returns the child class specified by the discriminator. + + Args: + model_class (OpenApiModel): the model class. + discr_name (string): the name of the discriminator property. + discr_value (any): the discriminator value. + cls_visited (list): list of model classes that have been visited. + Used to determine the discriminator class without + visiting circular references indefinitely. + + Returns: + used_model_class (class/None): the chosen child class that will be used + to deserialize the data, for example dog.Dog. + If a class is not found, None is returned. + """ + + if model_class in cls_visited: + # The class has already been visited and no suitable class was found. + return None + cls_visited.append(model_class) + used_model_class = None + if discr_name in model_class.discriminator: + class_name_to_discr_class = model_class.discriminator[discr_name] + used_model_class = class_name_to_discr_class.get(discr_value) + if used_model_class is None: + # We didn't find a discriminated class in class_name_to_discr_class. + # So look in the ancestor or descendant discriminators + # The discriminator mapping may exist in a descendant (anyOf, oneOf) + # or ancestor (allOf). + # Ancestor example: in the GrandparentAnimal -> ParentPet -> ChildCat + # hierarchy, the discriminator mappings may be defined at any level + # in the hierarchy. + # Descendant example: mammal -> whale/zebra/Pig -> BasquePig/DanishPig + # if we try to make BasquePig from mammal, we need to travel through + # the oneOf descendant discriminators to find BasquePig + descendant_classes = model_class._composed_schemas.get('oneOf', ()) + \ + model_class._composed_schemas.get('anyOf', ()) + ancestor_classes = model_class._composed_schemas.get('allOf', ()) + possible_classes = descendant_classes + ancestor_classes + for cls in possible_classes: + # Check if the schema has inherited discriminators. + if hasattr(cls, 'discriminator') and cls.discriminator is not None: + used_model_class = get_discriminator_class( + cls, discr_name, discr_value, cls_visited) + if used_model_class is not None: + return used_model_class + return used_model_class + + +def deserialize_model(model_data, model_class, path_to_item, check_type, + configuration, spec_property_naming): + """Deserializes model_data to model instance. + + Args: + model_data (int/str/float/bool/none_type/list/dict): data to instantiate the model + model_class (OpenApiModel): the model class + path_to_item (list): path to the model in the received data + check_type (bool): whether to check the data tupe for the values in + the model + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + + Returns: + model instance + + Raise: + ApiTypeError + ApiValueError + ApiKeyError + """ + + kw_args = dict(_check_type=check_type, + _path_to_item=path_to_item, + _configuration=configuration, + _spec_property_naming=spec_property_naming) + + if issubclass(model_class, ModelSimple): + return model_class(model_data, **kw_args) + elif isinstance(model_data, list): + return model_class(*model_data, **kw_args) + if isinstance(model_data, dict): + kw_args.update(model_data) + return model_class(**kw_args) + elif isinstance(model_data, PRIMITIVE_TYPES): + return model_class(model_data, **kw_args) + + +def deserialize_file(response_data, configuration, content_disposition=None): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + Args: + param response_data (str): the file data to write + configuration (Configuration): the instance to use to convert files + + Keyword Args: + content_disposition (str): the value of the Content-Disposition + header + + Returns: + (file_type): the deserialized file which is open + The user is responsible for closing and reading the file + """ + fd, path = tempfile.mkstemp(dir=configuration.temp_folder_path) + os.close(fd) + os.remove(path) + + if content_disposition: + filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition).group(1) + path = os.path.join(os.path.dirname(path), filename) + + with open(path, "wb") as f: + if isinstance(response_data, str): + # change str to bytes so we can write it + response_data = response_data.encode('utf-8') + f.write(response_data) + + f = open(path, "rb") + return f + + +def attempt_convert_item(input_value, valid_classes, path_to_item, + configuration, spec_property_naming, key_type=False, + must_convert=False, check_type=True): + """ + Args: + input_value (any): the data to convert + valid_classes (any): the classes that are valid + path_to_item (list): the path to the item to convert + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + key_type (bool): if True we need to convert a key type (not supported) + must_convert (bool): if True we must convert + check_type (bool): if True we check the type or the returned data in + ModelComposed/ModelNormal/ModelSimple instances + + Returns: + instance (any) the fixed item + + Raises: + ApiTypeError + ApiValueError + ApiKeyError + """ + valid_classes_ordered = order_response_types(valid_classes) + valid_classes_coercible = remove_uncoercible( + valid_classes_ordered, input_value, spec_property_naming) + if not valid_classes_coercible or key_type: + # we do not handle keytype errors, json will take care + # of this for us + if configuration is None or not configuration.discard_unknown_keys: + raise get_type_error(input_value, path_to_item, valid_classes, + key_type=key_type) + for valid_class in valid_classes_coercible: + try: + if issubclass(valid_class, OpenApiModel): + return deserialize_model(input_value, valid_class, + path_to_item, check_type, + configuration, spec_property_naming) + elif valid_class == file_type: + return deserialize_file(input_value, configuration) + return deserialize_primitive(input_value, valid_class, + path_to_item) + except (ApiTypeError, ApiValueError, ApiKeyError) as conversion_exc: + if must_convert: + raise conversion_exc + # if we have conversion errors when must_convert == False + # we ignore the exception and move on to the next class + continue + # we were unable to convert, must_convert == False + return input_value + + +def is_type_nullable(input_type): + """ + Returns true if None is an allowed value for the specified input_type. + + A type is nullable if at least one of the following conditions is true: + 1. The OAS 'nullable' attribute has been specified, + 1. The type is the 'null' type, + 1. The type is a anyOf/oneOf composed schema, and a child schema is + the 'null' type. + Args: + input_type (type): the class of the input_value that we are + checking + Returns: + bool + """ + if input_type is none_type: + return True + if issubclass(input_type, OpenApiModel) and input_type._nullable: + return True + if issubclass(input_type, ModelComposed): + # If oneOf/anyOf, check if the 'null' type is one of the allowed types. + for t in input_type._composed_schemas.get('oneOf', ()): + if is_type_nullable(t): return True + for t in input_type._composed_schemas.get('anyOf', ()): + if is_type_nullable(t): return True + return False + + +def is_valid_type(input_class_simple, valid_classes): + """ + Args: + input_class_simple (class): the class of the input_value that we are + checking + valid_classes (tuple): the valid classes that the current item + should be + Returns: + bool + """ + valid_type = input_class_simple in valid_classes + if not valid_type and ( + issubclass(input_class_simple, OpenApiModel) or + input_class_simple is none_type): + for valid_class in valid_classes: + if valid_class is object: + # this is to handle oneOf fields in a Model. AAPI team added this if condition to handle Quant API + return True + if input_class_simple is none_type and is_type_nullable(valid_class): + # Schema is oneOf/anyOf and the 'null' type is one of the allowed types. + return True + if not (issubclass(valid_class, OpenApiModel) and valid_class.discriminator): + continue + discr_propertyname_py = list(valid_class.discriminator.keys())[0] + discriminator_classes = ( + valid_class.discriminator[discr_propertyname_py].values() + ) + valid_type = is_valid_type(input_class_simple, discriminator_classes) + if valid_type: + return True + return valid_type + + +def validate_and_convert_types(input_value, required_types_mixed, path_to_item, + spec_property_naming, _check_type, configuration=None): + """Raises a TypeError is there is a problem, otherwise returns value + + Args: + input_value (any): the data to validate/convert + required_types_mixed (list/dict/tuple): A list of + valid classes, or a list tuples of valid classes, or a dict where + the value is a tuple of value classes + path_to_item: (list) the path to the data being validated + this stores a list of keys or indices to get to the data being + validated + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + _check_type: (boolean) if true, type will be checked and conversion + will be attempted. + configuration: (Configuration): the configuration class to use + when converting file_type items. + If passed, conversion will be attempted when possible + If not passed, no conversions will be attempted and + exceptions will be raised + + Returns: + the correctly typed value + + Raises: + ApiTypeError + """ + results = get_required_type_classes(required_types_mixed, spec_property_naming) + valid_classes, child_req_types_by_current_type = results + + input_class_simple = get_simple_class(input_value) + valid_type = is_valid_type(input_class_simple, valid_classes) + if not valid_type: + if configuration: + # if input_value is not valid_type try to convert it + converted_instance = attempt_convert_item( + input_value, + valid_classes, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=True, + check_type=_check_type + ) + return converted_instance + else: + raise get_type_error(input_value, path_to_item, valid_classes, + key_type=False) + + # input_value's type is in valid_classes + if len(valid_classes) > 1 and configuration: + # there are valid classes which are not the current class + valid_classes_coercible = remove_uncoercible( + valid_classes, input_value, spec_property_naming, must_convert=False) + if valid_classes_coercible: + converted_instance = attempt_convert_item( + input_value, + valid_classes_coercible, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=False, + check_type=_check_type + ) + return converted_instance + + if child_req_types_by_current_type == {}: + # all types are of the required types and there are no more inner + # variables left to look at + return input_value + inner_required_types = child_req_types_by_current_type.get( + type(input_value) + ) + if inner_required_types is None: + # for this type, there are not more inner variables left to look at + return input_value + if isinstance(input_value, list): + if input_value == []: + # allow an empty list + return input_value + for index, inner_value in enumerate(input_value): + inner_path = list(path_to_item) + inner_path.append(index) + input_value[index] = validate_and_convert_types( + inner_value, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration + ) + elif isinstance(input_value, dict): + if input_value == {}: + # allow an empty dict + return input_value + for inner_key, inner_val in input_value.items(): + inner_path = list(path_to_item) + inner_path.append(inner_key) + if get_simple_class(inner_key) != str: + raise get_type_error(inner_key, inner_path, valid_classes, + key_type=True) + input_value[inner_key] = validate_and_convert_types( + inner_val, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration + ) + return input_value + + +def model_to_dict(model_instance, serialize=True): + """Returns the model properties as a dict + + Args: + model_instance (one of your model instances): the model instance that + will be converted to a dict. + + Keyword Args: + serialize (bool): if True, the keys in the dict will be values from + attribute_map + """ + result = {} + + model_instances = [model_instance] + if model_instance._composed_schemas: + model_instances.extend(model_instance._composed_instances) + for model_instance in model_instances: + for attr, value in model_instance._data_store.items(): + if serialize: + # we use get here because additional property key names do not + # exist in attribute_map + attr = model_instance.attribute_map.get(attr, attr) + if isinstance(value, list): + if not value or isinstance(value[0], PRIMITIVE_TYPES): + # empty list or primitive types + result[attr] = value + elif isinstance(value[0], ModelSimple): + result[attr] = [x.value for x in value] + else: + result[attr] = [model_to_dict(x, serialize=serialize) for x in value] + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], + model_to_dict(item[1], serialize=serialize)) + if hasattr(item[1], '_data_store') else item, + value.items() + )) + elif isinstance(value, ModelSimple): + result[attr] = value.value + elif hasattr(value, '_data_store'): + result[attr] = model_to_dict(value, serialize=serialize) + else: + result[attr] = value + + return result + + +def type_error_message(var_value=None, var_name=None, valid_classes=None, + key_type=None): + """ + Keyword Args: + var_value (any): the variable which has the type_error + var_name (str): the name of the variable which has the typ error + valid_classes (tuple): the accepted classes for current_item's + value + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + """ + key_or_value = 'value' + if key_type: + key_or_value = 'key' + valid_classes_phrase = get_valid_classes_phrase(valid_classes) + msg = ( + "Invalid type for variable '{0}'. Required {1} type {2} and " + "passed type was {3}".format( + var_name, + key_or_value, + valid_classes_phrase, + type(var_value).__name__, + ) + ) + return msg + + +def get_valid_classes_phrase(input_classes): + """Returns a string phrase describing what types are allowed + """ + all_classes = list(input_classes) + all_classes = sorted(all_classes, key=lambda cls: cls.__name__) + all_class_names = [cls.__name__ for cls in all_classes] + if len(all_class_names) == 1: + return 'is {0}'.format(all_class_names[0]) + return "is one of [{0}]".format(", ".join(all_class_names)) + + +def convert_js_args_to_python_args(fn): + from functools import wraps + @wraps(fn) + def wrapped_init(self, *args, **kwargs): + spec_property_naming = kwargs.get('_spec_property_naming', False) + if spec_property_naming: + kwargs = change_keys_js_to_python(kwargs, self.__class__) + return fn(self, *args, **kwargs) + return wrapped_init + + +def get_allof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + used to make instances + constant_args (dict): var_name to var_value + used to make instances + + Returns + composed_instances (list) + """ + composed_instances = [] + for allof_class in self._composed_schemas['allOf']: + + # no need to handle changing js keys to python because + # for composed schemas, allof parameters are included in the + # composed schema and were changed to python keys in __new__ + # extract a dict of only required keys from fixed_model_args + kwargs = {} + var_names = set(allof_class.openapi_types.keys()) + for var_name in var_names: + if var_name in model_args: + kwargs[var_name] = model_args[var_name] + + # and use it to make the instance + kwargs.update(constant_args) + try: + allof_instance = allof_class(**kwargs) + composed_instances.append(allof_instance) + except Exception as ex: + raise ApiValueError( + "Invalid inputs given to generate an instance of '%s'. The " + "input data was invalid for the allOf schema '%s' in the composed " + "schema '%s'. Error=%s" % ( + allof_class.__name__, + allof_class.__name__, + self.__class__.__name__, + str(ex) + ) + ) from ex + return composed_instances + + +def get_oneof_instance(cls, model_kwargs, constant_kwargs, model_arg=None): + """ + Find the oneOf schema that matches the input data (e.g. payload). + If exactly one schema matches the input data, an instance of that schema + is returned. + If zero or more than one schema match the input data, an exception is raised. + In OAS 3.x, the payload MUST, by validation, match exactly one of the + schemas described by oneOf. + + Args: + cls: the class we are handling + model_kwargs (dict): var_name to var_value + The input data, e.g. the payload that must match a oneOf schema + in the OpenAPI document. + constant_kwargs (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Kwargs: + model_arg: (int, float, bool, str, date, datetime, ModelSimple, None): + the value to assign to a primitive class or ModelSimple class + Notes: + - this is only passed in when oneOf includes types which are not object + - None is used to suppress handling of model_arg, nullable models are handled in __new__ + + Returns + oneof_instance (instance) + """ + if len(cls._composed_schemas['oneOf']) == 0: + return None + + oneof_instances = [] + # Iterate over each oneOf schema and determine if the input data + # matches the oneOf schemas. + for oneof_class in cls._composed_schemas['oneOf']: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if oneof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + single_value_input = allows_single_value_input(oneof_class) + + if not single_value_input: + # transform js keys from input data to python keys in fixed_model_args + fixed_model_args = change_keys_js_to_python( + model_kwargs, oneof_class) + + # Extract a dict with the properties that are declared in the oneOf schema. + # Undeclared properties (e.g. properties that are allowed because of the + # additionalProperties attribute in the OAS document) are not added to + # the dict. + kwargs = {} + var_names = set(oneof_class.openapi_types.keys()) + for var_name in var_names: + if var_name in fixed_model_args: + kwargs[var_name] = fixed_model_args[var_name] + + # do not try to make a model with no input args + if len(kwargs) == 0: + continue + + # and use it to make the instance + kwargs.update(constant_kwargs) + + try: + if not single_value_input: + oneof_instance = oneof_class(**kwargs) + else: + if issubclass(oneof_class, ModelSimple): + oneof_instance = oneof_class(model_arg, **constant_kwargs) + elif oneof_class in PRIMITIVE_TYPES: + oneof_instance = validate_and_convert_types( + model_arg, + (oneof_class,), + constant_kwargs['_path_to_item'], + constant_kwargs['_spec_property_naming'], + constant_kwargs['_check_type'], + configuration=constant_kwargs['_configuration'] + ) + oneof_instances.append(oneof_instance) + except Exception: + pass + if len(oneof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None " + "of the oneOf schemas matched the input data." % + cls.__name__ + ) + elif len(oneof_instances) > 1: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. Multiple " + "oneOf schemas matched the inputs, but a max of one is allowed." % + cls.__name__ + ) + return oneof_instances[0] + + +def get_anyof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + The input data, e.g. the payload that must match at least one + anyOf child schema in the OpenAPI document. + constant_args (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Returns + anyof_instances (list) + """ + anyof_instances = [] + if len(self._composed_schemas['anyOf']) == 0: + return anyof_instances + + for anyof_class in self._composed_schemas['anyOf']: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if anyof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + # transform js keys to python keys in fixed_model_args + fixed_model_args = change_keys_js_to_python(model_args, anyof_class) + + # extract a dict of only required keys from these_model_vars + kwargs = {} + var_names = set(anyof_class.openapi_types.keys()) + for var_name in var_names: + if var_name in fixed_model_args: + kwargs[var_name] = fixed_model_args[var_name] + + # do not try to make a model with no input args + if len(kwargs) == 0: + continue + + # and use it to make the instance + kwargs.update(constant_args) + try: + anyof_instance = anyof_class(**kwargs) + anyof_instances.append(anyof_instance) + except Exception: + pass + if len(anyof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None of the " + "anyOf schemas matched the inputs." % + self.__class__.__name__ + ) + return anyof_instances + + +def get_additional_properties_model_instances( + composed_instances, self): + additional_properties_model_instances = [] + all_instances = [self] + all_instances.extend(composed_instances) + for instance in all_instances: + if instance.additional_properties_type is not None: + additional_properties_model_instances.append(instance) + return additional_properties_model_instances + + +def get_var_name_to_model_instances(self, composed_instances): + var_name_to_model_instances = {} + all_instances = [self] + all_instances.extend(composed_instances) + for instance in all_instances: + for var_name in instance.openapi_types: + if var_name not in var_name_to_model_instances: + var_name_to_model_instances[var_name] = [instance] + else: + var_name_to_model_instances[var_name].append(instance) + return var_name_to_model_instances + + +def get_unused_args(self, composed_instances, model_args): + unused_args = dict(model_args) + # arguments apssed to self were already converted to python names + # before __init__ was called + for var_name_py in self.attribute_map: + if var_name_py in unused_args: + del unused_args[var_name_py] + for instance in composed_instances: + if instance.__class__ in self._composed_schemas['allOf']: + for var_name_py in instance.attribute_map: + if var_name_py in unused_args: + del unused_args[var_name_py] + else: + for var_name_js in instance.attribute_map.values(): + if var_name_js in unused_args: + del unused_args[var_name_js] + return unused_args + + +def validate_get_composed_info(constant_args, model_args, self): + """ + For composed schemas, generate schema instances for + all schemas in the oneOf/anyOf/allOf definition. If additional + properties are allowed, also assign those properties on + all matched schemas that contain additionalProperties. + Openapi schemas are python classes. + + Exceptions are raised if: + - 0 or > 1 oneOf schema matches the model_args input data + - no anyOf schema matches the model_args input data + - any of the allOf schemas do not match the model_args input data + + Args: + constant_args (dict): these are the args that every model requires + model_args (dict): these are the required and optional spec args that + were passed in to make this model + self (class): the class that we are instantiating + This class contains self._composed_schemas + + Returns: + composed_info (list): length three + composed_instances (list): the composed instances which are not + self + var_name_to_model_instances (dict): a dict going from var_name + to the model_instance which holds that var_name + the model_instance may be self or an instance of one of the + classes in self.composed_instances() + additional_properties_model_instances (list): a list of the + model instances which have the property + additional_properties_type. This list can include self + """ + # create composed_instances + composed_instances = [] + allof_instances = get_allof_instances(self, model_args, constant_args) + composed_instances.extend(allof_instances) + oneof_instance = get_oneof_instance(self.__class__, model_args, constant_args) + if oneof_instance is not None: + composed_instances.append(oneof_instance) + anyof_instances = get_anyof_instances(self, model_args, constant_args) + composed_instances.extend(anyof_instances) + + # map variable names to composed_instances + var_name_to_model_instances = get_var_name_to_model_instances( + self, composed_instances) + + # set additional_properties_model_instances + additional_properties_model_instances = ( + get_additional_properties_model_instances(composed_instances, self) + ) + + # set any remaining values + unused_args = get_unused_args(self, composed_instances, model_args) + if len(unused_args) > 0 and \ + len(additional_properties_model_instances) == 0 and \ + (self._configuration is None or + not self._configuration.discard_unknown_keys): + raise ApiValueError( + "Invalid input arguments input when making an instance of " + "class %s. Not all inputs were used. The unused input data " + "is %s" % (self.__class__.__name__, unused_args) + ) + + # no need to add additional_properties to var_name_to_model_instances here + # because additional_properties_model_instances will direct us to that + # instance when we use getattr or setattr + # and we update var_name_to_model_instances in setattr + + return [ + composed_instances, + var_name_to_model_instances, + additional_properties_model_instances, + unused_args + ] diff --git a/openapi-schema.json b/openapi-schema.json index d9ab41f..013034b 100644 --- a/openapi-schema.json +++ b/openapi-schema.json @@ -7806,19 +7806,11 @@ "name": "document", "in": "query", "description": "Document Name", + "required": true, "schema": { "type": "string", "description": "Document Name" } - }, - { - "name": "directory", - "in": "query", - "description": "Directory to get the components", - "schema": { - "type": "string", - "description": "Directory to get the components" - } } ], "responses": { @@ -38133,7 +38125,8 @@ "id": { "type": "string" } - } + }, + "additionalProperties": false }, "SPARAccounts": { "type": "object", @@ -38145,7 +38138,8 @@ }, "description": "List of SPAR returnsType" } - } + }, + "additionalProperties": false }, "SPARAccountsRoot": { "required": [ @@ -38156,10 +38150,9 @@ "data": { "$ref": "#/components/schemas/SPARAccounts" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "ErrorSource": { "type": "object", @@ -38170,7 +38163,8 @@ "parameter": { "type": "string" } - } + }, + "additionalProperties": false }, "Error": { "type": "object", @@ -38184,7 +38178,8 @@ "source": { "$ref": "#/components/schemas/ErrorSource" } - } + }, + "additionalProperties": false }, "ClientErrorResponse": { "type": "object", @@ -38195,7 +38190,8 @@ "$ref": "#/components/schemas/Error" } } - } + }, + "additionalProperties": false }, "AccountDirectories": { "type": "object", @@ -38214,7 +38210,8 @@ }, "description": "List of directories." } - } + }, + "additionalProperties": false }, "AccountDirectoriesRoot": { "required": [ @@ -38225,10 +38222,9 @@ "data": { "$ref": "#/components/schemas/AccountDirectories" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "AFIOptimizerStrategyOverrides": { "type": "object", @@ -38260,7 +38256,8 @@ "type": "string", "description": "Transaction cost" } - } + }, + "additionalProperties": false }, "AFIOptimizerStrategy": { "required": [ @@ -38275,7 +38272,8 @@ "type": "string", "description": "OptimizerStrategy document path" } - } + }, + "additionalProperties": false }, "OptimizerAccountOverrides": { "type": "object", @@ -38296,7 +38294,8 @@ "type": "string", "description": "Currency" } - } + }, + "additionalProperties": false }, "OptimizerAccount": { "type": "object", @@ -38308,7 +38307,8 @@ "overrides": { "$ref": "#/components/schemas/OptimizerAccountOverrides" } - } + }, + "additionalProperties": false }, "Optimization": { "type": "object", @@ -38325,7 +38325,8 @@ "type": "string", "description": "Cash flow" } - } + }, + "additionalProperties": false }, "OptimizerTradesList": { "type": "object", @@ -38350,7 +38351,8 @@ "type": "boolean", "description": "Include cash" } - } + }, + "additionalProperties": false }, "OptimizerOptimalHoldings": { "type": "object", @@ -38379,7 +38381,8 @@ "type": "boolean", "description": "Exclude zero" } - } + }, + "additionalProperties": false }, "OptimalPortfolio": { "type": "object", @@ -38414,10 +38417,12 @@ "type": "string", "description": "Action if ofdb date exists" } - } + }, + "additionalProperties": false }, "OptimizerStats": { - "type": "object" + "type": "object", + "additionalProperties": false }, "OptimizerOutputTypes": { "type": "object", @@ -38434,7 +38439,8 @@ "stats": { "$ref": "#/components/schemas/OptimizerStats" } - } + }, + "additionalProperties": false }, "AFIOptimizationParameters": { "required": [ @@ -38455,10 +38461,12 @@ "outputTypes": { "$ref": "#/components/schemas/OptimizerOutputTypes" } - } + }, + "additionalProperties": false }, "OptimizerCalculationMeta": { - "type": "object" + "type": "object", + "additionalProperties": false }, "AFIOptimizationParametersRoot": { "type": "object", @@ -38469,7 +38477,8 @@ "meta": { "$ref": "#/components/schemas/OptimizerCalculationMeta" } - } + }, + "additionalProperties": false }, "CalculationInfo": { "type": "object", @@ -38478,7 +38487,8 @@ "type": "string", "description": "Calculation identifier" } - } + }, + "additionalProperties": false }, "CalculationInfoRoot": { "required": [ @@ -38489,10 +38499,9 @@ "data": { "$ref": "#/components/schemas/CalculationInfo" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "ObjectRoot": { "required": [ @@ -38500,13 +38509,10 @@ ], "type": "object", "properties": { - "data": { - "type": "object" - }, - "meta": { - "type": "object" - } - } + "data": { }, + "meta": { } + }, + "additionalProperties": false }, "AxiomaEquityOptimizerStrategyOverrides": { "type": "object", @@ -38538,7 +38544,8 @@ "type": "string", "description": "Transaction cost" } - } + }, + "additionalProperties": false }, "AxiomaEquityOptimizerStrategy": { "required": [ @@ -38553,7 +38560,8 @@ "type": "string", "description": "OptimizerStrategy document path" } - } + }, + "additionalProperties": false }, "AxiomaEquityOptimizationParameters": { "required": [ @@ -38574,7 +38582,8 @@ "outputTypes": { "$ref": "#/components/schemas/OptimizerOutputTypes" } - } + }, + "additionalProperties": false }, "AxiomaEquityOptimizationParametersRoot": { "type": "object", @@ -38585,7 +38594,8 @@ "meta": { "$ref": "#/components/schemas/OptimizerCalculationMeta" } - } + }, + "additionalProperties": false }, "SPARIdentifier": { "required": [ @@ -38606,6 +38616,7 @@ "description": "Benchmark prefix." } }, + "additionalProperties": false, "description": "The account/benchmark parameter for SPAR calculation." }, "SPARBenchmark": { @@ -38626,7 +38637,8 @@ }, "description": "List of SPAR identifiers" } - } + }, + "additionalProperties": false }, "SPARBenchmarkRoot": { "required": [ @@ -38637,10 +38649,9 @@ "data": { "$ref": "#/components/schemas/SPARBenchmark" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "ConstraintAction": { "type": "object", @@ -38655,7 +38666,8 @@ ], "type": "string" } - } + }, + "additionalProperties": false }, "BPMOptimizerStrategyAlphaOverride": { "type": "object", @@ -38676,7 +38688,8 @@ "returnMultiplier": { "type": "string" } - } + }, + "additionalProperties": false }, "BPMOptimizerStrategyOverrides": { "type": "object", @@ -38699,7 +38712,8 @@ "type": "string", "description": "Transaction cost\r\nCan be set to \"\" for local" } - } + }, + "additionalProperties": false }, "BPMOptimizerStrategy": { "required": [ @@ -38714,7 +38728,8 @@ "type": "string", "description": "OptimizerStrategy document path" } - } + }, + "additionalProperties": false }, "BPMOptimization": { "type": "object", @@ -38739,7 +38754,8 @@ "type": "string", "description": "Cash flow" } - } + }, + "additionalProperties": false }, "BPMOptimizationParameters": { "required": [ @@ -38760,7 +38776,8 @@ "outputTypes": { "$ref": "#/components/schemas/OptimizerOutputTypes" } - } + }, + "additionalProperties": false }, "BPMOptimizationParametersRoot": { "type": "object", @@ -38771,7 +38788,8 @@ "meta": { "$ref": "#/components/schemas/OptimizerCalculationMeta" } - } + }, + "additionalProperties": false }, "ColumnSummary": { "type": "object", @@ -38789,6 +38807,7 @@ "description": "Column Category" } }, + "additionalProperties": false, "description": "Column settings (name, directory, category)" }, "ColumnSummaryRoot": { @@ -38803,10 +38822,9 @@ "$ref": "#/components/schemas/ColumnSummary" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "Column": { "type": "object", @@ -38830,7 +38848,8 @@ "type": "string", "description": "Column Category" } - } + }, + "additionalProperties": false }, "ColumnRoot": { "required": [ @@ -38841,10 +38860,9 @@ "data": { "$ref": "#/components/schemas/Column" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "ColumnStatistic": { "type": "object", @@ -38853,7 +38871,8 @@ "type": "string", "description": "Column Statistic Name" } - } + }, + "additionalProperties": false }, "ColumnStatisticRoot": { "required": [ @@ -38867,10 +38886,9 @@ "$ref": "#/components/schemas/ColumnStatistic" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "ComponentSummary": { "type": "object", @@ -38887,7 +38905,8 @@ "type": "string", "description": "Component type." } - } + }, + "additionalProperties": false }, "ComponentSummaryRoot": { "required": [ @@ -38901,10 +38920,9 @@ "$ref": "#/components/schemas/ComponentSummary" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "PAIdentifier": { "required": [ @@ -38920,7 +38938,8 @@ "type": "string", "description": "Holdings Mode can be B&H, TBR, OMS or EXT." } - } + }, + "additionalProperties": false }, "PADateParameters": { "required": [ @@ -38942,6 +38961,7 @@ "description": "Calculation's frequency." } }, + "additionalProperties": false, "description": "The date parameters for PA calculation." }, "PAComponent": { @@ -38987,7 +39007,8 @@ "type": "string", "description": "Component type." } - } + }, + "additionalProperties": false }, "PAComponentRoot": { "required": [ @@ -38998,10 +39019,9 @@ "data": { "$ref": "#/components/schemas/PAComponent" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "VaultIdentifier": { "required": [ @@ -39014,6 +39034,7 @@ "description": "User's FactSet account path OR benchmark." } }, + "additionalProperties": false, "description": "The account/benchmark parameter for Vault calculation." }, "VaultDateParameters": { @@ -39036,6 +39057,7 @@ "description": "Calculation's frequency." } }, + "additionalProperties": false, "description": "The date parameters for Vault calculation" }, "VaultComponent": { @@ -39074,7 +39096,8 @@ "type": "string", "description": "Component type." } - } + }, + "additionalProperties": false }, "VaultComponentRoot": { "required": [ @@ -39085,10 +39108,9 @@ "data": { "$ref": "#/components/schemas/VaultComponent" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "VaultConfigurationSummary": { "type": "object", @@ -39097,7 +39119,8 @@ "type": "string", "description": "Configuration name." } - } + }, + "additionalProperties": false }, "VaultConfigurationSummaryRoot": { "required": [ @@ -39111,10 +39134,9 @@ "$ref": "#/components/schemas/VaultConfigurationSummary" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "ConfigurationAccount": { "type": "object", @@ -39143,7 +39165,8 @@ "type": "string", "description": "Account name." } - } + }, + "additionalProperties": false }, "VaultConfiguration": { "type": "object", @@ -39158,7 +39181,8 @@ "$ref": "#/components/schemas/ConfigurationAccount" } } - } + }, + "additionalProperties": false }, "VaultConfigurationRoot": { "required": [ @@ -39169,10 +39193,9 @@ "data": { "$ref": "#/components/schemas/VaultConfiguration" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "Currency": { "type": "object", @@ -39181,7 +39204,8 @@ "type": "string", "description": "Name of currency." } - } + }, + "additionalProperties": false }, "CurrencyRoot": { "required": [ @@ -39195,10 +39219,9 @@ "$ref": "#/components/schemas/Currency" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "DateParametersSummary": { "type": "object", @@ -39211,7 +39234,8 @@ "type": "string", "description": "End date in YYYYMMDD format." } - } + }, + "additionalProperties": false }, "DateParametersSummaryRoot": { "required": [ @@ -39222,10 +39246,9 @@ "data": { "$ref": "#/components/schemas/DateParametersSummary" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "DocumentDirectories": { "type": "object", @@ -39244,7 +39267,8 @@ }, "description": "List of directories." } - } + }, + "additionalProperties": false }, "DocumentDirectoriesRoot": { "required": [ @@ -39255,10 +39279,9 @@ "data": { "$ref": "#/components/schemas/DocumentDirectories" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "FIABIdentifier": { "required": [ @@ -39270,7 +39293,8 @@ "type": "string", "description": "User's FactSet account path." } - } + }, + "additionalProperties": false }, "FIABDateParameters": { "required": [ @@ -39288,6 +39312,7 @@ "description": "Calculation's end date." } }, + "additionalProperties": false, "description": "The date parameters for FIAB calculations." }, "FIABCalculationParameters": { @@ -39315,7 +39340,8 @@ "type": "string", "description": "FISettingsDocument (optional) - The given @FIS document will be used to\r\nconfigure analytics assumptions and settings. Expects a GUI-style path (Client:/foo/bar)" } - } + }, + "additionalProperties": false }, "FIABCalculationStatusSummary": { "type": "object", @@ -39330,7 +39356,8 @@ "description": "Last poll time of calculation.", "format": "date-time" } - } + }, + "additionalProperties": false }, "EventSummary": { "type": "object", @@ -39348,7 +39375,8 @@ "type": "string", "description": "Event's type" } - } + }, + "additionalProperties": false }, "FIABCalculationStatus": { "type": "object", @@ -39391,7 +39419,8 @@ }, "description": "List of batch events" } - } + }, + "additionalProperties": false }, "FISecurity": { "required": [ @@ -39446,7 +39475,8 @@ "type": "string", "description": "Discount curve" } - } + }, + "additionalProperties": false }, "FIJobSettings": { "required": [ @@ -39484,7 +39514,8 @@ "type": "string", "description": "Calculation from method" } - } + }, + "additionalProperties": false }, "FICalculationParameters": { "required": [ @@ -39511,7 +39542,8 @@ "jobSettings": { "$ref": "#/components/schemas/FIJobSettings" } - } + }, + "additionalProperties": false }, "CalculationMeta": { "type": "object", @@ -39561,7 +39593,8 @@ "type": "string", "default": "JsonStach" } - } + }, + "additionalProperties": false }, "FICalculationParametersRoot": { "type": "object", @@ -39572,7 +39605,8 @@ "meta": { "$ref": "#/components/schemas/CalculationMeta" } - } + }, + "additionalProperties": false }, "PaDoc": { "required": [ @@ -39584,7 +39618,8 @@ "type": "string", "description": "PA document path" } - } + }, + "additionalProperties": false }, "FPOAccount": { "required": [ @@ -39602,7 +39637,8 @@ "overrides": { "$ref": "#/components/schemas/OptimizerAccountOverrides" } - } + }, + "additionalProperties": false }, "OptimizerStrategyOverrides": { "type": "object", @@ -39630,7 +39666,8 @@ "type": "string", "description": "Transaction cost" } - } + }, + "additionalProperties": false }, "OptimizerStrategy": { "required": [ @@ -39645,7 +39682,8 @@ "overrides": { "$ref": "#/components/schemas/OptimizerStrategyOverrides" } - } + }, + "additionalProperties": false }, "FPOOptimizationParameters": { "required": [ @@ -39666,7 +39704,8 @@ "outputTypes": { "$ref": "#/components/schemas/OptimizerOutputTypes" } - } + }, + "additionalProperties": false }, "FPOOptimizationParametersRoot": { "type": "object", @@ -39677,7 +39716,8 @@ "meta": { "$ref": "#/components/schemas/OptimizerCalculationMeta" } - } + }, + "additionalProperties": false }, "Frequency": { "type": "object", @@ -39686,7 +39726,8 @@ "type": "string", "description": "Frequency name." } - } + }, + "additionalProperties": false }, "FrequencyRoot": { "required": [ @@ -39700,10 +39741,9 @@ "$ref": "#/components/schemas/Frequency" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "Group": { "type": "object", @@ -39720,7 +39760,8 @@ "type": "string", "description": "Group Category" } - } + }, + "additionalProperties": false }, "GroupRoot": { "required": [ @@ -39734,10 +39775,9 @@ "$ref": "#/components/schemas/Group" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "TemplateContentTypes": { "type": "object", @@ -39763,7 +39803,8 @@ }, "description": "Template locked fields" } - } + }, + "additionalProperties": false }, "LinkedPATemplateParameters": { "required": [ @@ -39787,7 +39828,8 @@ "content": { "$ref": "#/components/schemas/TemplateContentTypes" } - } + }, + "additionalProperties": false }, "LinkedPATemplateParametersRoot": { "required": [ @@ -39798,10 +39840,9 @@ "data": { "$ref": "#/components/schemas/LinkedPATemplateParameters" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "LinkedPATemplateSummary": { "type": "object", @@ -39818,7 +39859,8 @@ "type": "string", "description": "Template parent tile." } - } + }, + "additionalProperties": false }, "LinkedPATemplateSummaryRoot": { "required": [ @@ -39832,10 +39874,9 @@ "$ref": "#/components/schemas/LinkedPATemplateSummary" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "LinkedPATemplateUpdateParameters": { "type": "object", @@ -39851,7 +39892,8 @@ "content": { "$ref": "#/components/schemas/TemplateContentTypes" } - } + }, + "additionalProperties": false }, "LinkedPATemplateUpdateParametersRoot": { "required": [ @@ -39862,10 +39904,9 @@ "data": { "$ref": "#/components/schemas/LinkedPATemplateUpdateParameters" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "LinkedPATemplate": { "type": "object", @@ -39897,7 +39938,8 @@ "type": "string", "description": "Template parent tile." } - } + }, + "additionalProperties": false }, "LinkedPATemplateRoot": { "required": [ @@ -39908,19 +39950,16 @@ "data": { "$ref": "#/components/schemas/LinkedPATemplate" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "NPOOptimizerStrategyOverrides": { "type": "object", "properties": { "objective": { "type": "object", - "additionalProperties": { - "type": "object" - }, + "additionalProperties": { }, "description": "Objective parameters" }, "constraints": { @@ -39942,7 +39981,8 @@ "type": "string", "description": "Alpha" } - } + }, + "additionalProperties": false }, "NPOOptimizerStrategy": { "required": [ @@ -39957,7 +39997,8 @@ "type": "string", "description": "OptimizerStrategy document path" } - } + }, + "additionalProperties": false }, "NPOOptimizationParameters": { "required": [ @@ -39978,7 +40019,8 @@ "outputTypes": { "$ref": "#/components/schemas/OptimizerOutputTypes" } - } + }, + "additionalProperties": false }, "NPOOptimizationParametersRoot": { "type": "object", @@ -39989,7 +40031,8 @@ "meta": { "$ref": "#/components/schemas/OptimizerCalculationMeta" } - } + }, + "additionalProperties": false }, "PACalculationGroup": { "type": "object", @@ -39998,7 +40041,8 @@ "type": "string", "description": "FactSet-defined or User-defined Group identifier." } - } + }, + "additionalProperties": false }, "PACalculationColumn": { "type": "object", @@ -40014,7 +40058,8 @@ }, "description": "Column Statistic identifier" } - } + }, + "additionalProperties": false }, "PACalculationParameters": { "required": [ @@ -40065,7 +40110,8 @@ "type": "string", "description": "Component detail type for the PA component. It can be GROUPS or TOTALS." } - } + }, + "additionalProperties": false }, "PACalculationParametersRoot": { "type": "object", @@ -40080,7 +40126,8 @@ "meta": { "$ref": "#/components/schemas/CalculationMeta" } - } + }, + "additionalProperties": false }, "CalculationUnitStatus": { "type": "object", @@ -40111,7 +40158,8 @@ "type": "string", "description": "The progress of the calculation unit." } - } + }, + "additionalProperties": false }, "CalculationStatus": { "type": "object", @@ -40137,7 +40185,8 @@ }, "description": "Number of calculation units in batch." } - } + }, + "additionalProperties": false }, "CalculationUnitStatusMeta": { "type": "object", @@ -40146,7 +40195,8 @@ "type": "string", "description": "The Info URL of the calculation." } - } + }, + "additionalProperties": false }, "CalculationStatusMeta": { "type": "object", @@ -40158,7 +40208,8 @@ }, "description": "Meta of calculation units in batch." } - } + }, + "additionalProperties": false }, "CalculationStatusRoot": { "required": [ @@ -40172,7 +40223,8 @@ "meta": { "$ref": "#/components/schemas/CalculationStatusMeta" } - } + }, + "additionalProperties": false }, "PubIdentifier": { "required": [ @@ -40188,7 +40240,8 @@ "type": "string", "description": "Holdings Mode can be B&H, TBR, OMS or EXT." } - } + }, + "additionalProperties": false }, "PubDateParameters": { "required": [ @@ -40205,6 +40258,7 @@ "description": "Calculation's end date or as of date." } }, + "additionalProperties": false, "description": "The date parameters for Publisher calculation." }, "PubCalculationParameters": { @@ -40225,10 +40279,12 @@ "dates": { "$ref": "#/components/schemas/PubDateParameters" } - } + }, + "additionalProperties": false }, "PubCalculationMeta": { - "type": "object" + "type": "object", + "additionalProperties": false }, "PubCalculationParametersRoot": { "type": "object", @@ -40243,7 +40299,8 @@ "meta": { "$ref": "#/components/schemas/PubCalculationMeta" } - } + }, + "additionalProperties": false }, "QuantScreeningExpressionUniverse": { "required": [ @@ -40251,6 +40308,11 @@ "universeType" ], "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantUniverse" + } + ], "properties": { "universeExpr": { "type": "string" @@ -40265,20 +40327,275 @@ "securityExpr": { "type": "string" } - } + }, + "additionalProperties": false + }, + "QuantIdentifierUniverse": { + "required": [ + "identifiers", + "universeType" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantUniverse" + } + ], + "properties": { + "universeType": { + "enum": [ + "Equity", + "Debt" + ], + "type": "string" + }, + "identifiers": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "QuantUniverse": { + "required": [ + "source" + ], + "type": "object", + "properties": { + "source": { + "enum": [ + "ScreeningExpressionUniverse", + "UniversalScreenUniverse", + "IdentifierUniverse" + ], + "type": "string" + } + }, + "additionalProperties": false }, "QuantUniversalScreenUniverse": { "required": [ "screen" ], "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantUniverse" + } + ], "properties": { "screen": { "type": "string" } - } + }, + "additionalProperties": false }, - "QuantIdentifierUniverse": { + "QuantDateList": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantDate" + } + ], + "properties": { + "dates": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "QuantDate": { + "required": [ + "calendar", + "frequency", + "source" + ], + "type": "object", + "properties": { + "source": { + "enum": [ + "FdsDate", + "DateList" + ], + "type": "string" + }, + "frequency": { + "type": "string" + }, + "calendar": { + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantFdsDate": { + "required": [ + "endDate", + "startDate" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantDate" + } + ], + "properties": { + "startDate": { + "type": "string" + }, + "endDate": { + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantFqlExpression": { + "required": [ + "expr", + "name" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantFormula" + } + ], + "properties": { + "expr": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantUniversalScreenParameter": { + "required": [ + "name", + "referenceName" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantFormula" + } + ], + "properties": { + "referenceName": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantAllUniversalScreenParameters": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantFormula" + } + ], + "additionalProperties": false + }, + "QuantFormula": { + "required": [ + "source" + ], + "type": "object", + "properties": { + "source": { + "enum": [ + "ScreeningExpression", + "FqlExpression", + "UniversalScreenParameter", + "AllUniversalScreenParameters" + ], + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantScreeningExpression": { + "required": [ + "expr", + "name" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/QuantFormula" + } + ], + "properties": { + "expr": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantScreeningExpressionUniverseObsolete": { + "required": [ + "universeExpr", + "universeType" + ], + "type": "object", + "properties": { + "universeExpr": { + "type": "string" + }, + "universeType": { + "enum": [ + "Equity", + "Debt" + ], + "type": "string" + }, + "securityExpr": { + "type": "string" + }, + "source": { + "enum": [ + "ScreeningExpressionUniverse", + "UniversalScreenUniverse", + "IdentifierUniverse" + ], + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantUniversalScreenUniverseObsolete": { + "required": [ + "screen" + ], + "type": "object", + "properties": { + "screen": { + "type": "string" + }, + "source": { + "enum": [ + "ScreeningExpressionUniverse", + "UniversalScreenUniverse", + "IdentifierUniverse" + ], + "type": "string" + } + }, + "additionalProperties": false + }, + "QuantIdentifierUniverseObsolete": { "required": [ "identifiers", "universeType" @@ -40297,10 +40614,19 @@ "items": { "type": "string" } + }, + "source": { + "enum": [ + "ScreeningExpressionUniverse", + "UniversalScreenUniverse", + "IdentifierUniverse" + ], + "type": "string" } - } + }, + "additionalProperties": false }, - "QuantFdsDate": { + "QuantFdsDateObsolete": { "required": [ "calendar", "endDate", @@ -40321,9 +40647,10 @@ "calendar": { "type": "string" } - } + }, + "additionalProperties": false }, - "QuantDateList": { + "QuantDateListObsolete": { "required": [ "calendar", "frequency" @@ -40342,9 +40669,10 @@ "calendar": { "type": "string" } - } + }, + "additionalProperties": false }, - "QuantScreeningExpression": { + "QuantScreeningExpressionObsolete": { "required": [ "expr", "name" @@ -40357,9 +40685,10 @@ "name": { "type": "string" } - } + }, + "additionalProperties": false }, - "QuantFqlExpression": { + "QuantFqlExpressionObsolete": { "required": [ "expr", "name" @@ -40372,9 +40701,10 @@ "name": { "type": "string" } - } + }, + "additionalProperties": false }, - "QuantUniversalScreenParameter": { + "QuantUniversalScreenParameterObsolete": { "required": [ "name", "referenceName" @@ -40387,54 +40717,84 @@ "name": { "type": "string" } - } + }, + "additionalProperties": false }, - "QuantAllUniversalScreenParameters": { - "type": "object" + "QuantAllUniversalScreenParametersObsolete": { + "type": "object", + "additionalProperties": false }, "QuantCalculationParameters": { "type": "object", "properties": { - "screeningExpressionUniverse": { - "$ref": "#/components/schemas/QuantScreeningExpressionUniverse" - }, - "universalScreenUniverse": { - "$ref": "#/components/schemas/QuantUniversalScreenUniverse" - }, - "identifierUniverse": { - "$ref": "#/components/schemas/QuantIdentifierUniverse" - }, - "fdsDate": { - "$ref": "#/components/schemas/QuantFdsDate" - }, - "dateList": { - "$ref": "#/components/schemas/QuantDateList" - }, - "screeningExpression": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QuantScreeningExpression" - } - }, - "fqlExpression": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QuantFqlExpression" + "universe": { + "oneOf": [ + { + "$ref": "#/components/schemas/QuantUniversalScreenUniverse" + }, + { + "$ref": "#/components/schemas/QuantScreeningExpressionUniverse" + }, + { + "$ref": "#/components/schemas/QuantIdentifierUniverse" + } + ], + "discriminator": { + "propertyName": "source", + "mapping": { + "UniversalScreenUniverse": "#/components/schemas/QuantUniversalScreenUniverse", + "ScreeningExpressionUniverse": "#/components/schemas/QuantScreeningExpressionUniverse", + "IdentifierUniverse": "#/components/schemas/QuantIdentifierUniverse" + } } }, - "universalScreenParameter": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QuantUniversalScreenParameter" + "dates": { + "oneOf": [ + { + "$ref": "#/components/schemas/QuantFdsDate" + }, + { + "$ref": "#/components/schemas/QuantDateList" + } + ], + "discriminator": { + "propertyName": "source", + "mapping": { + "FdsDate": "#/components/schemas/QuantFdsDate", + "DateList": "#/components/schemas/QuantDateList" + } } }, - "allUniversalScreenParameters": { + "formulas": { "type": "array", "items": { - "$ref": "#/components/schemas/QuantAllUniversalScreenParameters" + "oneOf": [ + { + "$ref": "#/components/schemas/QuantScreeningExpression" + }, + { + "$ref": "#/components/schemas/QuantFqlExpression" + }, + { + "$ref": "#/components/schemas/QuantUniversalScreenParameter" + }, + { + "$ref": "#/components/schemas/QuantAllUniversalScreenParameters" + } + ], + "discriminator": { + "propertyName": "source", + "mapping": { + "ScreeningExpression": "#/components/schemas/QuantScreeningExpression", + "FqlExpression": "#/components/schemas/QuantFqlExpression", + "UniversalScreenParameter": "#/components/schemas/QuantUniversalScreenParameter", + "AllUniversalScreenParameters": "#/components/schemas/QuantAllUniversalScreenParameters" + } + } } } - } + }, + "additionalProperties": false }, "QuantCalculationMeta": { "type": "object", @@ -40487,7 +40847,8 @@ "type": "string", "default": "JsonStach" } - } + }, + "additionalProperties": false }, "QuantCalculationParametersRoot": { "type": "object", @@ -40502,7 +40863,8 @@ "meta": { "$ref": "#/components/schemas/QuantCalculationMeta" } - } + }, + "additionalProperties": false }, "SPARDateParameters": { "required": [ @@ -40525,6 +40887,7 @@ "description": "Calculation's frequency." } }, + "additionalProperties": false, "description": "The date parameters for SPAR calculation." }, "SPARCalculationParameters": { @@ -40554,7 +40917,8 @@ "type": "string", "description": "Currency ISO code for calculation." } - } + }, + "additionalProperties": false }, "SPARCalculationParametersRoot": { "type": "object", @@ -40569,7 +40933,8 @@ "meta": { "$ref": "#/components/schemas/CalculationMeta" } - } + }, + "additionalProperties": false }, "PAComponentData": { "type": "object", @@ -40613,7 +40978,8 @@ "type": "string", "description": "PA Storage type. It can be GROUPS or TOTALS or SECURITIES." } - } + }, + "additionalProperties": false }, "TemplatedPAComponentParameters": { "required": [ @@ -40637,7 +41003,8 @@ "componentData": { "$ref": "#/components/schemas/PAComponentData" } - } + }, + "additionalProperties": false }, "TemplatedPAComponentParametersRoot": { "required": [ @@ -40648,10 +41015,9 @@ "data": { "$ref": "#/components/schemas/TemplatedPAComponentParameters" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "TemplatedPAComponentSummary": { "type": "object", @@ -40668,7 +41034,8 @@ "type": { "type": "string" } - } + }, + "additionalProperties": false }, "TemplatedPAComponentSummaryRoot": { "required": [ @@ -40682,10 +41049,9 @@ "$ref": "#/components/schemas/TemplatedPAComponentSummary" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "TemplatedPAComponentUpdateParameters": { "type": "object", @@ -40701,7 +41067,8 @@ "componentData": { "$ref": "#/components/schemas/PAComponentData" } - } + }, + "additionalProperties": false }, "TemplatedPAComponentUpdateParametersRoot": { "required": [ @@ -40712,10 +41079,9 @@ "data": { "$ref": "#/components/schemas/TemplatedPAComponentUpdateParameters" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "UnlinkedPATemplateParameters": { "required": [ @@ -40778,7 +41144,8 @@ "content": { "$ref": "#/components/schemas/TemplateContentTypes" } - } + }, + "additionalProperties": false }, "UnlinkedPATemplateParametersRoot": { "required": [ @@ -40789,10 +41156,9 @@ "data": { "$ref": "#/components/schemas/UnlinkedPATemplateParameters" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "UnlinkedPATemplateSummary": { "type": "object", @@ -40806,7 +41172,8 @@ "category": { "type": "string" } - } + }, + "additionalProperties": false }, "UnlinkedPATemplateSummaryRoot": { "required": [ @@ -40820,10 +41187,9 @@ "$ref": "#/components/schemas/UnlinkedPATemplateSummary" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "UnlinkedPATemplateUpdateParameters": { "type": "object", @@ -40874,7 +41240,8 @@ "content": { "$ref": "#/components/schemas/TemplateContentTypes" } - } + }, + "additionalProperties": false }, "UnlinkedPATemplateUpdateParametersRoot": { "required": [ @@ -40885,10 +41252,9 @@ "data": { "$ref": "#/components/schemas/UnlinkedPATemplateUpdateParameters" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "UnlinkedPATemplate": { "type": "object", @@ -40950,7 +41316,8 @@ "category": { "type": "string" } - } + }, + "additionalProperties": false }, "UnlinkedPATemplateRoot": { "required": [ @@ -40961,10 +41328,9 @@ "data": { "$ref": "#/components/schemas/UnlinkedPATemplate" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "UnlinkedPATemplateCategoryAndType": { "type": "object", @@ -40977,7 +41343,8 @@ "type": "string", "description": "Unlinked template type" } - } + }, + "additionalProperties": false }, "UnlinkedPATemplateCategoryAndTypeRoot": { "required": [ @@ -40991,10 +41358,9 @@ "$ref": "#/components/schemas/UnlinkedPATemplateCategoryAndType" } }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "UnlinkedPATemplateCategoryAndTypeDetails": { "type": "object", @@ -41011,7 +41377,8 @@ "type": "string", "description": "Unlinked template type" } - } + }, + "additionalProperties": false }, "UnlinkedPATemplateCategoryAndTypeDetailsRoot": { "required": [ @@ -41022,10 +41389,9 @@ "data": { "$ref": "#/components/schemas/UnlinkedPATemplateCategoryAndTypeDetails" }, - "meta": { - "type": "object" - } - } + "meta": { } + }, + "additionalProperties": false }, "VaultCalculationParameters": { "required": [ @@ -41053,7 +41419,8 @@ "type": "string", "description": "Component detail type for the Vault component. It can be GROUPS or TOTALS." } - } + }, + "additionalProperties": false }, "VaultCalculationParametersRoot": { "type": "object", @@ -41068,7 +41435,8 @@ "meta": { "$ref": "#/components/schemas/CalculationMeta" } - } + }, + "additionalProperties": false } }, "securitySchemes": {