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

Schema helper functions #1197

Merged
merged 12 commits into from
Oct 3, 2019
Merged

Schema helper functions #1197

merged 12 commits into from
Oct 3, 2019

Conversation

andresmgot
Copy link
Contributor

Follow up #1196

Actual implementation of the schema helper function. There are two main functions:

  • retrieveBasicFormParams: Reads the schema and return the list of basic params.
  • setValues: Reads the give values and modify a certain value.

An issue to mention is that the yaml package doesn't support Typescript definitions (yet), the current package has some missing definitions and it's not usable so we need to work with plan javascript.

@andresmgot andresmgot requested a review from absoludity October 1, 2019 15:09
Copy link
Contributor

@absoludity absoludity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100%, but parts of this look like a re-implementation of parts of lodash - worth taking a look.

dashboard/src/shared/schema.test.ts Outdated Show resolved Hide resolved
expect(retrieveBasicFormParams(t.values, t.schema)).toEqual(t.result);
});
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 Excellent clear tests!

dashboard/src/shared/schema.test.ts Outdated Show resolved Hide resolved
dashboard/src/shared/schema.test.ts Outdated Show resolved Hide resolved
dashboard/src/shared/schema.test.ts Outdated Show resolved Hide resolved
return basicFormParameters;
}

// getDocumentElem returns the YAML element given a root element
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm missing something, rootElem here is just an object right? In which case, we can possibly remove (and not have to maintain) this function completely in favor of https://lodash.com/docs/#get ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, unfortunately it's not. It's a YAML.ast.Document from https://eemeli.org/yaml/#documents so we cannot use lodash methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - I see. Thanks for the reference. So... at least for this function: lodash.get(rootElem.toJSON(), path, defaults)?

Actually, checking the docs, it looks like YAML provides getIn for this reason? It takes an iterable which is the path segments, as in, if we had a document.contents AST representing { foo: { bar: [8, 9] } }, then we should be able to use rootElem.getIn(['foo', 'bar', 1]) to get the value 9? At least, that's what I understand from the docs... I'd be surprised if we needed to implement getValue and setValue ourselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, nice catch, I understood that differently (I thought that the *In methods only worked with collections/arrays) but it's true that we can use that. Thanks!

The only caveat I have found is that setIn returns an error if the object doesn't define the parent element:

> d.toString()
'a: b\n'
> d.setIn(["b", "c"], "foo")
Error: Expected YAML collection at b. Remaining path: c

In any case, that's the same limitation that we have now.

dashboard/src/shared/schema.ts Show resolved Hide resolved
dashboard/src/shared/schema.ts Outdated Show resolved Hide resolved
dashboard/src/shared/schema.ts Outdated Show resolved Hide resolved
dashboard/src/shared/schema.ts Outdated Show resolved Hide resolved
Andres Martinez Gotor added 2 commits October 2, 2019 12:41
Copy link
Contributor

@absoludity absoludity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info - more comments in-line :)

return basicFormParameters;
}

// getDocumentElem returns the YAML element given a root element
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - I see. Thanks for the reference. So... at least for this function: lodash.get(rootElem.toJSON(), path, defaults)?

Actually, checking the docs, it looks like YAML provides getIn for this reason? It takes an iterable which is the path segments, as in, if we had a document.contents AST representing { foo: { bar: [8, 9] } }, then we should be able to use rootElem.getIn(['foo', 'bar', 1]) to get the value 9? At least, that's what I understand from the docs... I'd be surprised if we needed to implement getValue and setValue ourselves.

dashboard/src/shared/schema.ts Show resolved Hide resolved
dashboard/src/shared/schema.ts Outdated Show resolved Hide resolved
defaultValues: string,
schemaProperties: {},
parentPath?: string,
params?: IBasicFormParam[],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found this argument odd - why pass the ref to the array as an arg, rather than just relying on the function return value? You could remove this argument - I think - if you...

schemaProperties: {},
parentPath?: string,
params?: IBasicFormParam[],
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

always create a params array here, and then...

// Use the default value either from the JSON schema or the default values
value: getDefaultValue(defaultValues, itemPath) || schemaProperties[propertyKey].default,
};
params = params ? params.concat(newParam) : [newParam];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only ever params.concat(newParam) here, then...

}
// If the property is an object, iterate recursively
if (schemaProperties[propertyKey].type === "object") {
params = lookForFormParams(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params.concat(lookForFormParams(...)) recursively here (but without passing in the params arg, obviously).

Or why do you need to rely on both a return value and an array reference as a function arg?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

took me a while to get my head around that one 😅

You are right, the params argument is not needed.

@andresmgot andresmgot changed the base branch from usernameInput to master October 2, 2019 14:12
@andresmgot
Copy link
Contributor Author

Apart from your feedback @absoludity I have added the types for yaml and json-schema so the code is easier to read and properly typed. To avoid the issue that getIn and setIn are not defined in the Typescript package, I had to convert the document (YAML.ast.Document) to any. Let me know what you think.

Copy link
Contributor

@absoludity absoludity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet, that's much clearer, thanks for the updates!

Still one thing I'm unsure about - why we call setValue with a string yaml doc and return a string, rather than just keeping a reference to the doc in the form state so that we'd pass the doc, update it, and return the same ref (or not even need to return it)?

But +1 either way.

if (schema && schema.properties) {
return lookForFormParams(defaultValues, schema.properties);
}
return {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function looks like it doesn't do much now - other than verify the schema has properties... why not rename lookForFormParams to retrieveBasicFormParams and have call-sites use that (if they have properties)? Given that lookForPormParams no longer has the array arg (which may have been awkward for call-sites to call with an empty array), it now looks exportable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we can do that, I wanted to split it for being a bit more readable but probably there is not much difference.

const doc = YAML.parseDocument(values);
const splittedPath = path.split(".");
return (doc as any).getIn(splittedPath) || defaultValue;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great - getValue and setValue are much simpler now. I'm still not sure why we pass string values into these, rather than passing the actual document. I expected that the form state would be the document, which gets updated with data by the user... converting that to a string and back on each change seems superfluous, but maybe there is context I'm missing?

Copy link
Contributor Author

@andresmgot andresmgot Oct 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning is that the the values.yaml is received as a string and it's always treated as a normal string in the Form component if it doesn't have support for the basic form.

My idea is to have this abstraction layer so the Form always work with the string values.yaml regardless of the implementation of this library. We are using yaml Documents but we could be using regexps or lodash to modify those values. That way, the Form component doesn't depend on the implementation details here.

Also, if we maintain in the Form state the Document we would need to implement there the transformation string<-->Document.

@andresmgot andresmgot merged commit ba6dd99 into master Oct 3, 2019
@andresmgot andresmgot deleted the schemaFunctions branch October 3, 2019 11:02
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 this pull request may close these issues.

2 participants