Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: altair-graphql/altair
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v8.0.0
Choose a base ref
...
head repository: altair-graphql/altair
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v8.0.1
Choose a head ref

Commits on Oct 27, 2024

  1. Copy the full SHA
    a8e69c1 View commit details

Commits on Nov 2, 2024

  1. Copy the full SHA
    a0cf5c8 View commit details
  2. cleanup

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    9d96e6c View commit details
  3. updated tests

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    f52f959 View commit details
  4. added test mocks

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    df0f9ca View commit details
  5. mock process.env instead

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    339815d View commit details
  6. dockerignore emails package

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    5687bfd View commit details
  7. another attempt

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    25fbae4 View commit details
  8. Copy the full SHA
    34b773b View commit details
  9. Copy the full SHA
    3186bd8 View commit details
  10. added any

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    d105fde View commit details
  11. disable validate nested

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    ea52430 View commit details
  12. subscribe pro users

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    7a9e5e7 View commit details
  13. migration

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    da776fb View commit details
  14. added goodbye email

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    70bd8b2 View commit details
  15. Copy the full SHA
    0b199f5 View commit details
  16. updated defaultFrom

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    3e52fba View commit details
  17. updated yarn.lock

    imolorhe committed Nov 2, 2024
    Copy the full SHA
    9fb7651 View commit details
  18. fix responder header section scroll (#2688)

    increase background contrast of app (#2631)
    imolorhe committed Nov 2, 2024
    Copy the full SHA
    423e62e View commit details

Commits on Nov 3, 2024

  1. Copy the full SHA
    79b0b81 View commit details
  2. reduce duplication

    imolorhe committed Nov 3, 2024
    Copy the full SHA
    76304ba View commit details
  3. fix linting

    imolorhe committed Nov 3, 2024
    Copy the full SHA
    b32252d View commit details
  4. added base64EncodeSafe

    imolorhe committed Nov 3, 2024
    Copy the full SHA
    24489f9 View commit details
  5. Copy the full SHA
    58948c0 View commit details
  6. Upgraded to v8.0.1

    imolorhe committed Nov 3, 2024
    Copy the full SHA
    516ec5d View commit details
Showing with 2,137 additions and 555 deletions.
  1. +4 −0 DEV.md
  2. +3 −0 Dockerfile
  3. +1 −1 VERSION
  4. +1 −1 chocolatey/altair-graphql.nuspec
  5. +1 −1 chocolatey/tools/chocolateyInstall.ps1
  6. +1 −1 cwex.yml
  7. +1 −1 examples/fastify-v5/package.json
  8. +1 −1 lerna.json
  9. +1 −1 libs/eslint-config-altair/package.json
  10. +2 −2 package.json
  11. +3 −3 packages/altair-api-utils/package.json
  12. +7 −5 packages/altair-api/package.json
  13. +2 −1 packages/altair-api/src/app.module.ts
  14. +11 −0 packages/altair-api/src/auth/user/user.service.ts
  15. +6 −0 packages/altair-api/src/common/config.ts
  16. +29 −0 packages/altair-api/src/email/email.service.spec.ts
  17. +106 −0 packages/altair-api/src/email/email.service.ts
  18. +3 −1 packages/altair-api/src/query-collections/dto/create-query-collection.dto.ts
  19. +15 −2 packages/altair-api/src/stripe-webhook/stripe-webhook.controller.ts
  20. +5 −0 packages/altair-api/src/stripe/stripe.service.ts
  21. +8 −0 packages/altair-api/test/e2e-test-utils.ts
  22. +8 −0 packages/altair-api/test/providers.ts
  23. +7 −7 packages/altair-app/package.json
  24. +189 −128 .../modules/altair/components/authorization/authorization-oauth2/authorization-oauth2.component.html
  25. +94 −13 ...pp/modules/altair/components/authorization/authorization-oauth2/authorization-oauth2.component.ts
  26. +12 −0 packages/altair-app/src/scss/_globals.scss
  27. +1 −1 packages/altair-app/src/scss/_layout.scss
  28. +1 −0 packages/altair-app/src/scss/components/_editor.scss
  29. +10 −0 packages/altair-core/jest.setup.js
  30. +2 −1 packages/altair-core/package.json
  31. +11 −1 packages/altair-core/src/config/index.ts
  32. +569 −2 packages/altair-core/src/oauth2/client.spec.ts
  33. +108 −14 packages/altair-core/src/oauth2/client.ts
  34. +33 −0 packages/altair-core/src/oauth2/helpers.spec.ts
  35. +5 −1 packages/altair-core/src/oauth2/helpers.ts
  36. +23 −1 packages/altair-core/src/oauth2/types.ts
  37. +3 −49 packages/altair-core/src/request/handlers/http.spec.ts
  38. +31 −0 packages/altair-core/src/test-helpers.ts
  39. +1 −1 packages/altair-db/package.json
  40. +2 −0 packages/altair-db/prisma/migrations/20241101163955_add_resend_contact_id/migration.sql
  41. +1 −0 packages/altair-db/prisma/schema.prisma
  42. +2 −2 packages/altair-docs/package.json
  43. +2 −2 packages/altair-electron-interop/package.json
  44. +2 −2 packages/altair-electron-settings-static/package.json
  45. +2 −2 packages/altair-electron-settings/package.json
  46. +7 −7 packages/altair-electron/package.json
  47. +3 −3 packages/altair-express-middleware/package.json
  48. +3 −3 packages/altair-fastify-plugin/package.json
  49. +3 −3 packages/altair-iframe-sandbox/package.json
  50. +4 −4 packages/altair-koa-middleware/package.json
  51. +2 −2 packages/altair-plugin/package.json
  52. +3 −3 packages/altair-static/package.json
  53. +2 −2 packages/gatsby-plugin-altair-graphql/package.json
  54. +3 −3 packages/login-redirect/package.json
  55. +22 −0 packages/transactional/package.json
  56. +94 −0 packages/transactional/src/emails/Welcome.tsx
  57. +6 −0 packages/transactional/src/index.tsx
  58. +15 −0 packages/transactional/tsconfig.json
  59. +1 −1 plugins/ai/manifest.json
  60. +2 −2 plugins/ai/package.json
  61. +1 −1 test-server/package.json
  62. +636 −273 yarn.lock
4 changes: 4 additions & 0 deletions DEV.md
Original file line number Diff line number Diff line change
@@ -144,3 +144,7 @@ psql --file=data.sql --dbname=altairgraphql-db --port=5432 --host=localhost --us
## Tips

- add `&pgbouncer=true` to your connection url in order to enable pgbouncer mode in Prisma

### Slow running development application

If the local development application feels extremely slow, check if you have the [Angular DevTools extension](https://chromewebstore.google.com/detail/angular-devtools/ienfalfjdbdpebioblfackkekamfmbnh) installed on your browser and disable it. It slows down the application significantly.
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -33,6 +33,9 @@ COPY nx.json nx.json
COPY tsconfig.json tsconfig.json
COPY CHECKS CHECKS
# build the project, running the prepare scripts
# Somehow get an unknown error if I don't install nx first
RUN yarn add nx -W
# yarn install 2> >(grep -v warning 1>&2)
RUN yarn
RUN yarn turbo run build --filter=@altairgraphql/api...

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.0.0
8.0.1
2 changes: 1 addition & 1 deletion chocolatey/altair-graphql.nuspec
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe
<!-- version should MATCH as closely as possible with the underlying software -->
<!-- Is the version a prerelease of a version? https://docs.nuget.org/create/versioning#creating-prerelease-packages -->
<!-- Note that unstable versions like 0.0.1 can be considered a released version, but it's possible that one can release a 0.0.1-beta before you release a 0.0.1 version. If the version number is final, that is considered a released version and not a prerelease. -->
<version>8.0.0</version>
<version>8.0.1</version>
<packageSourceUrl>https://github.com/altair-graphql/altair</packageSourceUrl>
<!-- owners is a poor name for maintainers of the package. It sticks around by this name for compatibility reasons. It basically means you. -->
<owners>Daniel Richter, Samuel Imolorhe</owners>
2 changes: 1 addition & 1 deletion chocolatey/tools/chocolateyInstall.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
$ErrorActionPreference = 'Stop'; # stop on all errors

$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$fileLocation = Join-Path $toolsDir '..\bins\altair_8.0.0_x64_win.exe'
$fileLocation = Join-Path $toolsDir '..\bins\altair_8.0.1_x64_win.exe'
$fileHash = Get-FileHash $fileLocation

$packageArgs = @{
2 changes: 1 addition & 1 deletion cwex.yml
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ manifestOptions:
name: Altair GraphQL Client
short_name: Altair
description: A beautiful feature-rich GraphQL client for all platforms
version: 8.0.0
version: 8.0.1
icons:
16: assets/img/altair_logo_128.png
48: assets/img/altair_logo_128.png
2 changes: 1 addition & 1 deletion examples/fastify-v5/package.json
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
"devDependencies": {
"@types/node": "^22.7.4",
"ts-node": "^10.9.2",
"typescript": "^5.6.2"
"typescript": "5.2.2"
},
"dependencies": {
"fastify": "^5.0.0"
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
"packages/*",
"plugins/*"
],
"version": "8.0.0",
"version": "8.0.1",
"registry": "https://registry.npmjs.org/",
"npmClient": "yarn",
"stream": true,
2 changes: 1 addition & 1 deletion libs/eslint-config-altair/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "eslint-config-altair",
"description": "ESlint configurations",
"version": "8.0.0",
"version": "8.0.1",
"dependencies": {
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "altair",
"description": "The best graphQL client you will ever need",
"version": "8.0.0",
"version": "8.0.1",
"author": "Samuel Imolorhe <altair@sirmuel.design> (https://sirmuel.design/)",
"bugs": "https://github.com/altair-graphql/altair/issues",
"collective": {
@@ -24,7 +24,7 @@
"inquirer": "^7.3.3",
"lerna": "^4.0.0",
"ng-packagr": "^13.0.8",
"nx": "19.4.3",
"nx": "17.1.3",
"opencollective": "^1.0.3",
"path-browserify": "^1.0.0",
"process": "^0.11.10",
6 changes: 3 additions & 3 deletions packages/altair-api-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "@altairgraphql/api-utils",
"version": "8.0.0",
"version": "8.0.1",
"dependencies": {
"@altairgraphql/db": "^8.0.0",
"altair-graphql-core": "^8.0.0",
"@altairgraphql/db": "^8.0.1",
"altair-graphql-core": "^8.0.1",
"ky": "^0.33.2",
"rxjs": "^7.0.0"
},
12 changes: 7 additions & 5 deletions packages/altair-api/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "@altairgraphql/api",
"description": "",
"version": "8.0.0",
"version": "8.0.1",
"author": "",
"dependencies": {
"@altairgraphql/api-utils": "^8.0.0",
"@altairgraphql/db": "^8.0.0",
"@altairgraphql/api-utils": "^8.0.1",
"@altairgraphql/db": "^8.0.1",
"@altairgraphql/emails": "^8.0.1",
"@langchain/anthropic": "^0.2.4",
"@langchain/community": "^0.2.19",
"@langchain/openai": "^0.2.2",
@@ -18,7 +19,7 @@
"@nestjs/platform-express": "^10.3.10",
"@nestjs/schedule": "^4.1.0",
"@nestjs/swagger": "^7.4.0",
"altair-graphql-core": "^8.0.0",
"altair-graphql-core": "^8.0.1",
"bcrypt": "^5.1.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
@@ -33,6 +34,7 @@
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.1.13",
"resend": "^4.0.0",
"rimraf": "^5.0.5",
"rxjs": "^7.8.1",
"stripe": "^16.2.0",
@@ -67,7 +69,7 @@
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.3"
"typescript": "5.2.2"
},
"license": "UNLICENSED",
"private": true,
3 changes: 2 additions & 1 deletion packages/altair-api/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import { ScheduleModule } from '@nestjs/schedule';
import { WinstonModule, utilities } from 'nest-winston';
import { format, transports } from 'winston';
import { AiModule } from './ai/ai.module';
import { EmailService } from './email/email.service';

@Module({
imports: [
@@ -59,6 +60,6 @@ import { AiModule } from './ai/ai.module';
AiModule,
],
controllers: [AppController, StripeWebhookController],
providers: [AppService, PasswordService],
providers: [AppService, PasswordService, EmailService],
})
export class AppModule {}
11 changes: 11 additions & 0 deletions packages/altair-api/src/auth/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -125,6 +125,17 @@ export class UserService {
});
}

updateUserResendContactId(userId: string, resendContactId: string) {
return this.prisma.user.update({
data: {
resendContactId,
},
where: {
id: userId,
},
});
}

async getPlanConfig(userId: string) {
const res = await this.prisma.userPlan.findUnique({
where: {
6 changes: 6 additions & 0 deletions packages/altair-api/src/common/config.ts
Original file line number Diff line number Diff line change
@@ -38,6 +38,12 @@ const config = {
model: process.env.ANTHROPIC_MODEL_NAME ?? 'claude-3-haiku-20240307',
},
},
email: {
resendApiKey: process.env.RESEND_API_KEY,
audienceId: process.env.RESEND_AUDIENCE_ID,
defaultFrom: 'Altair GraphQL <mail@mail.altairgraphql.dev>',
replyTo: 'info@altairgraphql.dev',
},
};

export type Config = typeof config;
29 changes: 29 additions & 0 deletions packages/altair-api/src/email/email.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Test, TestingModule } from '@nestjs/testing';
import { EmailService } from './email.service';
import { ConfigService } from '@nestjs/config';
import { testProviders } from 'test/providers';

describe('EmailService', () => {
let service: EmailService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
EmailService,
{
provide: ConfigService,
useValue: {
get: jest.fn().mockReturnValue('test'),
},
},
...testProviders,
],
}).compile();

service = module.get<EmailService>(EmailService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
106 changes: 106 additions & 0 deletions packages/altair-api/src/email/email.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Resend } from 'resend';
import { renderWelcomeEmail } from '@altairgraphql/emails';
import { UserService } from 'src/auth/user/user.service';
import { Config } from 'src/common/config';
import { User } from '@altairgraphql/db';

@Injectable()
export class EmailService {
private resend: Resend;
constructor(
private configService: ConfigService<Config>,
private readonly userService: UserService
) {
this.resend = new Resend(
this.configService.get('email.resendApiKey', { infer: true })
);
}

async subscribeUser(userId: string) {
const user = await this.userService.mustGetUser(userId);
if (user.resendContactId) {
return;
}
const audienceId = this.configService.get('email.audienceId', { infer: true });

if (!audienceId) {
console.error('No audience ID found');
return;
}

const { data, error } = await this.resend.contacts.create({
email: user.email,
audienceId,
firstName: this.getFirstName(user),
lastName: user.lastName ?? '',
});

if (error) {
console.error('Error subscribing user', error);
return;
}

if (!data?.id) {
console.error('No contact ID found');
return;
}

await this.userService.updateUserResendContactId(userId, data.id);
}

async sendWelcomeEmail(userId: string) {
const user = await this.userService.mustGetUser(userId);
const { data, error } = await this.resend.emails.send({
from:
this.configService.get('email.defaultFrom', { infer: true }) ??
'info@mail.altairgraphql.dev',
to: user.email,
replyTo: this.configService.get('email.replyTo', { infer: true }),
subject: 'Welcome to Altair GraphQL Cloud',
html: await renderWelcomeEmail({ username: this.getFirstName(user) }),
});
if (error) {
console.error('Error sending welcome email', error);
}

return { data, error };
}

async sendGoodbyeEmail(userId: string) {
const user = await this.userService.mustGetUser(userId);
const { data, error } = await this.resend.emails.send({
from:
this.configService.get('email.defaultFrom', { infer: true }) ??
'info@mail.altairgraphql.dev',
to: user.email,
replyTo: this.configService.get('email.replyTo', { infer: true }),
subject: 'Sorry to see you go 👋🏾',
html: `Hey ${this.getFirstName(user)},
<br><br>
Samuel here. I noticed you've cancelled your Altair GraphQL pro subscription and wanted to check in.
<br><br>
Would you mind sharing what led to your decision? Your feedback helps us make Altair better for everyone. Just hit reply to let me know.
<br><br>
If you ever want to come back, we'll be here! And of course, you can keep using Altair's free version as long as you like.
<br><br>
Thanks for giving the pro version a try!
<br><br>
Best wishes,
<br>
Samuel
<br><br>
P.S. If you cancelled because of a technical issue or need help with something, just let me know -- I'm happy to help!`,
});
if (error) {
console.error('Error sending goodbye email', error);
}

return { data, error };
}

private getFirstName(user: User) {
return user.firstName ?? user.email;
}
}
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import {
IsString,
IsBoolean,
ValidateNested,
IsArray,
} from 'class-validator';
import { CreateQuerySansCollectionIdDto } from 'src/queries/dto/create-query.dto';

@@ -17,7 +18,8 @@ export class CreateQueryCollectionDto implements ICreateQueryCollectionDto {
name!: string;

@IsOptional()
@ValidateNested()
// @ValidateNested({ each: true })
@IsArray()
@ApiProperty()
@Type(() => CreateQuerySansCollectionIdDto)
queries?: CreateQuerySansCollectionIdDto[];
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import { BadRequestException, Controller, Header, Post, Req } from '@nestjs/comm
import { Request } from 'express';
import { PrismaService } from 'nestjs-prisma';
import { UserService } from 'src/auth/user/user.service';
import { EmailService } from 'src/email/email.service';
import { StripeService } from 'src/stripe/stripe.service';
import { Stripe } from 'stripe';

@@ -16,7 +17,8 @@ export class StripeWebhookController {
constructor(
private readonly stripeService: StripeService,
private readonly userService: UserService,
private readonly prisma: PrismaService
private readonly prisma: PrismaService,
private readonly emailService: EmailService
) {
// TODO: Synchronise with Stripe on startup
}
@@ -68,14 +70,25 @@ export class StripeWebhookController {

if (planRole === BASIC_PLAN_ID) {
await this.userService.toBasicPlan(user.id);
if (shouldCancelPlan) {
// Send goodbye email
console.log('Sending goodbye email');
await this.emailService.sendGoodbyeEmail(user.id);
}
} else if (planRole === PRO_PLAN_ID) {
await this.userService.toProPlan(user.id, quantity);
// Send welcome email
console.log('Sending welcome email');
await this.emailService.sendWelcomeEmail(user.id);
// Subscribe user
console.log('Subscribing user');
await this.emailService.subscribeUser(user.id);
}
break;
}
case 'checkout.session.completed':
case 'checkout.session.async_payment_succeeded': {
// TODO: Handle credit purchase
// Handle credit purchase
const data: Stripe.Checkout.Session = event.data.object;
const checkoutSession = await this.stripeService.retrieveCheckoutSession(
data.id
Loading