Merge branch 'main' of github.com:solidtime-io/solidtime

This commit is contained in:
Gregor Vostrak
2024-04-23 03:27:49 +02:00
10 changed files with 65 additions and 21 deletions

View File

@@ -5,7 +5,7 @@ on:
- develop - develop
pull_request: pull_request:
paths: paths:
- 'workflows/build-private.yml' - '.github/workflows/build-private.yml'
- 'docker/prod/**' - 'docker/prod/**'
workflow_dispatch: workflow_dispatch:
@@ -37,6 +37,21 @@ jobs:
only_args: --no-dev --no-ansi --no-interaction --prefer-dist --ignore-platform-reqs --classmap-authoritative only_args: --no-dev --no-ansi --no-interaction --prefer-dist --ignore-platform-reqs --classmap-authoritative
php_version: 8.3 php_version: 8.3
- name: "Checkout services extension"
uses: actions/checkout@v4
with:
repository: solidtime-io/extension-services
path: extensions/Services
ssh-key: ${{ secrets.SSH_PRIVATE_KEY_SERVICES_EXTENSION }}
- name: "Install dependencies in services extension"
uses: php-actions/composer@v6
with:
working_dir: "extensions/Services"
command: install
only_args: --no-dev --no-ansi --no-interaction --prefer-dist --ignore-platform-reqs --classmap-authoritative
php_version: 8.3
- name: "Setup PHP with PECL extension" - name: "Setup PHP with PECL extension"
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
@@ -65,6 +80,9 @@ jobs:
- name: "Activate billing extension" - name: "Activate billing extension"
run: php artisan module:enable Billing run: php artisan module:enable Billing
- name: "Activate services extension"
run: php artisan module:enable Services
- name: "Login to GitHub Container Registry" - name: "Login to GitHub Container Registry"
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:

View File

@@ -5,7 +5,7 @@ on:
- develop - develop
pull_request: pull_request:
paths: paths:
- 'workflows/build-public.yml' - '.github/workflows/build-public.yml'
- 'docker/prod/**' - 'docker/prod/**'
workflow_dispatch: workflow_dispatch:

View File

@@ -50,8 +50,8 @@ jobs:
php artisan key:generate php artisan key:generate
php artisan passport:keys php artisan passport:keys
- name: "Run PHPUnit in parallel" - name: "Run PHPUnit"
run: php artisan test --parallel --stop-on-failure --coverage-text --coverage-clover=coverage.xml run: php artisan test --stop-on-failure --coverage-text --coverage-clover=coverage.xml
- name: "Upload coverage reports to Codecov" - name: "Upload coverage reports to Codecov"
uses: codecov/codecov-action@v4.3.0 uses: codecov/codecov-action@v4.3.0

View File

@@ -6,6 +6,7 @@ namespace App\Service\Import\Importers;
use Exception; use Exception;
use Spatie\TemporaryDirectory\TemporaryDirectory; use Spatie\TemporaryDirectory\TemporaryDirectory;
use ValueError;
use ZipArchive; use ZipArchive;
class TogglDataImporter extends DefaultImporter class TogglDataImporter extends DefaultImporter
@@ -20,7 +21,10 @@ class TogglDataImporter extends DefaultImporter
$zip = new ZipArchive(); $zip = new ZipArchive();
$temporaryDirectory = TemporaryDirectory::make(); $temporaryDirectory = TemporaryDirectory::make();
file_put_contents($temporaryDirectory->path('import.zip'), $data); file_put_contents($temporaryDirectory->path('import.zip'), $data);
$zip->open($temporaryDirectory->path('import.zip'), ZipArchive::RDONLY); $res = $zip->open($temporaryDirectory->path('import.zip'), ZipArchive::RDONLY);
if ($res !== true) {
throw new ImportException('Invalid ZIP, error code: '.$res);
}
$temporaryDirectory = TemporaryDirectory::make(); $temporaryDirectory = TemporaryDirectory::make();
$zip->extractTo($temporaryDirectory->path()); $zip->extractTo($temporaryDirectory->path());
$zip->close(); $zip->close();
@@ -107,6 +111,8 @@ class TogglDataImporter extends DefaultImporter
], [], (string) $task->id); ], [], (string) $task->id);
} }
} }
} catch (ValueError $exception) {
} catch (ImportException $exception) { } catch (ImportException $exception) {
throw $exception; throw $exception;
} catch (Exception $exception) { } catch (Exception $exception) {

View File

@@ -69,7 +69,7 @@ return [
'stack_production' => [ 'stack_production' => [
'driver' => 'stack', 'driver' => 'stack',
'channels' => ['single', 'sentry'], 'channels' => ['stderr', 'sentry'],
], ],
'daily' => [ 'daily' => [

View File

@@ -3,8 +3,7 @@
<CardTitle :title="title" :icon="icon"></CardTitle> <CardTitle :title="title" :icon="icon"></CardTitle>
<div <div
class="rounded-lg bg-card-background border border-card-border flex-1 flex items-stretch"> class="rounded-lg bg-card-background border border-card-border flex-1 flex items-stretch">
<div <div class="w-full flex flex-col">
class="w-full flex flex-col">
<slot></slot> <slot></slot>
</div> </div>
</div> </div>

View File

@@ -88,9 +88,7 @@ const switchToTeam = (team: Organization) => {
Organization Settings Organization Settings
</DropdownLink> </DropdownLink>
<DropdownLink <DropdownLink v-if="isBillingActivated()" href="/billing">
v-if="isBillingActivated()"
href="/billing">
Billing Billing
</DropdownLink> </DropdownLink>

View File

@@ -28,14 +28,16 @@ const deleteTeam = () => {
<ActionSection> <ActionSection>
<template #title> Delete Organization </template> <template #title> Delete Organization </template>
<template #description> Permanently delete this organization. </template> <template #description>
Permanently delete this organization.
</template>
<template #content> <template #content>
<div class="max-w-xl text-sm text-muted"> <div class="max-w-xl text-sm text-muted">
Once a organization is deleted, all of its resources and data will be Once a organization is deleted, all of its resources and data
permanently deleted. Before deleting this organization, please download will be permanently deleted. Before deleting this organization,
any data or information regarding this organization that you wish to please download any data or information regarding this
retain. organization that you wish to retain.
</div> </div>
<div class="mt-5"> <div class="mt-5">

View File

@@ -257,9 +257,9 @@ const displayableRole = (role: string) => {
<template #title> Pending Team Invitations</template> <template #title> Pending Team Invitations</template>
<template #description> <template #description>
These people have been invited to your organization and have been These people have been invited to your organization and have
sent an invitation email. They may join the organization by been sent an invitation email. They may join the
accepting the email invitation. organization by accepting the email invitation.
</template> </template>
<!-- Pending Organization Member Invitation List --> <!-- Pending Organization Member Invitation List -->
@@ -471,7 +471,8 @@ const displayableRole = (role: string) => {
<template #title> Remove Team Member</template> <template #title> Remove Team Member</template>
<template #content> <template #content>
Are you sure you would like to remove this person from the organization? Are you sure you would like to remove this person from the
organization?
</template> </template>
<template #footer> <template #footer>

View File

@@ -5,7 +5,9 @@ declare(strict_types=1);
namespace Tests\Unit\Service\Import\Importer; namespace Tests\Unit\Service\Import\Importer;
use App\Models\Organization; use App\Models\Organization;
use App\Service\Import\Importers\ImportException;
use App\Service\Import\Importers\TogglDataImporter; use App\Service\Import\Importers\TogglDataImporter;
use Exception;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Spatie\TemporaryDirectory\TemporaryDirectory; use Spatie\TemporaryDirectory\TemporaryDirectory;
@@ -27,6 +29,25 @@ class TogglDataImporterTest extends ImporterTestAbstract
return $zipPath; return $zipPath;
} }
public function test_import_throws_exception_if_data_is_not_zip(): void
{
// Arrange
$organization = Organization::factory()->create();
$importer = new TogglDataImporter();
$importer->init($organization);
// Act
try {
$importer->importData('not a zip');
} catch (Exception $e) {
$this->assertInstanceOf(ImportException::class, $e);
$this->assertSame('Invalid ZIP, error code: 19', $e->getMessage());
return;
}
$this->fail();
}
public function test_import_of_test_file_succeeds(): void public function test_import_of_test_file_succeeds(): void
{ {
// Arrange // Arrange
@@ -48,7 +69,6 @@ class TogglDataImporterTest extends ImporterTestAbstract
$this->assertSame(1, $report->usersCreated); $this->assertSame(1, $report->usersCreated);
$this->assertSame(2, $report->projectsCreated); $this->assertSame(2, $report->projectsCreated);
$this->assertSame(1, $report->clientsCreated); $this->assertSame(1, $report->clientsCreated);
} }
public function test_import_of_test_file_twice_succeeds(): void public function test_import_of_test_file_twice_succeeds(): void