Skip to content
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

feat: Add client side authentication to gravityforms rest api #7

Merged
merged 9 commits into from
Jan 7, 2024
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
3 changes: 3 additions & 0 deletions apps/svelte-gravity-forms/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PUBLIC_GF_CONSUMER_KEY=
PUBLIC_GF_CONSUMER_SECRECT=
PUBLIC_GF_API_URL=
3 changes: 3 additions & 0 deletions apps/svelte-gravity-forms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@sveltejs/kit": "^2.0.0",
"@sveltejs/package": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/crypto-js": "^4.2.1",
"@types/eslint": "8.56.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
Expand Down Expand Up @@ -63,8 +64,10 @@
"dependencies": {
"bits-ui": "^0.13.0",
"clsx": "^2.1.0",
"crypto-js": "^4.2.0",
"formsnap": "^0.4.2",
"lucide-svelte": "^0.304.0",
"oauth-1.0a": "^2.2.6",
"sveltekit-superforms": "^1.13.1",
"tailwind-merge": "^2.2.0",
"tailwind-variants": "^0.1.20",
Expand Down
9 changes: 7 additions & 2 deletions apps/svelte-gravity-forms/src/lib/components/root.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@
type $$Props = Props;

export let formId: $$Props['formId'] = undefined;
export let backendUrl: $$Props['backendUrl'] = undefined;
export let consumerKey: $$Props['consumerKey'] = undefined;
export let consumerSecret: $$Props['consumerSecret'] = undefined;

const {
methods: { onSubmitForm },

states: { formSchema, formFields, formObject, isSubmitted, defaultConfirmation },
refs: { formRef }
} = setCtx({
formId: formId
formId: formId,
backendUrl: backendUrl,
consumerKey: consumerKey,
consumerSecret: consumerSecret
});

$: form = $formSchema
Expand Down
5 changes: 4 additions & 1 deletion apps/svelte-gravity-forms/src/lib/components/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export type Props = {
formId?: number;
formId: number | undefined;
backendUrl: string | undefined;
consumerKey: string | undefined;
consumerSecret: string | undefined;
};
56 changes: 42 additions & 14 deletions apps/svelte-gravity-forms/src/lib/internal/gf.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { onMount } from 'svelte';
import { get, writable } from 'svelte/store';
import { PUBLIC_GF_API_URL } from '$env/static/public';
import { z, type AnyZodObject } from 'zod';
import { effect, omit, removeUndefined, toWritableStores } from '$lib/internal/helpers/index.js';

Expand All @@ -11,13 +10,21 @@ import type {
GFFormObjectProps
} from './types.js';
import type { HTMLAttributes } from 'svelte/elements';
import { getClientFormObject, sendSubmission } from './helpers/gf-rest.js';

export type CreateGravityFromsProps = {
formId?: number;
formId?: number | undefined;
backendUrl?: string;
consumerKey?: string;
consumerSecret?: string;
};

const defaultProps = {
formId: undefined
formId: undefined,
backendUrl: 'http://localhost:8888/wp-json',
formObjectData: undefined,
consumerKey: undefined,
consumerSecret: undefined
};

export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
Expand All @@ -27,13 +34,20 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
} satisfies CreateGravityFromsProps;

// Create writable stores for the form options
const options = toWritableStores(omit({ ...withDefaults }));
const options = toWritableStores(
omit({
...withDefaults
})
);

const { formId, backendUrl } = options;

// refs
const formRef = writable<HTMLFormElement | undefined>(undefined);
const submitButtonRef = writable<HTMLAttributes<HTMLButtonElement> | undefined>(undefined);

// states

const formObject = writable<GFFormObjectProps>(undefined);
const formFields = writable<GFFieldsProps[]>(undefined);
const formIdStore = writable(withDefaults.formId);
Expand All @@ -43,16 +57,23 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
const defaultConfirmation = writable<GFComfirmationProps>(undefined);
const isSubmitted = writable<boolean>(false);

const consumerKeyStore = writable(withDefaults.consumerKey);
const consumerSecretStore = writable(withDefaults.consumerSecret);

// Fetch form object from Gravity Forms API
async function getFormObject(formId: number) {
const res = await fetch(`${PUBLIC_GF_API_URL}/forms/${formId}`, { method: 'GET' });
const data = (await res.json()) as GFFormObjectProps;
return data;
}
async function onSubmitForm(req: { [x: string]: unknown }) {
try {
const submit = await sendSubmission(req, backendUrl, formId);

// Handle form submission
async function onSubmitForm(_formData: unknown) {
isSubmitted.set(true);
if (!submit.is_valid) {
throw new Error(submit.message);
}

isSubmitted.set(true);
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);
}
}

// Calculate column span for a field
Expand Down Expand Up @@ -159,7 +180,12 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
if (!get(formIdStore)) {
return;
}
const formData = await getFormObject(get(formIdStore));
const formData = await getClientFormObject(
backendUrl,
formIdStore,
consumerKeyStore,
consumerSecretStore
);
formObject.set(formData);
});

Expand Down Expand Up @@ -273,7 +299,9 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
formRequiredIndicator,
formSubmtiButton,
isSubmitted,
defaultConfirmation
defaultConfirmation,
formId,
backendUrl
},
methods: {
onSubmitForm
Expand Down
64 changes: 64 additions & 0 deletions apps/svelte-gravity-forms/src/lib/internal/helpers/gf-rest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { get, type Writable } from 'svelte/store';
import type { GFFormObjectProps } from '../types.js';

import OAuth from 'oauth-1.0a';
import CryptoJS from 'crypto-js';

export async function sendSubmission(
req: { [x: string]: unknown },
backendUrl: Writable<string | undefined>,
formId: Writable<number>
) {
const res = await fetch(`${get(backendUrl)}gf/v2/forms/${get(formId)}/submissions`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
}
});
const data = await res.json();
return data;
}

export async function getClientFormObject(
backendUrl: Writable<string | undefined>,
formId: Writable<number>,
consumer_key: Writable<string>,
consumer_secret: Writable<string>
) {
const oauth = new OAuth({
consumer: {
key: get(consumer_key),
secret: get(consumer_secret)
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
}
});

// Generate OAuth 1.0 parameters
const oauthData = oauth.authorize({
url: `${get(backendUrl)}gf/v2/forms/${get(formId)}`,
method: 'GET'
});

// Convert OAuth parameters to URL parameters
const params = new URLSearchParams({
oauth_consumer_key: oauthData.oauth_consumer_key,
oauth_nonce: oauthData.oauth_nonce,
oauth_signature_method: oauthData.oauth_signature_method,
oauth_timestamp: oauthData.oauth_timestamp.toString(),
oauth_version: oauthData.oauth_version,
oauth_signature: oauthData.oauth_signature
});

// Append OAuth parameters to URL
const url = `${get(backendUrl)}gf/v2/forms/${get(formId)}?${params.toString()}`;

const res = await fetch(url, { method: 'GET' });
// eslint-disable-next-line no-console
console.log(res);
const data = (await res.json()) as GFFormObjectProps;
return data;
}
4 changes: 1 addition & 3 deletions apps/svelte-gravity-forms/src/lib/internal/helpers/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Ref: https://github.com/huntabyte/vaul-svelte/blob/main/src/lib/internal/helpers/store.ts

import type { Readable, Stores, StoresValues, Updater, Writable } from 'svelte/store';
import { derived, writable } from 'svelte/store';
import { onDestroy, onMount } from 'svelte';
Expand Down Expand Up @@ -38,6 +36,7 @@ export function effect<S extends Stores>(
safeOnDestroy(unsub);
return unsub;
}

/**
* A utility function that creates a derived store that automatically
* unsubscribes from its dependencies.
Expand Down Expand Up @@ -133,7 +132,6 @@ export type ToWritableStores<T extends Record<string, unknown>> = {
};

/**
*
* Given an object of properties, returns an object of writable stores
* with the same properties and values.
*/
Expand Down
2 changes: 1 addition & 1 deletion apps/svelte-gravity-forms/src/lib/internal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export type GFButtonProps = {
id?: string;
};

type GFComfirmationProps = {
export type GFComfirmationProps = {
type?: string;
id?: string;
isDefault?: boolean;
Expand Down
12 changes: 11 additions & 1 deletion apps/svelte-gravity-forms/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
<script lang="ts">
import { SvelteGravityForm } from '$lib/index.js';
import {
PUBLIC_GF_API_URL,
PUBLIC_GF_CONSUMER_KEY,
PUBLIC_GF_CONSUMER_SECRECT
} from '$env/static/public';
</script>

<SvelteGravityForm formId={1} />
<SvelteGravityForm
formId={1}
backendUrl={PUBLIC_GF_API_URL}
consumerKey={PUBLIC_GF_CONSUMER_KEY}
consumerSecret={PUBLIC_GF_CONSUMER_SECRECT}
/>
8 changes: 7 additions & 1 deletion apps/wp/config/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

# END WordPress

#allow cors
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

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