Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not sure if this is a bug. CSS/js in production redirect to "base url" #34

Open
fithisux opened this issue Apr 18, 2022 · 14 comments · May be fixed by #43
Open

Not sure if this is a bug. CSS/js in production redirect to "base url" #34

fithisux opened this issue Apr 18, 2022 · 14 comments · May be fixed by #43

Comments

@fithisux
Copy link

fithisux commented Apr 18, 2022

I use swagger-ui-py==21.12.8 and locally Openapi documentation is created without problems and I can enjoy it at

http://localhost:8005/api/doc (8005 is custom).

We deploy in our preview envs, and this url works fine

https://preview-newe-2253-a9wd53-debug.preview.gfknewron.com/insight/additive-decomposition/api/doc/swagger.json

But htis one

https://preview-newe-2253-a9wd53-debug.preview.gfknewron.com/insight/additive-decomposition/api/doc/

has problems with resources

GET https://preview-newe-2253-a9wd53-debug.preview.gfknewron.com/api/doc/static/swagger-ui.css net::ERR_ABORTED 404

it should have been

https://preview-newe-2253-a9wd53-debug.preview.gfknewron.com/insight/additive-decomposition/api/doc/static/swagger-ui.css

The urls are generated from our deployment scripts.

Is there something I can do? Is this a misuse of the library on my part?

Any idea what is missing?

Python 3.10.3 on MacOS Big sur, x86-64, installed through pyenv. flask==2.1.0

@fithisux
Copy link
Author

fithisux commented Apr 18, 2022

My init

def init():
    app = Flask(__name__)
    app.config["JSON_SORT_KEYS"] = False
    app.config.update(settings)
    app.errorhandler(Exception)(exceptions_handler)

    metrics = GunicornInternalPrometheusMetrics.for_app_factory(defaults_prefix=NO_PREFIX)
    metrics.init_app(app)

    app.before_request(log_request_start)
    app.after_request(log_request_end)

    app.add_url_rule(rule="/actuator/health", endpoint="healthcheck", view_func=healthcheck, methods=["GET"])
    app.add_url_rule(rule="/v1/additive_decomposition/summary", endpoint="/v1/additive_decomposition/summary", view_func=additive_decomposition_v1_proxy, methods=["POST"])
    logging.config.dictConfig(app.config.get("LOGGING"))

    spec = APISpec(title="Additive Decomposition API", version="0.0.1", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()])

    with app.test_request_context():
        spec.path(view=additive_decomposition_v1_proxy)
        spec.path(view=healthcheck)

    with open(DOC_DIR, "w") as f:
        json.dump(spec.to_dict(), f, indent=2)
    api_doc(app, config_path=DOC_DIR, url_prefix="/api/doc", title="API doc")

    return app

@PWZER
Copy link
Owner

PWZER commented Apr 21, 2022

@fithisux It seems that a proxy is added before calling the api_doc function, and its uri is /insight/additive-decomposition, but this proxy does not work for /api/doc/static/swagger-ui.css.

@fithisux
Copy link
Author

Thank you @PWZER. So, this issue is outside my application. Correct?

@PWZER
Copy link
Owner

PWZER commented Apr 21, 2022

@fithisux I'm guessing you might need to use like this

api_doc(app, config_path=DOC_DIR, url_prefix="/insight/additive-decomposition/api/doc", title="API doc")

@PWZER
Copy link
Owner

PWZER commented Apr 21, 2022

@fithisux Could be the problem caused by this

spec.path(view=additive_decomposition_v1_proxy)

@dulanthaf
Copy link

@fithisux were you able to resolve this issue?

@fithisux
Copy link
Author

fithisux commented Nov 2, 2022

I went to a different solution @dulanthaf . I used apispec. https://apispec.readthedocs.io/en/latest/

Even there I had a poblem since routes were not rewritten correctly. So I downloaded in a separate folder, called "static"

the contents of https://github.com/swagger-api/swagger-ui.

Also there I added an empty "init.py"

Now I did in my code:


spec = APISpec(title="Additive Decomposition API", version="0.0.1", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()])

    with app.test_request_context():
        spec.path(view=xxx1)
        spec.path(view=xxx2)
        spec.path(view=healthcheck)

    with open(OPENAPI_FILE, "w") as f:
        json.dump(spec.to_dict(), f, indent=2)

where (I actually dumped the json in the static folder with the rest of swagger ui js)

STATIC_DIR = os.path.dirname(static.__file__)
OPENAPI_FILE = f"{STATIC_DIR}/swagger.json"

now flask can add a route to static as

app = Flask(__name__, static_url_path="/static", static_folder=STATIC_DIR)

@dulanthaf
Copy link

I'm not sure if it's the same setup, but we use nginx for the proxy setup. So what worked for me was, add the proxy path, in your case probably url_prefix="/insight/additive-decomposition/api/doc" that includes the proxy subdirectory, and then in the nginx config, add a new location entry for the server:

location ^~ /insight/additive-decomposition/api/doc/ {
    proxy_pass http://something:port/insight/additive-decomposition/api/doc/;
}

@fithisux
Copy link
Author

fithisux commented Nov 8, 2022

Thank you @dulanthaf this is exactly what I suspected, but our SREs did not like this solution. Thank you anyway.

@dulanthaf
Copy link

@fithisux any particular reason why the SRE's did not like the solution? I would assume it's one more entry into the existing proxy pass configuration?

@Olegt0rr
Copy link

Olegt0rr commented Feb 20, 2024

@fithisux any particular reason why the SRE's did not like the solution? I would assume it's one more entry into the existing proxy pass configuration?

Because it's workaround, which need to be described and supported. So really good SRE should avoid this solution

@Olegt0rr
Copy link

Olegt0rr commented Feb 20, 2024

@fithisux it happens, since template uses prefix as an absolute path

<head>
<meta charset="UTF-8">
<title> {{ title }} </title>
<link rel="stylesheet" type="text/css" href="{{ url_prefix }}/static/swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="{{ url_prefix }}/static/index.css" />
<link rel="icon" type="image/png" href="{{ url_prefix }}/static/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="{{ url_prefix }}/static/favicon-16x16.png" sizes="16x16" />
</head>

I.e. paths are looks like /api/doc/static/swagger-ui.css

Any external path override will cause this issue:
Screenshot 2024-02-20 at 11 14 17

Making static path relative to current swagger-ui location should help to avoid this behaviour and be independent from any location overrides

  <head>
    <meta charset="UTF-8">
    <title> {{ title }} </title>
    <link rel="stylesheet" type="text/css" href="{{ relative_prefix }}/static/swagger-ui.css" />
    <link rel="stylesheet" type="text/css" href="{{ relative_prefix }}/static/index.css" />
    <link rel="icon" type="image/png" href="{{ relative_prefix }}/static/favicon-32x32.png" sizes="32x32" />
    <link rel="icon" type="image/png" href="{{ relative_prefix }}/static/favicon-16x16.png" sizes="16x16" />
  </head>

So it should be rendered like this

  <head>
    <meta charset="UTF-8">
    <title> {{ title }} </title>
    <link rel="stylesheet" type="text/css" href="doc/static/swagger-ui.css" />
    <link rel="stylesheet" type="text/css" href="doc/static/index.css" />
    <link rel="icon" type="image/png" href="doc/static/favicon-32x32.png" sizes="32x32" />
    <link rel="icon" type="image/png" href="doc/static/favicon-16x16.png" sizes="16x16" />
  </head>

@Olegt0rr Olegt0rr linked a pull request Feb 20, 2024 that will close this issue
@Olegt0rr
Copy link

Olegt0rr commented Feb 20, 2024

@fithisux ,

I've made my suggestions with PR mentioned above.

But if you want workaround right now:

class ApplicationDocumentRelative(ApplicationDocument):
    @property
    def doc_html(self):
        doc_location_split = self.url_prefix.split("/")
        static_relative_location = doc_location_split.pop()
        self.set_relative_swagger_url()
        return self.env.get_template("doc.html").render(
            url_prefix=static_relative_location,
            title=self.title,
            config_url=self.swagger_json_uri_absolute,
            parameters=self.parameters,
            oauth2_config=self.oauth2_config,
        )

    def set_relative_swagger_url(self):
        split_path = self.swagger_json_uri_absolute.split("/")
        swagger_url_relative = "/".join(split_path[-2:])
        self.parameters["url"] = f'"{swagger_url_relative}"'


def api_doc(app, **kwargs):
    doc = ApplicationDocumentRelative(app, **kwargs)

    handler = doc.match_handler()

    if not handler:
        raise RuntimeError("No match application isinstance type!")

    if not callable(handler):
        raise RuntimeError("handler is callable required!")

    return handler(doc)

Result:
Screenshot 2024-02-20 at 12 49 21

@fithisux
Copy link
Author

Thank you very much. I do not work in this project anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants