def test_delete_configs(admin_api: FlaskClient[Any]) -> None: # delete a config and its description state.set_config("delete_this", "1") state.set_config_description("delete_this", "description for this config") assert state.get_uncached_config("delete_this") == 1 assert state.get_config_description( "delete_this") == "description for this config" response = admin_api.delete("/configs/delete_this") assert response.status_code == 200 assert state.get_uncached_config("delete_this") is None assert state.get_config_description("delete_this") is None # delete a config but not description state.set_config("delete_this", "1") state.set_config_description("delete_this", "description for this config") assert state.get_uncached_config("delete_this") == 1 assert state.get_config_description( "delete_this") == "description for this config" response = admin_api.delete("/configs/delete_this?keepDescription=true") assert response.status_code == 200 assert state.get_uncached_config("delete_this") is None assert state.get_config_description( "delete_this") == "description for this config"
def test_config_desc(self) -> None: state.set_config_description("foo", "Does foo") assert state.get_config_description("foo") == "Does foo" state.set_config_description("bar", "bars something") assert all(state.get_all_config_descriptions()[k] == d for k, d in [("foo", "Does foo"), ("bar", "bars something")]) state.delete_config_description("foo") assert state.get_config_description("foo") is None
def test_config_descriptions(admin_api: FlaskClient[Any]) -> None: state.set_config_description("desc_test", "description test") state.set_config_description("another_test", "another description") response = admin_api.get("/all_config_descriptions") assert response.status_code == 200 assert json.loads(response.data) == { "desc_test": "description test", "another_test": "another description", }
def test_get_configs(admin_api: FlaskClient[Any]) -> None: response = admin_api.get("/configs") assert response.status_code == 200 assert json.loads(response.data) == [] # Add string config state.set_config("cfg1", "hello world") # Add int config state.set_config("cfg2", "12") # Add float config state.set_config("cfg3", "1.0") # Add config with description state.set_config("cfg4", "test") state.set_config_description("cfg4", "test desc") response = admin_api.get("/configs") assert response.status_code == 200 assert json.loads(response.data) == [ { "key": "cfg1", "type": "string", "value": "hello world", "description": None }, { "key": "cfg2", "type": "int", "value": "12", "description": None }, { "key": "cfg3", "type": "float", "value": "1.0", "description": None }, { "key": "cfg4", "type": "string", "value": "test", "description": "test desc" }, ]
def config(config_key: str) -> Response: if request.method == "DELETE": user = request.headers.get(USER_HEADER_KEY) # Get the old value for notifications old = state.get_uncached_config(config_key) state.delete_config(config_key, user=user) if request.args.get("keepDescription") is None: state.delete_config_description(config_key, user=user) notification_client.notify( RuntimeConfigAction.REMOVED, { "option": config_key, "old": old, "new": None }, user, ) return Response("", 200) else: # PUT currently only supports editing existing config when old and # new types match. Does not currently support passing force to # set_config to override the type check. user = request.headers.get(USER_HEADER_KEY) data = json.loads(request.data) # Get the previous value for notifications old = state.get_uncached_config(config_key) try: new_value = data["value"] new_desc = data["description"] assert isinstance(config_key, str), "Invalid key" assert isinstance(new_value, str), "Invalid value" assert config_key != "", "Key cannot be empty string" state.set_config( config_key, new_value, user=user, ) state.set_config_description(config_key, new_desc, user=user) except (KeyError, AssertionError) as exc: return Response( json.dumps({"error": f"Invalid config: {str(exc)}"}), 400, {"Content-Type": "application/json"}, ) except (state.MismatchedTypeException): return Response( json.dumps({"error": "Mismatched type"}), 400, {"Content-Type": "application/json"}, ) # Value was updated successfully, refetch and return it evaluated_value = state.get_uncached_config(config_key) assert evaluated_value is not None evaluated_type = get_config_type_from_value(evaluated_value) # Send notification notification_client.notify( RuntimeConfigAction.UPDATED, { "option": config_key, "old": old, "new": evaluated_value }, user=user, ) config = { "key": config_key, "value": str(evaluated_value), "description": state.get_config_description(config_key), "type": evaluated_type, } return Response(json.dumps(config), 200, {"Content-Type": "application/json"})
def configs() -> Response: if request.method == "POST": data = json.loads(request.data) try: key, value, desc = data["key"], data["value"], data["description"] assert isinstance(key, str), "Invalid key" assert isinstance(value, str), "Invalid value" assert key != "", "Key cannot be empty string" except (KeyError, AssertionError) as exc: return Response( json.dumps({"error": f"Invalid config: {str(exc)}"}), 400, {"Content-Type": "application/json"}, ) existing_config = state.get_uncached_config(key) if existing_config is not None: return Response( json.dumps({"error": f"Config with key {key} exists"}), 400, {"Content-Type": "application/json"}, ) user = request.headers.get(USER_HEADER_KEY) state.set_config(key, value, user=user) state.set_config_description(key, desc, user=user) evaluated_value = state.get_uncached_config(key) assert evaluated_value is not None evaluated_type = get_config_type_from_value(evaluated_value) config = { "key": key, "value": str(evaluated_value), "description": state.get_config_description(key), "type": evaluated_type, } notification_client.notify( RuntimeConfigAction.ADDED, { "option": key, "old": None, "new": evaluated_value }, user, ) return Response(json.dumps(config), 200, {"Content-Type": "application/json"}) else: descriptions = state.get_all_config_descriptions() raw_configs: Sequence[Tuple[str, Any]] = state.get_raw_configs().items() sorted_configs = sorted(raw_configs, key=lambda c: c[0]) config_data = [{ "key": k, "value": str(v) if v is not None else None, "description": str(descriptions.get(k)) if k in descriptions else None, "type": get_config_type_from_value(v), } for (k, v) in sorted_configs] return Response( json.dumps(config_data), 200, {"Content-Type": "application/json"}, )