Skip to content

Commit

Permalink
fix(ui): file upload data shows 1970 (#941)
Browse files Browse the repository at this point in the history
* also fixes workflow keycloak enabled playwright tests
---------

Co-authored-by: Justin Law <[email protected]>
  • Loading branch information
andrewrisse and justinthelaw authored Aug 23, 2024
1 parent cdd3e61 commit 1a80646
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 22 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/e2e-playwright.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
- name: Generate Fake Playwright User Password
id: generate-password
run: |
PASSWORD=$(cat <(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9!@#$%^&*()_+-=[]{}|;:,.<>?' | head -c 20 | sed 's/\(.\{5\}\)/\1!@/g' | head -c 22))
PASSWORD=$(cat <(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9!@#$%^&*()_+-=[]{}|;:,.<>?' | head -c 20) <(echo '!@1Aa') | fold -w1 | shuf | tr -d '\n')
echo "::add-mask::$PASSWORD"
echo "FAKE_E2E_USER_PASSWORD=$PASSWORD" >> $GITHUB_ENV
Expand Down Expand Up @@ -120,7 +120,9 @@ jobs:
cp src/leapfrogai_ui/.env.example src/leapfrogai_ui/.env
mkdir -p playwright/auth
touch playwright/auth.user.json
SERVICE_ROLE_KEY=$(uds zarf tools kubectl get secret -n leapfrogai supabase-bootstrap-jwt -o jsonpath={.data.service-key} | base64 -d) TEST_ENV=CI USERNAME=doug PASSWORD=$FAKE_E2E_USER_PASSWORD PUBLIC_SUPABASE_ANON_KEY=$ANON_KEY npm --prefix src/leapfrogai_ui run test:integration:ci
SERVICE_ROLE_KEY=$(uds zarf tools kubectl get secret -n leapfrogai supabase-bootstrap-jwt -o jsonpath={.data.service-key} | base64 -d)
echo "::add-mask::$SERVICE_ROLE_KEY"
SERVICE_ROLE_KEY=$SERVICE_ROLE_KEY TEST_ENV=CI USERNAME=doug PASSWORD=$FAKE_E2E_USER_PASSWORD PUBLIC_SUPABASE_ANON_KEY=$ANON_KEY npm --prefix src/leapfrogai_ui run test:integration:ci
# Upload the Playwright report as an artifact
- name: Archive Playwright Report
Expand Down
4 changes: 2 additions & 2 deletions src/leapfrogai_ui/src/lib/components/ImportExport.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { Button, Spinner } from 'flowbite-svelte';
import { DownloadOutline } from 'flowbite-svelte-icons';
import { DownloadOutline, UploadOutline } from 'flowbite-svelte-icons';
import { threadsStore, toastStore } from '$stores';
import { threadsSchema } from '$schemas/threadSchema';
import type { LFThread } from '$lib/types/threads';
Expand Down Expand Up @@ -88,7 +88,7 @@
size="sm"
on:change={(e) => onUpload(e.detail)}
accept={['application/json']}
disabled={importing}>Import chat history</LFFileUploadBtn
disabled={importing}>Import chat history <UploadOutline /></LFFileUploadBtn
>
{/if}

Expand Down
3 changes: 3 additions & 0 deletions src/leapfrogai_ui/src/lib/helpers/dates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,6 @@ export const calculateDays = (beginDate: number, endDate: number) => {

return Math.round(differenceInDays);
};

export const convertToMilliseconds = (dateValue: number) =>
dateValue > 10000000000 ? dateValue : dateValue * 1000;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { superValidate, withFiles, fail } from 'sveltekit-superforms';
import { fail, superValidate, withFiles } from 'sveltekit-superforms';
import type { Actions } from './$types';
import { yup } from 'sveltekit-superforms/adapters';
import type { FileObject } from 'openai/resources/files';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import { fade } from 'svelte/transition';
import { yup } from 'sveltekit-superforms/adapters';
import { superForm } from 'sveltekit-superforms';
import { formatDate } from '$helpers/dates';
import { convertToMilliseconds, formatDate } from '$helpers/dates';
import { filesSchema } from '$schemas/files';
import { filesStore, toastStore } from '$stores';
import { ACCEPTED_FILE_TYPES } from '$constants';
Expand Down Expand Up @@ -350,9 +350,11 @@
{:else}
<TableBodyCell tdClass="px-4 py-3">{item.filename}</TableBodyCell>
{/if}
<TableBodyCell tdClass="px-4 py-3"
>{formatDate(new Date(item.created_at))}</TableBodyCell
>
{#if item.created_at}
<TableBodyCell tdClass="px-4 py-3"
>{formatDate(new Date(convertToMilliseconds(item.created_at)))}</TableBodyCell
>
{/if}
</TableBodyRow>
{/each}
</TableBody>
Expand Down
37 changes: 34 additions & 3 deletions src/leapfrogai_ui/tests/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,39 @@ type MyFixtures = {
openAIClient: OpenAI;
};

export const getOpenAIClient = () => {
export async function getAccessToken() {
const supabaseUrl = process.env.PUBLIC_SUPABASE_URL;
const serviceRoleKey = process.env.SERVICE_ROLE_KEY;

const response = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=password`, {
method: 'POST',
// @ts-expect-error: apikey is a required header for this request
headers: {
'Content-Type': 'application/json',
apikey: serviceRoleKey,
Authorization: `Bearer ${serviceRoleKey}`
},
body: JSON.stringify({
email: process.env.USERNAME,
password: process.env.PASSWORD
})
});

const data = await response.json();

if (response.ok) {
return data.access_token;
} else {
console.error('Error fetching access token:', data);
throw new Error(data.error_description || 'Failed to fetch access token');
}
}

export const getOpenAIClient = async () => {
const token = await getAccessToken();

return new OpenAI({
apiKey: process.env.OPENAI_API_KEY || process.env.SERVICE_ROLE_KEY,
apiKey: process.env.OPENAI_API_KEY || token,
baseURL: process.env.OPENAI_API_KEY
? `${process.env.LEAPFROGAI_API_BASE_URL}/v1`
: `${process.env.LEAPFROGAI_API_BASE_URL}/openai/v1`
Expand All @@ -17,7 +47,8 @@ export const getOpenAIClient = () => {
export const test = base.extend<MyFixtures>({
// eslint-disable-next-line no-empty-pattern
openAIClient: async ({}, use) => {
const client = getOpenAIClient();
const client = await getOpenAIClient();
console.log('client', client);
await use(client);
}
});
Expand Down
10 changes: 4 additions & 6 deletions src/leapfrogai_ui/tests/global.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { expect, test as setup } from './fixtures';
import * as OTPAuth from 'otpauth';
import { delay } from 'msw';
import type { Page } from '@playwright/test';
import { cleanup } from './helpers/cleanup';

const authFile = 'playwright/.auth/user.json';

Expand Down Expand Up @@ -86,7 +85,10 @@ const logout = async (page: Page) => {
}
};

setup('authenticate', async ({ page, openAIClient }) => {
// NOTE - do not try to use openAIClient from the fixtures here. The user that it attempts to get a token for
// exists in Keycloak because the workflow creates it, but the user has not yet logged in and does not exist in
// Supabase, so the tests will fail.
setup('authenticate', async ({ page }) => {
page.on('pageerror', (err) => {
console.log(err.message);
});
Expand Down Expand Up @@ -117,8 +119,4 @@ setup('authenticate', async ({ page, openAIClient }) => {
// End of authentication steps.

await page.context().storageState({ path: authFile });

if (process.env.TEST_ENV !== 'CI') {
await cleanup(openAIClient);
}
});
5 changes: 4 additions & 1 deletion src/leapfrogai_ui/tests/helpers/apiHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { getAccessToken } from '../fixtures';

export const deleteAllTestAPIKeys = async () => {
const token = await getAccessToken();
const res = await fetch(`${process.env.LEAPFROGAI_API_BASE_URL}/leapfrogai/v1/auth/api-keys`, {
headers: {
Authorization: `Bearer ${process.env.SERVICE_ROLE_KEY}`
Authorization: `Bearer ${token}`
}
});
const keys = await res.json();
Expand Down
5 changes: 2 additions & 3 deletions src/leapfrogai_ui/tests/helpers/cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import { deleteAllGeneratedFixtureFiles, deleteAllTestFilesWithApi } from './fil
import { deleteAllAssistants, deleteAssistantAvatars } from './assistantHelpers';
import { deleteAllTestThreadsWithApi } from './threadHelpers';
import type OpenAI from 'openai';
import { deleteAllTestAPIKeys } from './apiHelpers';

export const cleanup = async (openAIClient: OpenAI) => {
deleteAllGeneratedFixtureFiles();
await deleteAllTestFilesWithApi(openAIClient);
await deleteAllAssistants(openAIClient);
await deleteAllTestThreadsWithApi(openAIClient);
await deleteAssistantAvatars();
// TODO - the deleteAllTestAPIKeys helper uses a leapfrog endpoint that is not authorizing the SERVICE_ROLE_KEY
// https://github.com/defenseunicorns/leapfrogai/issues/936
// await deleteAllTestAPIKeys();
await deleteAllTestAPIKeys();
};
2 changes: 2 additions & 0 deletions src/leapfrogai_ui/tests/helpers/fileHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import pptxgen from 'pptxgenjs';
import { expect } from '../fixtures';
import type { FileObject } from 'openai/resources/files';
import { getTableRow } from './helpers';
import { formatDate } from '../../src/lib/helpers/dates';

export const uploadFileWithApi = async (
filename = 'test.pdf',
Expand Down Expand Up @@ -219,6 +220,7 @@ export const testFileUpload = async (filename: string, page: Page, openAIClient:
expect(rowCheckboxesBefore.length).toEqual(0);
await expect(fileUploadedIcon).toBeVisible();
await expect(uploadingFileIcon).not.toBeVisible();
await expect(row.getByText(formatDate(new Date()))).toBeVisible();

// Checkbox should now be present
const rowCheckboxesAfter = await row!.getByRole('checkbox').all();
Expand Down

0 comments on commit 1a80646

Please sign in to comment.