How to Wrap a REST API POST nested object with GraphQL - javascript

I am trying to figure out how to wrap mutate a nested object with graphql mutations, if possible. For instance I have the following schema:
type Equipment{
assetId: ID
assetName: String
assetTag: String
createdAt: String
updatedAt: String
available: Boolean
category: Category
purchaseAt: String
purchaseCost: Float
serial: String
notes: String
}
type Category{
categoryId: ID,
name: String
}
type Mutation {
equipamento( e: EquipmentInput): Equipment
}
input EquipmentInput{
assetName: String
assetTag: String
available: Boolean
purchaseAt: String
category: CategoryInput
purchaseCost: Float
serial: String
notes: String
}
input CategoryInput{
categoryId: ID
name : String
}
This is the POST call on REST. This call works
{
"assetName": "Dell Laptop E5570",
"assetTag": "A0001",
"available": false,
"category":{
"categoryId": 1
} ,
"purchaseAt": "2018-09-10",
"purchaseCost": 1200.00,
"serial": "Serial",
"notes": "Novo"
}
I am trying to do a equivalent on GraphQL using graphql-yoga, but category is a nested object and the create call fail on REST service.
equipamento: (parent, args) => {
const equipment = {
assetName: args.e.assetName,
assetTag: args.e.assetTag,
available: args.e.available,
category: args.e.category.categoryId,
purchaseAt: args.e.purchaseAt,
purchaseCost: args.e.purchaseCost,
serial: args.e.serial,
notes: args.e.notes
}
return fetch(`${equipmentURL}/`, {
method: 'POST',
body: JSON.stringify(equipment),
headers: { 'Content-Type': 'application/json' }
}).then(res => res.json());
}```
Console Log for the GraphQL is
```{"assetName":"Dell Laptop E5570","assetTag":"A0001","available":false,"category":1,"purchaseAt":"2018-09-10","purchaseCost":1200,"serial":"Serial","notes":"Novo"}```
So the question is, how can i format my mutation to have nested object?

Related

How to get an array element in the same index as the query in mongoose

I have the following Schema:
const PublicationSchema = mongoose.Schema({
title: {
type: String,
required: true
},
files:[{
contentType: String,
data: Buffer,
name: String
}]
})
What I'm trying to do is to get the file with the same index as the query.For example I have this object:
_id: new ObjectId("637f20ce6ce5c48d9788a1ff"),
title: 'TEST',
files: [
{
contentType: 'application/pdf',
name: 'imId1',
_id: new ObjectId("id1")
},
{
contentType: 'application/pdf',
name: 'imId2',
_id: new ObjectId("id2")
}
]
where if I query id2 it only retrieves:
{
contentType: 'application/pdf',
name: 'imId2',
_id: new ObjectId("id2")
}
What I was trying to use was const onePublication = await Publication.findOne({ "files._id": req.body.fileId},{}) but this retrieves every field.
I was going to just tell it to not retrieve the other field using field:0 but I realized that this will still retrieve the files in other indexes of the field.
Is there a way to tell it to only retrieve the one with the same index or should I be using another query entirely?
One of the options is to $unwind the array first. $match by your criteria. Then, $replaceRoot to get your array entry.
db.collection.aggregate([
{
"$unwind": "$files"
},
{
$match: {
"files._id": "id2"
}
},
{
"$replaceRoot": {
"newRoot": "$files"
}
}
])
Mongo Playground
Consider changing your schema to store files as an individual collection, if most of the time you are going to access the array objects only.

Query all collection documents and select data to query specific documents from another collection using Fauna and FQL

I have a use case where I would like to query all connections from the connections collection, and at the same time find the right document using employerId and employeeId from their respective collections (employer and employee collection) using Fauna DB. So, if I get an employerId back from the query, I would like to get the data back from the document within the employer collection.
A document from the connections collection looks like this:
{
"ref": Ref(Collection("connections"), "336420921043583169"),
"ts": 1657094868435000,
"data": {
"employerId": "330616765804445892",
"employeeId": "330616700633350340",
"isEmployeeApproved": true,
"isEmployerApproved": true,
"connectionAcceptedOnDate": "2022-07-06T08:07:47.846Z"
}
}
I tried to create my function as the following, where I'm getting the data from the connectionscollection but I do not get anything from the other collections mentioned using the employerId and employeeId:
const getConnections = async () => {
return await faunaClient.query(
q.Map(
q.Paginate(q.Match(q.Index("connections"))),
q.Lambda("connectionDoc", q.Get(q.Var("connectionDoc")))
),
q.Ref(Collection("employer"), q.Select("employerId", q.Var("connectionDoc"))),
q.Ref(
Collection("employees"),
q.Select("employeeId", q.Var("connectionDoc"))
),
q.Get(q.Var("employerDoc")),
q.Get(q.Var("employeeDoc"))
)
}
The ref I'm getting seems to be the first document within the connections collection. This is what I'm getting if I console log it:
data: Array [ {…}, {…} ]
​​
0: Object { ref: {…}, ts: 1656490913630000, data: {…} }
​​​
data: Object { employeeId: "330074643026149574", employerId: "331454006483222721", isEmployeeApproved: true, … }
​​​
ref: Object { "#ref": {…} }
​​​​
"#ref": Object { id: "335787295330271425", collection: {…} }
​​​​​
collection: Object { "#ref": {…} }
​​​​​​
"#ref": Object { id: "connections", collection: {…} }
​​​​​​​
collection: Object { "#ref": {…} }
​​​​​​​​
"#ref": Object { id: "collections" }
​​​​​​​​​
id: "collections"
​​​​​​​​​
<prototype>: Object { … }
​​​​​​​​
<prototype>: Object { … }
​​​​​​​
id: "connection_request"
​​​​​​​
<prototype>: Object { … }
​​​​​​
<prototype>: Object { … }
​​​​​
id: "335787295330271425"
​​​​​
<prototype>: Object { … }
​​​​
<prototype>: Object { … }
​​​
ts: 1656490913630000
​​​
<prototype>: Object { … }
How can I select the employerId and look up the right document from the employer collection? I would like to do the same for the employeeId and the employee collection in the same query.
Updates
A suggestion has been made:
const connections = async () => {
return await faunaClient.query(
q.Map(q.Paginate(q.Documents(Collection("connections")))),
q.Lambda(
"ref",
q.Let(
{
ref: q.Ref(Collection("connections"), id),
connectionDoc: q.Get(q.Var("ref")),
employerId: q.Select(["data", "employerId"], q.Var("connectionDoc")),
employeeId: q.Select(
["data", "employeeId"],
q.Var("connectionDoc")
),
employerRef: q.Ref(Collection("employers"), q.Var("employerId")),
employeeRef: q.Ref(
Collection("employees"),
q.Var("employeeId")
),
employerDoc: q.Get(q.Var("employerRef")),
employeeDoc: q.Get(q.Var("employeeRef")),
},
{
ref: q.Var("ref"),
ts: q.Select(["ts"], q.Var("connectionDoc")),
data: {
employee: q.Var("employeeDoc"),
employer: q.Var("employerDoc"),
isEmployeeApproved: q.Select(
["data", "isEmployeeApproved"],
q.Var("connectionDoc")
),
isEmployerApproved: q.Select(
["data", "isEmployerApproved"],
q.Var("connectionDoc")
),
connectionAcceptedOnDate: q.Select(
["data", "connectionAcceptedOnDate"],
q.Var("connectionDoc")
),
},
}
)
)
)
}
I receive the following error message when trying to use the function above:
InvalidArity: Map function requires 2 argument(s) but 1 were given
For more info, see the docs: https://docs.fauna.com/fauna/current/api/fql/functions/map
Your query is malformed: the connectionDoc variable is only valid within the Lambda function, yet you depend on it outside of the Lambda in multiple locations.
Also, you appear to store only the document ID of the connection document's employerId and employeeId fields, which requires you to reconstruct references every time you want to use those values.
Before talking about performing the desired lookups in bulk, let's look at performing the lookup for a single connection document.
When you need to fetch a document and the details of related documents, Let is your friend: it allows you to capture intermediate values, and compose an appropriate result. For example, to work with your example connection document, we could do this:
Let(
{
ref: Ref(Collection("connections"), "336420921043583169"),
connectionDoc: Get(Var("ref"))
},
Var("connectionDoc")
)
When this query is run, the result is:
{
ref: Ref(Collection("connections"), "336420921043583169"),
ts: 1657556091310000,
data: {
employerId: '330616765804445892',
employeeId: '330616700633350340',
isEmployeeApproved: true,
isEmployerApproved: true,
connectionAcceptedOnDate: '2022-07-06T08:07:47.846Z'
}
}
Note: My document has a different timestamp, because I just created my own copy.
So, Let allowed us to define a variable called ref, which was used to define the reference to the connection document that we want to work on, and then we defined the variable connectionDoc to contain the fetched document with Get. Finally, the result is just the value of the connectionDoc variable. So far, so good.
Now, we want to replace the employerId field's value with the values from the document that this document ID represents. Same with the employeeId field. To do that, there are two changes to the query that must be made:
We have to fetch the associated documents, and to do that, we have to compose references based on the available Id values.
We have to expand the response from Let to specify what values to return. Since the documents referred to by the employerId and employeeId field values aren't in the connection document, we have to compose a new object that looks like a fully-populated connection document.
Let(
{
ref: Ref(Collection("connections"), "336420921043583169"),
connectionDoc: Get(Var("ref")),
employerId: Select(["data", "employerId"], Var("connectionDoc")),
employeeId: Select(["data", "employeeId"], Var("connectionDoc")),
employerRef: Ref(Collection("employers"), Var("employerId")),
employeeRef: Ref(Collection("employees"), Var("employeeId")),
employerDoc: Get(Var("employerRef")),
employeeDoc: Get(Var("employeeRef")),
},
{
ref: Var("ref"),
ts: Select("ts", Var("connectionDoc")),
data: {
employer: Var("employerDoc"),
employee: Var("employeeDoc"),
isEmployeeApproved: Select(["data", "isEmployeeApproved"], Var("connectionDoc")),
isEmployerApproved: Select(["data", "isEmployerApproved"], Var("connectionDoc")),
connectionAcceptedOnDate: Select(["data", "connectionAcceptedOnDate"], Var("connectionDoc"))
}
}
)
Note that, since you did not provide example employer or employee documents, I've created very basic documents with the same document IDs that you provided.
The result of this query is:
{
ref: Ref(Collection("connections"), "336420921043583169"),
ts: 1657556091310000,
data: {
employer: {
ref: Ref(Collection("employers"), "330616765804445892"),
ts: 1657556658720000,
data: { name: 'FooBar Co.' }
},
employee: {
ref: Ref(Collection("employees"), "330616700633350340"),
ts: 1657556709680000,
data: { name: 'Alice Lidell' }
},
isEmployeeApproved: true,
isEmployerApproved: true,
connectionAcceptedOnDate: '2022-07-06T08:07:47.846Z'
}
}
Since the query has the entire employer and employee documents included, it is not necessary to include the employerId and employeeId fields in the result, since the references that include these values is already available. However, you could easily include those if you really want them.
Now that the query work for a single connection document reference, let's extend it slightly.
You didn't provide the definition for the connections index, but since your query uses it with no terms, and it appears that the index only returns references, it's a collection index, and is equivalent to using the Documents function.
Map(
Paginate(Documents(Collection("connections"))),
Lambda(
"ref",
Let(
{
connectionDoc: Get(Var("ref")),
employerId: Select(["data", "employerId"], Var("connectionDoc")),
employeeId: Select(["data", "employeeId"], Var("connectionDoc")),
employerRef: Ref(Collection("employers"), Var("employerId")),
employeeRef: Ref(Collection("employees"), Var("employeeId")),
employerDoc: Get(Var("employerRef")),
employeeDoc: Get(Var("employeeRef")),
},
{
ref: Var("ref"),
ts: Select("ts", Var("connectionDoc")),
data: {
employer: Var("employerDoc"),
employee: Var("employeeDoc"),
isEmployeeApproved: Select(["data", "isEmployeeApproved"], Var("connectionDoc")),
isEmployerApproved: Select(["data", "isEmployerApproved"], Var("connectionDoc")),
connectionAcceptedOnDate: Select(["data", "connectionAcceptedOnDate"], Var("connectionDoc"))
}
}
)
)
)
The only significant difference between this query and the previous one, is that the Let expression is embedded within the Lambda for the Map function.
Since I only have one connection document, the result is:
{
data: [
{
ref: Ref(Collection("connections"), "336420921043583169"),
ts: 1657556091310000,
data: {
employer: {
ref: Ref(Collection("employers"), "330616765804445892"),
ts: 1657556658720000,
data: { name: 'FooBar Co.' }
},
employee: {
ref: Ref(Collection("employees"), "330616700633350340"),
ts: 1657556709680000,
data: { name: 'Alice Lidell' }
},
isEmployeeApproved: true,
isEmployerApproved: true,
connectionAcceptedOnDate: '2022-07-06T08:07:47.846Z'
}
}
]
}
If you have more connection documents, there would be more entries in the outermost data array.

Cannot fetch array of strings

Hello i'm trying to fetch some data in my mongoDB collections through graphQL,
Here's my graphQl schema :
type Account {
_id: String
id: String
account_id: Int
limit: Int!
products: [String]
}
type Query {
account(_id: String): [Account]
}
Here's a console log of the resolver return
[ { _id: 5ca4bbc7a2dd94ee58162a49,
account_id: 142442,
limit: 9000,
products:
[ 'Commodity',
'CurrencyService',
'Derivatives',
'InvestmentFund',
'InvestmentStock' ],
id: '5ca4bbc7a2dd94ee58162a49' } ]
but here's the query returns from graphiQL
{
"data": {
"account": [
{
"id": "5ca4bbc7a2dd94ee58162a49",
"_id": "5ca4bbc7a2dd94ee58162a49",
"account_id": null,
"limit": 9000,
"products": null
}
]
}
}
I can't figure out why my products and account_id fields return a null value, they seems to have the correct type, did i miss something ?
Okay nevermind the problem was in the mongoose schema i forgot to add these fields
const accounts = new Schema({
limit: Number,
products: [String],
account_id: Number
})

How to remove everything underneath a key from a JSON object

I have a JSON (All under req.body) in the format
{body :
{ Cust : {...} },
{ Input : {...} },
{ Reciept : {image: [Array] } }
}
I want to be able to remove all the key Reciept so the new JSON would look like ...
{body :
{ Cust : {...} },
{ Input : {...} }
}
I've tried to use delete req.body.Reciept and delete req.body.Reciept.image
Both are unable to change the JSON for me.
What should I be doing?
edit:
When I console log req.body this is what I get :
{ body:
{ CustData:
{ customer: 'T',
address1: '',
address2: '',
billing: '',
signature: [Object] },
InputData:
{ InputDate: '2019-10-21 23:25:28',
Workers: [],
Equipment: [],
Purchases: [] },
Reciept: { image: [Array] } } }
I haven't found a solution because I have a JSON and then an array as a value for a key. I am simply trying to remove the whole Reciept key and everything attached to it.
Here is what is I am running
console.log(req.body);
delete req.body.Receipt;
console.log(req.body);
Here is what I get returned in the terminal
{ body:
{ CustData:
{ customer: 'Test',
address1: '',
address2: '',
billing: '',
signature: [Object] },
InputData:
{ InputDate: '2019-10-22 0:9:33',
Workers: [],
Equipment: [],
Purchases: [] },
Receipt: { image: [Array] } } }
//followed by
{ body:
{ CustData:
{ customer: 'Test',
address1: '',
address2: '',
billing: '',
signature: [Object] },
InputData:
{ InputDate: '2019-10-22 0:9:33',
Workers: [],
Equipment: [],
Purchases: [] },
Receipt: { image: [Array] } } }
JSON-> javascript object notation. It is just an object with key and value .Ntg else. Treat is as object nothing more specific than that. you have clearly one object which holds key as body and data is object which have nested key and value pair.
Since the JSON format is text only, it can easily be sent to and from a server, and used as a data format by any programming language.
JavaScript has a built in function to convert a string, written in JSON format, into native JavaScript objects:
JSON.parse()
So, if you receive data from a server, in JSON format, you can use it like any other JavaScript object.Hope this will clear you concept of json.
delete request.body.Reciept; It is working.

Nested Javascript Object : recursion and "complex" transformation (with lodash)

I apologize in advance for the complex example here; I tried to trim it down as much as I could to illustrate what I try to achieve
I have a complex structure that I need to traverse and transform based on some conditions; Here's an (short) example of the structure that should cover most scenarios:
{ PROP1: {
metadata: Object, // somewhere deeper in metadata I have a `value` key
parent: { $ref: String },
employee: {
parent: { $ref: String },
id: String,
metadata: Object,
products: {
metadata: Object,
model: { $ref: String },
type: 'array',
value: ["String", "String" , "String"] ,
[...]
},
model: {
id: String,
email: {
metadata: Object,
value: 'a#b.com',
type: 'string',
validity: Object,
[...]
},
name: {
firstName: {
metadata: Object,
value: 'John',
type: String,
validity: Object,
[...]
},
lastName: {
metadata: Object,
value: 'Smith',
type: String,
validity: Object,
[...]
},
}
},
operations: {
id: String,
items: [
{ action: {value: "UPDATE", model: {$ref: String }, [...] },
{...}
],
metadata: Object,
[...]
}
}
},
PROP2: {
// similar as PROP1
},
[... and so on ...]
}
I basically need to clean that up before sending it to the backend;
Whenever a value contains $ref, I don't want the key/val pair (e.g.: PROP1.parent is of no use and can be omitted)
whenever a value contains value, I need to omit everything else and move the value of value as the value of key (e.g.: PROP1.employee.products should equal ['String', 'String', 'String'])
keys like id, metadata, validity (etc) can be completely omitted regardless of its content
So the end result should be along those lines:
{ PROP1: {
employee: {
products: ['item','item','item'],
model: {
email: 'a#b.com',
name: { firstName: 'John', lastName: 'Smith'},
},
operations: [
{action: 'UPDATE'}
]
}
},
PROP2: { ... }
}
I tried lots of different approaches using different lodash methods but couldn't wrap my head around this...
Any help will be greatly appreciated
Thanks
In pseudo code, try something like this. Implement the specifics and post more info when you run into trouble.
var ignoreKeyArray = ["id", ...] // keys to ignore and remove
var newJSON = "{}";
for (property in JSON) {
var newProp = parseJSON(key, property);
insert newProp in newJSON object
}
var parseJSON = function (key, jsonBlob) {
if (key in ignoreKeyArray || jsonBlob contains value "$ref")
return "";
var jsonOut = key + ": {}";
for (child in jsonBlob) {
add parseJSON(child) to jsonOut;
}
return jsonOut;
}
If you have any questions, comment so I can extend the answer and clarify.

Categories

Resources