Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
Backend
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Container Registry
Analyze
Contributor analytics
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Arkindex
Backend
Commits
3f03cd65
Commit
3f03cd65
authored
3 years ago
by
ml bonhomme
Committed by
Erwan Rouchet
3 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Validate user_configuration in WorkerVersion's configuration
parent
2a8a38a8
No related branches found
Branches containing commit
No related tags found
Tags containing commit
1 merge request
!1595
Validate user_configuration in WorkerVersion's configuration
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
arkindex/dataimport/serializers/workers.py
+71
-0
71 additions, 0 deletions
arkindex/dataimport/serializers/workers.py
arkindex/dataimport/tests/test_workers.py
+266
-0
266 additions, 0 deletions
arkindex/dataimport/tests/test_workers.py
with
337 additions
and
0 deletions
arkindex/dataimport/serializers/workers.py
+
71
−
0
View file @
3f03cd65
import
urllib
from
collections
import
defaultdict
from
enum
import
Enum
from
drf_spectacular.utils
import
extend_schema_field
from
rest_framework
import
serializers
...
...
@@ -42,6 +44,61 @@ class WorkerSerializer(WorkerLightSerializer):
fields
=
WorkerLightSerializer
.
Meta
.
fields
+
(
'
repository_id
'
,
)
class
UserConfigurationFieldType
(
Enum
):
Int
=
'
int
'
Float
=
'
float
'
String
=
'
str
'
Enum
=
'
enum
'
class
UserConfigurationFieldSerializer
(
serializers
.
Serializer
):
title
=
serializers
.
CharField
()
type
=
EnumField
(
UserConfigurationFieldType
)
required
=
serializers
.
BooleanField
(
default
=
False
)
choices
=
serializers
.
ListField
(
required
=
False
,
allow_empty
=
False
,
allow_null
=
True
)
def
to_internal_value
(
self
,
data
):
errors
=
defaultdict
(
list
)
allowed_fields
=
[
'
title
'
,
'
type
'
,
'
required
'
,
'
default
'
,
'
choices
'
]
data_types
=
{
UserConfigurationFieldType
.
Int
:
serializers
.
IntegerField
,
UserConfigurationFieldType
.
Float
:
serializers
.
FloatField
,
UserConfigurationFieldType
.
String
:
serializers
.
CharField
,
UserConfigurationFieldType
.
Enum
:
serializers
.
ChoiceField
}
for
field
in
data
:
if
field
not
in
allowed_fields
:
errors
[
field
].
append
(
'
Configurable properties can only be defined using the following keys: title, type, required, default, choices.
'
)
default_value
=
data
.
get
(
'
default
'
)
data
=
super
().
to_internal_value
(
data
)
field_type
=
data
.
get
(
'
type
'
)
choices
=
data
.
get
(
'
choices
'
)
if
choices
:
if
field_type
!=
UserConfigurationFieldType
.
Enum
:
errors
[
'
choices
'
].
append
(
'
The
"
choices
"
field can only be set for an
"
enum
"
type property.
'
)
# If the configuration parameter is of enum type, an eventual default value won't match the field type
if
default_value
and
default_value
not
in
choices
:
errors
[
'
default
'
].
append
(
f
'
{
default_value
}
is not an available choice.
'
)
elif
default_value
:
try
:
data_type
=
data_types
[
field_type
]
data_type
().
to_internal_value
(
default_value
)
except
ValidationError
:
errors
[
'
default
'
].
append
(
f
'
Default value is not of type
{
field_type
.
value
}
.
'
)
except
KeyError
:
errors
[
'
default
'
].
append
(
f
'
Cannot check type:
{
field_type
.
value
}
.
'
)
if
errors
:
raise
ValidationError
(
errors
)
return
data
class
WorkerVersionSerializer
(
serializers
.
ModelSerializer
):
"""
Serialize a worker version
...
...
@@ -86,6 +143,20 @@ class WorkerVersionSerializer(serializers.ModelSerializer):
except
Revision
.
DoesNotExist
:
raise
ValidationError
({
'
revision
'
:
[
'
Revision with this ID does not exist.
'
]})
def
validate_configuration
(
self
,
configuration
):
errors
=
defaultdict
(
list
)
user_configuration
=
configuration
.
get
(
'
user_configuration
'
)
if
not
user_configuration
:
return
configuration
field
=
serializers
.
DictField
(
child
=
UserConfigurationFieldSerializer
())
try
:
field
.
to_internal_value
(
user_configuration
)
except
ValidationError
as
e
:
errors
[
'
user_configuration
'
].
append
(
e
.
detail
)
if
errors
:
raise
ValidationError
(
errors
)
return
configuration
def
validate
(
self
,
data
):
# Assert that a version set to available has a docker image
state
=
data
.
get
(
'
state
'
)
or
self
.
instance
and
self
.
instance
.
state
...
...
This diff is collapsed.
Click to expand it.
arkindex/dataimport/tests/test_workers.py
+
266
−
0
View file @
3f03cd65
...
...
@@ -602,6 +602,272 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
self
.
assertEqual
(
data
[
'
state
'
],
'
created
'
)
self
.
assertEqual
(
data
[
'
gpu_usage
'
],
'
disabled
'
)
def
test_no_user_configuration_ok
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
beep
"
:
"
boop
"
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
def
test_valid_user_configuration
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
demo_integer
"
:
{
"
title
"
:
"
Demo Integer
"
,
"
type
"
:
"
int
"
,
"
required
"
:
True
,
"
default
"
:
1
}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
json
()[
'
configuration
'
],
{
"
user_configuration
"
:
{
"
demo_integer
"
:
{
"
title
"
:
"
Demo Integer
"
,
"
type
"
:
"
int
"
,
"
required
"
:
True
,
"
default
"
:
1
}
}
})
def
test_valid_user_configuration_enum
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
demo_choice
"
:
{
"
title
"
:
"
Decisions
"
,
"
type
"
:
"
enum
"
,
"
required
"
:
True
,
"
default
"
:
1
,
"
choices
"
:
[
1
,
2
,
3
]}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
json
()[
'
configuration
'
],
{
"
user_configuration
"
:
{
"
demo_choice
"
:
{
"
title
"
:
"
Decisions
"
,
"
type
"
:
"
enum
"
,
"
required
"
:
True
,
"
default
"
:
1
,
"
choices
"
:
[
1
,
2
,
3
]
}
}
})
def
test_valid_user_configuration_not_list_choices
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
demo_choice
"
:
{
"
title
"
:
"
Decisions
"
,
"
type
"
:
"
enum
"
,
"
required
"
:
True
,
"
default
"
:
1
,
"
choices
"
:
"
eeee
"
}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
self
.
assertEqual
(
response
.
json
(),
{
"
configuration
"
:
{
"
user_configuration
"
:
[{
"
demo_choice
"
:
{
"
choices
"
:
[
"
Expected a list of items but got type
\"
str
\"
.
"
]
}
}]
}
})
def
test_invalid_user_configuration_not_dict
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
"
non
"
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
self
.
assertEqual
(
response
.
json
(),
{
"
configuration
"
:
{
"
user_configuration
"
:
[[
"
Expected a dictionary of items but got type
\"
str
\"
.
"
]]}})
def
test_invalid_user_configuration_wrong_field_type
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
something
"
:
{
"
title
"
:
"
some thing
"
,
"
type
"
:
"
uh oh
"
,
"
required
"
:
2
}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
self
.
assertEqual
(
response
.
json
(),
{
"
configuration
"
:
{
"
user_configuration
"
:
[{
"
something
"
:
{
"
required
"
:
[
"
Must be a valid boolean.
"
],
"
type
"
:
[
"
Value is not of type UserConfigurationFieldType
"
]
}
}]
}
})
def
test_invalid_user_configuration_wrong_default_type
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
one_float
"
:
{
"
title
"
:
"
a float
"
,
"
type
"
:
"
float
"
,
"
default
"
:
"
bonjour
"
,
"
required
"
:
True
}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
self
.
assertEqual
(
response
.
json
(),
{
"
configuration
"
:
{
"
user_configuration
"
:
[{
"
one_float
"
:
{
"
default
"
:
[
"
Default value is not of type float.
"
]
}
}]
}
})
def
test_invalid_user_configuration_choices_no_enum
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
something
"
:
{
"
title
"
:
"
some thing
"
,
"
type
"
:
"
int
"
,
"
required
"
:
False
,
"
choices
"
:
[
1
,
2
,
3
]
}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
self
.
assertEqual
(
response
.
json
(),
{
"
configuration
"
:
{
"
user_configuration
"
:
[{
"
something
"
:
{
"
choices
"
:
[
"
The
\"
choices
\"
field can only be set for an
\"
enum
\"
type property.
"
]
}
}]
}
})
def
test_invalid_user_configuration_missing_key
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
demo_integer
"
:
{
"
type
"
:
"
int
"
,
"
required
"
:
True
,
"
default
"
:
1
}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
self
.
assertEqual
(
response
.
json
(),
{
"
configuration
"
:
{
"
user_configuration
"
:
[{
"
demo_integer
"
:
{
"
title
"
:
[
"
This field is required.
"
]
}
}]
}
}
)
def
test_invalid_user_configuration_invalid_key
(
self
):
self
.
client
.
force_login
(
self
.
internal_user
)
response
=
self
.
client
.
post
(
reverse
(
"
api:worker-versions
"
,
kwargs
=
{
"
pk
"
:
str
(
self
.
worker_2
.
id
)}),
data
=
{
"
revision
"
:
str
(
self
.
rev2
.
id
),
"
configuration
"
:
{
"
user_configuration
"
:
{
"
demo_integer
"
:
{
"
title
"
:
"
an integer
"
,
"
type
"
:
"
int
"
,
"
required
"
:
True
,
"
default
"
:
1
,
"
some_key
"
:
"
oh no
"
,
}
}
},
"
gpu_usage
"
:
"
disabled
"
,
},
format
=
"
json
"
,
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
self
.
assertEqual
(
response
.
json
(),
{
"
configuration
"
:
{
"
user_configuration
"
:
[
{
"
demo_integer
"
:
{
"
some_key
"
:
[
"
Configurable properties can only be defined using the following keys: title, type, required, default, choices.
"
]
}
}
]
}
})
def
test_retrieve_version_invalid_id
(
self
):
self
.
client
.
force_login
(
self
.
user
)
response
=
self
.
client
.
get
(
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment