Skip to content

Commit

Permalink
Controler and testing for new endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
ClaudioMartinH committed Nov 24, 2024
1 parent fd2583a commit 222e579
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 0 deletions.
2 changes: 2 additions & 0 deletions services/wiki/.env
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ DB_PASS=123456
DB_PORT=10911
DB_USER=postgres
DB_SCHEMA=public

HUGGINGFACE_API_KEY=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
82 changes: 82 additions & 0 deletions services/wiki/src/__tests__/resources/description.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import supertest from 'supertest'
import { describe, it, expect } from 'vitest'
import { server } from '../globalSetup'
import { pathRoot } from '../../routes/routes'
import { authToken } from '../mocks/ssoHandlers/authToken'
import { checkInvalidToken } from '../helpers/checkInvalidToken'

const url: string = `${pathRoot.v1.resources}/generate-description`
const urlToTest: string = 'http://www.example.com'
const topic: string = 'Testing topic'
const title: string = 'Test title'

describe('Resources Generate Description', () => {
it('responds with a 200 status code and a description', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ url: urlToTest, topic, title })
expect(response.status).toBe(200)
expect(response.body).toHaveProperty('description')
})
it('should fail is language param is missing', async () => {
const response = await supertest(server)
.post(`${url}`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ url: urlToTest, title, topic })

expect(response.status).toBe(400)
expect(response.body.error).toBe('Language parameter is required')
})
it('should fail with missing params (title, url, topic)', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({})

expect(response.status).toBe(400)
// expect(response.body.error).toBe('All parameters are required')
})

it('should fail if missing title', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ url: 'http://example.com', topic: 'Testing topic' })

expect(response.status).toBe(400)
// expect(response.body.error).toBe('All parameters are required')
})

it('should fail if missing url', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ title: 'Test Title', topic: 'Testing topic' })

expect(response.status).toBe(400)
// expect(response.body.error).toBe('All parameters are required')
})

it('should fail if missing topic', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ title: 'Test Title', url: 'http://example.com' })

expect(response.status).toBe(400)
expect(response.body.error).toBe('All parameters are required')
})
it('Should return error 401 if no token is provided', async () => {
const response = await supertest(server)
.post(`${pathRoot.v1.resources}`)
.send({ url: urlToTest, title, topic })
expect(response.status).toBe(401)
expect(response.body.message).toBe('Missing token')
})
checkInvalidToken(`${pathRoot.v1.resources}`, 'post', {
url: urlToTest,
title,
topic,
})
})
59 changes: 59 additions & 0 deletions services/wiki/src/controllers/resources/generateDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Koa, { Middleware } from 'koa'
import { getLanguageInput } from '../../helpers/wiki/getLanguageInput'

export const generateDescription: Middleware = async (ctx: Koa.Context) => {
const { title, url, topic } = ctx.request.body
const { language } = ctx.query

try {
if (!language) {
ctx.status = 400
ctx.body = {
error: 'Language parameter is required',
}
return
}

if (!title || !url || !topic) {
ctx.status = 400
ctx.body = {
error: 'All parameters are required',
}
return
}
const input = getLanguageInput(language as string, title, url, topic)

const response = await fetch(
'https://api-inference.huggingface.co/models/Qwen/Qwen2.5-Coder-32B-Instruct',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HUGGINGFACE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputs: input,
parameters: {
max_length: 450,
temperature: 0.7,
top_p: 0.95,
},
}),
}
)
if (!response.ok) {
ctx.status = 400
ctx.body = {
error: 'Error fetching data from external API',
}
return
}

const description = await response.json()
ctx.status = 200
ctx.body = { description: description[0]?.generated_text.trim() }
} catch (error) {
ctx.status = 500
ctx.body = { message: 'An error occured while getting the description' }
}
}
17 changes: 17 additions & 0 deletions services/wiki/src/helpers/wiki/getLanguageInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const getLanguageInput = (
language: string,
title: string,
url: string,
topic: string
): string => {
switch (language) {
case 'en':
return `Please provide a detailed summary of the following resource ${title}, including the key points, the main purpose, and the most relevant concepts. Use a clear and accessible tone. The resource can be found at ${url}, and its topic is ${topic}. The summary should be between 200 and 300 words. Return just the summary.`
case 'es':
return `Por favor, proporciona una resumen detallado de la siguiente fuente ${title}, incluyendo los puntos clave, el propósito principal y los conceptos relevantes. Usa un tono claro y accesible. La fuente puede ser encontrada en ${url}, y su tema es ${topic}. El resumen debe estar entre 200 y 300 palabras.`
case 'ca':
return `Si us plau, porporciona un resum detallat de la següent font ${title}, incloent els punts clau, el propòsit principal i els conceptes més rellevants. Empra un to clar i accesible. La font es pot trobar a ${url}, i el seu tema és ${topic}. El resum ha de tenir entre 200 a 300 paraules.`
default:
throw new Error('Unsupported language')
}
}
76 changes: 76 additions & 0 deletions services/wiki/src/openapi/routes/resources/generateDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import z from 'zod'
import { pathRoot } from '../../../routes/routes'
import { cookieAuth } from '../../components/cookieAuth'
import { generateDescriptionSchema } from '../../../schemas/resource/generateDescriptionSchema'
import { registry } from '../../registry'
import { ZodValidationError } from '../../components/errorSchemas'
import { invalidTokenResponse } from '../../components/responses/authMiddleware'

registry.registerPath({
method: 'post',
tags: ['resources'],
path: `${pathRoot.v1.resources}/generate-description`,
operationId: 'postResourcesGenerateDescription',
description:
'Allows an authenticated user to generate a description for a resource. Requires language as a query parameter and title, url, and topic in the request body.',
security: [{ [cookieAuth.name]: [] }],
request: {
query: z.object({
language: z.string().min(2).max(2).optional().openapi({
example: 'es',
}),
}),
body: {
content: {
'application/json': {
schema: generateDescriptionSchema,
},
},
},
},
responses: {
200: {
description: 'A description for the resource is generated.',
content: {
'application/json': {
schema: z.object({
description: z.string(),
}),
},
},
},
400: {
description: 'Error fetching data from external API',
content: {
'application/json': {
schema: z.object({
error: z.string().openapi({
example: 'Error fetching data from external API',
}),
}),
},
},
},
401: invalidTokenResponse,
422: {
description: 'Missing required parameters',
content: {
'application/json': {
schema: ZodValidationError,
},
},
},
500: {
description: 'An error occured while getting the description',
content: {
'application/json': {
schema: z.object({
message: z.string().openapi({
example: 'An error occured while getting the description',
}),
}),
},
},
},
},
})
8 changes: 8 additions & 0 deletions services/wiki/src/routes/resourcesRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { resourceCreateSchema, resourcesListParamsSchema } from '../schemas'
import { pathRoot } from './routes'
import { patchResource } from '../controllers/resources/patchResource'
import { resourcePatchSchema } from '../schemas/resource/resourcePatchSchema'
import { generateDescriptionSchema } from '../schemas/resource/generateDescriptionSchema'
import { generateDescription } from '../controllers/resources/generateDescription'

const resourcesRouter = new Router()

Expand All @@ -23,6 +25,12 @@ resourcesRouter.post(
validate(z.object({ body: resourceCreateSchema })),
postResource
)
resourcesRouter.post(
'/generate-description',
authenticate,
validate(z.object({ body: generateDescriptionSchema })),
generateDescription
)

resourcesRouter.get(
'/',
Expand Down
13 changes: 13 additions & 0 deletions services/wiki/src/schemas/resource/generateDescriptionSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { knexResourceSchema } from './resourceSchema'

export const generateDescriptionSchema = knexResourceSchema.omit({
id: true,
slug: true,
resource_type: true,
category_id: true,
description: true,
created_at: true,
updated_at: true,
user_id: true,
topics: true,
})

0 comments on commit 222e579

Please sign in to comment.