-
Notifications
You must be signed in to change notification settings - Fork 549
O3-310: allow Put operations on an application-writable config.json file to openmrs/data/frontends/config.json #595
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
base: master
Are you sure you want to change the base?
Changes from 5 commits
a44a9d3
9c6d3ea
1abae4c
bf46700
6ffb3dd
a78f76d
b426de6
b6ef2eb
3abd011
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,128 @@ | ||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * This Source Code Form is subject to the terms of the Mozilla Public License, | ||||||||||||||||||||||||||||||||||
| * v. 2.0. If a copy of the MPL was not distributed with this file, You can | ||||||||||||||||||||||||||||||||||
| * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under | ||||||||||||||||||||||||||||||||||
| * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS | ||||||||||||||||||||||||||||||||||
| * graphic logo is a trademark of OpenMRS Inc. | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| package org.openmrs.module.webservices.rest.web.v1_0.controller.openmrs1_9; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import java.io.BufferedReader; | ||||||||||||||||||||||||||||||||||
| import java.io.ByteArrayInputStream; | ||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||
| import java.io.File; | ||||||||||||||||||||||||||||||||||
| import java.io.FileInputStream; | ||||||||||||||||||||||||||||||||||
| import java.io.InputStream; | ||||||||||||||||||||||||||||||||||
| import java.io.OutputStream; | ||||||||||||||||||||||||||||||||||
| import java.nio.file.Files; | ||||||||||||||||||||||||||||||||||
| import java.nio.charset.StandardCharsets; | ||||||||||||||||||||||||||||||||||
| import javax.servlet.http.HttpServletRequest; | ||||||||||||||||||||||||||||||||||
| import javax.servlet.http.HttpServletResponse; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import org.openmrs.User; | ||||||||||||||||||||||||||||||||||
| import org.openmrs.api.AdministrationService; | ||||||||||||||||||||||||||||||||||
| import org.openmrs.api.context.Context; | ||||||||||||||||||||||||||||||||||
| import org.openmrs.module.webservices.rest.web.RestConstants; | ||||||||||||||||||||||||||||||||||
| import org.openmrs.module.webservices.rest.web.v1_0.controller.BaseRestController; | ||||||||||||||||||||||||||||||||||
| import org.openmrs.util.OpenmrsUtil; | ||||||||||||||||||||||||||||||||||
| import org.slf4j.Logger; | ||||||||||||||||||||||||||||||||||
| import org.slf4j.LoggerFactory; | ||||||||||||||||||||||||||||||||||
| import org.springframework.http.HttpStatus; | ||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Controller; | ||||||||||||||||||||||||||||||||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||||||||||||||||||||||||||||||||
| import org.springframework.web.bind.annotation.RequestMethod; | ||||||||||||||||||||||||||||||||||
| import org.springframework.web.bind.annotation.ResponseStatus; | ||||||||||||||||||||||||||||||||||
| import org.apache.commons.io.IOUtils; | ||||||||||||||||||||||||||||||||||
| import org.codehaus.jackson.JsonProcessingException; | ||||||||||||||||||||||||||||||||||
| import org.codehaus.jackson.map.ObjectMapper; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @Controller | ||||||||||||||||||||||||||||||||||
| @RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/frontend/config.json") | ||||||||||||||||||||||||||||||||||
| public class FrontendJsonConfigController1_9 extends BaseRestController { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private static final String DEFAULT_FRONTEND_DIRECTORY = "frontend"; | ||||||||||||||||||||||||||||||||||
| private static final String GP_LOCAL_DIRECTORY = "spa.local.directory"; | ||||||||||||||||||||||||||||||||||
| private static final String JSON_CONFIG_FILE_NAME = "config.json"; | ||||||||||||||||||||||||||||||||||
| private static final Logger log = LoggerFactory.getLogger(FrontendJsonConfigController1_9.class); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @RequestMapping(method = RequestMethod.GET) | ||||||||||||||||||||||||||||||||||
| public void getFrontendConfigFile(HttpServletRequest request, HttpServletResponse response) throws IOException { | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| File jsonConfigFile = getJsonConfigFile(); | ||||||||||||||||||||||||||||||||||
| if (!jsonConfigFile.exists()) { | ||||||||||||||||||||||||||||||||||
| log.debug("Configuration file does not exist"); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| log.debug("Configuration file does not exist"); | |
| log.warn("Configuration file does not exist"); |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding Content-Disposition: attachment makes it impossible to use this on the frontend because you're not returning the JSON file as a REST response. You're returning a response instructing the user agent to download that as a file. Without some kind of endpoint that allows us to make a get request and get the JSON document back as part of the response without the Content-Disposition we cannot make use of this configuration file. (I'd suggest adding an optional parameter, e.g., ?download so that:
GET /openmrs/ws/rest/v1/frontend/config.json returns like requesting any other JSON file and GET /openmrs/ws/rest/v1/frontend/config.json?download sends it as an attachment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i have added the Content-Disposition: attachment for both GET /openmrs/ws/rest/v1/frontend/config.json?download and GET /openmrs/ws/rest/v1/frontend/config.json?download=true. As an addition, i have Set their content type to application/x-download
Thank you for this clarification
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd keep the content type! It's still a JSON document.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| log.error("Error reading Configuration file: " + jsonConfigFile.getAbsolutePath(), e); | |
| log.error("Error reading Configuration file {}", jsonConfigFile.getAbsolutePath(), e); |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the other line I see indented by a tab.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as here #595 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably invent a privilege for this purpose so we don't require people to be super users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkayiwa should i go ahead and create a ticket or talk post for this new requirement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do not have to, because that would be metadata which does not require a core change.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the above line also for validation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, this is just for reading the request body. Line 86 does the validation
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
| String requestBody = IOUtils.toString( inputStream , "UTF-8"); | |
| String requestBody = IOUtils.toString(inputStream , "UTF-8"); |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| log.error("Invalid JSON format", e); | |
| log.error("Invalid JSON format when reading: {}", requestBody, e); |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| try { | |
| // verify that is in a valid JSON format | |
| new ObjectMapper().readTree(requestBody); | |
| } catch (JsonProcessingException e) { | |
| log.error("Invalid JSON format", e); | |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid JSON format"); | |
| } | |
| try { | |
| // verify that is in a valid JSON format | |
| new ObjectMapper().readTree(requestBody); | |
| } catch (JsonProcessingException e) { | |
| log.error("Invalid JSON format", e); | |
| response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid JSON format"); | |
| } | |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we just use the requestBody string we already have?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not so sure if i get you correctly but i was advised to use OpenMRSUtil's copyFile
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is unnecessary. copyFile() would've thrown an exception (which should potentially be handled here) if there was a failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never end a function like this with an if without an else. What response would the consumer get if the jsonConfigFile.exists() check returned false? (This is just intended as a pedagogic remark; as I said above, the entire if is unnecessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine, but:
- The error should be logged
- The consuming code should probably catch this exception and return something without the full path
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check folder.isAbsolute() rather than folder.exists() for this.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The formatting here looks ... 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you address the formatting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed the extra spaces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the indention?