-
Notifications
You must be signed in to change notification settings - Fork 69
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
Document labels: use fields other than _id wherever possible #569
Conversation
@@ -33,7 +34,9 @@ export class DocDBDocumentTreeItem implements IAzureTreeItem { | |||
} | |||
|
|||
public get label(): string { | |||
return this.document.id; | |||
const presentFields = documentDefaultFields.filter(element => this._document.hasOwnProperty(element)); | |||
const canonicalField = presentFields.find((element) => this._document[element] && this._document[element].toString() && !this._document[element].toString().startsWith("[object")); |
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 three conditions in the find :
- Removes values being false-y (null, undefined)
- Removes cases where the toString is blank ""
- Removes cases where
this._document[element]
is an Object which does not have it's own implementation. In such cases, the toString value returned is one of[object Object]
,[object null]
,[object undefined]
etc.
A simple type check does not work because typeof <ObjectID>
is object
¯\_(ツ)_/¯
src/constants.ts
Outdated
@@ -32,3 +32,5 @@ var isAccepted = collection.queryDocuments( | |||
|
|||
if (!isAccepted) throw new Error('The query was not accepted by the server.'); | |||
};` | |||
|
|||
export const documentDefaultFields = ["name", "Name", "ID", "UUID", "id", "_id"]; |
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.
Are the DBs case-sensitive?
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.
Yes
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.
Add "Id"?
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.
?
@@ -33,7 +34,9 @@ export class DocDBDocumentTreeItem implements IAzureTreeItem { | |||
} | |||
|
|||
public get label(): string { | |||
return this.document.id; | |||
const presentFields = documentDefaultFields.filter(element => this._document.hasOwnProperty(element)); | |||
const canonicalField = presentFields.find((element) => this._document[element] && this._document[element].toString() && !this._document[element].toString().startsWith("[object")); |
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.
typeof == "string"
@@ -33,7 +34,9 @@ export class DocDBDocumentTreeItem implements IAzureTreeItem { | |||
} | |||
|
|||
public get label(): string { | |||
return this.document.id; | |||
const presentFields = documentDefaultFields.filter(element => this._document.hasOwnProperty(element)); | |||
const canonicalField = presentFields.find((element) => this._document[element] && this._document[element].toString() && !this._document[element].toString().startsWith("[object")); |
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.
multiple calls to toString()
@@ -18,24 +19,22 @@ export class DocDBDocumentTreeItem implements IAzureTreeItem { | |||
public readonly commandId: string = 'cosmosDB.openDocument'; | |||
|
|||
public readonly partitionKeyValue: string | undefined; | |||
public label: string; |
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.
private _label:string;
public get label() {
return this._label;
etc.
ditto for next class
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 update operation for the collection view in Mongo would have the side-effect of updating a document's label too. In order for that to work, the label field needs a setter. After the offline discussion, it looks like a private+getter+setter is preferred as opposed to public.
src/constants.ts
Outdated
@@ -32,3 +32,5 @@ var isAccepted = collection.queryDocuments( | |||
|
|||
if (!isAccepted) throw new Error('The query was not accepted by the server.'); | |||
};` | |||
|
|||
export const documentDefaultFields = ["name", "Name", "ID", "UUID", "id", "_id"]; |
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.
documentLabelFields?
@@ -87,6 +86,7 @@ export class DocDBDocumentTreeItem implements IAzureTreeItem { | |||
} | |||
}); | |||
}); | |||
this.label = getDocumentTreeItemLabel(this._document); |
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.
don't you have to call refreshLabel or something?
src/utils/vscodeUtils.ts
Outdated
export function getDocumentTreeItemLabel(document: IMongoDocument | RetrievedDocument): string { | ||
for (let field of documentDefaultFields) { | ||
if (document.hasOwnProperty(field)) { | ||
if (document[field]) { //ignore if false-y value: null, undefined, "". |
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.
let value = document[field];
if (value) ...
Slightly better perf, but more importantly, less chance of mistakes or botched bug fixes.
src/utils/vscodeUtils.ts
Outdated
} else { // A simple instanceOf check doesn't seem to work. I run into this issue detailed here : | ||
//https://libertyseeds.ca/2015/01/03/Mongo-ObjectID-fails-instanceof-type-test-in-nodeunit/ | ||
const result = document[field].toString(); | ||
if (!result.startsWith("[object ")) { |
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.
Still not 100% convinced, but I guess we'll see if we hit any issues.
What's the status of this? |
@@ -24,7 +24,9 @@ export class DocDBDocumentNodeEditor implements ICosmosEditor<RetrievedDocument> | |||
} | |||
|
|||
public async update(document: RetrievedDocument): Promise<RetrievedDocument> { | |||
return await this._documentNode.treeItem.update(document); | |||
const updatedDoc = await this._documentNode.treeItem.update(document); | |||
this._documentNode.refresh(); |
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.
Do you need to wait for the promise?
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 say you should unless there's a reason you specifically think we shouldn't.
@@ -52,7 +55,7 @@ export class DocDBDocumentTreeItem implements IAzureTreeItem { | |||
} | |||
|
|||
public async deleteTreeItem(_node: IAzureNode): Promise<void> { | |||
const message: string = `Are you sure you want to delete document '${this.label}'?`; | |||
const message: string = `Are you sure you want to delete document '${this._label}'?`; |
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: Since you have a reader for "label" you should probably still use "label" here.
@@ -38,6 +38,8 @@ export class MongoCollectionNodeEditor implements ICosmosEditor<IMongoDocument[] | |||
const documentNode = documentNodes.find((node) => node.treeItem.document._id.toString() === updatedDoc._id.toString()); | |||
if (documentNode) { | |||
documentNode.treeItem.document = updatedDoc; | |||
documentNode.treeItem.label = getDocumentTreeItemLabel(documentNode.treeItem.document); |
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 the right thing to do here is:
- put the logic to set _label in refreshLabel()
- call documentNode.refresh() whenever you want the label to get refreshed
} | ||
|
||
public get id(): string { | ||
return this.document.id; | ||
} | ||
|
||
public get label(): string { | ||
return this.document.id; | ||
public async refreshLabel() { |
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.
return value
no need to be async
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.
Good observation.
However, IAzureTreeItem asks for a refreshLabel()
that returns a Promise<void>
Not having it async or a return type of void
instead of Promise<void>
gives type errors.
This is also the case in the MongoDocTreeItem class.
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'll add the return type on the function header to make it clearer. :)
src/utils/vscodeUtils.ts
Outdated
for (let field of documentLabelFields) { | ||
if (document.hasOwnProperty(field)) { | ||
let value = document[field]; | ||
if (value) { //ignore if false-y value: null, undefined, "". |
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 will also ignore zero, which I don't think would be desirable.
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.
Good catch.
Looking at the other falsy values, I think we'll be covered after acconting for 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.
In this case I'd suggest the following:
I'd suggest this:
(note: you're effectively checking that it's not an object with your value.toString().startsWith("[object ")) below. I'm not sure this idea behavior when the property to display is an object or array, but it seems reasonable to skip it and try something else. But typeof is a lot better than the toString.startsWith()
// If doesn't have value, or is null, ObjectId or other object type, try a different property
if (typeof value !== 'undefined' && typeof value !== 'object') {
return String(value);
}
I think that handles it all, right?
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.
Yeah. It relies on null having type object (slightly obfuscated). If that's well known, then I'll go ahead with 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.
Unfortunately, typeof null = "object" in JS. Well-known "bug" that can't be fixed now.
src/utils/vscodeUtils.ts
Outdated
} | ||
} | ||
} | ||
return document["_id"].toString(); |
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.
Can _id be undefined or null?
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.
Yes. In case of SQL documents, the id
field should be present.
I have both these fields in the documentLabelFields, so they should be covered in the loop. What would a good default return be to ensure type-safety?
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 leave it as _id, since we'll want to change documentLabelFields to be user-settable anyway. Looks like previous code had return document._id.toString(). Still, I'd rather be conservative and not assume this won't give a null ref exception.
I think that String(document._id) is safe for null/undefined? Can you verify? Thx.
Have you reviewed this yourself and looked for issues I might find? There's at least one more issue I haven't mentioned. Thanks. |
@@ -25,7 +25,9 @@ export class MongoDocumentNodeEditor implements ICosmosEditor<IMongoDocument> { | |||
} | |||
|
|||
public async update(document: IMongoDocument): Promise<IMongoDocument> { | |||
return await this._documentNode.treeItem.update(document); | |||
const updatedDoc = await this._documentNode.treeItem.update(document); | |||
this._documentNode.refresh(); |
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.
Forgot to await this.
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.
Good catch
if (node) { | ||
return (<IAzureNode<MongoDocumentTreeItem>>node).treeItem.update(newDocument); | ||
result = await (<IAzureNode<MongoDocumentTreeItem>>node).treeItem.update(newDocument); |
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 issue I saw. Why was the return removed here?
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.
My bad. We need to kick off the refresh before returning, but after updating the value.
} else { | ||
// If the node isn't cached already, just update it to Mongo directly (without worrying about updating the tree) | ||
const db = await this._databaseNode.treeItem.getDb(); | ||
result = await MongoDocumentTreeItem.update(db.collection(this._collectionName), newDocument); |
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.
Felt this control flow looked clearer. If you prefer a return statement after each block (functionally the same), I'll do that.
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'm fine with either one, really depends on the rest of the code as to which is easier to read/maintain (e.g. how many control flows, how big the then case is, etc.)
src/constants.ts
Outdated
@@ -34,3 +34,5 @@ export const defaultStoredProcedure = | |||
};` ; | |||
|
|||
export const emptyPartitionKeyValue = {}; | |||
|
|||
export const documentLabelFields = ["name", "Name", "ID", "UUID", "id", "_id"]; |
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.
Please add "Id", "uuid"
src/utils/vscodeUtils.ts
Outdated
for (let field of documentLabelFields) { | ||
if (document.hasOwnProperty(field)) { | ||
let value = document[field]; | ||
if (value || value === 0) { //ignore if false-y value: null, undefined, "". |
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 were supposed to remove this line:
if (value || value === 0)
src/utils/vscodeUtils.ts
Outdated
if (document.hasOwnProperty(field)) { | ||
let value = document[field]; | ||
if (value || value === 0) { //ignore if false-y value: null, undefined, "". | ||
if (value != 'undefined' && typeof value !== 'object') { |
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.
typeof value
@PrashanthCorp CLA will pass if you push a new commit |
Ping. |
Fixes #381
Fixes #145