In the feathersjs docs the explanation provided is as follows:
pluck discards all fields except for the specified ones, either from
the data submitted or from the result. If the data is an array or a
paginated find result the hook will remove the field(s) for every
item.
import _pluck from '../common/_pluck';
import checkContextIf from './check-context-if';
import getItems from './get-items';
import replaceItems from './replace-items';
export default function (...fieldNames) {
return context => {
checkContextIf(context, 'before', ['create', 'update', 'patch'], 'pluck');
if(context.params.provider) {
replaceItems(context, _pluck(getItems(context), fieldNames));
}
return context;
};
}
The getItems utility returns the items in either hook.data or
hook.result depending on whether the hook is being used as a before or
after hook. hook.result.data or hook.result is returned for a find
method.
The returned items are always an array to simplify further processing.
The replaceItems utility is the reverse of getItems , returning the
items where they came from.
My question relates to the checkContextIf function. This function prevents the pluck hook from being called except before the create,update and patch methods. How then does the pluck hook work on the results of the query. Are not the results produced after the service call and handled in an after hook?
As the documentation states:
The getItems utility returns the items in either hook.data or hook.result depending on whether the hook is being used as a before or after hook.
hook.data is the data (body) sent with a create, patch or update request so it can be used to omit fields that you do not want to be saved to the database. This is also documented in the hooks API:
data - The request data (for create, update and patch)
Related
I'm currently trying to write some code that retrieves a collection from my Firestore instance.
My codebase uses the service repository pattern to keep business logic seperate from the code that retrieves data. For this reason I've made the following code:
import { injectable, inject } from "inversify";
import { IOfficeRepository, TYPES } from "../common/types";
import { Firestore } from "#google-cloud/firestore";
#injectable()
export default class OfficeRepository implements IOfficeRepository {
private fireStoreClient: Firestore;
constructor(#inject(TYPES.FireStoreFactory) firestoreFactory: () => Firestore) {
this.fireStoreClient = firestoreFactory();
};
public async getOffice(officeId: string): Promise<FirebaseFirestore.QueryDocumentSnapshot<FirebaseFirestore.DocumentData>> {
const officeCollection = "offices";
const document = await this.fireStoreClient.collection(officeCollection).get();
return document;
};
}
What I'd like to do is return the value from the get() call to my service, in the service I will be performing checks and executing the business logic that I need.
The get() returns a Promise<FirebaseFirestore.QuerySnapshot<FirebaseFirestore.DocumentData>>, but I am unable to use this as a return type for the function in my repository. I just get the following error:
Type 'QuerySnapshot' is missing the following properties from type 'QueryDocumentSnapshot': createTime, updateTime, data, exists, and 3 more.
I've already looked-up the error, but I wasn't able to find any solution or a post where someone was trying to return the result from the get() function before performing any logic on the result.
So my question is: How would I be able to make this setup work? Or is there something I am doing wrong with this setup? If so, what would be another approach to work this out while using the service repository pattern?
Your declared return type of QueryDocumentSnapshot doesn't match the actual return type of QuerySnapshot.
This line of code:
const document = await this.fireStoreClient.collection(officeCollection).get();
performs a query for all of the documents in the officeCollection collection. As you can see from the API documentation, CollectionReference.get() yields a QuerySnapshot object. The entire set of documents will be available in the returned docs property.
It seems that you expect getOffice to return a single document instead. I'm noticing that you never used the argument officeId to narrow down your query to just the one document you want. Perhaps you meant to do something like this instead to get a single document using its ID?
const document = await this.fireStoreClient
.collection(officeCollection)
.doc(officeId)
.get();
In this case, document will be a DocumentSnapshot object.
I am trying to check for the existence of an optional field in an API request, and if that field exists, perform a nested validation to check if two other fields (one or the other, or implicitly both) exist inside of it. I am using Express Validator to try and accomplish this task.
// Sample request body
{
<...>
thresholds: {
min: 3,
max: 5
}
}
// (Attempted) validation chain
check('thresholds').optional()
.custom( innerBody => {
console.log('THRESHOLDS', innerBody);
oneOf([
check('innerBody.min').optional(),
check('innerBody.max').optional()
]);
})
The above snippet is part of a larger validation chain I'm validating the full request body on. I also tried removing the innerBody. string from the inner checks but still no luck. I am console.loging the threshold body, and it prints out correctly, however I still get a validation error, when I'm trying to get my integration test to pass:
{"name":"ValidationError","message":"ValidationError: Validation failed","errors":[{"location":"body","param":"thresholds","value":{"min":3,"max":5},"msg":"Invalid value"}]}
I am relatively new to Express Validator so if I'm chaining the validation wrong/not using oneOf correctly or something would love some pointers!
Thanks
Looks like the .custom function needs to return a Promise. Answer below:
.custom(innerBody => {
if (!(innerBody.min) || !(innerBody.max)) return Promise.reject('Missing min or max');
return Promise.resolve();
})
Remember: Always return a boolean value from the callback of .custom()
function. Otherwise your validation might not work as desired.
Source: Custom validation guide
In general, you might have needs in use of Promises if you deal with asynchronous .custom() function. Then you'll be obligated to return Promise.resolve() / Promise.reject() for correct validator behaviour.
Source: SO answer
Let me preface this by saying I am not a javascript developer, so I'm probably missing something very obvious. I'm a data warehouse developer and creating a graphql server that can communicate with our DW got dropped in my lap.
I've been trying to get dataloaders to work on my graphql server by using a single object in the context, containing multiple dataloaders. I'm then trying to call the appropriate dataloader in the resolver. However, I've been unable to get this to work correctly. The consolidated dataloader object only works if I individually reference the dataloaders in the server context.
I'm trying to follow a similar pattern with the loaders as I have with my models, which is each broken out into a separate file, then consolidated for use as a single object via recursion through the file structure.
Example is I have an object called loaders which contains two loaders: countryLoader and marketsectorLoader, each of which is defined in a separate file under the "loaders" directory. In my server context, the following works
import * as loaders from "./loaders"
graphQLServer.use('/graphql', bodyParser.json(),
graphqlExpress({
schema,
context: {
countryLoader: loaders.countryLoader()
I can then call this in my resolver:
StateProvince: {
Country: (parent, args, {countryLoader}) => {
countryLoader.load(parent.Country_fkey) }},
This functions correctly, batching and returning the correct query result, but I'd prefer not to have to declare each specific dataloader from the loaders object as part of the context. However, I've been unable to figure out the syntax to use the loaders object in the context and call the appropriate
individual dataloader in the appropriate resolver.
I've tried several variants of the following example:
https://github.com/relay-tools/react-relay-network-layer/blob/master/examples/dataLoaderPerBatchRequest.js
which seems to be using the type of technique I'm trying to leverage:
//context snippet:
context: {
request: req, // just for example, pass request to context
dataLoaders: initDataLoaders(),
},
However, no luck. I suspect the issue is with my resolver syntax, but I'm not sure, and I haven't been able to find working examples with multiple dataloaders.
If I'm reading your code correctly, importing your loaders using a wildcard import like this:
import * as loaders from './loaders'
results in an object wherein each property is a function that creates an instance of a particular DataLoader. So we just need to iterate through each property. For example:
// Using forEach
const dataLoaders = {}
Object.keys(loaders).forEach(loaderName => {
dataLoaders[loaderName] = loaders[loaderName]()
})
// Or using reduce
const dataLoaders = Object.keys(loaders).reduce((result, loaderName) => {
result[loaderName] = loaders[loaderName]()
return result
}, {})
Using lodash, you can also just do something like:
const dataLoaders = _.mapValues(loaders, loader => loader())
I have a lambda which sits on the business layer (GoalFeed), it's function is to aggregate data from two other lamdbas (Goals and Users).
The GoalFeed invokes Goals (RESTful/GET) and iterates over the results, something of this nature (excuse missing code for brevity):
lambda.invoke( goalsParms, function( err, data ) {
var items = data.Payload.body.Items;
items.forEach( function( element ) {
lambda.invoke( teamsParms, function( err, data ) {
// PROBLEM: element is always the *last* element here!!
});
});
});
So the issue I'm having is that I'd like to pass (reference) each element in the nested lambda.invoke callback, but I don't see a way to make that happen. Referencing element in the lambda.invoke (teams) call always gives me the last element in the list.
How do I go about passing the element/or properly referencing it inside the callback for the nested lambda.invoke (teams), so that when the nested invoke executes it will fetch data for the current element during the initial invoke?
I believe this was something to do with how I was handling promises further up the chain, though creating a separate function and calling it with element seems to have resolved the problem.
I'm working with Dust.js and Node/Express. Dust.js documents the context helpers functions, where the helper is embedded in the model data as a function. I am adding such a function in my JSON data model at the server, but the JSON response to the browser doesn't have the function property (i.e. from the below model, prop1 and prop2 are returned but the helper property is not.
/* JSON data */
model: {
prop1: "somestring",
prop2: "someotherstring",
helper: function (chunk, context, bodies) {
/* I help, then return a chunk */
}
/* more JSON data */
I see that JSON.stringify (called from response.json()) is removing the function property. Not sure I can avoid using JSON.stringify so will need an alternative method of sharing this helper function between server/client. There probably is a way to add the helper functions to the dust base on both server and client. That's what I'm looking for. Since the Dust docs are sparse, this is not documented. Also, I can't find any code snippets that demonstrate this.
Thanks for any help.
send your helpers in a separate file - define them in a base context in dust like so:
base = dust.makeBase({foo:function(){ code goes here }})
then everytime you call your templates, do something like this:
dust.render("index", base.push({baz: "bar"}), function(err, out) {
console.log(out);
});
what this basically does is it merges your template's context into base, which is like the 'global' context. don't worry too much about mucking up base if you push too much - everytime you push, base recreates a new context with the context you supplied AND the global context - the helpers and whatever variables you defined when you called makeBase.
hope this helps
If you want stringify to preserve functions you can use the following code.
JSON.stringify(model, function (key, value) {
if (typeof(value) === 'function') {
return value.toString();
} else {
return value;
}
});
This probably doesn't do what you want though. You most likely need to redefine the function on the client or use a technology like nowjs.