Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ ssh:
docker exec -it postgres /bin/bash

install:
docker exec postgres /sql-bin/install.sh
docker exec postgres /sql-bin/install.sh
5 changes: 3 additions & 2 deletions packages/data-types/types/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ INSERT INTO customers (domain) VALUES
### Image and Attachment Domains

The `image` domain stores JSON objects with URL and MIME type information. The `attachment` domain accepts either that JSON shape or a plain URL string.
The `upload` domain uses the same JSON object shape as `image`, ensuring both the file URL and MIME type are present.

```sql
-- Valid image
Expand All @@ -184,7 +185,7 @@ INSERT INTO customers (document) VALUES ('https://storage.example.com/favicon.ic
| `hostname` | `text` | Domain name without protocol | `example.com` |
| `image` | `json` | Image metadata with URL and MIME | `{"url": "...", "mime": "image/jpeg"}` |
| `attachment` | `json` | File attachment URL or metadata | `{"url": "...", "mime": "application/pdf"}` or `https://example.com/favicon.ico` |
| `upload` | `text` | File upload identifier | Various formats |
| `upload` | `json` | File upload metadata (URL + MIME) | `{"url": "...", "mime": "application/pdf"}` |
| `single_select` | `text` | Single selection value | Text value |
| `multiple_select` | `text[]` | Multiple selection values | Array of text values |

Expand Down Expand Up @@ -212,7 +213,7 @@ The test suite validates:
- Email format validation (valid and invalid cases)
- URL format validation with extensive test cases
- Hostname format validation
- Image and attachment JSON structure validation
- Image, upload, and attachment JSON structure validation

## Related Tooling

Expand Down
32 changes: 31 additions & 1 deletion packages/data-types/types/__tests__/domains.pgutils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ const invalidImages = [
{ url: 'https://foo.bar/some.png' }
];

const validUploads = [
{ url: 'http://www.foo.bar/some.jpg', mime: 'image/jpg' },
{ url: 'https://foo.bar/some.PNG', mime: 'image/png' }
];

const invalidUploads = [
{ url: 'hi there' },
{ url: 'https://foo.bar/some.png' },
{ url: 'ftp://foo.bar/some.png', mime: 'image/png' }
];

let pg: PgTestClient;
let teardown: () => Promise<void>;

Expand All @@ -79,7 +90,8 @@ CREATE TABLE customers (
image image,
attachment attachment,
domain hostname,
email email
email email,
upload upload
);
`);
});
Expand Down Expand Up @@ -129,6 +141,24 @@ describe('types', () => {
}
});

it('valid upload', async () => {
for (const upload of validUploads) {
await pg.any(`INSERT INTO customers (upload) VALUES ($1::json);`, [upload]);
}
});

it('invalid upload', async () => {
for (const upload of invalidUploads) {
let failed = false;
try {
await pg.any(`INSERT INTO customers (upload) VALUES ($1::json);`, [upload]);
} catch (e) {
failed = true;
}
expect(failed).toBe(true);
}
});

it('valid url', async () => {
for (const value of validUrls) {
await pg.any(`INSERT INTO customers (url) VALUES ($1);`, [value]);
Expand Down
32 changes: 31 additions & 1 deletion packages/data-types/types/__tests__/domains.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ const invalidImages = [
{ url: 'https://foo.bar/some.png' }
];

const validUploads = [
{ url: 'http://www.foo.bar/some.jpg', mime: 'image/jpg' },
{ url: 'https://foo.bar/some.PNG', mime: 'image/png' }
];

const invalidUploads = [
{ url: 'hi there' },
{ url: 'https://foo.bar/some.png' },
{ url: 'ftp://foo.bar/some.png', mime: 'image/png' }
];

let pg: PgTestClient;
let teardown: () => Promise<void>;

Expand All @@ -79,7 +90,8 @@ CREATE TABLE customers (
image image,
attachment attachment,
domain hostname,
email email
email email,
upload upload
);
`);
});
Expand Down Expand Up @@ -129,6 +141,24 @@ describe('types', () => {
}
});

it('valid upload', async () => {
for (const upload of validUploads) {
await pg.any(`INSERT INTO customers (upload) VALUES ($1::json);`, [upload]);
}
});

it('invalid upload', async () => {
for (const upload of invalidUploads) {
let failed = false;
try {
await pg.any(`INSERT INTO customers (upload) VALUES ($1::json);`, [upload]);
} catch (e) {
failed = true;
}
expect(failed).toBe(true);
}
});

it('valid url', async () => {
for (const value of validUrls) {
await pg.any(`INSERT INTO customers (url) VALUES ($1);`, [value]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

BEGIN;

CREATE DOMAIN upload AS text CHECK (VALUE ~ '^(https?)://[^\s/$.?#].[^\s]*$');
CREATE DOMAIN upload AS jsonb CHECK (
value ?& ARRAY['url', 'mime']
AND
value->>'url' ~ '^(https?)://[^\s/$.?#].[^\s]*$'
);
COMMENT ON DOMAIN upload IS E'@name launchqlInternalTypeUpload';

COMMIT;
9 changes: 6 additions & 3 deletions packages/data-types/types/sql/launchql-types--0.9.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ CREATE DOMAIN single_select AS jsonb

COMMENT ON DOMAIN single_select IS '@name launchqlInternalTypeSingleSelect';

CREATE DOMAIN upload AS text
CHECK (value ~ E'^(https?)://[^\\s/$.?#].[^\\s]*$');
CREATE DOMAIN upload AS jsonb
CHECK (
value ?& ARRAY['url', 'mime']
AND (value ->> 'url') ~ E'^(https?)://[^\\s/$.?#].[^\\s]*$'
);

COMMENT ON DOMAIN upload IS '@name launchqlInternalTypeUpload';

CREATE DOMAIN url AS text
CHECK (value ~ E'^(https?)://[^\\s/$.?#].[^\\s]*$');

COMMENT ON DOMAIN url IS '@name launchqlInternalTypeUrl';
COMMENT ON DOMAIN url IS '@name launchqlInternalTypeUrl';
1 change: 1 addition & 0 deletions packages/metrics/achievements/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
maxWorkers: 1,

// Match both __tests__ and colocated test files
testMatch: ['**/?(*.)+(test|spec).{ts,tsx,js,jsx}'],
Expand Down
Loading