How to test an Attribute in Hot Chocolate - hotchocolate

Using Hot Chocolate, how do I integration test the implementation of a ObjectFieldDescriptorAttribute?
Given the following implementation:
public class ValidApiKeyAttribute : ObjectFieldDescriptorAttribute
{
public override void OnConfigure(IDescriptorContext descriptorContext, IObjectFieldDescriptor descriptor, MemberInfo member)
{
descriptor
.Use(next => async context =>
{
var httpContextAccessor = context.Services.GetService(typeof(IHttpContextAccessor))
as IHttpContextAccessor;
var authenticationSettings = context.Services.GetService(typeof(IOptions<AuthenticationSettings>))
as IOptions<AuthenticationSettings>;
// --
await next(context);
});
}
}
Which is called from a Query class
public class Query
{
[ValidApiKey]
public IQueryable<Something> GetSomething()
{
// --
}
}
How do I unit test the middleware? I assume I need to mock the FieldDelegate and the IObjectFieldDescriptor? This is what I've got so far:
[Fact]
public async Task Query_GetSomething_Success()
{
// arrange
var request = QueryRequestBuilder.New()
.SetQuery("")
.Create();
//? var fieldDelegate = new FieldDelegate(async (context) => await Task.CompletedTask);
var executor = await new ServiceCollection()
.AddGraphQL()
.AddType<Query>()
.BuildRequestExecutorAsync();
// act and assert
var result = await executor.ExecuteAsync(request);
}

Related

.NET MAUI Setting an item SelectedIndex on Page Load is Delayed

When the view appears on the screen there is a short delay setting the values to each control. Is it possible to set the values before the user sees the view?
public UserSettingsView()
{
InitializeComponent();
LoadAsync();
}
private async void LoadAsync()
{
try
{
// Loading data from API
Languages = await _languageService.GetAsync(AccessToken);
USStates = await _uSStateService.GetAsync(AccessToken);
// Assigning the list to the ItemSource of each Picker.
ddlLanguages.ItemsSource = Languages;
ddlUSStates.ItemsSource = USStates;
// Getting the user's preferred settings
var userSettings = await _accountService.GetSettingsAsync(UserID, AccessToken);
if (userSettings != null)
{
// Setting user values to each Picker control.
// This is where the delay happens.
ddlLanguages.SelectedIndex = Languages.FindIndex(x => x.ID == userSettings .LanguageID);
ddlUSStates.SelectedIndex = USStates.FindIndex(x => x.ID == userSettings .USStateID);
cbAge.IsChecked = currentSettings.AgeQualified;
}
}
catch
{
await DisplayAlert("Oh no!", "Error loading the page", "OK");
}
}
To resolve the delay, I am passing the two lists for the languages and the States from the previous page.
public UserSettingsView(List<Language> _languages, List<USState> _usStates)
{
InitializeComponent();
Languages = _languages;
USStates = _usStates;
LoadAsync();
}
private async void LoadAsync()
{
try
{
ddlLanguages.ItemsSource = Languages;
ddlUSStates.ItemsSource = USStates;
var currentSettings = await _accountService.GetSettingsAsync(UserID, AccessToken);
if (currentSettings != null)
{
ddlLanguages.SelectedIndex = Languages.FindIndex(x => x.ID == currentSettings.LanguageID);
ddlUSStates.SelectedIndex = USStates.FindIndex(x => x.ID == currentSettings.USStateID);
switchAgeQualification.IsToggled = currentSettings.AgeQualified;
}
}
catch
{
await DisplayAlert("Error", "Could not load page data", "OK");
}
}
If I understand correctly, you currently have a line like this:
await Navigation.PushModalAsync(new UserSettingsView());
I don't see the types of the properties involved, but the basic idea is to do all the slow awaits BEFORE doing new UserSettingsView....
Something like this:
public class UserSettingsData
{
SomeType1 Languages;
SomeType2 USStates;
SomeType3 UserSettings;
}
...
// Slow await calls.
var data = await UserSettingsView.PrepAsync(UserId, AccessToken);
// Now create and display the view.
await Navigation.PushModalAsync(new UserSettingsView(data));
...
public static async UserSettingsData PrepAsync(SomeType4 UserId, SomeType5 AccessToken)
{
var data = new UserSettingsData();
data.Languages = await _accountService.GetSettingsAsync(...);
data.USStates = await ...;
data.UserSettings = await ...;
}
public UserSettingsView(UserSettingsData data)
{
...
// NOT AN ASYNC METHOD, so happens immediately, before page is shown.
Load(data);
}
// NOT AN ASYNC METHOD, so happens immediately.
private void Load(UserSettingsData data)
{
Languages = data.Languages;
USStates = data.USStates;
var userSettings = data.UserSettings;
...
// if still need DisplayAlert
Dispatcher.InvokeOnMainThread(async () =>
await DisplayAlert...
);
}
Replace "SomeType" etc with your actual types.

Constructor of class 'Blob' is private and only accessible within the class declaration

import { Blob } from '#firebase/firestore-types';
#Injectable()
export class ImagehandlerProvider {
nativepath: any;
firestore = firebase.storage();
imagestore: any;
constructor(public filechooser: FileChooser, public blob: Blob) {
console.log('Hello ImagehandlerProvider Provider');
}
uploadimage() {
var promise = new Promise((resolve, reject)=> {
this.filechooser.open().then((url)=> {
(<any>window).FilePath.resolveNativePath(url, (result)=> {
this.nativepath = result;
(<any>window).resolveLocalFileSystemURL(this.nativepath, (res)=> {
res.File((resFile)=> {
var reader = new FileReader();
reader.readAsArrayBuffer(resFile);
reader.onloadend = (ent: any) => {
// var imgBlob = new Blob([ent.target.result], {type: 'image/jpeg'});
var imgBlob = new Blob([ent.target.result], {type: 'image/jpeg'});
var imgStore = this.firestore.ref(firebase.auth().currentUser.uid).child('profilepic');
imgStore.put(imgBlob).then((res)=> {
alert('Upload successful');
}).catch((error)=> {
alert('Upload failed' + error);
})
}
})
})
})
})
})
}
}
The code above has a simple problem. I am on the 3rd day learning ionic-angular, I want to implement image upload, and I think by default angular makes all imported classes private. I have searched online how to Blob is implemented and have sen that it is by instantiating the Blob class, which I did, since the class is made private by default it's not accessible so I have to create a public property in the constructor of this class. In short way, without creating this property I am getting this error:
[ts] Constructor of class 'Blob' is private and only accessible within the class declaration.
How do I make use of the Blob class

ngShow expression is evaluated too early

I have a angular component and controller that look like this:
export class MyController{
static $inject = [MyService.serviceId];
public elements: Array<string>;
public errorReceived : boolean;
private elementsService: MyService;
constructor(private $elementsService: MyService) {
this.errorReceived = false;
this.elementsService= $elementsService;
}
public $onInit = () => {
this.elements = this.getElements();
console.log("tiles: " + this.elements);
}
private getElements(): Array<string> {
let result: Array<string> = [];
this.elementsService.getElements().then((response) => {
result = response.data;
console.log(result);
}).catch(() => {
this.errorReceived = true;
});
console.log(result);
return result;
}
}
export class MyComponent implements ng.IComponentOptions {
static componentId = 'myId';
controller = MyController;
controllerAs = 'vm';
templateUrl = $partial => $partial.getPath('site.html');
}
MyService implementation looks like this:
export class MyService {
static serviceId = 'myService';
private http: ng.IHttpService;
constructor(private $http: ng.IHttpService) {
this.http = $http;
}
public getElements(): ng.IPromise<{}> {
return this.http.get('./rest/elements');
}
}
The problem that I face is that the array elements contains an empty array after the call of onInit(). However, later, I see that data was received since the success function in getELements() is called and the elements are written to the console.
elements I used in my template to decide whether a specific element should be shown:
<div>
<elements ng-show="vm.elements.indexOf('A') != -1"></elements>
</div>
The problem now is that vm.elements first contains an empty array, and only later, the array is filled with the actual value. But then this expression in the template has already been evaluated. How can I change that?
Your current implementation doesn't make sense. You need to understand how promises and asynchronous constructs work in this language in order to achieve your goal. Fortunately this isn't too hard.
The problem with your current implementation is that your init method immediately returns an empty array. It doesn't return the result of the service call so the property in your controller is simply bound again to an empty array which is not what you want.
Consider the following instead:
export class MyController {
elements: string[] = [];
$onInit = () => {
this.getElements()
.then(elements => {
this.elements = elements;
});
};
getElements() {
return this.elementsService
.getElements()
.then(response => response.data)
.catch(() => {
this.errorReceived = true;
});
}
}
You can make this more readable by leveraging async/await
export class MyController {
elements: string[] = [];
$onInit = async () => {
this.elements = await this.getElements();
};
async getElements() {
try {
const {data} = await this.elementsService.getElements();
return data;
}
catch {
this.errorReceived = true;
}
}
}
Notice how the above enables the use of standard try/catch syntax. This is one of the many advantages of async/await.
One more thing worth noting is that your data services should unwrap the response, the data property, and return that so that your controller is not concerned with the semantics of the HTTP service.

Byte array and JSON in [FromBody]

I am trying pass an object which consists of different data type. I am always getting null value for orderDetails in Web API.
However if do this,
purchaseOrder.Attachments = null,
in the client then orderDetails is no longer null and I have other informations like "SendEmail" and PurchaseOrderNumber.
It looks I might not be correctly set the parameter in the client (angular 2).
However testing the same Web Api method from Console app works fine and I am not getting a null value.
Do I need to separate the JSON data and byte array?
regards,
-Alan-
Models
public class Attachments
{
public int AttachmentId { get; set; }
public string FileName { get; set ;}
public byte[] FileData { get; set ;}
}
public class UpdatePurchaseOrderViewModel
{
public bool SendEmail { get; set; }
public int PurchaseOrderNumber { get; set; }
public Attachments Attachments { get; set;
}
Here is my Web API put method definition
[HttpPut("AddPurchaseOrderNumber/{purchaseOrderId}")]
public StatusCodeResult AddPurchaseOrderNumber(int purchaseOrderId, [FromBody] UpdatePurchaseOrderViewModel orderDetails)
{
try
{
var status = _service.AddPurchaseOrderNumber(purchaseOrderId, orderDetails);
if (status == 200)
_unitOfWorkAsync.SaveChanges();
else return StatusCode(status);//No Data
}
catch
{
return StatusCode(400); // Bad Request
}
return StatusCode(200);//OK
}
Typescript snippet
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept','application/json');
let options = new RequestOptions({ headers: headers });
var body = JSON.stringify(
purchaseOrder
);
var uri = 'http://localhost:33907/api/purchaseorder/addpurchaseordernumber/' + purchaseOrderId;
return this._http.put(uri, body , options)
.map((response: Response) => {
let data = response.json();
if (data) {
return true;
}
else {
return false;
}
})
Update
The orderDetails is created as below
let file = Observable.create((observer) => {
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
observer.next(fr.result);
observer.complete();
};
fr.onerror = (err) => {
observer.error(err);
}
fr.onabort = () => {
observer.error("aborted");
}
});
file.map((fileData) => {
//build the attachment object which will be sent to Web API
let attachment: Attachments = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber:this.form.controls["purchaseOrderNumber"].value * 1, //for casting purpose
Attachments: attachment
}
console.log("Loading completed");
return order;
})
When sending objects that have byte arrays as a property back and forth between a client to a WebAPI endpoint, I typically use a DTO that stores the property to explicitly define it as a Base64 string. On the server side I map the DTO to my entity by converting the Base64 string to / from the byte array for server side operations and storing in the database.
The serializer will do something like this automatically but the format passed from JavaScript may not match what the WebAPI JSON serializer is expecting (which is why it's working from your C# Console App).
You didn't include how you are creating the purchaseOrder object in your JavaScript so I can't comment on how that object is being setup - which may be where your issue is.

Can't access this from $http callback

I'm using angular 1.5 with typescript, I can't access this property from the callback being returned from $http promise.
When I'm trying to access a private method from the callback 'this' is undefined
I have the following ServerAPI service:
export class ServerAPI implements IServerAPI {
static $inject:Array<string> = ['$http', '$q'];
constructor(private $http:ng.IHttpService,
private $q:ng.IQService) {
}
postHandler(partialUrl:string, data?:any, config?:any):ng.IPromise<any> {
let url = this.buildUrl(partialUrl);
var result:ng.IPromise< any > = this.$http.post(url, data, config)
.then((response:any):ng.IPromise<any> => this.handlerResponded(response, data))
.catch((error:any):ng.IPromise<any> => this.handlerError(error, data));
return result;
}
private handlerResponded(response:any, params:any):any {
response.data.requestParams = params;
return response.data;
}
private handlerError(error:any, params:any):any {
error.requestParams = params;
return error;
}
}
Which been consumed by user.service:
export class UserService implements IUserService {
static $inject:Array<string> = ['$q', 'serverAPI'];
constructor(private $q:ng.IQService,
private serverAPI:blocks.serverAPI.ServerAPI) {
var vm = this;
$rootScope.globals = $rootScope.globals || {};
$rootScope.globals.currentUser = JSON.parse($window.localStorage.getItem('currentUser')) || null;
this.getUserPermissions();
}
private getUserPermissions:() => IPromise<any> = () => {
var promise = this.serverAPI.postHandler('MetaDataService/GetUserPermissions',
{userID: this.getUser().profile.UserID})
.then((res) => {
this.updateUser('permissions', res.GetUserPermissionsResult); // NOT WORKING, this is undefined
})
.catch((response:any):ng.IPromise<any> => {
this.updateUser('permissions', res.GetUserPermissionsResult); // NOT WORKING, this is undefined
});
return promise;
};
private updateUser:(property:string, value:any) => void = (property, value) => {
};
}
The issue is this line:
.then((response:any):ng.IPromise<any> => this.handlerResponded(response, data))
While your lexical scope is maintained in order to find the handlerResponded method the scope is not fully preserved in the output.
you can get around this in 2 ways:
inline your handler function rather than have it as a function on your class
you can bind the call to handlerResponded to the instance
example of binding:
.then((response:any):ng.IPromise<any> => this.handlerResponded(response, data).bind(this))

Resources