Advanced Settings - Image Input#15342
Conversation
|
@spalger I tagged you as a reviewer, since you're the Observables expert, but if you'd like me to defer to someone else, just lemme know. |
|
other side note, feel free to ignore: you could defer file size to the server's max payload size config. I'll stop intruding on this - sorry! |
|
@jbudz please don't stop intruding! I figured we'd want to stop huge images from getting into the |
|
Nope makes sense, I'd expect 200kb to be fine for a logo and if there's other uses we can always bump it up then. |
I think it's important that we enforce this validation on the server too, if we really want there to be a limit |
| import { Observable } from 'rxjs/Rx'; | ||
| const module = uiModules.get('kibana'); | ||
|
|
||
| const $readFileContents = (file) => { |
There was a problem hiding this comment.
Personal option, createFileContent$ (create file content stream) reads a little better, especially when you're assigning it to a variable:
const fileContent$ = createFileContent$(file)There was a problem hiding this comment.
Makes sense, I will make it so.
| const reader = new FileReader(); | ||
| reader.onerror = (err) => { | ||
| observer.error(err); | ||
| observer.complete(); |
There was a problem hiding this comment.
observer.error() is like rejecting a promise, calling observer.complete() afterward does nothing.
There was a problem hiding this comment.
Good point, I'll remove the unnecessary completion.
spalger
left a comment
There was a problem hiding this comment.
LGTM, tested by adding a logo to Kibanas uiSettings https://gist.github.com/anonymous/b2c3a92fee83fc7749af8113349258e7
| .map(dataUrl => { | ||
| const validations = validators.map(validator => validator(dataUrl)); | ||
|
|
||
| return { dataUrl, validations }; |
There was a problem hiding this comment.
More opinions: when I need to carry data onto another part of a stream I break up the original stream rather than require the previous stream to pass on it's input value. It might look something like this here:
// produce fileContent$ whenever the $element 'change' event is triggered.
const fileContent$ = Observable
.fromEvent($elem, 'change')
.map(e => e.target.files)
.switchMap(files => {
if (files.length === 0) {
return [];
}
if (files.length > 1) {
throw new Error('Only one file is supported at this time');
}
return $readFileContents(files[0]);
})
.share();
// validate the content of the files after it is loaded
const validations$ = fileContent$
.map(fileContent => (
validators.map(validator => validator(fileContent))
))
.toArray()
// push results from input/validation to the ngModel
const unsubscribe = Observable
.combineLatest(fileContent$, validations$)
.subscribe(([ fileContent, validations ]) => {
$scope.$evalAsync(() => {
...
})
})There was a problem hiding this comment.
I like it, more readable! I will make it so.
spalger
left a comment
There was a problem hiding this comment.
Sorry, mixed signals, I think we should enforce the size limit on the server too, perhaps the max image size should come from the config value, maybe something like this: https://gist.github.com/anonymous/c5b669c4df1ab0a813670a217b3ea83a
|
@spalger wrt to the server enforcing the size limit, that makes sense, I'll make those changes. |
| } | ||
|
|
||
| if (files.length > 1) { | ||
| throw new Error('Only one file is supported at this time'); |
There was a problem hiding this comment.
Couldn't this be a validator rather than a fatal error?
There was a problem hiding this comment.
Also, is it possible to submit multiple files since the input[type=file] requires the multiple attribute to enable multi-selection?
There was a problem hiding this comment.
I don't really want this error to be displayed to the end-user as much as the developer who tries to use it on an input with the multiple attribute, so I don't think it makes much sense to have it as a validator.
There was a problem hiding this comment.
We could, potentially, but it makes the code more complicated to support multiple files (when we don't really need it yet) so I'm throwing "better" errors more places to make this more obvious.
won't really do anything for other data-types
|
@spalger I'm now enforcing the maximum size of the setting on the server as well, using a configurable value specified like: The The "proper" solution to this would likely require a rather significant effort, does the current implementation seem reasonable enough for the time-being, or would you rather I investigate a more unified and robust manner for validating ui settings? |
src/ui/ui_settings/routes/set.js
Outdated
| await uiSettings.set(key, value); | ||
| } catch (err) { | ||
| if (err instanceof InvalidValueError) { | ||
| return boom.badRequest(err.message); |
There was a problem hiding this comment.
throw should be appropriate here
| * Invalid value for UiSetting | ||
| * @class InvalidValudError | ||
| */ | ||
| export class InvalidValueError extends Error { |
There was a problem hiding this comment.
We have a fairly established convention for handling service-specific errors. See:
There was a problem hiding this comment.
Is this an approach that we intend to support with the new platform? Throwing boom errors, which are http specific, seems like it won't scale that well as we start to support more "background" tasks that aren't attached to a specific http request/response.
| const validations = defaults[key].validations; | ||
| const value = changes[key]; | ||
| for (const validation of Object.values(validations)) { | ||
| const result = validation.schema.validate(value); |
There was a problem hiding this comment.
I think it's important to capture result.value here, since JoiSchema#validate() will return defaults and such if they are configured. Also, why not have a single joi schema for the value? Joi can merge multiple validations into a single error message and if you use abortEarly: false it will list all errors rather than just the first.
spalger
left a comment
There was a problem hiding this comment.
Hmm, I'm a little concerned by the amount of flexibility implemented currently. I'm not opposed to having validation be a feature of the UiSettings service, but the fact that we are supporting the Joi JSON-DSL in the uiSettingDefaults means we're kind of adding a whole new DSL to our API. I wouldn't expect support for Joi to follow us into the new platform so I'm thinking we should narrow the scope of the solution here a bit... Thoughts?
|
@spalger narrowing the solution seems fine to me, I has hesitant to add such flexible validation support to uiSettings, and at one point I was just hard-coding the maxLength validation here but it felt like this was bolted on, and it really only worked well for strings, since we're checking the "length" of the value. There's also a commit before switching to |
|
I personally think that having type-specific validation options is fine, so PS: I like PPS: Since a setting like |
* Beginning to work on image advanced setting type * Making the maxSize code and UI more user-friendly (hopefully). * Getting rid of that border, I was using the wrong class * Displaying an image for the preview * Not displaying the image if we don't have a value * Removing no longer used code * Adding explicit throw in the subscription to make our intents more clear * Changing some of the Observable style based on input from the peer review * Using the maxSizeKB option to enforce the size * Adding support for no max * Throwing better errors * No longer duplicated the multiple usage error message * Using the maxSize attribute to control the max size of the settings * No longer using JSON.stringify, it's already a string and that approach won't really do anything for other data-types * Extracting validate to it's own method * No longer hardcoding the maxLength validation in the uiSettings * Using a Joi schema instead * Punting on server-side validation for the time being
* Beginning to work on image advanced setting type * Making the maxSize code and UI more user-friendly (hopefully). * Getting rid of that border, I was using the wrong class * Displaying an image for the preview * Not displaying the image if we don't have a value * Removing no longer used code * Adding explicit throw in the subscription to make our intents more clear * Changing some of the Observable style based on input from the peer review * Using the maxSizeKB option to enforce the size * Adding support for no max * Throwing better errors * No longer duplicated the multiple usage error message * Using the maxSize attribute to control the max size of the settings * No longer using JSON.stringify, it's already a string and that approach won't really do anything for other data-types * Extracting validate to it's own method * No longer hardcoding the maxLength validation in the uiSettings * Using a Joi schema instead * Punting on server-side validation for the time being
Adds an "image" advanced setting type. The maximum size is currently hard-coded for all images to 200kB, we could potentially make this configurable and allow the uiSetting configuration to specify this value, but I figured 200kB was reasonable for the time being. The
inputBaseSixtyFouronly supports a single file for the time being, and will throw an Error that results in a "fatal error" if it's given multiple files.I used RxJs/Observables for the
inputBaseSixtyFourdirective so that we could abort the FileReader when thechangeevent is fired before the reading has finished."Release Note: Added image support to the Advanced Settings"