diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ceffb780..45af5dafab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ # Changelog +## 35.2.0 [#982](https://github.com/openfisca/openfisca-core/pull/982) + +#### Technical changes + +- Allow parameters to be arrays. + ### 35.1.1 [#981](https://github.com/openfisca/openfisca-core/pull/981) #### Technical changes - Fix false negative web API test following an update in the country template used for testing. -### 35.1.0 [#973](https://github.com/openfisca/openfisca-core/pull/973) +## 35.1.0 [#973](https://github.com/openfisca/openfisca-core/pull/973) #### Technical changes diff --git a/openfisca_core/parameters.py b/openfisca_core/parameters.py index 0255c1a93d..acb4db26fd 100644 --- a/openfisca_core/parameters.py +++ b/openfisca_core/parameters.py @@ -36,7 +36,7 @@ # 'unit' and 'reference' are only listed here for backward compatibility. # It is now recommended to include them in metadata, until a common consensus emerges. COMMON_KEYS = {'description', 'metadata', 'unit', 'reference', 'documentation'} -ALLOWED_PARAM_TYPES = (float, int, bool, type(None)) +ALLOWED_PARAM_TYPES = (float, int, bool, type(None), List) def date_constructor(loader, node): @@ -319,7 +319,7 @@ def validate(self, data): ) if not isinstance(value, ALLOWED_PARAM_TYPES): raise ParameterParsingError( - "Invalid value in {} : {}".format(self.name, value), + "Value in {} has type {}, which is not one of the allowed types ({}): {}".format(self.name, type(value), ALLOWED_PARAM_TYPES, value), self.file_path ) @@ -851,7 +851,7 @@ def load_parameter_file(file_path, name = ''): :returns: An instance of :any:`ParameterNode` or :any:`Scale` or :any:`Parameter`. """ if not os.path.exists(file_path): - raise ValueError("{} doest not exist".format(file_path)) + raise ValueError("{} does not exist".format(file_path)) if os.path.isdir(file_path): return ParameterNode(name, directory_path = file_path) data = _load_yaml_file(file_path) diff --git a/openfisca_web_api/openAPI.yml b/openfisca_web_api/openAPI.yml index 7fe4a64427..d0c52f9a14 100644 --- a/openfisca_web_api/openAPI.yml +++ b/openfisca_web_api/openAPI.yml @@ -34,49 +34,49 @@ paths: post: summary: "Run a simulation" tags: - - Calculations + - Calculations operationId: "calculate" consumes: - - "application/json" + - "application/json" produces: - - "application/json" + - "application/json" parameters: - in: "body" name: "Situation" - description: 'Describe the situation (persons and entities). Add the variable you wish to calculate in the proper entity, with null as the value. Learn more in our official documentation: https://openfisca.org/doc/openfisca-web-api/input-output-data.html' + description: "Describe the situation (persons and entities). Add the variable you wish to calculate in the proper entity, with null as the value. Learn more in our official documentation: https://openfisca.org/doc/openfisca-web-api/input-output-data.html" required: true schema: - $ref: '#/definitions/SituationInput' + $ref: "#/definitions/SituationInput" responses: 200: description: "The calculation result is sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" schema: - $ref: '#/definitions/SituationOutput' + $ref: "#/definitions/SituationOutput" 404: description: "A variable mentioned in the input situation does not exist in the loaded tax and benefit system. Details are sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" 400: description: "The request is invalid. Details about the error are sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" /parameters: get: tags: - - "Parameters" + - "Parameters" summary: "List all available parameters" operationId: "getParameters" produces: - - "application/json" + - "application/json" responses: 200: description: "The list of parameters is sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" schema: - $ref: '#/definitions/Parameters' + $ref: "#/definitions/Parameters" /parameter/{parameterID}: get: tags: @@ -95,13 +95,13 @@ paths: 200: description: "The requested parameter's information is sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" schema: $ref: "#/definitions/Parameter" 404: description: "The requested parameter does not exist" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" /variables: get: tags: @@ -114,7 +114,7 @@ paths: 200: description: "The list of variables is sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" schema: $ref: "#/definitions/Variables" /variable/{variableID}: @@ -135,13 +135,13 @@ paths: 200: description: "The requested variable's information is sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" schema: $ref: "#/definitions/Variable" 404: description: "The requested variable does not exist" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" /entities: get: tags: @@ -154,61 +154,61 @@ paths: 200: description: "The list of the entities as well as their information is sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" schema: $ref: "#/definitions/Entities" /trace: post: summary: "Explore a simulation's steps in details." tags: - - Calculations + - Calculations operationId: "trace" consumes: - - "application/json" + - "application/json" produces: - - "application/json" + - "application/json" parameters: - in: "body" name: "Situation" - description: 'Describe the situation (persons and entities). Add the variable you wish to calculate in the proper entity, with null as the value.' + description: "Describe the situation (persons and entities). Add the variable you wish to calculate in the proper entity, with null as the value." required: true schema: - $ref: '#/definitions/SituationInput' + $ref: "#/definitions/SituationInput" responses: 200: description: "The calculation details are sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" schema: - $ref: '#/definitions/Trace' + $ref: "#/definitions/Trace" 404: description: "A variable mentioned in the input situation does not exist in the loaded tax and benefit system. Details are sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" 400: description: "The request is invalid. Details about the error are sent back in the response body" headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" /spec: get: summary: Provide the API documentation in an OpenAPI format tags: - - Documentation + - Documentation operationId: spec produces: - - application/json + - application/json responses: 200: description: The API documentation is sent back in the response body headers: - $ref: '#/commons/Headers' + $ref: "#/commons/Headers" definitions: Parameter: type: "object" properties: values: - $ref: '#/definitions/Values' + $ref: "#/definitions/Values" brackets: type: "object" additionalProperties: @@ -219,7 +219,7 @@ definitions: type: "object" properties: definition: - type: 'string' + type: "string" metadata: type: "object" description: @@ -237,9 +237,9 @@ definitions: type: "object" properties: description: - type: 'string' + type: "string" href: - type: 'string' + type: "string" Variable: type: "object" @@ -284,9 +284,9 @@ definitions: type: "object" properties: description: - type: 'string' + type: "string" href: - type: 'string' + type: "string" Formula: type: "object" @@ -303,10 +303,11 @@ definitions: format: "float" Values: + description: All keys are ISO dates. Values can be numbers, booleans, or arrays of a single type (number, boolean or string). type: "object" - additionalProperties: - type: "number" - format: "float" + additionalProperties: true +# propertyNames: # this keyword is part of JSON Schema but is not supported in OpenAPI Specification at the time of writing, see https://swagger.io/docs/specification/data-models/keywords/#unsupported +# pattern: "^[12][0-9]{3}-[01][0-9]-[0-3][0-9]$" # all keys are ISO dates Entities: type: "object" diff --git a/setup.py b/setup.py index a1ed4b3915..ff4ce2cd90 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ setup( name = 'OpenFisca-Core', - version = '35.1.1', + version = '35.2.0', author = 'OpenFisca Team', author_email = 'contact@openfisca.org', classifiers = [ diff --git a/tests/core/parameter_validation/array_type.yaml b/tests/core/parameter_validation/array_type.yaml new file mode 100644 index 0000000000..dfce6552fa --- /dev/null +++ b/tests/core/parameter_validation/array_type.yaml @@ -0,0 +1,6 @@ +description: Array types should be allowed. +values: + 2013-01-01: + value: [ FR ] + 2018-06-01: + value: [ ES, FR, IT, NZ ] diff --git a/tests/core/parameter_validation/test_parameter_validation.py b/tests/core/parameter_validation/test_parameter_validation.py index b4fd57344a..5afe149f55 100644 --- a/tests/core/parameter_validation/test_parameter_validation.py +++ b/tests/core/parameter_validation/test_parameter_validation.py @@ -8,7 +8,7 @@ year = 2016 -def check(file_name, keywords): +def check_fails_with_message(file_name, keywords): path = os.path.join(BASE_DIR, file_name) + '.yaml' try: load_parameter_file(path, file_name) @@ -22,7 +22,7 @@ def check(file_name, keywords): @pytest.mark.parametrize("test", [ ('indentation', {'Invalid YAML', 'indentation.yaml', 'line 2', 'mapping values are not allowed'}), ('wrong_scale', {'Unexpected property', 'scale[1]', 'treshold'}), - ('wrong_value', {'Invalid value', 'wrong_value[2015-12-01]', '1A'}), + ('wrong_value', {'not one of the allowed types', 'wrong_value[2015-12-01]', '1A'}), ('unexpected_key_in_parameter', {'Unexpected property', 'unexpected_key'}), ('wrong_type_in_parameter', {'must be of type object'}), ('wrong_type_in_value_history', {'must be of type object'}), @@ -37,7 +37,12 @@ def check(file_name, keywords): ]) def test_parsing_errors(test): with pytest.raises(ParameterParsingError): - check(*test) + check_fails_with_message(*test) + + +def test_array_type(): + path = os.path.join(BASE_DIR, 'array_type.yaml') + load_parameter_file(path, 'array_type') def test_filesystem_hierarchy():