Skip to content

Commit

Permalink
wip form session webhook log
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilReinking committed Apr 7, 2023
1 parent 09db0b8 commit 273cf40
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 11 deletions.
1 change: 1 addition & 0 deletions app/Http/Resources/FormSessionResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public function toArray($request)
'completed_at' => (string) $this->getRawOriginal('is_completed'),
'params' => $this->params ? json_encode($this->params) : null,
'responses' => $this->getResponses(),
'webhooks' => $this->webhooks
];
}

Expand Down
11 changes: 10 additions & 1 deletion app/Jobs/CallWebhookJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use App\Http\Resources\FormSessionResource;
use App\Models\FormSessionWebhook;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

Expand Down Expand Up @@ -43,7 +44,7 @@ public function handle()
{
$payload = FormSessionResource::make($this->session)->resolve();

Http::withHeaders(
$response = Http::withHeaders(
$this->webhook->headers ?? []
)->send(
$this->webhook->webhook_method,
Expand All @@ -54,5 +55,13 @@ public function handle()
);

// TODO: we need to somehow track status of the webhook submit in a new table with relation to the session and the webhook

$this->session->webhooks()->updateOrCreate([
'form_webhook_id' => $this->webhook->id
], [
'status' => $response->status(),
'response' => $response->body(),
'tries' => $this->session->webhooks()->where('form_webhook_id', $this->webhook->id)->count() + 1,
]);
}
}
5 changes: 5 additions & 0 deletions app/Models/FormSession.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public function form()
return $this->belongsTo(Form::class, 'form_id', 'id');
}

public function webhooks()
{
return $this->hasMany(FormSessionWebhook::class);
}

public static function getByTokenAndForm(String $token, Form $form)
{
return self::where('token', $token)->where('form_id', $form->id)->first();
Expand Down
13 changes: 13 additions & 0 deletions app/Models/FormSessionWebhook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class FormSessionWebhook extends Model
{
use HasFactory;

protected $guarded = [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use App\Models\FormSession;
use App\Models\FormWebhook;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('form_session_webhooks', function (Blueprint $table) {
$table->id();
$table->integer('status')->nullable();
$table->json('response')->nullable();
$table->integer('tries')->default(0);
$table->foreignIdFor(FormSession::class);
$table->foreignIdFor(FormWebhook::class);
$table->timestamps();

// make form_session_id and form_webhook_id unique, so we can't have duplicate entries
$table->unique(['form_session_id', 'form_webhook_id']);
});
}
};
38 changes: 36 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"copy-text-to-clipboard": "^3.0.1",
"eslint": "^8.28.0",
"eslint-plugin-vue": "^8.7.1",
"floating-vue": "^2.0.0-beta.20",
"highlight.js": "^11.6.0",
"laravel-vite-plugin": "^0.7.4",
"lodash": "^4.17.19",
Expand Down
11 changes: 11 additions & 0 deletions resources/css/_tooltip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* Style */
.v-popper--theme-tooltip .v-popper__inner {
background: #1e293b;
color: #ffffff;
padding: 3px 12px;
border-radius: 6px;
}

.v-popper--theme-tooltip .v-popper__arrow-outer {
border-color: #1e293b;
}
1 change: 1 addition & 0 deletions resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* CUSTOM */
@import "./_scrollbar.css";
@import "./_smooth-dnd.css";
@import "./_tooltip.css";

/* TAILWIND */
@import "tailwindcss/utilities";
Expand Down
11 changes: 11 additions & 0 deletions resources/js/app.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "floating-vue/dist/style.css";
import "@css/app.css";
import { createApp, h } from "vue";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
Expand All @@ -6,6 +7,7 @@ import { InertiaProgress } from "@inertiajs/progress";
import { createPinia } from "pinia";
import { PiniaDebounce } from "@pinia/plugin-debounce";
import { createI18n } from "vue-i18n";
import FloatingVue from "floating-vue";
import debounce from "lodash/debounce";

import localeEN from "@i18n/en.json";
Expand Down Expand Up @@ -41,6 +43,15 @@ createInertiaApp({
.use(plugin)
.use(pinia)
.use(i18n)
.use(FloatingVue, {
themes: {
tooltip: {
autoHide: true,
placement: "auto",
html: true,
},
},
})
.mixin({ methods: { route: window.route } })
.mount(el);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@
class="mt-1"
v-bind="{ params: submission.params }"
/>
<!-- Status for each Webhook Configured -->
<div class="flex">
<SubmissionWebhookStatus name="Webhook" />
</div>
</td>
<td
class="min-w-[200px] max-w-xs p-4 text-sm text-grey-500"
v-for="header in headers"
:key="submission.id + header.id"
>
<span class="block" v-if="submission.responses[header.id]">
<span class="block space-x-2" v-if="submission.responses[header.id]">
<span
class="block"
class="inline-block rounded bg-grey-100 px-2 py-1"
v-for="response in submission.responses[header.id].data"
:key="response.id"
>
Expand All @@ -43,6 +47,7 @@
<script lang="ts" setup>
import FormattedDate from "@/forms/common/LocaleDate.vue";
import SubmissionParams from "@/components/Factory/Submissions/SubmissionParams.vue";
import SubmissionWebhookStatus from "@/components/Factory/Submissions/SubmissionWebhookStatus.vue";
import { D9Button } from "@deck9/ui";
import { callDeleteFormSubmission } from "@/api/forms";
import { useForm } from "@/stores";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<div class="inline-flex cursor-pointer items-center" v-tooltip="'200 OK'">
<span class="mr-1 inline-block h-3 w-3 rounded-full bg-green-400"></span>
<span>{{ name }}</span>
</div>
</template>

<script lang="ts" setup>
defineProps<{
name: string;
}>();
</script>
82 changes: 82 additions & 0 deletions tests/Feature/FormSessionWebhookLogTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

use App\Models\Form;
use App\Models\FormSession;
use App\Models\FormWebhook;
use App\Jobs\CallWebhookJob;
use Illuminate\Support\Facades\Http;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('can create a database entry for a session and configured form webhook response', function () {
Http::fake();

$form = Form::factory()->has(
FormWebhook::factory()
)->create();
$session = FormSession::factory()->for($form)->create();

CallWebhookJob::dispatch($session, $form->formWebhooks->first());

$this->assertDatabaseHas('form_session_webhooks', [
'form_session_id' => $session->id,
'form_webhook_id' => $form->formWebhooks->first()->id,
'status' => 200,
'tries' => 1,
]);

Http::assertSentCount(1);
});

it('will use the same database entry if a webhook gets called twice for the same session', function () {
Http::fake();

$form = Form::factory()->has(
FormWebhook::factory()
)->create();
$session = FormSession::factory()->for($form)->create();

CallWebhookJob::dispatch($session, $form->formWebhooks->first());
CallWebhookJob::dispatch($session, $form->formWebhooks->first());

$this->assertDatabaseHas('form_session_webhooks', [
'form_session_id' => $session->id,
'form_webhook_id' => $form->formWebhooks->first()->id,
'status' => 200,
'tries' => 2,
]);

Http::assertSentCount(2);
});

it('submissions api endpoint will include the session webhook data', function () {
Http::fake(function () {
return Http::response('OK!', 200);
});

$form = Form::factory()->has(
FormWebhook::factory()
)->create();
$session = FormSession::factory()->for($form)->completed()->create();

CallWebhookJob::dispatch($session, $form->formWebhooks->first());

$response = $this->actingAs($form->user)
->json('get', route('api.forms.submissions', ['form' => $form->uuid]))
->assertStatus(200);

$response->assertJson([
'data' => [
[
'webhooks' => [
[
'response' => 'OK!',
'status' => 200,
'tries' => 1,
],
],
],
],
]);
});
Loading

0 comments on commit 273cf40

Please sign in to comment.