Can I make connections NonNullable in GraphQL .NET? - graphql-dotnet

I'm working on a project, where we need to make non optional connections, and I can't really find anything on the subject.
I made the fields work with the NonNullableGraphType as shown beneath, but I have no luck getting the connections to be non nullable.
I've searched far and wide, and can't find anything about the issue, so I hope someone can help me here, as I'm completely lost.
The fields that are non nullable are written as such:
Field<NonNullGraphType<OrderPresetGraphType>>(
"preset",
resolve: context => {
var loader = dataLoader.Context.GetOrAddBatchLoader<int, Base.Entities.Orders.OrderPreset>(
"OrderPresetById", orderPresetController.GetOrderPresetsByIdAsync
);
return loader.LoadAsync(context.Source.PresetId);
}
);
Sadly, the same method doesn't work with lists.
Any help is much appreciated!
Edit
My current attempt at solving this, looks like this:
Connection<NonNullGraphType<AssetFilterPresetFieldGraphType>>()
.Name("fields")
.Unidirectional()
.Description("Returns a list of AssetFilterPresetFieldGraphType connected to this AsetFilterPresetGraphType")
.ResolveAsync(async context =>
{
var result = await assetFilterPresetController.GetFilterPresetFieldsByIdAsync(context.Source.Id)
.ConfigureAwait(false);
return ConnectionResolver.ToConnection(result.Data, context);
});

I stumbled into this problem as well. I solved by wrapping the current type with NonNullableGraphType using an extension method.
public static ConnectionBuilder<TSource> NonNullable<TSource>(
this ConnectionBuilder<TSource> builder
)
{
builder.FieldType.Type = typeof(NonNullGraphType<>).MakeGenericType(builder.FieldType.Type);
return builder;
}
Use it like this:
Connection<UserGraphType>()
.Name("users")
.NonNullable()
.Resolve(ctx => /*...*/);

Related

Generating multiple OpenAPI documents with NSwag

I have a set of APIs that's available under /api/v1/ route, and a set of internal APIs (stuff that makes the admin panel tick and such) under /admin/api/v1/ route. I currently have NSwag with OpenAPI documents and SwaggerUI3 implemented, but it contains all routes.
After a couple of hours of googling, cursing, and I'm pretty sure getting a mild aneurysm, I managed to scavenge what precious few bits and pieces of information exist and cobble together this bit of code:
// OpenAPI
app.UseOpenApi();
app.UseSwaggerUi3(settings =>
{
settings.Path = "/swagger";
});
// OpenAPI Internal
app.UseOpenApi(settings =>
{
settings.Path = settings.Path.Replace("swagger", "swagger-internal");
});
app.UseSwaggerUi3(settings =>
{
settings.Path = "/swagger-internal";
settings.DocumentPath = settings.DocumentPath.Replace("swagger", "swagger-internal");
});
which does provide me with two separate documents, and two separate UIs. However, despite their differing routes, they share the functionality, fully.
I found this issue that barely has enough information to direct me to a deleted file from the repo and this documentation that's of minuscule if any help at all.
I know that I can write some filter or something (how, though, that's anybody's guess) and use this bit of code:
services.AddOpenApiDocument(settings =>
{
// the settings here in particular
});
and perhaps, yet again, duplicate it or something. But this is where I'm absolutely and utterly stuck and I have no idea how to progress further. I only know that all those bits and pieces of code are glued together with magic strings, but where to apply the glue in this case, that I do not know.
I heard rumors and legends that someone somewhen knew a distant relative of somebody who managed to do that, so perhaps I'll be lucky and this person is here.
Update 1:
I did manage to hack together this bit
public class IncludeInternalApisProcessor : IOperationProcessor
{
public bool Process(OperationProcessorContext context)
{
return context.ControllerType.FullName?.ToUpper().Contains("ADMIN") ?? false;
}
}
and
services.AddOpenApiDocument(settings =>
{
settings.SchemaNameGenerator = new NSwagNestedNameGenerator();
});
services.AddOpenApiDocument(settings =>
{
settings.DocumentName = "internal";
settings.OperationProcessors.Add(new IncludeInternalApisProcessor());
settings.SchemaNameGenerator = new NSwagNestedNameGenerator();
});
but now I feel filthy having done that. And I lose out on versioning.

NGRX bulk effect of already defined single effect

So, Im working on an app with a concept of "Plans" and each plan you can add a comment. That part works fine, but it seems to fail and get confused if i try to run this in a loop.
The Action:
export class AddComment implements Action {
readonly type = CommentActionTypes.AddComment;
constructor(public payload: Comment) {}
}
export class AddCommentSuccess implements Action {
readonly type = CommentActionTypes.AddCommentSuccess;
constructor(public payload: Comment) {}
}
Effect
#Effect()
addComment$: Observable<Action> = this.actions$
.ofType<AddComment>(CommentActionTypes.AddComment).pipe(
switchMap(action => this.commentService.addComment(this.disciplineType, action.payload)),
map((comment: any) => new AddCommentSuccess(comment)),
catchError(err => of(new AddCommentFail(err)))
);
Implementation
What im struggling with is firing this off in rapid success/ I have a situation where I want to add a duplicate comment to multiple plans.
saveSet.forEach(x => {
comment.plan_id = x.id;
this.store.dispatch(this.buildAddCommentAction(comment));
});
For reference:
buildAddCommentAction(comment: DisciplineComment) : Action {
return new CommentActions.AddComment(comment);
}
What is Happening
If i have a list of 5 plans, and want to add a duplicate comment to all of them, Im only getting a successful response for the last item in the loop.
Now i know that is overly chatty, that is 5 separate client/service calls. What I cant figure out, its what the prescribed approach to this should be?
1.) A new BulkAddComment Action, effect, etc. Im loathe to do this becuase I have Comments, Concerns (similar in function and need), and one of each for every "discipline". Thatd be about 36 new effects and twice that in actions. A serious refactor is needed.
2.) Modify the actions and effects for 1 or multiple
3.)?
Thanks for input
This is because you're using the switchMap operator which will cancel the current running observable, in your case the service call.
You'll have to use concatMap or mergeMap. If the order is important use concatMap, if not use mergeMap because this will make your service calls in parallel.
For more info, watch this.

Http rxjs - Passing Subscribe result to another function

Please would you kindly guide me in the right direction?
I am trying to pull records from an old Http REST service, restructure the data, then post it to firebase.
I'm not sure if this is the completely wrong approach or I misunderstand observable's completely. Your guidance will be most appreciated. How to send this new listing to another DataService function: exportToFirebase(new_listing) ?
onServiceCall() {
this.httpDataService.getData().subscribe((itemData) => {
this.itemDataJSON = itemData;
if (this.itemDataJSON) {
this.itemDataJSON.forEach(function(value) {
let new_listing = new Listing(value.id, value.name, value.category);
//console.log(new_listing);
//How to send this new listing to another firebaseDataService function exportToFirebase(new_listing);
});
}
});
}
This demo does what you want, using mocked http & firebase services.
// --- make http call
httpCall.pipe(
// --- construct an array of "new_listing" (of just one, up to u) and pass along the observable chain
concatMap(httpNumbers => of(...httpNumbers)),
// --- make the firebase call
concatMap(number => firebaseCall(number))
).subscribe(val => console.log(val))
Hope this helps. If this does not make sense, then you need to read up on RxJS.

change collection before publishing

I would like to add a property to the objects that get published to the client.
My publish function looks like that
Meteor.publish("forms", function() {
return Forms.find();
});
I would like to do something like this
Meteor.publish("forms", function() {
var forms = Forms.find();
forms.forEach(function (form) {
form.nbForms = 12;
}
return forms;
});
What I would like is that all the documents in forms have a new count attribute which gets sent to the client.
But this obviously does not work.
thank you for your help
Not sure it will work in your case but you might use the new transform collection function introduced with Meteor 0.5.8
When declaring your collection, add this function as the second parameter :
Forms = new Meteor.Collection("forms", {
transform: function(f) {
f.nbForms = 12;
return f;
}
});
But this will be on both server and client. I don't know if there is a way to define a transform function in a publish context.
I think you need to do something similar to this Meteor counting example in Publish:
How does the messages-count example in Meteor docs work?
I also posted a question here that may help once it's answered. Meteor has a this.added which may work, but I'm currently uncertain how to use it. Hence the question below:
Meteor, One to Many Relationship & add field only to client side collection in Publish?

How to work with async code in Mongoose virtual properties?

I'm trying to work with associating documents in different collections (not embedded documents) and while there is an issue for that in Mongooose, I'm trying to work around it now by lazy loading the associated document with a virtual property as documented on the Mongoose website.
The problem is that the getter for a virtual takes a function as an argument and uses the return value for the virtual property. This is great when the virtual doesn't require any async calls to calculate it's value, but doesn't work when I need to make an async call to load the other document. Here's the sample code I'm working with:
TransactionSchema.virtual('notebook')
.get( function() { // <-- the return value of this function is used as the property value
Notebook.findById(this.notebookId, function(err, notebook) {
return notebook; // I can't use this value, since the outer function returns before we get to this code
})
// undefined is returned here as the properties value
});
This doesn't work since the function returns before the async call is finished. Is there a way I could use a flow control library to make this work, or could I modify the first function so that I pass the findById call to the getter instead of an anonymous function?
You can define a virtual method, for which you can define a callback.
Using your example:
TransactionSchema.method('getNotebook', function(cb) {
Notebook.findById(this.notebookId, function(err, notebook) {
cb(notebook);
})
});
And while the sole commenter appears to be one of those pedantic types, you also should not be afraid of embedding documents. Its one of mongos strong points from what I understand.
One uses the above code like so:
instance.getNotebook(function(nootebook){
// hey man, I have my notebook and stuff
});
While this addresses the broader problem rather than the specific question, I still thought it was worth submitting:
You can easily load an associated document from another collection (having a nearly identical result as defining a virtual) by using Mongoose's query populate function. Using the above example, this requires specifying the ref of the ObjectID in the Transaction schema (to point to the Notebook collection), then calling populate(NotebookId) while constructing the query. The linked Mongoose documentation addresses this pretty thoroughly.
I'm not familiar with Mongoose's history, but I'm guessing populate did not exist when these earlier answers were submitted.
Josh's approach works great for single document look-ups, but my situation was a little more complex. I needed to do a look-up on a nested property for an entire array of objects. For example, my model looked more like this:
var TransactionSchema = new Schema({
...
, notebooks: {type: [Notebook]}
});
var NotebookSchema = new Schema({
...
, authorName: String // this should not necessarily persist to db because it may get stale
, authorId: String
});
var AuthorSchema = new Schema({
firstName: String
, lastName: String
});
Then, in my application code (I'm using Express), when I get a Transaction, I want all of the notebooks with author last name's:
...
TransactionSchema.findById(someTransactionId, function(err, trans) {
...
if (trans) {
var authorIds = trans.notebooks.map(function(tx) {
return notebook.authorId;
});
Author.find({_id: {$in: authorIds}, [], function(err2, authors) {
for (var a in authors) {
for (var n in trans.notebooks {
if (authors[a].id == trans.notebooks[n].authorId) {
trans.notebooks[n].authorLastName = authors[a].lastName;
break;
}
}
}
...
});
This seems wildly inefficient and hacky, but I could not figure out another way to accomplish this. Lastly, I am new to node.js, mongoose, and stackoverflow so forgive me if this is not the most appropriate place to extend this discussion. It's just that Josh's solution was the most helpful in my eventual "solution."
As this is an old question, I figured it might use an update.
To achieve asynchronous virtual fields, you can use mongoose-fill, as stated in mongoose's github issue: https://github.com/Automattic/mongoose/issues/1894

Resources