How to Mongoose send different value on find of specific field - javascript

I have a field which contain url to an image which is protected. It needs a secret to access file and the secret expire after a time. I want that when I do Mode.find() then the url value get replaced by anther url which contains the secret. So, that I don't have to manually every where I find from the model.
const schema = new Schema({
url:String
})
const Model = model('ModelName', Schema)
Saved url in database
url:"id_of_image.jpg"
Expected url when find
url:"/uploads/id_of_image.jpg?secret=xxxxxxxxxxxxxxxxx"

In this case, you can use virtuals. Something like:
const schema = new Schema({
url:String
}, {
// use these options to include virtual fields in response
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
schema.virtual('secretUrl').get(function() {
return this.url + ' ' + yourSecret;
});
Or if you want to replace your url field with secret url, you can use getters.

Related

Upsert data with a dynamic field name

I just try to do something simple with Mongo but it doesn't work:
I want to upsert datas in an object like: module.xxx.yyy then I tried many things like :
UsersRights.upsert({
condoId: condoId,
userId: manager._id,
}, {
condoId: condoId,
userId: manager._id,
module: {
[defaultRight.xxx] : {
[defaultRight.yyy] : defaultRight.default
}
}
});
but when I want to add a new xxx or a new yyy, it will erase and replace the entire module object and not only add a new key.
I also tried this :
UsersRights.upsert({
condoId: condoId,
userId: manager._id,
}, {
condoId: condoId,
userId: manager._id,
["module." + defaultRight.module + "." + defaultRight.right] : defaultRight.default,
});
but the server show me an error like: MinimongoError: Key module.xxx.yyy must not contain '.'
You need to use the following form:
YourCollection.upsert({
_id: id, (can be other selectors as well)
}, {
$set: setter
});
Setter is an object you create before and should have the following form:
const setter = {};
setter[`${#1Level}.${#2Level}`] = data;
Where #1Level & #2Level are vars naming the fields you want to modify or to add.

How to upload an image to AWS S3 using GraphQL?

I'm uploading a base64 string but the GraphQL gets hung. If I slice the string to less than 50,000 characters it works. After 50,000 characters, graphQL never makes it to the resolve function, yet does not give an error. On the smaller strings, it works just fine.
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
const imageArray = reader.result;
this.context.fetch('/graphql', {
body: JSON.stringify({
query: `mutation s3Upload($img: String!) {
s3Upload(file: $img) {
logo,
}
}`,
variables: {
img: imageArray,
},
}),
}).then(response => response.json())
.then(({ data }) => {
console.log(data);
});
}
const s3Upload = {
type: S3Type,
args: {
file: { type: new NonNull(StringType) },
},
resolve: (root, args, { user }) => upload(root, args, user),
};
const S3Type = new ObjectType({
name: 'S3',
fields: {
logo: { type: StringType },
},
});
The correct approach here is to perform an actual S3 upload via a complex type using AWS AppSync - what you illustrate here looks more like you are attempting to save a base64 encoded image as a string to a field in what I can only assume to be a DynamoDB table entry. For this to work, though, you need to modify your mutation such that the file field is not a String!, but an S3ObjectInput.
There's a few moving parts under the hood you need to make sure you have in place before this "just works" (TM). First of all, you need to make sure you have an appropriate input and type for an S3 object defined in your GraphQL schema
enum Visibility {
public
private
}
input S3ObjectInput {
bucket: String!
region: String!
localUri: String
visibility: Visibility
key: String
mimeType: String
}
type S3Object {
bucket: String!
region: String!
key: String!
}
The S3ObjectInput type, of course, is for use when uploading a new file - either by way of creating or updating a model within which said S3 object metadata is embedded. It can be handled in the request resolver of a mutation via the following:
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id),
},
#set( $attribs = $util.dynamodb.toMapValues($ctx.args.input) )
#set( $file = $ctx.args.input.file )
#set( $attribs.file = $util.dynamodb.toS3Object($file.key, $file.bucket, $file.region, $file.version) )
"attributeValues": $util.toJson($attribs)
}
This is making the assumption that the S3 file object is a child field of a model attached to a DynamoDB datasource. Note that the call to $utils.dynamodb.toS3Object() sets up the complex S3 object file, which is a field of the model with a type of S3ObjectInput. Setting up the request resolver in this way handles the upload of a file to S3 (when all the credentials are set up correctly - we'll touch on that in a moment), but it doesn't address how to get the S3Object back. This is where a field level resolver attached to a local datasource becomes necessary. In essence, you need to create a local datasource in AppSync and connect it to the model's file field in the schema with the following request and response resolvers:
## Request Resolver ##
{
"version": "2017-02-28",
"payload": {}
}
## Response Resolver ##
$util.toJson($util.dynamodb.fromS3ObjectJson($context.source.file))
This resolver simply tells AppSync that we want to take the JSON string that is stored in DynamoDB for the file field of the model and parse it into an S3Object - this way, when you do a query of the model, instead of returning the string stored in the file field, you get an object containing the bucket, region, and key properties that you can use to build a URL to access the S3 Object (either directly via S3 or using a CDN - that's really dependent on your configuration).
Do make sure you have credentials set up for complex objects, however (told you I'd get back to this). I'll use a React example to illustrate this - when defining your AppSync parameters (endpoint, auth, etc.), there is an additional property called complexObjectCredentials that needs to be defined to tell the client what AWS credentials to use to handle S3 uploads, e.g.:
const client = new AWSAppSyncClient({
url: AppSync.graphqlEndpoint,
region: AppSync.region,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: () => Auth.currentCredentials()
},
complexObjectsCredentials: () => Auth.currentCredentials(),
});
Assuming all of these things are in place, S3 uploads and downloads via AppSync should work.
AWS AppSync (https://aws.amazon.com/appsync/) provides this with functionality known as "Complex Objects" where you can have a types for the S3 Object and the input:
type S3Object {
bucket: String!
key: String!
region: String!
}
input S3ObjectInput {
bucket: String!
key: String!
region: String!
localUri: String
mimeType: String
}
You could then do something like this to define this object as part of another type:
type UserProfile {
id: ID!
name: String
file: S3Object
}
And then specify a mutation to add it:
type Mutation {
addUser(id: ID! name: String file: S3ObjectInput): UserProfile!
}
Your client operations would need to specify the appropriate bucket, key (with file extension), region, etc.
More here: https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-react.html#complex-objects

Using "#" in key when inserting to MongoDB with mongoose

Mongoose schema won't let me use # sign in key when inserting to MongoDB with using Node.js. For instance:
var blogSchema = new Schema({
#context : Object //error illegal token
#id : String // illegal token
}, {strict: false});
I tried key with unicode characters like this one:
"\u0040context" = Object // ignored unicode, inserted as context
"\x40context" = Object // ignored unicode, inserted as context
\x40context = Object // illegal token
Also tried with normal way using this link(first way), still cannot define key with #:
http://blog.modulus.io/mongodb-tutorial
My purpose is to create document with using JSON-LD format which requires of using # symbol in key. How to accomplish this? Here are the similar links I have looked for solution:
variable with mongodb dotnotation
Syntax error Unexpected token ILLEGAL Mongo Console
How to use mongoose model schema with dynamic keys?
How to do a query using dot( . ) through Mongoose in Node.js and How to add an empty array
Create a Schema object in Mongoose/Handlebars with custom keys/values
http://mongoosejs.com/docs/schematypes.html
You can use directly # between quotes such as "#field" :
"use strict";
var mongoose = require('./node_modules/mongoose');
var Schema = mongoose.Schema;
var db = mongoose.connection;
var itemSchema = new Schema({
"#field": {
type: String,
required: true
},
"#field2": {
type: String,
required: true
}
});
var Items = mongoose.model('Items', itemSchema);
var db = mongoose.connect('localhost', 'testDB');
Items.create({ "#field": "value", "#field2": "value" }, function(err, doc) {
console.log("created");
if (err)
console.log(err);
else {
console.log(doc);
}
db.disconnect();
});

MeanJS - Adding ArticleID to Comment Mongo Collection

I have 2 Collections of article and comment and i would like to add the articleID to the Comment Collection when creating a new comment. The same way in which the UserID automatically goes in.
comment.client.controller.js
// Create new Comment
$scope.create = function() {
// Create new Comment object
var comment = new Comments ({
details: this.details,
status: this.status,
created: this.created,
});
// Redirect after save
comment.$save(function(response) {
$location.path('comments/' + response._id);
// Clear form fields
$scope.status = '';
$scope.details = '';
$scope.created = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
comment.server.model.js
var CommentSchema = new Schema({
userName: {
type: String,
default: 'To Do',
required: 'Please fill Comment name',
trim: true
},
created: {
type: Date,
default: Date.now
},
details: {
type: String,
trim: true
}
,
user: {
type: Schema.ObjectId,
ref: 'User'
},
article: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Article'
}
});
Essentially what i need to know is where does the ObJectID for the User come from without having user : this.user or similar like you need for details, status and created. How can i get the same for Article to just automatically include the Article ObjectID?
The id will come from the route itself or if you intentionally send it inside the POST request body.
Example with article id in route:
If a user posts a comment to an article (with id 123456789) it will make a POST request to /api/articles/123456789/posts then in the backend of the app you will have the endpoint defined as follows:
app.route('/api/articles/:articleId/posts').post(article.postComment);
Note the :articleId in the route, now what you can do is bind that route param to use in your server controllers like so:
app.param('articleId', article.articleById);
articleById is the middleware that will use mongoose to find the article. articleById is defined in the article server controllers file.
Then in your postComment controller you can access the id using req.article.
If you take a look at the server routes for the articles module in meanjs, you will notice that it was already done, so you can probably use the req.article to access the article id.

MongoDB _id as a String (MEAN Stack)

TL;DR - I am trying to use a collected value from a form input as a document _id but am getting a 404.
I've got a modal that opens and collects form data. My first input in the form is:
<input type="text" id="name" name="name" data-ng-model="name" />
When I try to modify the Mongo (Mongoose) model, to use name as the _id, the form wont post. I get a 404 from http://sitegoeshere/#!/somethings/whatever_i_type_in_for_name
Example model:
var SomethingSchema = new Schema({
_id: {
type: String,
default: 'default',
trim: true
}
}
mongoose.model('Something', SomethingSchema);
And in my Angular controller:
$scope.create = function() {
// Create new Something object
var something = new Somethings ({
_id: this.name
});
// Redirect after save
something.$save(function(response) {
$location.path('somethings/' + response._id);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
I've been told that MongoDB allows Strings as the _id type so what gives? Any ideas?
UPDATE: Here's something strange, too. I wanted to see if maybe this was a limitation or bug of Mongoose so I got into the database and created two documents:
> db.subnets.find().pretty()
{ "_id" : ObjectId("546bef63395b0694d51b5cbe"), "description" : "description!" }
{ "_id" : "mystring", "description" : "more description!" }
When I go to my app and try to pull their individual views up, I can see the data for my custom _id document but get a 500 Internal Server Error when I try to access the other.
GET http://localhost:3000/somethings/546bef63395b0694d51b5cbe 500 (Internal Server Error)
GET http://localhost:3000/somethings/mystring 200 OK
The problem is most likely with this.name - looks like it's undefined.

Categories