-
Notifications
You must be signed in to change notification settings - Fork 338
[comp] Production Deploy #3322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
[comp] Production Deploy #3322
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
f0f6aeb
chore: merge release v3.94.2 back to main [skip ci]
github-actions[bot] 4e3f9ac
feat(frameworks): allow editing custom framework name and description
tofikwest 8db8ac5
fix(frameworks): reject whitespace-only custom framework name
tofikwest 070a964
fix(tasks): bound sanitized inputs check to prevent timeout on manual…
tofikwest bec645c
Merge pull request #3319 from trycompai/tofik/cs-edit-custom-framewor…
tofikwest 99f4eca
Merge branch 'main' into tofik/cs-689-bug-run-on-sanitized-inputs
tofikwest e5f847b
fix(frameworks): reject null fields and empty payload on custom frame…
tofikwest 7eca843
Merge branch 'main' into tofik/cs-edit-custom-framework-validation
tofikwest e134859
Merge pull request #3321 from trycompai/tofik/cs-689-bug-run-on-sanit…
tofikwest 26bafcb
Merge branch 'main' into tofik/cs-edit-custom-framework-validation
tofikwest 8148a9a
Merge pull request #3323 from trycompai/tofik/cs-edit-custom-framewor…
tofikwest 6050902
fix(integrations): use catalog-defined field names for fivetran basic…
tofikwest dcf3dc1
fix(frameworks): trim custom framework name/description in update DTO
tofikwest 811e035
Merge pull request #3325 from trycompai/tofik/cs-custom-framework-dto…
tofikwest 21259bc
Merge branch 'main' into tofik/cs-690-fivetran-integration-required-c…
tofikwest 640a58d
Merge pull request #3324 from trycompai/tofik/cs-690-fivetran-integra…
tofikwest File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
apps/api/src/frameworks/dto/update-custom-framework.dto.spec.ts
This file contains hidden or 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,67 @@ | ||
| import { plainToInstance } from 'class-transformer'; | ||
| import { validate, type ValidationError } from 'class-validator'; | ||
| import { UpdateCustomFrameworkDto } from './update-custom-framework.dto'; | ||
|
|
||
| function propsWithErrors(errors: ValidationError[]): string[] { | ||
| return errors.map((e) => e.property); | ||
| } | ||
|
|
||
| async function validatePayload(payload: Record<string, unknown>) { | ||
| const dto = plainToInstance(UpdateCustomFrameworkDto, payload); | ||
| return validate(dto, { whitelist: true, forbidNonWhitelisted: true }); | ||
| } | ||
|
|
||
| function transform(payload: Record<string, unknown>) { | ||
| return plainToInstance(UpdateCustomFrameworkDto, payload); | ||
| } | ||
|
|
||
| describe('UpdateCustomFrameworkDto', () => { | ||
| it('accepts a name-only payload', async () => { | ||
| expect(await validatePayload({ name: 'Internal Controls' })).toHaveLength(0); | ||
| }); | ||
|
|
||
| it('accepts a description-only payload', async () => { | ||
| expect(await validatePayload({ description: 'Covers X' })).toHaveLength(0); | ||
| }); | ||
|
|
||
| it('accepts an empty payload (field-level; empty PATCH is rejected in the service)', async () => { | ||
| expect(await validatePayload({})).toHaveLength(0); | ||
| }); | ||
|
|
||
| it('rejects an explicit null name', async () => { | ||
| const errors = await validatePayload({ name: null }); | ||
| expect(propsWithErrors(errors)).toContain('name'); | ||
| }); | ||
|
|
||
| it('rejects an explicit null description', async () => { | ||
| const errors = await validatePayload({ description: null }); | ||
| expect(propsWithErrors(errors)).toContain('description'); | ||
| }); | ||
|
|
||
| it('rejects a non-string name', async () => { | ||
| const errors = await validatePayload({ name: 42 }); | ||
| expect(propsWithErrors(errors)).toContain('name'); | ||
| }); | ||
|
|
||
| it('rejects an empty-string name (MinLength)', async () => { | ||
| const errors = await validatePayload({ name: '' }); | ||
| expect(propsWithErrors(errors)).toContain('name'); | ||
| }); | ||
|
|
||
| it('rejects a whitespace-only name (trimmed to empty)', async () => { | ||
| const errors = await validatePayload({ name: ' ' }); | ||
| expect(propsWithErrors(errors)).toContain('name'); | ||
| }); | ||
|
|
||
| it('trims surrounding whitespace from a valid name', async () => { | ||
| const payload = { name: ' Internal Controls ' }; | ||
| expect(await validatePayload(payload)).toHaveLength(0); | ||
| expect(transform(payload).name).toBe('Internal Controls'); | ||
| }); | ||
|
|
||
| it('trims the description', async () => { | ||
| expect(transform({ description: ' Covers X ' }).description).toBe( | ||
| 'Covers X', | ||
| ); | ||
| }); | ||
| }); |
30 changes: 30 additions & 0 deletions
30
apps/api/src/frameworks/dto/update-custom-framework.dto.ts
This file contains hidden or 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,30 @@ | ||
| import { IsString, MaxLength, MinLength, ValidateIf } from 'class-validator'; | ||
| import { Transform } from 'class-transformer'; | ||
| import { ApiPropertyOptional } from '@nestjs/swagger'; | ||
|
|
||
| // Trim strings so a whitespace-only value collapses to '' and is rejected by | ||
| // MinLength. Guard on typeof so null/non-strings pass through unchanged and are | ||
| // still rejected by @IsString (using value?.trim() would turn null into | ||
| // undefined, which ValidateIf would then treat as an omitted field). | ||
| const trimIfString = ({ value }: { value: unknown }) => | ||
| typeof value === 'string' ? value.trim() : value; | ||
|
|
||
| export class UpdateCustomFrameworkDto { | ||
| // ValidateIf (rather than @IsOptional) only skips validation when the field is | ||
| // omitted (undefined). An explicit `null` still runs @IsString and is rejected | ||
| // with a 400, instead of slipping through to a non-null DB column. | ||
| @ApiPropertyOptional({ description: 'Framework name', example: 'Internal Controls' }) | ||
| @ValidateIf((_, value) => value !== undefined) | ||
| @Transform(trimIfString) | ||
| @IsString() | ||
| @MinLength(1) | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||
| @MaxLength(120) | ||
| name?: string; | ||
|
|
||
| @ApiPropertyOptional({ description: 'Framework description' }) | ||
| @ValidateIf((_, value) => value !== undefined) | ||
| @Transform(trimIfString) | ||
| @IsString() | ||
| @MaxLength(2000) | ||
| description?: string; | ||
| } | ||
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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
39 changes: 39 additions & 0 deletions
39
apps/api/src/integration-platform/services/basic-auth-credential-fields.spec.ts
This file contains hidden or 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,39 @@ | ||
| import type { BasicAuthConfig } from '@trycompai/integration-platform'; | ||
| import { buildBasicAuthCredentialFields } from './basic-auth-credential-fields'; | ||
|
|
||
| describe('buildBasicAuthCredentialFields', () => { | ||
| it('maps usernameField/passwordField to labeled fields (Fivetran api_key/api_secret)', () => { | ||
| const config: BasicAuthConfig = { | ||
| usernameField: 'api_key', | ||
| passwordField: 'api_secret', | ||
| }; | ||
|
|
||
| const fields = buildBasicAuthCredentialFields(config); | ||
|
|
||
| // Ids must equal the config field names so the runtime finds them when | ||
| // building the Basic auth header; labels must read as API Key / API Secret. | ||
| expect(fields).toEqual([ | ||
| { | ||
| id: 'api_key', | ||
| label: 'API Key', | ||
| type: 'text', | ||
| required: true, | ||
| placeholder: 'Enter API Key', | ||
| }, | ||
| { | ||
| id: 'api_secret', | ||
| label: 'API Secret', | ||
| type: 'password', | ||
| required: true, | ||
| placeholder: 'Enter API Secret', | ||
| }, | ||
| ]); | ||
| }); | ||
|
|
||
| it('falls back to generic username/password when field names are unset', () => { | ||
| const fields = buildBasicAuthCredentialFields({} as BasicAuthConfig); | ||
|
|
||
| expect(fields.map((f) => f.id)).toEqual(['username', 'password']); | ||
| expect(fields.map((f) => f.label)).toEqual(['Username', 'Password']); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Numeric or boolean JSON values for
name/descriptioncan be accepted as strings at runtime because the globalValidationPipeenables class-transformer's implicit conversion before@IsString()runs. That makes requests like{ "name": 42 }update the framework name to"42"even though this DTO and its tests intend to reject non-strings; using the original plain-object value in the transform (or otherwise disabling implicit conversion for these fields) would keep the validation strict.Prompt for AI agents