-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
361 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,71 @@ | ||
using AiTestimonials.Models; | ||
using AiTestimonials.Repository; | ||
using AiTestimonials.Services; | ||
using AiTestimonials.Util; | ||
using Microsoft.AspNetCore.Http.HttpResults; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace AiTestimonials.Api; | ||
|
||
public static class AiTestimonialsApi | ||
{ | ||
public static void RegisterAiTestimonialsEndpoints(this IEndpointRouteBuilder routes) | ||
public static void RegisterAiTestimonialsEndpoints(this IEndpointRouteBuilder routes) | ||
{ | ||
var route = routes.MapGroup("/api/v1/ai-testimonials").WithOpenApi(); | ||
route.MapPost("/generate", PostGenerateAiTestimonialAsync); | ||
route.MapPost("/redo", PostRedoTestimonialsAsync); | ||
route.MapPost("/save", PostSaveTestimonialsAsync); | ||
route.MapGet("", GetTestimonialsAsync); | ||
} | ||
|
||
private static async Task<Ok<TestimonialResult>> PostGenerateAiTestimonialAsync(string name, string skills, [FromHeader(Name = "OPENAI_KEY")] string? openAIKey, AiTestimonialsService service, VercelPostgresRepository repo) | ||
private static async Task<Ok<Identity>> PostGenerateAiTestimonialAsync(TestimonialInput input, [FromHeader(Name = "OPENAI_KEY")] string? openAIKey, AiTestimonialsService service, VercelPostgresRepository repo) | ||
{ | ||
service.SetupOpenAIService(openAIKey); | ||
var res = await service.GenerateAiTestimonialAsync(name, skills); | ||
await repo.CreatTestimonialsTableAsync(); | ||
await repo.AddTestimonialAsync(res); | ||
return TypedResults.Ok(res); | ||
var testimonialId = (await repo.CreateNewTestimonialAsync(input)).ToString(); | ||
try | ||
{ | ||
GenerateAiTestimonialAsync(testimonialId, input.Name, input.Skills, service, repo).Forget(); | ||
return TypedResults.Ok(new Identity() { Id = testimonialId.ToString() }); | ||
} | ||
catch | ||
{ | ||
await repo.UpdateTestimonialAsync(TestimonialStatus.FAILED, testimonialId); | ||
throw; | ||
} | ||
} | ||
|
||
private static async Task<IResult> PostRedoTestimonialsAsync(string id, AiTestimonialsService service, VercelPostgresRepository repo) | ||
{ | ||
var entity = await repo.GetTestimonialsEntityAsync(id); | ||
if (entity != null && entity.Status != TestimonialStatus.PENDING && entity.Status != TestimonialStatus.SAVED && entity.Input != null) | ||
{ | ||
await repo.UpdateTestimonialAsync(TestimonialStatus.PENDING, id); | ||
GenerateAiTestimonialAsync(id, entity.Input.Name, entity.Input.Skills, service, repo).Forget(); | ||
} | ||
else | ||
{ | ||
return TypedResults.BadRequest(); | ||
}; | ||
return TypedResults.Ok(); | ||
} | ||
|
||
private static async Task<Ok> PostSaveTestimonialsAsync(string id, VercelPostgresRepository repo) | ||
{ | ||
await repo.UpdateTestimonialAsync(TestimonialStatus.SAVED, id); | ||
return TypedResults.Ok(); | ||
} | ||
|
||
private static async Task<Ok<List<TestimonialResult>>> GetTestimonialsAsync(VercelPostgresRepository repo) | ||
private static async Task<Ok<List<TestimonialResult>>> GetTestimonialsAsync(string? id, VercelPostgresRepository repo) | ||
{ | ||
var res = await repo.GetTestimonialsAsync(); | ||
var res = await repo.GetTestimonialsAsync(id); | ||
return TypedResults.Ok(res); | ||
} | ||
|
||
private static async Task GenerateAiTestimonialAsync(string id, string name, string skills, AiTestimonialsService service, VercelPostgresRepository repo) | ||
{ | ||
var res = await service.GenerateAiTestimonialAsync(name, skills); | ||
await repo.CreatTestimonialsTableAsync(); | ||
await repo.AddTestimonialAsync(res, id); | ||
await repo.UpdateTestimonialAsync(TestimonialStatus.SUCCESSFUL, id); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
namespace AiTestimonials.Util; | ||
|
||
public static class TaskExtensions | ||
{ | ||
/// <summary> | ||
/// Observes the task to avoid the UnobservedTaskException event to be raised. | ||
/// </summary> | ||
public static void Forget(this Task task) | ||
{ | ||
// note: this code is inspired by a tweet from Ben Adams: https://twitter.com/ben_a_adams/status/1045060828700037125 | ||
// Only care about tasks that may fault (not completed) or are faulted, | ||
// so fast-path for SuccessfullyCompleted and Canceled tasks. | ||
if (!task.IsCompleted || task.IsFaulted) | ||
{ | ||
// use "_" (Discard operation) to remove the warning IDE0058: Because this call is not awaited, execution of the current method continues before the call is completed | ||
// https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards?WT.mc_id=DT-MVP-5003978#a-standalone-discard | ||
_ = ForgetAwaited(task); | ||
} | ||
|
||
// Allocate the async/await state machine only when needed for performance reasons. | ||
// More info about the state machine: https://blogs.msdn.microsoft.com/seteplia/2017/11/30/dissecting-the-async-methods-in-c/?WT.mc_id=DT-MVP-5003978 | ||
async static Task ForgetAwaited(Task task) | ||
{ | ||
|
||
try | ||
{ | ||
// No need to resume on the original SynchronizationContext, so use ConfigureAwait(false) | ||
await task.ConfigureAwait(false); | ||
} | ||
catch | ||
{ | ||
// Nothing to do here | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
--- | ||
swagger: '2.0' | ||
info: | ||
title: AI Testimonials | ||
description: API for generating fake testimonials | ||
version: 1.1.0 | ||
schemes: | ||
- https | ||
produces: | ||
- application/json | ||
x-google-backend: | ||
address: https://ai-testimonials-trtv3l63ra-ey.a.run.app | ||
paths: | ||
/api/v1/ai-testimonials/generate: | ||
post: | ||
tags: | ||
- AiTestimonialsApi | ||
operationId: "GenerateAiTestimonial" | ||
consumes: | ||
- application/json | ||
produces: | ||
- application/json | ||
parameters: | ||
- in: header | ||
name: OPENAI_KEY | ||
type: string | ||
- in: body | ||
name: body | ||
required: true | ||
schema: | ||
$ref: '#/definitions/TestimonialInput' | ||
responses: | ||
'200': | ||
description: OK | ||
schema: | ||
$ref: '#/definitions/Identity' | ||
/api/v1/ai-testimonials/redo: | ||
post: | ||
tags: | ||
- AiTestimonialsApi | ||
operationId: "RedoAiTestimonial" | ||
parameters: | ||
- in: query | ||
name: id | ||
required: true | ||
type: string | ||
collectionFormat: multi | ||
responses: | ||
'200': | ||
description: OK | ||
/api/v1/ai-testimonials/save: | ||
post: | ||
tags: | ||
- AiTestimonialsApi | ||
operationId: "SaveAiTestimonial" | ||
parameters: | ||
- in: query | ||
name: id | ||
required: true | ||
type: string | ||
collectionFormat: multi | ||
responses: | ||
'200': | ||
description: OK | ||
/api/v1/ai-testimonials: | ||
get: | ||
tags: | ||
- AiTestimonialsApi | ||
operationId: "GetAiTestimonials" | ||
produces: | ||
- application/json | ||
parameters: | ||
- in: query | ||
name: id | ||
type: string | ||
collectionFormat: multi | ||
responses: | ||
'200': | ||
description: OK | ||
schema: | ||
type: array | ||
items: | ||
$ref: '#/definitions/TestimonialResult' | ||
definitions: | ||
Identity: | ||
type: object | ||
properties: | ||
id: | ||
type: string | ||
additionalProperties: false | ||
TestimonialInput: | ||
type: object | ||
properties: | ||
name: | ||
type: string | ||
skills: | ||
type: string | ||
additionalProperties: false | ||
TestimonialResult: | ||
type: object | ||
properties: | ||
testimonial: | ||
type: string | ||
testifierName: | ||
type: string | ||
testifierCompany: | ||
type: string | ||
testifierPosition: | ||
type: string | ||
logoUrl: | ||
type: string | ||
logoB64: | ||
type: string | ||
additionalProperties: false | ||
securityDefinitions: | ||
API_KEY: | ||
type: apiKey | ||
name: x-api-key | ||
in: header | ||
description: ApiKey must appear in header | ||
security: | ||
- API_KEY: [ ] |
Oops, something went wrong.