Make type aliases known in Swashbuckle for ASP.NET Core - swashbuckle

We have some type abstractions to make generic values typeable, like specific Guid types.
public sealed class TenantId
{
public Guid Value { get; set; }
}
We also have associated JsonConverter so that ASP.NET Core serializes the corresponding values correctly in our API models to / from plain Guid values.
public class MyDemoApiModel
{
public TenantId TenantId { get; set; }
}
// Json
{
"tenantId: "fd0aeaf0-8303-45f6-8797-74670768f85e"
}
Nevertheless, Swashbuckle now generates and OpenAPI curious data:
- name: TenantId.Value
in: path
required: true
schema:
type: string
format: uuid
- name: tenantId
in: path
required: true
schema:
type: string
What do we need to configure in Swashbuckle so that the Custom Type Abstraction is correctly represented as a Guid?

Related

Type 'string' is not assignable to type 'Date nest js unit testing

I created a sample project with CRUD using NestJs. I create a User with a randomly generated Id, name, birthdate and email. I need to do unit testing for this create CRUD operation. But I do not know how to test the birthDate. It gives me an error.
This is what I got when I tried to pass the date
User Entity:
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
} from 'typeorm';
#Entity('users')
export class User {
//auto increment
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ nullable: true, default: null })
name: string;
#CreateDateColumn({ nullable: true })
birthDate: Date;
#Column({ type: 'varchar' })
email: string;
}
You're passing a string, but typescript expect Date.
try changing birthDate: '2000.6.7' to birthDate: new Date('2000-06-07').

Loopback 4 OpenAPI connector: Specify Authorization header value per request

I have set up an OpenAPI connector in Loopback 4 as described here and for unauthorized requests, it is working well; I managed to create the respective datasource, service and controller. My service is similar to the GeocoderProvider example, but, let's say, with the following service interface.
export interface MyExternalService {
search_stuff(params: {query?: string}): Promise<MyExternalServiceResponse>;
}
export interface MyExternalServiceResponse {
text: string;
}
From my controller, I invoke it like this, where this.myExternalService is the injected service (kind of unrelated, but can Loopback also implicitly parse a JSON response from an external API datasource?):
#get('/search')
async searchStuff(#param.query.string('query') query: string): Promise<void> {
return JSON.parse(
(await this.myExternalService.search_stuff({query})).text,
);
}
Now, the external endpoint corresponding to myExternalService.search_stuff needs an Authorization: Bearer <token> header, where the token is sent to Loopback by the client, i.e. it's not a static API key or so. Assuming I added #param.query.string('token') token: string to the parameter list of my searchStuff controller method, how can I forward that token to the OpenAPI connector? This is the relevant part of the underlying OpenAPI YAML definition file:
paths:
/search:
get:
security:
- Authorization: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResults'
operationId: search-stuff
components:
securitySchemes:
Authorization:
type: http
scheme: Bearer
I am now using the underlying execute function of the OpenAPI connector and manually intercept the request (the object that is passed to requestInterceptor is later passed directly to the http module by Swagger):
return JSON.parse(
(
await this.myExternalService.execute(
'search_stuff',
{query},
{
requestInterceptor: (req: {headers: {Authorization: string}}) => {
req.headers.Authorization = 'Bearer ' + token;
return req;
},
},
)
).text,
);
I also added the following method to the MyExternalService interface, inspired by the connector's actual execute function:
execute(
operationId: string,
parameters: object,
options: object,
): Promise<MyExternalServiceResponse>;
Some things I found:
Loopback internally uses the swagger-client module to do OpenAPI-based requests.
Specifically the securities option of Swagger's execute function expects a Security Definitions Object. There are some quirks with actually passing it to Swagger as well.
Internally, Swagger builds the final HTTP request that is sent out here in its source code. There, the securities key is mentioned, yet is is never actually used for the request. This means that manually specifying it in the third parameter of this.myExternalService.execute will change nothing.
I'll not accept this answer yet and I'm looking forward to finding a more Loopback-like approach.
I configured my service like this, to inject the basic authentication.
import {inject, lifeCycleObserver, LifeCycleObserver} from '#loopback/core';
import {juggler} from '#loopback/repository';
const SwaggerClient = require('swagger-client');
const config = {
name: 'jira',
connector: 'openapi',
spec: 'swagger-v2.json',
validate: false,
httpClient: (request: any) => {
request.headers["Authorization"] = "Basic " + Buffer.from("test:test").toString('base64');
return SwaggerClient.http(request);
},
};
#lifeCycleObserver('datasource')
export class JiraDataSource extends juggler.DataSource
implements LifeCycleObserver {
static dataSourceName = 'jira';
static readonly defaultConfig = config;
constructor(
#inject('datasources.config.jira', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}

TypeORM: How to set ForeignKey explicitly without having property for loading relations?

I don't want to create a property for loading relation into it (as shown in all the examples). The only thing I need is to have an explicit foreign key property so that the migration will be able to create appropriate constraints for it in the database. The closest decorator to the one I need is #RelationId but it still requires the presence of a property of the relational class.
For clarity let's take the example from the documentation:
#Entity()
export class Post {
#ManyToOne(type => Category)
category: Category;
#RelationId((post: Post) => post.category) // it still requires the presence of the `category` proeprty
categoryId: number;
}
I don't need the category property here. I want to have the categoryId property and mark it as foreign key to Category.Id. It should look like this:
#Entity()
export class Post {
#ForeignKey((category: Category) => category.Id) // it's a foreign key to Category.Id
categoryId: number;
}
Is it possible?
"I need is to have an explicit foreign key property"...
No, you could not. TypeOrm will automatically create foreign key property when you use #ManyToOne decorator. Just combine #ManyToOne and #JoinColumn decorators together like this:
#ManyToOne(type => Category)
#JoinColumn({ name: 'custom_field_name_if_you_want' })
category: Category;
Maybe you can create and write your own migration and use it like this :
const queryRunner = connection.createQueryRunner();
await queryRunner.createTable(new Table({
name: "question",
columns: [
{
name: "id",
type: "int",
isPrimary: true
},
{
name: "name",
type: "varchar",
}
]
}), true);
await queryRunner.createTable(new Table({
name: "answer",
columns: [
{
name: "id",
type: "int",
isPrimary: true
},
{
name: "name",
type: "varchar",
},
{
name: "questionId",
isUnique: connection.driver instanceof CockroachDriver, // CockroachDB requires UNIQUE constraints on referenced columns
type: "int",
}
]
}), true);
// clear sqls in memory to avoid removing tables when down queries executed.
queryRunner.clearSqlMemory();
const foreignKey = new TableForeignKey({
columnNames: ["questionId"],
referencedColumnNames: ["id"],
referencedTableName: "question",
onDelete: "CASCADE"
});
await queryRunner.createForeignKey("answer", foreignKey);
This code snippet is extracted from the functional test of type orm and you can use it to create your own constraint on the database I think.
It's actually possible to do so:
#Entity()
export class Post {
// this will add categoryId
#ManyToOne(type => Category)
category: Category;
// and you can use this for accessing post.categoryId
// only column you mark with #Column decorator will be mapped to a database column
// Ref: https://typeorm.io/#/entities
categoryId: number;
}
The added categoryId won't be mapped to column and will then be use for setting explicitly the id or for accessing its value as in:
post.categoryId = 1;
// or
const id = post.categoryId
Check with these two places Auth module(JwtModule.register()) and JWT strategy(super({...})). Make sure you have secret /secretOrKey is set to the same key. In my case "secret: process.env.JWT_SECRET_KEY" & "secretOrKey: process.env.JWT_SECRET_KEY"
I have encountered the same problem recently.
I still use the Entity but only with the primary key value of the referenced entity.
i.e. I do not query the database for the referenced entity.
Suppose your category entity looks like this:
#Entity()
export class Category{
#PrimaryGeneratedColumn()
id: number;
// ... other stuff
}
Now using your codes as example.
Dircely assigning relation using a foreign key value would be like.
// You wish to assign category #12 to a certain post
post.category = { id: 12 } as Category

How to pass IEnumerable<Category> as Sitecore ViewRendering model

Suppose you have a model class in MVC called Category (which has id and name fields)
Is there any way I can return IEnumerable<Category> as view rendering model to MVC view file?
You have a few choices to get your data to your ASP.NET MVC View in Sitecore. All of my examples below will be from the community MVC project that is available for your review. That can be found here:
https://github.com/Sitecore-Community/sample-sitecore-mvc
View Renderings
To do what you are trying to do, having a custom model in a view rendering. You need to create a model that inherits from IRenderingModel. Using IRenderingModel requires you have a function called Initialize. In Initialize you will create the logic you need to build your IEnumerable<Catergory>. If you want the details of your category to be editable, you will need to make sure you return your model fields as HtmlString types.
I want to note that most developers do not use this method because it is not testable.
public class Car : IRenderingModel
{
public HtmlString Make { get; set; }
public HtmlString Model { get; set; }
// Rendering represents the context rendering - this particular model is only ever used by View Renderings
public Sitecore.Mvc.Presentation.Rendering Rendering { get; set; }
// Item represents the rendering's datasource, and PageItem represents the context page
// These properties exist on Sitecore's own RenderingModel object
public Item Item { get; set; }
public Item PageItem { get; set; }
public void Initialize(Sitecore.Mvc.Presentation.Rendering rendering)
{
// Use the Rendering object passed in by Sitecore to set the datasource Item and context PageItem properties
Rendering = rendering;
Item = rendering.Item;
PageItem = PageContext.Current.Item;
// Set property values using FieldRenderer to ensure that the values are editable in the Page Editor
Make = new HtmlString(FieldRenderer.Render(Item, "Make"));
Model = new HtmlString(FieldRenderer.Render(Item, "Model"));
}
}
Then in /sitecore/layout/Models you will create a new model for your rendering. In the type field you specify the model you create above. In the example it is MVC.Data.Models.Car.
Finally in your view rendering, you specify the model class and use the Model data.
#model MVC.Data.Models.Car
<!-- To set the 'Background' parameter, go into the Page Editor > select 'Design' mode, click on the Featured Car component >
click More > click Edit component properties > Scroll down to the 'Parameters' section -->
<div class="highlight" style="background-color: ##Model.Rendering.Parameters["Background"]">
<h3>This week's featured car</h3>
<p><strong>Model:</strong> #Model.Model</p>
<p><strong>Make:</strong> #Model.Make</p>
</div>
Controller Renderings
In most cases when you have custom data, I recommend using a controler rendering. It is clean, reusable and testable.
Model
Create a traditional model for your rendering to use.
namespace MVC.Data.Models
{
/// <summary>
/// This light-weight model is used as an example of how you can isolate Sitecore-dependent code into a repository, which
/// means your models have no dependency on Sitecore and look much more like regular ASP.NET MVC models. See DriverController.cs
/// for usage.
/// </summary>
public class Driver
{
public HtmlString Name { get; set; }
public HtmlString Text { get; set; }
public HtmlString Image { get; set; }
}
}
View model
In the example, the author creates a View Model that has the Driver model, plus information about the rendering like the datasource item.
public class DriverViewModel
{
public Driver Driver { get; set; }
public string Background { get; set; }
public Item ContextItem { get; set; }
}
Controller
Then create a controller/repository to fill that model. Finally passing it to the rendering.
public ActionResult Featured()
{
var repository = _driverRepository;
var driver = repository.GetDriver();
var driverViewModel = repository.GetDriverViewModel(driver);
return View(driverViewModel);
}
Rendering
#model MVC.Data.Models.DriverViewModel
<!-- To set the 'Background' parameter, go into the Page Editor > select 'Design' mode, click on the Featured Driver component >
click More > click Edit component properties > Scroll down to the 'Parameters' section -->
<div class="highlight" style="background: ##Model.Background">
<h3>This week's featured driver</h3>
<p><strong>Name:</strong> #Model.Driver.Name</p>
<p><strong>Description:</strong></p> #Model.Driver.Text
</div>

Ember Resource URL: how to make it use other values rather than ID?

I need to make a Ember Resource to send its query using one of values in its schema.
Let's say I've defined this resource:
App.SomeModule = Ember.Resource.define(
{
url: './api/some',
schema:
{
id: Number,
fodder: Number,
whatever: Number,
units:
{
type: Ember.ResourceCollection,
itemType: 'App.OtherModule',
url: './api/other/%#'
}
}
});
When this resource need to load its 'units', it will automatically send a query using the url "api/other/(id). However I need to make it use other value such as (fodder) or (whatever), not id. I think (%#) need to be replaced but how?
You probably want to define url as a function instead:
units: {
url: function() {
// your custom code here:
return '/api/other/%#'.fmt(this.get('fodder'));
}
}