Nickolaki
Nickolaki
Explore posts from servers
DTDrizzle Team
Created by Nickolaki on 8/16/2023 in #help
Db race condition
Is there any tool with drizzle that can help with handling race conditions?
38 replies
DTDrizzle Team
Created by Nickolaki on 8/13/2023 in #help
Query where clause with array.
How can I get something like this to work?
const products = ["subscription", "insurance"]

const productsToLink = await tx.query.products.findMany({
where: (products, { eq }) => eq(products.name, products),
});
const products = ["subscription", "insurance"]

const productsToLink = await tx.query.products.findMany({
where: (products, { eq }) => eq(products.name, products),
});
I want the where clause to match any product that has a name thats inside the products array.
4 replies
DTDrizzle Team
Created by Nickolaki on 8/11/2023 in #help
Insert One to Many
How can I insert relations as well at create the parent?
import { Customer } from "@/types/customer";
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import { db } from "@/lib/db";
import { journeys } from "@/lib/db/schema";
import { v4 as uuidv4 } from "uuid";

export async function POST(req: Request) {
const body = (await req.json()) as Customer;
// Body has product refs...

const journeyRef = uuidv4();

await db.transaction(async (tx) => {
await tx.insert(journeys).values({ ...body, journeyRef });
// Then need to link products to that journey...
});

cookies().set({
name: "journeyRef",
value: journeyRef,
httpOnly: true,
path: "/",
});

return NextResponse.json({});
}
import { Customer } from "@/types/customer";
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import { db } from "@/lib/db";
import { journeys } from "@/lib/db/schema";
import { v4 as uuidv4 } from "uuid";

export async function POST(req: Request) {
const body = (await req.json()) as Customer;
// Body has product refs...

const journeyRef = uuidv4();

await db.transaction(async (tx) => {
await tx.insert(journeys).values({ ...body, journeyRef });
// Then need to link products to that journey...
});

cookies().set({
name: "journeyRef",
value: journeyRef,
httpOnly: true,
path: "/",
});

return NextResponse.json({});
}
My schema:
import { mysqlTable, serial, text } from "drizzle-orm/mysql-core";
import { relations } from "drizzle-orm";

export const journeys = mysqlTable("journey", {
id: serial("id").primaryKey(),
journeyRef: text("journeyRef"),
customer_firstName: text("customer_firstName"),
customer_lastName: text("customer_lastName"),
customer_emailAddress: text("customer_emailAddress"),
type: text("type"),
});

export const products = mysqlTable("product", {
id: serial("id").primaryKey(),
name: text("name"),
});

export const journeysRelations = relations(journeys, ({ many }) => ({
products: many(products),
}));
import { mysqlTable, serial, text } from "drizzle-orm/mysql-core";
import { relations } from "drizzle-orm";

export const journeys = mysqlTable("journey", {
id: serial("id").primaryKey(),
journeyRef: text("journeyRef"),
customer_firstName: text("customer_firstName"),
customer_lastName: text("customer_lastName"),
customer_emailAddress: text("customer_emailAddress"),
type: text("type"),
});

export const products = mysqlTable("product", {
id: serial("id").primaryKey(),
name: text("name"),
});

export const journeysRelations = relations(journeys, ({ many }) => ({
products: many(products),
}));
I want to add products to the insert for creating the new journey. Currently using a transaction but not sure what the code needs to be for linking products to the journey?
52 replies
CC#
Created by Nickolaki on 8/5/2023 in #help
✅ Override JWT Bearer service for Tests
My app uses this setup for its authentication:
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
...

options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
}
);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
...

options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
}
);
I then usually extract my userId's like this:
public string UserId()
{
return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
public string UserId()
{
return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
What would be a good way to override this authentication so that I don't have to use real Clerk https://clerk.com/ JWT's during testing. I'm thinking that I simply override these params:
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
For "Test" JWT's and use a helper function to create these during my tests?
12 replies
CC#
Created by Nickolaki on 8/4/2023 in #help
❔ ConfigureTestServices not overriding connection string value :(
Hi! I'm so lost as to why this isn't working. Currently the RemoveDbContext removes the service as expected. And I get no exceptions from re-adding with the new config. I have debugged that if I change my development appsettings to the hardcoded connection string from the created container it works fine. But as soon as I change dev connection string back to what it is I get db login issues/couldn't establish connection issues. So it must not be overriding the config. Any ideas on how I can fix this?
public class JobbyHttpApiFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
public string JobbyDbConnectionString { get; set; } = string.Empty;
private readonly MsSqlContainer _mssqlContainer;
private readonly IConfiguration _configuration;
public HttpClient HttpClient { get; private set; } = null!;

public async Task InitializeAsync()
{
await _mssqlContainer.StartAsync();

JobbyDbConnectionString = _mssqlContainer.GetConnectionString();
}

public new async Task DisposeAsync()
{
...
}

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.RemoveDbContext<JobbyDbContext>();

services.AddDbContext<JobbyDbContext>(options =>
{
options.UseSqlServer(_mssqlContainer.GetConnectionString());
});

services.EnsureDbCreatedAsync<JobbyDbContext>().ConfigureAwait(true);
});
}
}
public class JobbyHttpApiFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
public string JobbyDbConnectionString { get; set; } = string.Empty;
private readonly MsSqlContainer _mssqlContainer;
private readonly IConfiguration _configuration;
public HttpClient HttpClient { get; private set; } = null!;

public async Task InitializeAsync()
{
await _mssqlContainer.StartAsync();

JobbyDbConnectionString = _mssqlContainer.GetConnectionString();
}

public new async Task DisposeAsync()
{
...
}

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.RemoveDbContext<JobbyDbContext>();

services.AddDbContext<JobbyDbContext>(options =>
{
options.UseSqlServer(_mssqlContainer.GetConnectionString());
});

services.EnsureDbCreatedAsync<JobbyDbContext>().ConfigureAwait(true);
});
}
}
public static class ServiceCollectionExtensions
{
public static void RemoveDbContext<T>(this IServiceCollection services) where T : DbContext
{
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(T));
if (descriptor != null)
{
services.Remove(descriptor);
}
}
}
public static class ServiceCollectionExtensions
{
public static void RemoveDbContext<T>(this IServiceCollection services) where T : DbContext
{
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(T));
if (descriptor != null)
{
services.Remove(descriptor);
}
}
}
11 replies
CC#
Created by Nickolaki on 8/4/2023 in #help
❔ Unable to get sqlcmd working on Apple Silicon
I'm running a test Docker container on my M2 Mac and the test always get stuck on this command and just repeatedly tries to fire it. I've cloned my repo on my PC (Windows) and it runs fine and creates the DB tables as expected. Does anyone have any advice on how I can fix this?
[testcontainers.org 00:03:07.98] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container 6cf4885c1216
[testcontainers.org 00:03:07.98] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container 6cf4885c1216
5 replies
CC#
Created by Nickolaki on 8/2/2023 in #help
❔ Change default behaviour of .NET Web API Validation Errors
Hi does anyone know how I can override the default problem details/validation error response .net gives when things like Guid and DateTime's are in an incorrect format?
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-9f2acabaefdccec268c6f8bc2594d0ba-bdd955faf7e6202a-00",
"errors": {
"command": [
"The command field is required."
],
"$.boardId": [
"The JSON value could not be converted to System.Guid. Path: $.boardId | LineNumber: 0 | BytePositionInLine: 30."
]
}
}
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-9f2acabaefdccec268c6f8bc2594d0ba-bdd955faf7e6202a-00",
"errors": {
"command": [
"The command field is required."
],
"$.boardId": [
"The JSON value could not be converted to System.Guid. Path: $.boardId | LineNumber: 0 | BytePositionInLine: 30."
]
}
}
I have implemented my own validation error responses but can't seem to avoid these type of ones firing^ Mine look like this:
{
"status": 422,
"message": "Validation errors occurred",
"errors": [
{
"property": "BoardId",
"message": "Board Id is required."
}
]
}
{
"status": 422,
"message": "Validation errors occurred",
"errors": [
{
"property": "BoardId",
"message": "Board Id is required."
}
]
}
FYI: my validation logic is driven by FluentValidation, I'm not using attributes etc.
4 replies
CC#
Created by Nickolaki on 3/9/2023 in #help
❔ Identity Framework Users with OAuth clients.
https://i.imgur.com/A5jjfhk.png I'm following the following drawing in my NextJs frontend and .Net 6 Web API. My implementation is working as the diagram states, however, I've run into the following issue: - The JWT that I sign and give back to my frontend has the Id and Username from the Github but I'm using Identity Framework to store my users. Technically my app doesn't need an identity user to be stored to run, my object UserId property would just have a Github, Google, Facebook user ids. But as you can tell this is far from ideal, what do you think would be a good solution here? I had thought of maybe checking if a user with that email exists in the database first, if not create the user. But I believe Identity Framework would require the users password which I cannot provide. Here is some code snippets to give further context:
[HttpPost("exchange-token/github", Name = "ExchangeGithubToken")]
public async Task<ActionResult<ExchangeTokenResponse>> ExchangeGithubToken([FromBody] ExchangeTokenRequest request)
{
var result = await _authenticationService.ExchangeGithubToken(request);

return Ok(result);
}
[HttpPost("exchange-token/github", Name = "ExchangeGithubToken")]
public async Task<ActionResult<ExchangeTokenResponse>> ExchangeGithubToken([FromBody] ExchangeTokenRequest request)
{
var result = await _authenticationService.ExchangeGithubToken(request);

return Ok(result);
}
public async Task<ExchangeTokenResponse> ExchangeGithubToken(ExchangeTokenRequest request)
{
var httpClient = _httpClientFactory.CreateClient("Github");

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", request.Token);

var response = await httpClient.GetAsync("/user");

if (!response.IsSuccessStatusCode) throw new NotImplementedException();

var content = await response.Content.ReadAsStringAsync();

var user = JsonSerializer.Deserialize<GithubUser>(content);

var token = _jwtTokenGenerator.GenerateToken(user.Id.ToString(), user.Login);

return new ExchangeTokenResponse(token);
}
public async Task<ExchangeTokenResponse> ExchangeGithubToken(ExchangeTokenRequest request)
{
var httpClient = _httpClientFactory.CreateClient("Github");

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", request.Token);

var response = await httpClient.GetAsync("/user");

if (!response.IsSuccessStatusCode) throw new NotImplementedException();

var content = await response.Content.ReadAsStringAsync();

var user = JsonSerializer.Deserialize<GithubUser>(content);

var token = _jwtTokenGenerator.GenerateToken(user.Id.ToString(), user.Login);

return new ExchangeTokenResponse(token);
}
Any help/advice would be much appreciated! 🤘
2 replies
CC#
Created by Nickolaki on 3/7/2023 in #help
❔ Querying Many to Many EF Relationships
What is the best way to query Many to Many relationships? I'm using Ardalis.Specification package and this is my current workaround to get a Job's list of Contacts. It feels like there is something simpler staring at me 🤣
Content: I'm using JobContact in either side of this relationship.
public sealed class GetJobContactsSpecification : Specification<Job, List<Contact>>
{
public GetJobContactsSpecification(Guid jobId, string userId)
{
Query
.Select(x => x.Contacts.ToList())
.Include(x => x.Contacts)
.ThenInclude(x => x.Companies)
.Include(x => x.Contacts)
.ThenInclude(x => x.Phones)
.Include(x => x.Contacts)
.ThenInclude(x => x.Emails)
.AsSplitQuery()
.AsNoTracking()
.Where(x => x.Id == jobId && x.OwnerId == userId);
}
}
public sealed class GetJobContactsSpecification : Specification<Job, List<Contact>>
{
public GetJobContactsSpecification(Guid jobId, string userId)
{
Query
.Select(x => x.Contacts.ToList())
.Include(x => x.Contacts)
.ThenInclude(x => x.Companies)
.Include(x => x.Contacts)
.ThenInclude(x => x.Phones)
.Include(x => x.Contacts)
.ThenInclude(x => x.Emails)
.AsSplitQuery()
.AsNoTracking()
.Where(x => x.Id == jobId && x.OwnerId == userId);
}
}
public class Job : Entity
{
// Database Relationship Properties
public List<JobContact> JobContacts { get; set; }

// Rest of properties....
}
public class Job : Entity
{
// Database Relationship Properties
public List<JobContact> JobContacts { get; set; }

// Rest of properties....
}
public class Contact : Entity
{
// Database Relationship Properties
public List<JobContact> JobContacts { get; set; }

// Rest of properties....

}
public class Contact : Entity
{
// Database Relationship Properties
public List<JobContact> JobContacts { get; set; }

// Rest of properties....

}
public class JobContact
{
public Guid JobId { get; set; }
public Job Job { get; set; }

public Guid ContactId { get; set; }
public Contact Contact { get; set; }

}
public class JobContact
{
public Guid JobId { get; set; }
public Job Job { get; set; }

public Guid ContactId { get; set; }
public Contact Contact { get; set; }

}
3 replies
CC#
Created by Nickolaki on 8/25/2022 in #help
How to use RedirectToAction(), RedirectToRoute() or Redirect() to specific endpoints on this Action?
3 replies