示例#1
0
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"
示例#2
0
 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
示例#3
0
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",
    }
示例#4
0
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"
        },
    ]
示例#5
0
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"})
示例#6
0
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"},
        )