HotChocolate GraphQL Configure not being called - graphql

I have a Query:
public class Query : ObjectType
{
protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
{
Console.WriteLine("Hit Configure");
}
public IQueryable<DataStory> GetDataStories([Service]MicipContext context)
{
return context.DataStories;
}
}
And in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddGraphQL(sp =>
{
var schema = SchemaBuilder.New()
.AddDocumentFromString(this.ReadSchema())
.BindResolver<Query>(c => c.To<Query>())
.AddServices(sp)
.Create();
return schema;
}
}
And my schema graphql:
type Query {
dataStories: [DataStory!]!
}
type DataStory {
id: Int!
title: String!
}
When I call the query with:
query GetDataStories {
dataStories {
title
}
}
The resolver returns correctly but my configure method is never called. What am I doing wrong? Shouldn't Configure be called at some point?

Figured out that Hot Chocolate has not added support for pagination/sort/filter on schema first projects. We are doing schema first so we have to implement it ourselves.

Related

GraphQL Generics Unexpected Value Type Error

I can't figure out how to make the GraphQL types happy with my Generics plan. I am trying to have a response wrapper for all my query and mutation definitions.
Everything was working fine with my query definition using PaginationType and my service returning the Pagination model and all was good. I added the ResponseType wrapper and now I get the conflict.
Expected value of type "ABC.GraphQL.Framework.DTO.ResponseType1[ABC.GraphQL.Framework.Types.Object.PaginationType]\" for \"ResponseType\" but got: ABC.GraphQL.Framework.DTO.Response1[ABC.GraphQL.Framework.DTO.Pagination].",
FieldAsync<ResponseType<PaginationType>>(
"PaginationSearch",
"Returns paginated groups for specified search terms",
arguments: new QueryArguments(...),
resolve: async context => {
return await Service.GetPagination(...);
}
);
public class PaginationType : ObjectGraphType<Pagination>
{
public PaginationType()
{
Field(x => x.totalRecords, nullable: true).Description("Total Records");
....
}
}
public class ResponseType<T> : ObjectGraphType<Response<T>>
{
public ResponseType()
{
Name = "ResponseType";
Field(x => x.success, nullable: true).Description("Operation Success");
Field(x => x.message, nullable: true).Description("Operation Message");
Field(x => x.response, nullable: true, typeof(T)).Description("Operation Response");
}
}
Of course the plain backing models
public class Pagination
{
public int totalRecords { get; set; }
.....
}
public class Response<T>
{
public bool success { get; set; }
public string message { get; set; }
public T response { get; set; }
}
Now my service class returns the plain objects and this has been working to this point so not sure why adding the Response wrapper is now breaking it.
public async Task<Response<Pagination>> GetPagination(...)
{...}
I debugged into the GraphQL library and found it is by design. So I begun digging into to why and found this:
https://github.com/graphql-dotnet/graphql-dotnet/issues/2279

Filtering schema out of NSwag swagger

I have an ASP.NET full framework application with API endpoints. I am using NSwag to generate a swagger document. This all works.
I need to generate a document for only a small subset of the endpoints. The paths are filtered, but the schema is not. How do I filter the schema objects to match the path filtering?
Example:
I have this filter
public class IncludeControllersInSwagger : IOperationProcessor
{
public Task<bool> ProcessAsync(OperationProcessorContext context)
{
return Task.FromResult(
context.ControllerType == typeof(ControllerA));
}
}
And this at startup:
settings.GeneratorSettings.OperationProcessors.Add(new IncludeControllersInSwagger());
The controllers are:
public class AResponse
{
public string Message { get; set; }
public bool Flag { get; set; }
}
public class BResponse
{
public string Message { get; set; }
public int Count { get; set; }
}
[Route("a")]
public class ControllerA : ApiController
{
[HttpGet]
public AResponse Get()
{
return new AResponse
{
Message = "Hello from A",
Flag = true
};
}
}
[Route("b")]
public class ControllerB : ApiController
{
[HttpGet]
public BResponse Get()
{
return new BResponse
{
Message = "Hello from B",
Count = 42
};
}
}
Then the generated swagger contains just one path:
"paths": {
"/a": {
"get": { .. etc
}
}
And that's all, this is correct.
But the schemas contains both:
"schemas": {
"AResponse": {
"type": "object",
"additionalProperties": false,
etc
},
"BResponse": {
"type": "object",
"additionalProperties": false,
etc
}
}
}
The BResponse type should not be there. How do you remove it?
This extra data makes the Schemas section extremely verbose and unsuitable for public documentation in the case where there are over 10 endpoints, and only 2 are exposed via a gateway and therefor documented in swagger.
There is a ISchemaProcessor but it does not return a Boolean like the IOperationProcessor.
Have you tried to add the operation filter as first element?
i.e. OperationProcessors.Insert(0, new IncludeControllersInSwagger())
I think this is important as it will filter out the operation before the dto schemas are generated and added to the document.
This is not an answer to your problem, as you already got an answer that seems to work. I do have a suggestion however. Instead of checking the type of controller in your processor, I would suggest to create a custom attribute:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class IncludeInSwaggerAttribute : Attribute
{
}
Then change your processor to look for this attribute:
public class IncludeInSwaggerOperationProcessor : IOperationProcessor
{
public async Task<bool> ProcessAsync(OperationProcessorContext context)
{
return context.ControllerType.GetCustomAttributes<IncludeInSwaggerAttribute>().Any() ||
context.MethodInfo.GetCustomAttributes<IncludeInSwaggerAttribute>().Any();
}
}
This way, you can add the attribute to any new controller you want to include in swagger without having to change your processor. You can also add the attribute on a single action to only include that action, leaving the rest of the actions in the controller out of swagger.

How to implement Custom Scalar in Graphql-java?

I am new to graphql. I am trying to implement custom scalar type "Email". but am getting below error. Could you please help me?
by: com.coxautodev.graphql.tools.SchemaClassScannerError: Expected a
user-defined GraphQL scalar type with name 'Email' but found none! at
com.coxautodev.graphql.tools.SchemaClassScanner.validateAndCreateResult(SchemaClassScanner.kt:144)
~[graphql-java-tools-4.3.0.jar:na] at
com.coxautodev.graphql.tools.SchemaClassScanner.scanForClasses(SchemaClassScanner.kt:94)
~[graphql-java-tools-4.3.0.jar:na]
Configurations :
scalar Email
type Greeting {
id: ID!
message: String!
email:Email
}
type Query {
getGreeting(id: ID!): Greeting
}
type Mutation {
newGreeting(message: String!): Greeting!
}
Version info:
springBootVersion = '1.5.8.RELEASE'
com.graphql-java:graphql-java:6.0')
com.graphql-java:graphql-java-tools:4.3.0')
com.graphql-java:graphql-java-servlet:4.7.0')
org.springframework.boot:spring-boot-starter-web')
com.graphql-java:graphql-spring-boot-starter:3.10.0')
com.graphql-java:graphiql-spring-boot-starter:3.10.0')
Please help...
Try this:
#Component
public class ScalarEMail extends GraphQLScalarType {
public ScalarEMail() {
super("Email", "Scalar Email", new Coercing() {
#Override
public Object serialize(Object o) throws CoercingSerializeException {
return ((Email) o).getEmail();
}
#Override
public Object parseValue(Object o) throws CoercingParseValueException {
return serialize(o);
}
#Override
public Object parseLiteral(Object o) throws CoercingParseLiteralException {
return Email.valueOf(((StringValue) o).getValue());
}
});
}
}

Web API controller returning boolean

I have a Web API where one of the methods in a controller return true or false when validating user id which is a string of numbers. I do no have an actual database yet, so I sort of mocked up the set of values in the repository.
Below is my code:
My repository class:
public class myRepository
{
public myClasses.Employee[] GetAllEmployees()
{
return new myClasses.Employee[]
{
new myClasses.Employee
{
empId="111111",
empFName = "Jane",
empLName="Doe"
},
new myClasses.Employee
{
empId="222222",
empFName = "John",
empLName="Doe"
}
};
}
public bool VerifyEmployeeId(string id)
{
myClasses.Employee[] emp = new myClasses.Employee[]
{
new myClasses.Employee
{
empId="111111",
empFName = "Jane",
empLName="Doe"
},
new myClasses.Employee
{
empId="222222",
empFName = "John",
empLName="Doe"
}
};
for (var i = 0; i <= emp.Length - 1; i++)
{
if (emp[i].empId == id)
return true;
}
return false;
}
}
and my model class:
public class myClasses
{
public class Employee
{
public string empId { get; set; }
public string empFName { get; set; }
public string empLName { get; set; }
}
}
and here is my controller:
public class myClassesController : ApiController
{
private myRepository empRepository;
public myClassesController()
{
this.empRepository = new myRepository();
}
public myClasses.Employee[] GetEmployees()
{
return empRepository.GetAllEmployees();
}
public bool VerifyEmployee(string id)
{
return empRepository.VerifyEmployeeId(string id);
}
}
Now when i compile it I get an error:
} expected
Type or namespace definition, or end-of-file expected
; expected
in line
return empRepository.VerifyEmployeeId(string id);
of my controller.
My question is using boolean the best way to return Success or Failure from Web API method or is there a better way? and also why am I getting this error. I am new to Web API
The compile error is caused by this;
return empRepository.VerifyEmployeeId(string id);
You should rewrite to:
return empRepository.VerifyEmployeeId(id);
You don't have you specify the type of the argument when calling a function.
About returning true or false; if you intend to only check whether the employee is valid or not, I should leave it this way. If you plan to use that employee data more you could rewrite that function so it returns the actual employee itself, and return 404: Not Found when the Employee is not found for instance.

Repository Pattern and Azure Table Storage(???)

While doing the following simple example, I found the following difficulties
As the title says, I am intending to use the Repository pattern while I am storing data in the Azure table storage.now I have couple of classes, Repository.cs, IRepository.cs, DataContext.cs and the Controller.
During my reading I found some info and been doing as follows.
IRepository.cs
public interface IRepository<T> where T: TableServiceEntity
{
T GetById(int Id);
IQueryable<T> GetAll();
}
and the DataContext.cs
public class DataContext<T>:TableServiceContext where T:TableServiceEntity
{
public DataContext(CloudStorageAccount storageaccount, StorageCredentials credentials)
: base(storageaccount.TableEndpoint.AbsoluteUri, credentials)
{
// _storageAccount = storageaccount;
var storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue(KEY_STORAGE));
storageAccount.CreateCloudTableClient().CreateTableIfNotExist(tableName);
}
public IQueryable<T> DeviceTable
{
get { return CreateQuery<T>(tableName); }
}
}
plus some part of the controller(I have already data in the table which I created before)
public class DeviceMeController : Controller
{
private IRepository<Entity>_repository;
public Controller() : this(new Repository<Entity>())
{
}
public Controller(IRepository<Entity> repository)
{
_repository = repository;
}
public ActionResult Index()
{
var List = _repository.GetAll();
return View(deviceList);
}
and the the Implementation of the interface Reposistory.cs, here is where I have an error and got lost somewhere
public class Repository<T>:IRepository<T> where T:TableServiceEntity
{
private DataContext<T> _serviceContext;
// here get tablename as pararameter;
// so the Enities call this function
public Repository()
{
// the same context for multiple tables ?
}
// perhaps it should take the table Name
public void Add(T item)
{
_serviceContext.AddObject(TableName,item);
_serviceContext.SaveChangesWithRetries();
}
public IQueryable<T> GetAll()
{
var results = from c in _serviceContext.Table
select c;
return results;
Error is about the null reference, the debugger shows the variable results is null?
In the end I need to know few things.
what should I do in the Repository.cs constructor? I believe the Datacontext.cs class has to be in a separate class ...
any Hint here
Hy,
first of all I presume you left out some code, because I don't see how you get your context in your repository. But supposing you do set it correctly, (injection?) taking into account the way you desinged your datacontext the repository doesn't need to know the table name because it is set in the following lines of code:
public IQueryable<T> DeviceTable
{
get { return CreateQuery<T>(Constants.DeviceTableName); }
}
So when you create a query based on the IQueryable DeviceTable, the table name is already set.
The thing is I don't see the need for your context class, especially as it can only bring over a single entity type (it is generic and based on an entity).
A basic layout of my Repository for Azure Table Storage is:
public abstract class CloudRepository<TEntity> : ICloudRepository<TEntity>
{
private TableServiceContext _tableServiceContext;
private string _tableName;
public string TableName
{
get { return _tableName ?? ( _tableName = typeof(TEntity).Name.Replace("Entity", string.Empty).ToLower()); }
}
public CloudStorageAccount StorageAccount
{
get
{
return CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString"));
}
}
public CloudTableClient TableClient
{
get
{
CloudTableClient cloudTableClient = StorageAccount.CreateCloudTableClient();
cloudTableClient.CreateTableIfNotExist(TableName);
return cloudTableClient;
}
}
public TableServiceContext ServiceContext
{
get
{
return _tableServiceContext ?? (_tableServiceContext = TableClient.GetDataServiceContext());
}
}
public IEnumerable<TEntity> FindAll()
{
return ServiceContext.CreateQuery<TEntity>(TableName).ToList();
}
}
Hope this helps you.

Resources