Retrieving comment(s) on an article - Conduit Project - Thinkster - node.js

https://thinkster.io/tutorials/node-json-api/adding-comments-to-articles > Section Retrieving comment(s) on an article.
Environment
Thinkster Conduit Project: Angular<->API(node.js)<->MongoDB
npm(1) (JavaScript): 6.9.0
mongoose : ^4.4.10 (Compass Community Version)
Goal
Store a comment on a (own written) article and retrieve this comment.
Note
To get the local API (for this purpose I used an exact copy of the production ready API supplied by Thinkster on GitHub) working, in particular: posting a comment on an own written article, I had to change in;
Model: User.js UserSchema (2x) >>> .push(id); => .concat([id]);
Route: routes/api/articles.js router.post('/:article/comments', ...) >>> .push(comment); => .concat([comment]);
These changes had something to do with the drop of 'usePushEach' (versions ago). Nothing is mentioned about it in the current supplied documents on thinkster.
The change on the posting route is apparently important: concatenation of comment-objects onto the article when posting a comment, but it works (see pictures).
Passes
Storage of a comment on a own written article (checked and tested
with Postman & Mongo)
Remove of the comment (checked and tested with Postman & Mongo)
Problem!
Can NOT retrieve, by all means, comment(s) on a own written article (as a list [GET-route]). Somehow the 'comment'-objects are correctly mapped back to the article.
Question: How to solve this? Is this under construction? How to do a callback of the function in which the 'comment' (as argument) has to be mapped back to the article? This argument 'comment' seems to be a empty object, see picture 5: line 356: comment.toJSONFor(user) ???,
In addition, I noticed, on the conduit client talking to the remote 'production-ready' api, own written articles ar not listed as own feed, however they do appear in de global list.
1) Storage of a comment on a own writer article works fine
2) Mongo Db:
3) Retrieve (.get) comments by Postman:
4) Response on the terminal:
5) Section of the Json:

Solution
The problem occurred by two pitfalls:
The promise 'comment' does not get aborted
It is not possible to 'push' directly into the 'req.article.comments' array property
Fixes
In the original code (GitHub) the .catch(next) is fit on the User [Document], this
stopper needs to sit on the promise stream 'comment' instead.
'Old fashion' JavaScript is needed to keep track of existing comment ids, load them separately into a new Array and push the new comment id onto it (so far pushing is allowed). Also the very first comment id on a article needs to be stored in an array type variable.
In both cases a new created array is allowed to map (=) onto the 'req.article.comment'-array property; pushing on it causes an $pushAll error and .concat(...) does not work either.

...better Solution
Circumscribe the sync post-route into an asynchronous post-route
But keep fix 2]1

Related

Resource Conflict after syncing with PouchDB

I am new to CouchDB / PouchDB and until now I somehow could manage the start of it all. I am using the couchdb-python library to send initial values to my CouchDB before I start the development of the actual application. Here I have one database with templates of the data I want to include and the actual database of all the data I will use in the application.
couch = couchdb.Server()
templates = couch['templates']
couch.delete('data')
data = couch.create('data')
In Python I have a loop in which I send one value after another to CouchDB:
value = templates['Template01']
value.update({ '_id' : 'Some ID' })
value.update({'Other Attribute': 'Some Value'})
...
data.save(value)
It was working fine the whole time, I needed to run this several times as my data had to be adjusted. After I was satisfied with the results I started to create my application in Javascript. Now I synced PouchDB with the data database and it was also working. However, I found out that I needed to change something in the Python code, so I ran the first python script again, but now I get this error:
couchdb.http.ResourceConflict: (u'conflict', u'Document update conflict.')
I tried to destroy() the pouchDB database data and delete the CouchDB database as well. But I still get this error at this part of the code:
data.save(value)
What I also don't understand is, that a few values are actually passed to the database before this error comes. So some values are saved() into the db.
I read it has something to do with the _rev values of the documents, but I cannot get an answer. Hope someone can help here.

DocumentDB Replace not Working

I recently realized that DocumentDB supports stand alone update operations via ReplaceDocumentAsync.
I've replaced the Upsert operation below with the Replace operation.
var result = _client
.UpsertDocumentAsync(_collectionUri, docObject)
.Result;
So this is now:
var result = _client
.ReplaceDocumentAsnyc(_collectionUri, docObject)
.Result;
However, now I get the exception:
Microsoft.Azure.Documents.BadRequestException : ResourceType Document is unexpected.
ActivityId: b1b2fd71-3029-4d0d-bd5d-87d8d0a2fc95
No idea why, upsert and replace are of the same vein and the object is the same that worked for upsert, so I would expect it to work without problems.
All help appreciated.
Thanks
Update: Have tried to implement this using the SelfLink approach, and it works for Replace, but selflink does not work with Upsert. The behavior is quite confusing. I don't like that I have to build a self link in code using string concatenation.
I'm afraid that building the selflink with string concatenation is your only option here because ReplaceDocument(...) requires a link to the document. You show a link to the collection in your example. It won't suck the id out and find the document as you might wish.
The NPM module, documentdb-utils, has library functions for building these links but it's just using string concatenation. I have seen an equivalent library for .NET but I can't remember where. Maybe it was in an Azure example or even in the SDK now.
You can build a document link for a replace using the UriFactory helper class:
var result = _client
.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, docObject.Id), docObject)
.Result;
Unfortunately it's not very intuitive, as Larry has already pointed out, but a replace expects a document to already be there, while an upsert is what it says on the tin. Two different use-cases, I would say.
In order to update a document, you need to provide the Collection Uri. If you provide the Document Uri it returns the following:
ResourceType Document is unexpected.
Maybe the _collectionUri is a Document Uri, the assignment should look like this:
_collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName);

ScriptError using Google Apps Script Execution API

Following these guides https://developers.google.com/apps-script/guides/rest/quickstart/target-script and https://developers.google.com/apps-script/guides/rest/quickstart/nodejs, I am trying to use the Execution API in node to return some data that are in a Google Spreadsheet.
I have set the script ID to be the Project Key of the Apps Script file. I have also verified that running the function in the Script Editor works successfully.
However, when running the script locally with node, I get this error:
The API returned an error: Error: ScriptError
I have also made sure the script is associated with the project that I use to auth with Google APIs as well.
Does anyone have any suggestion on what I can do to debug/ fix this issue? The error is so generic that I am not sure where to look.
UPDATE: I've included a copy of the code in this JSBin (the year function is the entry point)
https://jsbin.com/zanefitasi/edit?js
UPDATE 2: The error seems to be caused by the inclusion of this line
var spreadsheet = SpreadsheetApp.open(DriveApp.getFileById(docID));
It seems that I didn't request the right scopes. The nodejs example include 'https://www.googleapis.com/auth/drive', but I also needed to include 'https://www.googleapis.com/auth/spreadsheets' in the SCOPES array. It seems like the error message ScriptError is not very informative here.
In order to find what scopes you'd need, to go the Script Editor > File > Project Properties > Scopes. Remember to delete the old credentials ~/.credentials/old-credential.json so that the script will request a new one.
EDIT: With the update in information I took a closer look and saw you are returning a non-basic type. Specifically you are returning a Sheet Object.
The basic types in Apps Script are similar to the basic types in
JavaScript: strings, arrays, objects, numbers and booleans. The
Execution API can only take and return values corresponding to these
basic types -- more complex Apps Script objects (like a Document or
Sheet) cannot be passed by the API.
https://developers.google.com/apps-script/guides/rest/api
In your Account "Class"
this.report = spreadsheet.getSheetByName(data.reportSheet);
old answer:
'data.business_exp' will be null in this context. You need to load the data from somewhere. Every time a script is called a new instance of the script is created. At the end of execution chain it will be destroyed. Any data stored as global objects will be lost. You need to save that data to a permanent location such as the script/user properties, and reloaded on each script execution.
https://developers.google.com/apps-script/reference/properties/

Grocery crud : How to get the current id that had been used when editing and viewing a record?

How to get the current id that had been used when editing and viewing a record?
I am currently following the instruction by Alfonso Secas that he posted in this topic..
http://www.grocerycrud.com/forums/topic/1326-how-to-use-grocery-crud-and-image-crud-together/page-2
And now on the part where he said "When editing a record, get the current ID from $this->getStateInfo(); to compose the iframe's source url."
It seems like getStateInfo doesn't exist yet so it shows an error "Fatal error: Call to undefined method Examples::getStateInfo() "
http://www.grocerycrud.com/documentation/options_functions/getState
this will do what u want . give it a try
u need to use the CRUD object to call this function instead of $this ..
You have some options:
Primary key
getStateInfo()->get_primary_key
Last uri segment
end($this->uri->segments)
Does not work in list action.
As a workaround you can use one of CodeIgniter's methods to get id from page url
https://www.codeigniter.com/userguide3/libraries/uri.html
You need to take last segment from URI
$segmentsCount = $this->uri->total_segments();
$itemID = intval($this->uri->segment($segmentsCount));
p.s. it is not best solution but it can do the trick

does mongoose have an isDirty check?

I have a mongoose setup which involves an embedded-schema, lets say: A Blogpost with embedded comments. Comments can be edited by the original publisher as well as by an editor/admin. After adding / editing a comment the entire blogpost is saved.
I have some custom mongoose's 'pre' middleware set up on the embedded comment-schema which automatically sets the lasteditdate for that particular comment.
The thing is that 'pre' is called on EVERY comment on the blogpost, since I call save() on the blogpost. (For other reasons I need to do it like this) . Therefore, I need a way to check which comments have changed (or are new) since they were last saved (as part of the Blogpost overall save())
The questio: how to check in 'pre' whether a comment has changed or not? Obviously calling this.isNew isn't sufficient, since comments could be edited (i.e: aren't new) as well.
Is there any isDirty or similar that I'm overlooking?
For version 3.x
if(doc.isModified()){
// do stuff
}
In Mongoose you can use the Document method isModified(#STRING).
The most recent documentation for the method can be found here.
So to check a specific property with doc.isModified you can do this:
doc.comments[4].message = "Hi, I've made an edit to my post";
// inside pre hook
if ( this.isModified('comments') ) {
// do something
}
If you want to check a specific comment you can do that with the following notation this.isModified('comments.0.message')
Since the argument takes a string if you needed to know specifically which comment was modified you could loop through each comment and run this.isModified('comments['+i+']message')
You may use the modified getter:
if (doc.modified) {
// :)
}
This may be relevant to Mongoose users circa mid-2020 who are seeing this error:
"errorType": "TypeError",
"errorMessage": "Cannot set property 'isDirty' of null",
Upgrade to the latest version of Mongoose to fix it.
https://github.com/Automattic/mongoose/issues/8719

Resources