Modifying the DTO name appearing in OpenAPI (Swagger) schemas in NestJS - swagger

I am facing a problem where my DTO types are named one thing, but I want them to appear with a different name in the OpenAPI doc page.
For example, I have a UserDto class that I use in my controller, but wanted it to appear as simply "User" in the schemas section (and everywhere else this applies). Is that possible? Is there any decorator I can use?
I know I can simply modify the class name, but there is already a different user class used elsewhere.
I have searched everywhere with no avail.
BTW, I am using typescript and nestjs.
Every help will be appreciated, thanks!

Out of the box, Nest.js doesn't yet offer a ready-made solution. There is an open pull request (as mentioned earlier) https://github.com/nestjs/swagger/pull/983, but when it will be merged is unknown.
You can change the DTO name in schemas using one of the following approaches:
Add a static name property to your DTO.
class UserDto {
static name = 'User'; // <- here
#ApiProperty()
firstName: string;
// ...
}
But in strict mode, TypeScript will show an error like:
Static property 'name' conflicts with built-in property 'Function.name' of constructor function 'UserDto'.
Write a decorator with an interface as suggested in the pull request and use it until the desired functionality appears in Nest.js.
The decorator adds the name property with the needed value to the wrapper class for the DTO.
type Constructor<T = object> = new(...args: any[]) => T;
type Wrapper<T = object> = { new(): (T & any), prototype: T };
type DecoratorOptions = { name: string };
type ApiSchemaDecorator = <T extends Constructor>(options: DecoratorOptions) => (constructor: T) => Wrapper<T>;
const ApiSchema: ApiSchemaDecorator = ({ name }) => {
return (constructor) => {
const wrapper = class extends constructor { };
Object.defineProperty(wrapper, 'name', {
value: name,
writable: false,
});
return wrapper;
}
}
Use as suggested in the proposal:
#ApiSchema({ name: 'User' }) // <- here
class UserDto {
#ApiProperty()
firstName: string;
// ...
}
And don't forget that in TypeScript 5 the decorator API will change to something close to the implementation in JavaScript 😉

I solved in my case using #ApiModel
like this
#ApiModel(value="MeuLindoDto")
public class NameOriginalClassResponseDto ...

Related

Serializing/Deserializing an object that is derived from Java.Lang.Object throws exception (using System.Text.Json)

In one of my .net7-android project, I am trying to serialize an object using System.Text.Json. My object is derived from Java.Lang.Object. I am not intrested in serializing/deserializing the base class (Java.Lang.Object).
The exception I am getting is "Serialization and deserialization of 'System.Type' instances are not supported.". Anyone has any ideas how can it be fixed?
One of the public properties declared by Java.Lang.Object must (directly or indirectly) return an object of type System.Type, thereby causing the exception. Since you don't want to serialize any of these properties anyway, you could create a custom JsonTypeInfo modifier that excludes all properties declared by Java.Lang.Object.
First, define the following extension methods:
public static class JsonExtensions
{
public static Action<JsonTypeInfo> IgnorePropertiesDeclaredBy(Type declaringType)
=> (Action<JsonTypeInfo>) (typeInfo =>
{
if (typeInfo.Kind != JsonTypeInfoKind.Object || !declaringType.IsAssignableFrom(typeInfo.Type))
return;
foreach (var property in typeInfo.Properties)
if (property.GetDeclaringType() == declaringType)
property.ShouldSerialize = static (obj, value) => false;
});
public static Action<JsonTypeInfo> IgnorePropertiesDeclaredBy<TDeclaringType>() => IgnorePropertiesDeclaredBy(typeof(TDeclaringType));
public static Type? GetDeclaringType(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo)?.DeclaringType;
}
And now you can use JsonExtensions.IgnorePropertiesDeclaredBy<Java.Lang.Object>() to omit all properties declared by Java.Lang.Object when serializing instances of derived types like so:
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { JsonExtensions.IgnorePropertiesDeclaredBy<Java.Lang.Object>() },
},
// Add other options as required
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
var json = JsonSerializer.Serialize(myJavaObject, options);
Note that this will only suppress properties declared by some base type. Suppressing properties declared by an interface that the type implements is not implemented.
Demo fiddle using a mockup of Java.Lang.Object here: https://dotnetfiddle.net/8vNQS6.

Mock data generation for storybook

I am trying to generate mock data using relay for storybook.
My query is
const QUERY_LIST = graphql`
query modelControllerAllUsersQuery #relay_test_operation {
allUsers {
pageInfo {
hasNextPage
}
edges {
node {
id
firstName
lastName
}
}
}
}
`
and provided RelayEnvironmentProvider as a decorator to the story. I'm trying to return some default values to my query using custom mock resolvers.
const customMockResolvers = {
...mockResolvers,
allUsers:() => ({
pageInfo:{
hasNextPage:false,
},
edges:[
{
node:{
id :'id',
firstName:'fname',
lastName :'lname',
},
},
],
}),
};
and calling it as
(operation) => MockPayloadGenerator.generate(operation, customMockResolvers)
I don't seem to be able to get the default values returned.
Currently, it is returning
{"allUsers":{"pageInfo":{"hasNextPage":false},"edges":[{"node":{"id":"<UserNode-mock-id-1>","firstName":"<mock-value-for-field-\"firstName\">","lastName":"<mock-value-for-field-\"lastName\">"}}]}}
What am I doing wrong?
When using the #relay-test-operation, the keys within your customMockResolvers object must match the type name of the fields, which can be different from the field names themselves.
For example, you could have the following in your schema:
type Foo {
id: ID!
name: String!
}
and the following query:
query FooQuery #relay_test_operation {
foo {
id
name
}
}
Then the customMockResolvers object would look like this:
const customMockResolvers = {
Foo: () => ({
id: "fooId",
name: "fooName"
})
}
Notice that I'm passing in Foo as the key instead of foo.
You can check your schema and see what the the type name of allUsers is. I suspect it would be something like AllUsers or allUsersConnection, or something similar.
Also, if you're interested in creating Storybook stories for Relay components, I created a NPM package just for that: https://www.npmjs.com/package/use-relay-mock-environment
It doesn't require adding the #relay-test-operation directive to your query, and instead relies only on resolving the String type (which is the default for all scalar properties). You can of course still add the #relay-test-operation directive and also extend the resolvers by providing customResolvers in the config.
You can also extend the the String resolver as well, by providing extendStringResolver in the config.
Feel free to review the source code here if you want to implement something similar: https://github.com/richardguerre/use-relay-mock-environment.
Note: it's still in its early days, so some things might change, but would love some feedback!

Autofac get decorated QueryHandler by convention based on constructor parameter name?

We inject IQueryHandler<TQUery,TResult> into our MVC controllers. We globally register all of these in the container
We have written a decorator that can cache the results of IQueryHandler.
We want to sometimes get cached reults and other times not from the same handler.
Is it possible to conditionally get a decorated handler based on the name of the constructor parameter. e.g. inject IQueryHandler<UnemployedQuery, IEnumerable<People>> cachedPeopleHandler if we prefix constructor parameter name with cached we actually get it wrapped with decorator?
Just trying to use a more convention over configuration approach to simplify things.
Yes it's possible to do it. Below is a simple working example on how you can achieve it:
class Program
{
public interface IQueryHandler{}
private class QueryHandler : IQueryHandler
{
}
private class CacheQueryHandler : IQueryHandler
{
}
public interface IService
{
}
private class Service : IService
{
private readonly IQueryHandler _queryHandler;
private readonly IQueryHandler _cacheQueryHandler;
public Service(IQueryHandler queryHandler, IQueryHandler cacheQueryHandler)
{
_queryHandler = queryHandler;
_cacheQueryHandler = cacheQueryHandler;
}
public override string ToString()
{
return string.Format("_queryHandler is {0}; _cacheQueryHandler is {1}", _queryHandler,
_cacheQueryHandler);
}
}
static void Main(string[] args)
{
var builder = new ContainerBuilder();
// Register the dependency
builder.RegisterType<QueryHandler>().As<IQueryHandler>();
// Register the decorator of the dependency
builder.RegisterType<CacheQueryHandler>().Keyed<IQueryHandler>("cache");
// Register the service implementation
builder.RegisterType<Service>().AsSelf();
// Register the interface of the service
builder.Register(c =>
{
var ctor = typeof (Service).GetConstructors()[0];
var parameters =
ctor.GetParameters()
.Where(p => p.Name.StartsWith("cache"))
.Select(p => new NamedParameter(p.Name, c.ResolveKeyed("cache", p.ParameterType)));
return c.Resolve<Service>(parameters);
}).As<IService>();
using (var container = builder.Build())
{
var service = container.Resolve<IService>();
Console.WriteLine(service.ToString());
Console.ReadKey();
}
}
}
Update:
Basically you need to:
1. Think up a general convention. Prefix "cache" of ctor parameter name in your case.
2. Register your dependencies as usual.
3. Register your decorators, so they don't overwrite your original dependencies and you can easily resolve them basing on your convention. e.g. Keyed, Named, via Attribute, etc.
4. Register you actual implementation of class that uses decorators
5. Register your interface that describes the class via lambda expression that has all magic inside.
Note: I provided just a simple and working example. It's on you to make it nice, easy to use and fast e.g. make it as an extension, generic, cache reflection results etc. It's not difficult anyway.
Thanks.

Ninject binds to the wrong anonymous method when there is more than one method bound type

I am building a framework that I don't want to couple to a particular IOC container so have created a layer on top of Ninject / structuremap etc.
I have a binding class that accepts a Func to allow binding to a method.
For example
public class Binding
{
public Type Source { get; set; }
public Func<object> Method {get; set; }
public Scope { get; set; }
}
If I have a binding like...
var binding = new Binding() {
Source = typeof(IRepository),
Method = () => new Repository(new LinqToSqlRepository(connectionString)),
Scope = Scope.HttpRequest
};
The framework wrapping Ninject creates Ninject bindings for my generic binding like this
Module :NinjectModule
{
IList<Binding> _Bindings;
public Module(IList<Binding> bindings)
{
_Bindings = bindings;
}
public override void Load() {
foreach (var binding in _Bindings) {
switch(binding.Scope) {
case IocScope.HttpRequest:
Bind(binding.Source).ToMethod(c => binding.Method()).InRequestScope();
break;
// ... omitted for brevity
}
}
}
}
This works fine when there is only one binding being bound to a method. When there are multiple bindings being bound within the same module to methods however the incorrect type is returned. From debugging, it looks as if the last binding is always used.
Thus the problem with an example;
var binding1 = new Binding() {
Source = typeof(IRepository),
Method = () => new Repository(new LinqToSqlRepository(connectionString)),
Scope = Scope.HttpRequest
};
var binding2 = new Binding() {
Source = typeof(ICalendar),
Method = () => new MvcCalendar( ..... )
Scope = Scope.HttpRequest
};
At runtime when Ninject is requested to new up an MVC Controller which takes in an IRepository and an ICalendar, I receive a type conversion error saying that a MvcCalendar cannot be converted to an IRepository. I have discovered that for some reason the last binding is always being returned for the first requested type.
This is a highly simplified version of what is really going on to try and highlight the actual issue, the wrong method being bound to a requested type when there are multiple method bindings. I hope this still explains the issue though.
This appears to be related to some sort of closure scoping issue. I also wonder whether Ninject is getting is getting confused by the Func instead of Func usage.
Unit Test Example
Here is a test module I load into my custom IOC container. This does not depend on any particular IOC framework. When I instantiate a NinjectIocContainer to handle the DI, the internal binding of this in Ninject occurs as example further up (see NinjectModule)
public class MultipleMethodBoundTypesModule : IocModule
{
public override void Load()
{
Bind<IPerson>().To(() => new Person()).In(IocScope.Transient);
Bind<IRobot>().To(() => new Robot(new Person())).In(IocScope.Transient);
}
}
Here is a simple test that tries to retrieve each of the types.
[Test]
public void Expect_That_Multiple_Method_Bound_Types_Can_Exist_Within_The_Same_Module()
{
// arrange
var container = Get_Container_With_Module(new MultipleMethodBoundTypesModule());
// act
var person = container.Get<IPerson>();
var robot = container.Get<IRobot>();
// assert
Assert.IsNotNull(person);
Assert.IsNotNull(robot);
}
As explained eariler, this throws a type conversion where the last closure (for the robot) is being bound to a person.
TestCase 'Ioc.Test.NinjectContainerTest.Expect_That_Multiple_Method_Bound_Types_Can_Exist_Within_The_Same_Module'
failed: System.InvalidCastException : Unable to cast object of type 'Ioc.Test.Robot' to type 'Ioc.Test.IPerson'.
at System.Linq.Enumerable.d__b11.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters)
NinjectIocContainer.cs(40,0): at Ioc.Ninject.NinjectIocContainer.GetTInstance
IocTestBase.cs(149,0): at Ioc.Test.IocTestBase.Expect_That_Multiple_Method_Bound_Types_Can_Exist_Within_The_Same_Module()
In the snippet:
Bind(binding.Source).ToMethod(binding.Method()).InRequestScope();
You're dereferencing the Method bit. You want to be doing that as either binding.Method or ()=>binding.Method() (the former may not unambiguously be inferrable based on the C# type inference rules).
You mentioned this is heavily stripped down from your real code. As a result, this may not be the actual issue. I'd still be betting on some form of closure confusion though (see the section Comparing capture strategies: complexity vs power in this CSID excerpt for a nice walkthrough).
You also probably meant to use .InScope(binding.Scope) rather than .InRequestScope() too,.

Set Inner Dependency by Type using Structuremap

I have a structuremap configuration that has me scratching my head. I have a concrete class that requires a interfaced ui element which requires an interfaced validation class. I want the outer concrete class to get the default ui element, but get a concrete-class-specific validation object. Something like this:
class MyView
{
IPrompt prompt
}
class GenericPrompt : IPrompt
{
IValidator validator
}
class MyValidator : IValidator
{
bool Validate() {}
}
How can I configure structuremap with the Registry DSL to only use MyValidator when creating dependencies for MyView. (And assumedly using BobsValidator when creating dependencies for BobsView)
Are you getting MyView (and BobsView) from the container? Can we assume that they will all take an instance of IPrompt?
One approach would be to register all of your validators with a name that matches the names of your view. You could implement your own type scanner that just removes the Validator suffix:
public class ValidatorScanner : ITypeScanner
{
public void Process(Type type, PluginGraph graph)
{
if (!typeof (IValidator).IsAssignableFrom(type)) return;
var validatorName = type.Name.Replace("Validator", "");
graph.AddType(typeof(IValidator), type, validatorName);
}
}
Now, if you assume an IPrompt will always be requested by a View that follows that naming convention, your registry could look like:
public class ValidatorRegistry : Registry
{
public ValidatorRegistry()
{
Scan(scan =>
{
scan.TheCallingAssembly();
scan.With<ValidatorScanner>();
});
ForRequestedType<IPrompt>().TheDefault.Is.ConstructedBy(ctx =>
{
var viewName = ctx.Root.RequestedType.Name.Replace("View", "");
ctx.RegisterDefault(typeof(IValidator), ctx.GetInstance<IValidator>(viewName));
return ctx.GetInstance<GenericPrompt>();
});
}
}
To retrieve your view with the appropriate validator, you would have to request the concrete type:
var view = container.GetInstance<MyView>();
Note that this will only work if you are retrieving your view with a direct call to the container (service location), since it depends on the "Root.RequestedType". Depending on how you plan to get your views, you might be able to walk up the BuildStack looking for a View (instead of assuming it is always Root).

Resources