jsconfcl / gql_api Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
Cuando queramos crear un ticket (No un userTicket
).
La mutación inicial está hecha. Hay que validar los permisos.
Permisos:
Esto: https://www.honeycomb.io/pricing-faq mas esto https://github.com/evanderkoogh/otel-cf-workers
Debería darnos practicamente todo lo q necesitamos :)
Workers tienen que ser lo mas pequeños posible. Pero con la implementación actual, todos los correos están incluidos en el worker principal.
Esto implica que cada instancia del worker principal carga en memoria información referente a correos que quizás nunca enviará en su ciclo de vida. (React, archivos en TSX, tailwind implementation, rendering, etc).
Por lo mismo, deberíamos tener un worker/servicio especializado para el envío de correos, el problema radica en lo complejo que es mantener multiples workers interactuando el uno con otro.
Para solucionar estos problemas, podemos usar RPC's nativos en workers que recientemente Cloudflare implementó.
Como no estamos editando tickets aun, comentamos esa edición cuando pasamos a soportar multiples precios en cada tickets (precio CLP y USD).
Debemos volver y arreglar la edición de precios.
(Quizas separarlo en su propia mutación?)
Existen dos tablas events_users
y events_users_roles
que la única diferencia que tienen es que events_users_roles
tiene un enum de roles. por lo demás son iguales y se podría eliminar la tabla events_users
dado que solo replicará información.
=)
Posteriormente:
Deberiamos poder traernos para cada Evento la cantidad de userTickets
filtrados por diferentes cosas.
Esto a modo de que los admins puedan filtrar por tipo de ticket, y buscar usuarios mas facil.
Por ejemplo:
Permisos:
Cuando se hace una query como esta:
query comunidadesUsuariosYEventos {
communities {
id
name
status
users {
id
username
lastName
name
bio
}
events {
id
name
address
description
startDateTime
endDateTime
}
}
}
Los valores retornados se ven algo así
{
"data": {
"communities": [
{
"id": "ad398d4b-e0a8-49d5-a3f4-f111451a6757",
"name": "",
"status": "inactive",
"users": [],
"events": [
{
"id": "70f6acdd-304b-4cf2-bbaf-dad4e195824f",
"name": "Meetup De Miedo — 3",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "0c9843fe-ac7a-4a1a-8618-f53cf0fb8cba",
"name": "Meetup De Miedo — 2",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "8dc704b6-18fe-4cbc-af03-bd2f34fc39ed",
"name": "Meetup De Miedo!",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
}
]
},
{
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f",
"name": "JavaScript Chile",
"status": "inactive",
"users": [],
"events": [
{
"id": "70f6acdd-304b-4cf2-bbaf-dad4e195824f",
"name": "Meetup De Miedo — 3",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "0c9843fe-ac7a-4a1a-8618-f53cf0fb8cba",
"name": "Meetup De Miedo — 2",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "8dc704b6-18fe-4cbc-af03-bd2f34fc39ed",
"name": "Meetup De Miedo!",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
}
]
}
]
}
}
Esta query debería retornar los usuarios que están en esta comunidad. En el caso de esta query, solo existia un usuario en la BDD, que es el ADMIN de ambas comunidades. Pero el campo users
no retorna nada de info.
"inactive"
(Para cambiar una comunidad a estado active
, lo hacemos desde la mutación de EditarComunidad)
Agregar validaciones para largos de textos, al menos en los nombres de comunidades/eventos, o slugs
Se pueden crear por ejemplo, comunidades con nombre vacío (O con string de largo 0). Deberíamos usar ZOD
para validar tamaños mínimos de string.
Un usuario puede regalar un userTicket
a otro usuario.
Cuando un userTicket
se regala a otro usuario, el userTicket
queda en estado "pendiente de aceptación".
este userTicket
no puede ser redimido hasta que la invitación sea aceptada, rechazada, o retirada.
Permisos:
Un "TICKET" pueden definir que cuando un userTicket
se crea, necesita ser aprobado por un admin, antes de ser "activado".
(Por si sirve de contexto... esto es lo que hacemos en LUMA para algunos eventos de nodeschool por ejemplo)
Necesitamos una mutación que le permita a un admin on superAdmin, el "aceptar" un "userTicket"
Permisos
Para cuando queramos editar un Ticket
(No un userTicket
).
Si queremos cambiarle por ejemplo, el valor a un ticket.
Permisos:
El usuario tiene que poder updatear sus datos de usuario.
Por ahora, deberiamos permitir updatear:
Permisos
Esto requiere cambios en Turso y en los modelos.
Hay documentación de como hacerlo acá.
Un usuario puede regalar un userTicket a otro usuario.
Para asignarlo a un usuario nuevo tiene que ser aceptado por el usuario nuevo.
Cuando un userTicket
se regala a otro usuario, el userTicket queda en estado "pendiente de aceptación".
Cuando el userTicket
se acepta, se cambia el usuario asignado al userTicket
por el usuario que aceptó el regalo.
PS: userTicket
no puede ser redimido hasta que la invitación sea aceptada, rechazada, o retirada.
Permisos:
Solo el receptor del regalo puede aceptarlo
Parecido a #74
active
o inactive
)Esta URL esta hardcodeada
gql_api/src/datasources/stripe/index.ts
Line 109 in ba503dc
Necesitamos parametrizarla.
Por ahora/Inicialmente, lo haremos por environment, una variable de entorno en produccion/staging/dev/local
Posteriormente, esto debería (O podría) ser definido por organización. Onda si Comunidad A, quiere definir su propia URL de respuesta distinto de Comunidad B, por ejemplo.
Aunque con un default apuntando a una URL por defecto.
Hoy por hoy usamos Sanity. Nuestro uso no es grande, pero podríamos reducirlo muchísimo si tenemos un worker que:
Boom! Profit :) Muy barato
Un usuario puede regalar un userTicket a otro usuario.
Tanto el usuairo nuevo como el que regaló el ticket puede cancelar el regalo.
De ser así, el userTicket
vuelve a su estado inicial.
PS: userTicket
no puede ser redimido hasta que la invitación sea aceptada, rechazada, o retirada.
Permisos:
Un userTicket
es la instancia de un ticket. Es la entrada de un usuario a un evento, la entidad que realmente se va a validar/quemar cuando el usuario atienda el evento.
Un userTicket
esta a asociado a un "TICKET" .
Permisos/validaciones
Cuando se compran tickets, el usuario no esta en nuestro dominio, y (posblemente) es redireccionado a una URL que nosotros definamos.
El problema está en que no tenemos como saber directamente, si la orden de compra fue correctamente pagada.
Para solucionarlo tenemos 3 maneras:
id_orden_compra
con en que podamos llamar a una mutación para sincronizar datos.Hay tradeoffs en todos los casos, el approach es tener una función agnóstica que reciba un ID de orden de compra y sincronice todo, e implementar al largo plazo los 3 flujos, con el order: Mutación
, WebHook
, Queue System
.
Para el tercero podemos usar https://developers.cloudflare.com/queues/
Error en el path del comando generate, se arregla eliminando el src del path
"generate": "tsx ./src/generated/generate.ts && graphql-codegen --config codegen.ts"
¿Cual es el problema?
Al intentar crear un evento obtengo un mensaje de error informando que Neon no soporta transacciones.
¿Cual es una posible solución?
Una búsqueda rápida del problema me llevó a esta discusión: https://community.neon.tech/t/how-do-i-handle-transactions/1067
El problema entonces se encontraría aquí:
gql_api/src/datasources/db/index.ts
Lines 1 to 23 in edb1f00
Cambiando la función por esto permite crear el evento sin problemas:
import { Pool, neonConfig } from "@neondatabase/serverless";
import { NeonDatabase, drizzle } from "drizzle-orm/neon-serverless";
import * as schema from "./schema";
neonConfig.fetchConnectionCache = true;
export type ORM_TYPE = NeonDatabase<typeof schema>;
let db: ORM_TYPE | null = null;
export const getDb = ({ neonUrl }: { neonUrl: string }) => {
if (!db) {
const pool = new Pool({ connectionString: neonUrl });
db = drizzle(pool, {
schema: { ...schema },
logger: {
logQuery(query, params) {
// eslint-disable-next-line no-console
console.log(query, params);
},
},
});
return db;
}
return db;
};
Esta API no esta creada para ser usada en una sola aplicación, por lo que cuando un usuario compre exitosamente sus tickets, deberíamos permitir que cada comunidad pueda decidir a donde redireccionar a sus usuarios post compra.
Resend es bkn pero es medio carosi cuando escalemos. Onda 50.000 correos salen 20 USD al mes. (Es caleta la verdad 😆, pero tenemos creditos de azure, que deberiamos usar). Segun mis cálculos. Con nuestros créditos de azure, podriamos mandar 500.000 correos x Mes.
Esto no es mega importante ahora, pero seria genial cachar cosas como:
Cuanto tiempo pasamos:
Si tenemos una query lenta. Podemos cachar porque es lenta?
Para cuando estemos en un evento y tengamos que redimir un ticket.
Básicamente cuando estamos en la entrada y escaneamos el ticket.
Permisos:
Mailchannels anunció EOL para su soporte con Cloudflare.
https://support.mailchannels.com/hc/en-us/articles/26814255454093-End-of-Life-Notice-Cloudflare-Workers
(Seems they suck TBH 😞)
https://community.cloudflare.com/t/this-week-mailchannels-will-enforce-domain-lockdown-on-legacy-workers-users/544874/1
Migrarse a Resend. Free for now, luego 20USD mes.
podríamos migrarnos a azure, pero la lata de implementación/soporte es mucha.
Sendgrid sale 25 centavos menos al menos 🤷🏼 y es mas lenta la implementación.
No vamos a realmente eliminar ningún userTicket
.
Pero tenemos que poder hacer un Soft-delete de cosas, asi que sería setear el campo deletedAt
y el status.
En el proyecto, se maneja una relación many-to-many entre eventos y comunidades.
Esta relación se evidencia en la siguiente imagen, específicamente en la tabla events_communities:
Sin embargo, el código actual devuelve solo la primera comunidad asociada a cada evento, lo cual parece incoherente con la naturaleza de la relación many-to-many. El fragmento de código que está causando este comportamiento es el siguiente:
Lines 72 to 88 in f90245c
Además, en algunas consultas, la comunidad devuelta no corresponde a la asociada al evento, similar al problema reportado en el issue #81.
Un ejemplo concreto de este problema se observa en la siguiente query y sus resultados, donde el evento con id 9d38e167-e376-476d-829d-10953675921e está vinculado incorrectamente a la comunidad con id c012d08f-e8e5-40ea-997a-d78bc1de4952:
query events {
events {
id
name
community {
id
name
}
}
}
La verdad... no crea una conexión a la BDD y no levanta un servidor de graphql cuando corre... pero carga los archivos de graphql / bdd / etc y eso es harto para un worker que realmente solo esta recibiendo un JSON, parseando react a html, y llamando a una API. Ese worker no necesita saber de graphql por ejemplo.
Consideremos: https://blog.cloudflare.com/sending-email-from-workers-with-mailchannels
Correo debería enviarse cuando se acepta una orden de compra (Ya sea por que compraste solo tickets gratis, o porque terminaste de pagar tus tickets")
Esto es para poder crear voluntarios o otros admins. Si queremos hacer que un usuario sea voluntario. O sumar un usuario como admin a una comunidad.
Permisos:
La siguiente query.
query comunidadesUsuariosYEventos {
communities {
id
name
status
events {
id
name
address
description
startDateTime
endDateTime
}
}
}
retorna los siguientes valores:
{
"data": {
"communities": [
{
"id": "ad398d4b-e0a8-49d5-a3f4-f111451a6757",
"name": "",
"status": "inactive",
"events": [
{
"id": "70f6acdd-304b-4cf2-bbaf-dad4e195824f",
"name": "Meetup De Miedo — 3",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "0c9843fe-ac7a-4a1a-8618-f53cf0fb8cba",
"name": "Meetup De Miedo — 2",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "8dc704b6-18fe-4cbc-af03-bd2f34fc39ed",
"name": "Meetup De Miedo!",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
}
]
},
{
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f",
"name": "JavaScript Chile",
"status": "inactive",
"events": [
{
"id": "70f6acdd-304b-4cf2-bbaf-dad4e195824f",
"name": "Meetup De Miedo — 3",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "0c9843fe-ac7a-4a1a-8618-f53cf0fb8cba",
"name": "Meetup De Miedo — 2",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
},
{
"id": "8dc704b6-18fe-4cbc-af03-bd2f34fc39ed",
"name": "Meetup De Miedo!",
"address": null,
"description": "Dummy Meetup de Octubre",
"startDateTime": "2024-10-18T06:58:39.081Z",
"endDateTime": null
}
]
}
]
}
}
Los eventos de una comunidad aparecen en la otra. Lo que quiere decir que no estamos filtrando por comunidad, al obtener los eventos de una comunidad.
Es mas notorio si hacemos la siguiente query, donde nos traemos comindades -> evnetos -> comunidad de cada evento.
query comunidadesUsuariosYEventos {
communities {
id
events {
id
community {
id
}
}
}
}
los resultados son:
{
"data": {
"communities": [
{
"id": "ad398d4b-e0a8-49d5-a3f4-f111451a6757",
"events": [
{
"id": "70f6acdd-304b-4cf2-bbaf-dad4e195824f",
"community": {
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f"
}
},
{
"id": "0c9843fe-ac7a-4a1a-8618-f53cf0fb8cba",
"community": {
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f"
}
},
{
"id": "8dc704b6-18fe-4cbc-af03-bd2f34fc39ed",
"community": {
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f"
}
}
]
},
{
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f",
"events": [
{
"id": "70f6acdd-304b-4cf2-bbaf-dad4e195824f",
"community": {
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f"
}
},
{
"id": "0c9843fe-ac7a-4a1a-8618-f53cf0fb8cba",
"community": {
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f"
}
},
{
"id": "8dc704b6-18fe-4cbc-af03-bd2f34fc39ed",
"community": {
"id": "e1330085-cc7f-4a82-a74d-b84b2d8c760f"
}
}
]
}
]
}
}
Tenemos que soportar la creación de purchase-orders o de links de pago, a través de MercadoPago, para precios Chilenos.
Crear un metodo para revisar si una OC está "no pagada" más allá de su tiempo de expiración (31 minutos).
De ser así, marcarla como cancelada.
Este método se tiene que llamar desde un cron, cada 5 minutos.
Además, se debe llamar al final de la mutación de checkPurchaseOrderStatus
Implementar Resend + react-email
Para enviar correos transaccionales.
Esto nos debería desbloquear para poder implementar la "validación de correo laboral"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.