-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[home] Include ability to publish kibana saved objects from add data tutorial #19559
Conversation
💔 Build Failed |
isInstalling: false, | ||
installStatus: INCOMPLETE, | ||
}; | ||
} |
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.
If you don't need the constructor for anything else, you can set the shorthand:
export class SavedObjectsInstaller extends React.Component {
state = {
isInstalling: false,
installStatus: INCOMPLETE,
};
}
|
||
this.state = { | ||
isInstalling: false, | ||
installStatus: INCOMPLETE, |
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.
For consistency installStatus
should be a boolean like isInstalling
(and named isComplete
).
|
||
return { | ||
title: 'Load Kibana objects', | ||
status: this.state.installStatus, |
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.
If status only has two modes, I think it would be better to make it a boolean.
let statusMsg = `${this.props.savedObjects.length} saved objects successfully added`; | ||
if (errors.length > 0) { | ||
statusMsg = `Unable to load kibana saved objects, Error: ${errors[0]}`; | ||
} |
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 prefer const
since it gives the assurance that this variable won't be reassigned later. If you use a ternary you can avoid let
.
this.setState({ | ||
isInstalling: false, | ||
installStatusMsg: statusMsg, | ||
installStatus: errors.length === 0 ? COMPLETE : INCOMPLETE, |
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.
Instead of having two different checks for error (errors.length === 0
and errors.length > 0
) maybe just have one const hasError = errors.length > 0
.
This way they won't somehow come out of sync in a future change.
@@ -49,6 +51,7 @@ function getRoute() { | |||
const getter = indexPatterns.getIds; | |||
getter.clearCache(); | |||
}; | |||
$scope.bulkCreate = savedObjectsClient.bulkCreate; |
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 think we made a mistake by coupling rest clients to Angular in the past. Instead they should have been framework-agnostic static functions.
If we keep coupling new code (in this case React) to Angular we make it harder to switch away from Angular, and increase technical debt.
It's easier to use an existing client but I don't think the alternative is much more difficult. You don't have to re-implement the entire savedObjectsClient - just create a starting point with the endpoints you need.
Afaict this is all you need:
export function bulkCreate(savedObjects, { overwrite }) {
return kfetch({
pathname: `/api/saved_objects/_bulk_create`,
method: 'POST',
query: {
overwrite
},
body: JSON.stringify(savedObjects)
});
}
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.
@epixa Do we already have a strategy for consuming Angular services from outside Angular - and how to migrate them? I've already spent a fair amount of time de-angularizing some services and it would be beneficial if everyone were aligned. If we don't have anything I'm open for doing a write-up of the approach I've used, and how I see us moving forward.
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 do not think this is a problem and is exactly how we are interacting with saved objects in other react components. From react's perspective, it is just an injected function. Eventually, the savedObjectClient will be decoupled from angular but all saved object interaction should go through the savedObjectClient.
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.
but all saved object interaction should go through the savedObjectClient
Maybe I don't know enough about the savedObjectClient
. What does it provide wrt bulkCreate
, that a static method can't do?
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 savedObjectClient converts the results into savedObject classes and camel cases the object keys. But more importantly, it puts all calls to the saved object API in a single place so if the API ever changes, only the savedObjectClient has to be updated instead of hunting down lots of segregated fetches.
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.
But more importantly, it puts all calls to the saved object API in a single place so if the API ever changes, only the savedObjectClient has to be updated instead of hunting down lots of segregated fetches
Totally agree. IMHO it is okay temporarily to have two "clients": the old Angular client, and a new framework agnostic service. Over time the Angular client could even call the framework agnostic client, and eventually the Angular client would be dropped. That's just my view, but interested to hear what other ideas are floating around (ping @epixa ;) )
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.
Btw. I think it's fine to leave it as is. Just my opinion on how to do it going forward.
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.
Not in this PR, but I'd rather just remove the angular dependency from the saved object client we have today. It shouldn't be terribly difficult, we'd just need to update any references to the saved object client from angular code to invoke $scope.$apply() after the call.
"attributes": { | ||
"title": apmIndexPattern, | ||
"timeFieldName": "@timestamp", | ||
"fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"beat.hostname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.timezone\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.process.pid\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.process.title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.http_version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.method\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.full\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.hash\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.hostname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.pathname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.port\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.protocol\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.request.url.search\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.response.finished\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.response.status_code\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.agent.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.agent.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.environment\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.framework.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.framework.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.language.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.language.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.runtime.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.runtime.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.service.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.system.architecture\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.system.hostname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.system.platform\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.tags.foo\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.tags.lorem\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.tags.multi-line\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.tags.this-is-a-very-long-tag-name-without-any-spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.user.email\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.user.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"context.user.username\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error id icon\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.code\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.culprit\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.exception.code\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.exception.handled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.exception.message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.exception.module\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.exception.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.grouping_key\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.log.level\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.log.logger_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.log.message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.log.param_message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.namespace\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"listening\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.cloud.availability_zone\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.cloud.instance_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.cloud.instance_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.cloud.machine_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.cloud.project_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.cloud.provider\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.cloud.region\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"processor.event\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"processor.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sourcemap.bundle_filepath\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sourcemap.service.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sourcemap.service.version\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.duration.us\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.parent\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.start.us\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.duration.us\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"transaction.name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.result\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.sampled\",\"type\":\"boolean\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.span_count.dropped.total\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"view errors\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"view spans\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" |
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 this just for debugging purposes? I assume this should be dynamically generated, so that when the mapping changes this doesn't get out of sync.
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.
Storing the index pattern saved object like this is on purpose. If the mappings change between versions then this will need to be updated - just like the other saved objects will need to be updated in the event that field names change and so on.
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.
True, we still need to update the dashboards if a field is renamed or removed. But up until now we've been able to make backwards compatible changes to the schema (like adding a new field) that didn't require us to update anything in Kibana.
What is the advantage of storing the static object, instead of creating it dynamically?
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 advantage is being able to link the other saved objects directly to the index-pattern by id. How does the import work now, how do the saved objects link to the index-pattern? Are the users prompted with a list of index-patterns that match the index-pattern title?
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 user might access APM UI (which also needs the index pattern) before opening the Getting Started Guide. We therefore need a single solution for creating the index pattern, so we don't end up with duplicate saved objects files that needs to be updated whenever apm-server schema changes.
I outlined three different solutions here: elastic/apm-server#991
If you have some alternative solutions I'm all ears :)
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.
@nreese You might be right. The thinking behind having two buttons ["Import index pattern", "Import index pattern and dashboards"] is that users will need to manually update the index pattern whenever they update their version of the stack. However, clicking "Import index pattern and dashboards" will also overwrite existing visualizations (AFAIK, correct me if I'm wrong), which can be annoying.
But, we might be overthinking this. Thoughts?
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.
Bit more context: The reason for mentioning "Index pattern" specifically is because we have this call out to the user in the UI: #20077
Having said that, I like what you did with abstracting all the details away by writing "Import Kibana objects". Maybe that's much better. We can have the details on a documentation page, or something. @formgeist @sqren Thoughts?
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.
+1 for just having one option and calling it "Import Kibana objects".
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.
Fine with me. If this causes issues for the users we can iterate on it.
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.
Agreed, let's go with one button. Thanks for input @nreese.
Copy suggestion for the setup instructions step:
Title:
Kibana objects
Description:
Imports index pattern, visualizations and pre-defined dashboards. Index pattern is required for some features in the APM UI.
Button copy:
Load/Import Kibana objects
*/ | ||
|
||
/* eslint max-len: 0 */ | ||
/* eslint quotes: 0 */ |
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.
Instead of disabling eslint, I think the JSON should be moved to JSON-files. That will also make it easier to update.
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.
These can not move to a json file since they need javascript logic to set the apm index pattern title
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.
Perhaps this comment was meant for get_index_pattern_saved_object.js
?
💔 Build Failed |
💚 Build Succeeded |
@nreese @alexfrancoeur I do like being more specific as in "Load Kibana dashboards and visualizations" However, if I read things correctly the objects can also be index patterns and searches, so that title isn't quite accurrate. How about: Load Kibana saved objects Add saved searches, visualizations, dashboards, and index patterns. The other steps don't start with "Click the button" so I'd eliminate that here. Also, no need to init cap the objects |
From a general point of view, it makes sense to keep the copy about "objects" since other Getting Started Guides might want to import searches, visualizations etc (like @gchaps mentions). The use case for APM however is pretty specific: we are interested in importing dashboards and index patterns - preferably as separate flows (but a single flow might work also). We mention the Getting Started Guide and link to it from the APM ui when using the new Query Bar:
And also for creating ML jobs:
Would be great if the terminology in APM ui matched the terminology in the APM Getting Started Guide, so the user won't be confused when clicking the link. |
💚 Build Succeeded |
Just to follow up on this: we can either change the copy in APM UI to be about "objects" instead of "index patterns", or vice versa for the Getting Started Guide. @gchaps what do you think? |
Current copy in Getting Started Guide for reference Button:
Potential warning:
|
💚 Build Succeeded |
@sqren I lean toward using "index patterns and dashboards" in the Getting Started because the APM use case is specific to those objects. There's room in the GS for that text and its close to what's in the UI. If you feel it needs to b an exact match, then just use "index patterns". |
@sqren and I agree: "Load Kibana index patterns and dashboards" |
// Button at bottom of page, move into view before clicking | ||
const loadBtn = await testSubjects.find('loadSavedObjects'); | ||
await remote.moveMouseTo(loadBtn); | ||
await testSubjects.click('loadSavedObjects'); |
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.
Have you tried just doing testSubjects.click
? The move logic is internal:
async click(selector, timeout = defaultFindTimeout) {
log.debug(`TestSubjects.click(${selector})`);
return await retry.try(async () => {
const element = await this.find(selector, timeout);
await remote.moveMouseTo(element);
await element.click();
});
}
await remote.moveMouseTo(loadBtn); | ||
await testSubjects.click('loadSavedObjects'); | ||
await retry.try(async () => { | ||
const successMsgExists = await await testSubjects.exists('loadSavedObjects_success'); |
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.
Two awaits
here. Also, have you tried just extending the timeout? You can do something like:
await testSubjects.exists('loadSavedObjects_success', 10000)
if the default timeout of 1 second is not long enough.
Another option is to just do await testSubjects.find('loadSavedObjects_success');
That should have an internal retry since it assumes it's supposed to exist:
async _ensureElementWithTimeout(timeout, getElementFunction) {
try {
const remoteWithTimeout = remote.setFindTimeout(timeout);
return await retry.try(async () => {
const element = await getElementFunction(remoteWithTimeout);
// Calling any method forces a staleness check
element.isEnabled();
return element;
});
} finally {
remote.setFindTimeout(defaultFindTimeout);
}
}
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.
nm, I see you already got rid of the second await in a follow up commit.
cc @spalger - I think this is a deficiency to having non-squashed commits. How do you keep up with all the changes from commit to commit to commit?
For instance I reviewed before this commit. I wanted to see the functional tests added so I went to that commit and added the comment. But the next commit fixed it. So what would your flow be? Start at commit x and review each one, x -> x + 1 -> x + 2 ... ? What if an x + n commit fixed something you commented on prior? Just go back and delete it?
Or do you review all commits from one commit onward but not the pull merges? Doesn't seem possible to do this in the git UI. It's either review all changes, or review commit by commit.
I signed in as a read only user and got a spinning button but no error message: If this is a legit bug, can you add tests for it? Think a jest test would cover it, if (as an aside, my license is expired, so it's possible that is causing an issue too... I'll check with a non expired license) |
test('should display error message when bulkCreate request fails', async () => { | ||
const bulkCreateMock = () => { | ||
return Promise.reject(new Error('simulated bulkRequest error')); | ||
}; |
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.
With "async/await everything" I've started to write:
const bulkCreateMock = async () => {
throw new Error('simulated bulkRequest error')
}
It is not much shorter but I've come to like it. Use it if you want :)
💚 Build Succeeded |
💚 Build Succeeded |
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.
LGTM.
Looked over the code. Only one real comment.
I loaded the Kibana objects, and it seemed to work fine.
|
||
const TITLE_KEY = 'xpack.apm.indexPattern'; | ||
const DEFAULT_TITLE = 'apm*'; | ||
function getIndexPatternTitle(config) { |
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.
Might be nice to just have a config.getOrDefault(TITLE_KEY, DEFAULT_TITLE)
method. Seems like I've seen this same conditional in lots of places, not just in this PR.
💚 Build Succeeded |
…tutorial (elastic#19559) * add savedObjects to tutorial schema, add savedObjects to APM, add bulk create endpoint * SavedObjectInstaller component * bulkCreate fixes * fix tutorial jest test * update from sqren review * updated copy * move saved object json into seperate json files * minor commit clean up * ensure isMounted before setting state after async call, allow manifest to customize saved object install message * remove duplicated logic for getting config xpack.apm.indexPattern * refactor get index pattern title * add functional test that loads APM saved objects * remove extra await * display overwrite message * use angular free savedObjectClient * functional test cleanup * handle bulkRequest exception and add jest tests for SavedObjectsInstaller * use Promise.reject instead of throw * update copy
Thanks for doing this @nreese !! |
…tutorial (#19559) (#20939) * add savedObjects to tutorial schema, add savedObjects to APM, add bulk create endpoint * SavedObjectInstaller component * bulkCreate fixes * fix tutorial jest test * update from sqren review * updated copy * move saved object json into seperate json files * minor commit clean up * ensure isMounted before setting state after async call, allow manifest to customize saved object install message * remove duplicated logic for getting config xpack.apm.indexPattern * refactor get index pattern title * add functional test that loads APM saved objects * remove extra await * display overwrite message * use angular free savedObjectClient * functional test cleanup * handle bulkRequest exception and add jest tests for SavedObjectsInstaller * use Promise.reject instead of throw * update copy
fixes #17803
cc @makwarth @formgeist