gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant-backoffice] branch master updated (2672d10 -> d5671c2)


From: gnunet
Subject: [taler-merchant-backoffice] branch master updated (2672d10 -> d5671c2)
Date: Mon, 09 May 2022 18:30:32 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository merchant-backoffice.

    from 2672d10  error handling upon withdrawal
     new 1a59c8f  fix and test for auth update
     new 3beb4dc  fix and test for auth update also for admin
     new d5671c2  add min age in the contract terms form

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/components/form/InputPaytoForm.tsx         |  22 ++-
 .../src/components/product/ProductForm.tsx         |   9 ++
 packages/merchant-backoffice/src/declaration.d.ts  |  12 ++
 packages/merchant-backoffice/src/hooks/instance.ts |   6 +-
 .../paths/instance/orders/create/CreatePage.tsx    |  20 ++-
 packages/merchant-backoffice/src/schemas/index.ts  |   2 +
 packages/merchant-backoffice/tests/axiosMock.ts    |  19 ++-
 .../tests/context/backend.test.tsx                 | 172 +++++++++++++++++++++
 8 files changed, 249 insertions(+), 13 deletions(-)
 create mode 100644 packages/merchant-backoffice/tests/context/backend.test.tsx

diff --git 
a/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx 
b/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
index 95045aa..9cfef07 100644
--- a/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
+++ b/packages/merchant-backoffice/src/components/form/InputPaytoForm.tsx
@@ -182,13 +182,15 @@ export function InputPaytoForm<T>({
   }
   const i18n = useTranslator();
 
-  const url = new URL(`payto://${value.target}${payToPath}`);
   const ops = value.options!;
-  Object.keys(ops).forEach((opt_key) => {
-    const opt_value = ops[opt_key];
-    if (opt_value) url.searchParams.set(opt_key, opt_value);
-  });
-  const paytoURL = url.toString();
+  const url = tryUrl(`payto://${value.target}${payToPath}`);
+  if (url) {
+    Object.keys(ops).forEach((opt_key) => {
+      const opt_value = ops[opt_key];
+      if (opt_value) url.searchParams.set(opt_key, opt_value);
+    });
+  }
+  const paytoURL = !url ? "" : url.toString();
 
   const errors: FormErrors<Entity> = {
     target: value.target === noTargetValue ? i18n`required` : undefined,
@@ -380,3 +382,11 @@ export function InputPaytoForm<T>({
     </InputGroup>
   );
 }
+
+function tryUrl(s: string): URL | undefined {
+  try {
+    return new URL(s);
+  } catch (e) {
+    return undefined;
+  }
+}
diff --git 
a/packages/merchant-backoffice/src/components/product/ProductForm.tsx 
b/packages/merchant-backoffice/src/components/product/ProductForm.tsx
index e481f1b..e177caf 100644
--- a/packages/merchant-backoffice/src/components/product/ProductForm.tsx
+++ b/packages/merchant-backoffice/src/components/product/ProductForm.tsx
@@ -100,6 +100,10 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
     }
     delete (value as any).stock;
 
+    if (typeof value.minimum_age !== "undefined" && value.minimum_age < 1) {
+      delete value.minimum_age;
+    }
+
     return value as MerchantBackend.Products.ProductDetail & {
       product_id: string;
     };
@@ -139,6 +143,11 @@ export function ProductForm({ onSubscribe, initial, 
alreadyExist }: Props) {
           label={i18n`Description`}
           tooltip={i18n`product description for customers`}
         />
+        <Input<Entity>
+          name="minimum_age"
+          label={i18n`Ages restricted`}
+          tooltip={i18n`is this product restricted for customer below certain 
age?`}
+        />
         <Input<Entity>
           name="unit"
           label={i18n`Unit`}
diff --git a/packages/merchant-backoffice/src/declaration.d.ts 
b/packages/merchant-backoffice/src/declaration.d.ts
index 377b030..6699601 100644
--- a/packages/merchant-backoffice/src/declaration.d.ts
+++ b/packages/merchant-backoffice/src/declaration.d.ts
@@ -189,6 +189,9 @@ export namespace MerchantBackend {
 
         // time indicating when this product should be delivered
         delivery_date?: TalerProtocolTimestamp;
+
+        // Minimum age buyer must have (in years). Default is 0.
+        minimum_age?: Integer;
     }
     interface Merchant {
         // label for a location with the business address of the merchant
@@ -536,6 +539,8 @@ export namespace MerchantBackend {
             // Identifies when we expect the next restocking to happen.
             next_restock?: Timestamp;
 
+            // Minimum age buyer must have (in years). Default is 0.
+            minimum_age?: Integer;
         }
         //   PATCH /private/products/$PRODUCT_ID
         interface ProductPatchDetail {
@@ -577,6 +582,8 @@ export namespace MerchantBackend {
             // Identifies when we expect the next restocking to happen.
             next_restock?: Timestamp;
 
+            // Minimum age buyer must have (in years). Default is 0.
+            minimum_age?: Integer;
         }
 
         // GET /private/products
@@ -633,6 +640,8 @@ export namespace MerchantBackend {
             // Identifies when we expect the next restocking to happen.
             next_restock?: Timestamp;
 
+            // Minimum age buyer must have (in years). Default is 0.
+            minimum_age?: Integer;
         }
 
         // POST /private/products/$PRODUCT_ID/lock
@@ -1417,6 +1426,9 @@ export namespace MerchantBackend {
         // Useful when the merchant needs to store extra information on a
         // contract without storing it separately in their database.
         extra?: any;
+
+        // Minimum age buyer must have (in years). Default is 0.
+        minimum_age?: Integer;
     }
 
 }
diff --git a/packages/merchant-backoffice/src/hooks/instance.ts 
b/packages/merchant-backoffice/src/hooks/instance.ts
index 9153e19..748bb82 100644
--- a/packages/merchant-backoffice/src/hooks/instance.ts
+++ b/packages/merchant-backoffice/src/hooks/instance.ts
@@ -85,7 +85,7 @@ export interface AdminAPI {
 
 export function useManagementAPI(instanceId: string): InstanceAPI {
   const mutateAll = useMatchMutate();
-  const { url, token } = useBackendContext();
+  const { url, token, updateLoginStatus } = useBackendContext();
 
   const updateInstance = async (
     instance: MerchantBackend.Instances.InstanceReconfigurationMessage
@@ -125,6 +125,7 @@ export function useManagementAPI(instanceId: string): 
InstanceAPI {
       data: { method: "token", token: newToken },
     });
 
+    updateLoginStatus(url, newToken)
     mutateAll(/\/management\/instances/);
   };
 
@@ -133,7 +134,7 @@ export function useManagementAPI(instanceId: string): 
InstanceAPI {
 
 export function useInstanceAPI(): InstanceAPI {
   const { mutate } = useSWRConfig();
-  const { url: baseUrl, token: adminToken } = useBackendContext();
+  const { url: baseUrl, token: adminToken, updateLoginStatus } = 
useBackendContext();
   const { token: instanceToken, id, admin } = useInstanceContext();
 
   const { url, token } = !admin
@@ -180,6 +181,7 @@ export function useInstanceAPI(): InstanceAPI {
       data: { method: "token", token: newToken },
     });
 
+    updateLoginStatus(baseUrl, newToken)
     mutate([`/private/`, token, url], null);
   };
 
diff --git 
a/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx 
b/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx
index e648daa..38ac657 100644
--- 
a/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice/src/paths/instance/orders/create/CreatePage.tsx
@@ -106,6 +106,7 @@ interface Payments {
   max_wire_fee?: string;
   wire_fee_amortization?: number;
   createToken: boolean;
+  minimum_age?: number;
 }
 interface Entity {
   inventoryProducts: ProductMap;
@@ -247,6 +248,7 @@ export function CreatePage({
           : undefined,
         delivery_location: value.shipping.delivery_location,
         fulfillment_url: value.shipping.fullfilment_url,
+        minimum_age: value.payments.minimum_age,
       },
       inventory_products: inventoryList.map((p) => ({
         product_id: p.product.id,
@@ -331,6 +333,11 @@ export function CreatePage({
     totalAsString
   );
 
+  const minAgeByProducts = allProducts.reduce(
+    (cur, prev) =>
+      !prev.minimum_age || cur > prev.minimum_age ? cur : prev.minimum_age,
+    0
+  );
   return (
     <div>
       <section class="section is-main-section">
@@ -500,6 +507,16 @@ export function CreatePage({
                   label={i18n`Create token`}
                   tooltip={i18n`Uncheck this option if the merchant backend 
generated an order ID with enough entropy to prevent adversarial claims.`}
                 />
+                <InputNumber
+                  name="payments.minimum_age"
+                  label={i18n`Minimum age required`}
+                  tooltip={i18n`Any value greater than 0 will limit the coins 
able be used to pay this contract. If empty the age restriction will be defined 
by the products`}
+                  help={
+                    minAgeByProducts > 0
+                      ? i18n`Min age defined by the producs is 
${minAgeByProducts}`
+                      : undefined
+                  }
+                />
               </InputGroup>
 
               <InputGroup
@@ -538,7 +555,7 @@ export function CreatePage({
   );
 }
 
-function asProduct(p: ProductAndQuantity) {
+function asProduct(p: ProductAndQuantity): MerchantBackend.Product {
   return {
     product_id: p.product.id,
     image: p.product.image,
@@ -547,5 +564,6 @@ function asProduct(p: ProductAndQuantity) {
     quantity: p.quantity,
     description: p.product.description,
     taxes: p.product.taxes,
+    minimum_age: p.product.minimum_age,
   };
 }
diff --git a/packages/merchant-backoffice/src/schemas/index.ts 
b/packages/merchant-backoffice/src/schemas/index.ts
index 4223596..a1cd597 100644
--- a/packages/merchant-backoffice/src/schemas/index.ts
+++ b/packages/merchant-backoffice/src/schemas/index.ts
@@ -179,6 +179,7 @@ export const ProductCreateSchema = yup.object().shape({
   stock: yup.object({
 
   }).optional(),
+  minimum_age: yup.number().optional().positive(),
 })
 
 export const ProductUpdateSchema = yup.object().shape({
@@ -189,6 +190,7 @@ export const ProductUpdateSchema = yup.object().shape({
   stock: yup.object({
 
   }).optional(),
+  minimum_age: yup.number().optional().positive(),
 })
 
 
diff --git a/packages/merchant-backoffice/tests/axiosMock.ts 
b/packages/merchant-backoffice/tests/axiosMock.ts
index 412d2a0..13ddab5 100644
--- a/packages/merchant-backoffice/tests/axiosMock.ts
+++ b/packages/merchant-backoffice/tests/axiosMock.ts
@@ -36,7 +36,9 @@ interface PatchQuery { patch: string }
 
 const JEST_DEBUG_LOG = process.env['JEST_DEBUG_LOG'] !== undefined
 
-type TestValues = [axios.AxiosRequestConfig | undefined, { query: Query<any, 
any>; params?: { request?: any, qparam?: any, response?: any } } | undefined]
+type ExpectationValues = { query: Query<any, any>; params?: { auth?: string, 
request?: any, qparam?: any, response?: any } }
+
+type TestValues = [axios.AxiosRequestConfig | undefined, ExpectationValues | 
undefined]
 
 const defaultCallback = (actualQuery?: axios.AxiosRequestConfig): 
axios.AxiosPromise<any> => {
   if (JEST_DEBUG_LOG) {
@@ -50,17 +52,22 @@ setAxiosRequestAsTestingEnvironment(
 );
 
 export class AxiosMockEnvironment {
-  expectations: Array<{ query: Query<any, any>, params?: { request?: any, 
qparam?: any, response?: any }, result: { args: axios.AxiosRequestConfig | 
undefined } } | undefined> = []
+  expectations: Array<{
+    query: Query<any, any>,
+    auth?: string,
+    params?: { request?: any, qparam?: any, response?: any },
+    result: { args: axios.AxiosRequestConfig | undefined }
+  } | undefined> = []
   // axiosMock: jest.MockedFunction<axios.AxiosStatic>
 
-  addRequestExpectation<RequestType, ResponseType>(expectedQuery: 
Query<RequestType, ResponseType>, params: { request?: RequestType, qparam?: 
any, response?: ResponseType }): void {
+  addRequestExpectation<RequestType, ResponseType>(expectedQuery: 
Query<RequestType, ResponseType>, params: { auth?: string, request?: 
RequestType, qparam?: any, response?: ResponseType }): void {
     const result = mockAxiosOnce(function (actualQuery?: 
axios.AxiosRequestConfig): axios.AxiosPromise {
 
       if (JEST_DEBUG_LOG) {
         console.log('query to the backend is made', actualQuery)
       }
       if (!expectedQuery) {
-        return Promise.reject()
+        return Promise.reject("a query was made but it was not expected")
       }
       if (JEST_DEBUG_LOG) {
         console.log('expected query:', params?.request)
@@ -160,6 +167,10 @@ export function assertNextRequest(env: 
AxiosMockEnvironment): void {
     expect(actualQuery.params).toMatchObject(expectedQuery.params.qparam)
   }
 
+  if (expectedQuery.params?.auth) {
+    expect(actualQuery.headers.Authorization).toBe(expectedQuery.params?.auth)
+  }
+
 }
 
 ////////////////////
diff --git a/packages/merchant-backoffice/tests/context/backend.test.tsx 
b/packages/merchant-backoffice/tests/context/backend.test.tsx
new file mode 100644
index 0000000..b7b50fd
--- /dev/null
+++ b/packages/merchant-backoffice/tests/context/backend.test.tsx
@@ -0,0 +1,172 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { renderHook } from "@testing-library/preact-hooks";
+import { ComponentChildren, h, VNode } from "preact";
+import { act } from "preact/test-utils";
+import { BackendContextProvider } from "../../src/context/backend";
+import { InstanceContextProvider } from "../../src/context/instance";
+import { MerchantBackend } from "../../src/declaration";
+import {
+  useAdminAPI,
+  useInstanceAPI,
+  useManagementAPI,
+} from "../../src/hooks/instance";
+import {
+  API_CREATE_INSTANCE,
+  API_GET_CURRENT_INSTANCE,
+  API_UPDATE_CURRENT_INSTANCE_AUTH,
+  API_UPDATE_INSTANCE_AUTH_BY_ID,
+  assertJustExpectedRequestWereMade,
+  AxiosMockEnvironment,
+} from "../axiosMock";
+
+interface TestingContextProps {
+  children?: ComponentChildren;
+}
+
+function TestingContext({ children }: TestingContextProps): VNode {
+  return (
+    <BackendContextProvider defaultUrl="http://backend"; initialToken="token">
+      {children}
+    </BackendContextProvider>
+  );
+}
+function AdminTestingContext({ children }: TestingContextProps): VNode {
+  return (
+    <BackendContextProvider defaultUrl="http://backend"; initialToken="token">
+      <InstanceContextProvider
+        value={{
+          token: "token",
+          id: "default",
+          admin: true,
+          changeToken: () => null,
+        }}
+      >
+        {children}
+      </InstanceContextProvider>
+    </BackendContextProvider>
+  );
+}
+
+describe("backend context api ", () => {
+  it("should use new token after updating the instance token in the settings 
as user", async () => {
+    const env = new AxiosMockEnvironment();
+
+    const { result, waitForNextUpdate } = renderHook(
+      () => {
+        const instance = useInstanceAPI();
+        const management = useManagementAPI("default");
+        const admin = useAdminAPI();
+
+        return { instance, management, admin };
+      },
+      { wrapper: TestingContext }
+    );
+
+    if (!result.current) {
+      expect(result.current).toBeDefined();
+      return;
+    }
+
+    env.addRequestExpectation(API_UPDATE_INSTANCE_AUTH_BY_ID("default"), {
+      request: {
+        method: "token",
+        token: "another_token",
+      },
+      response: {
+        name: "instance_name",
+      } as MerchantBackend.Instances.QueryInstancesResponse,
+    });
+
+    await act(async () => {
+      await result.current?.management.setNewToken("another_token");
+    });
+
+    // await waitForNextUpdate({ timeout: 1 });
+
+    assertJustExpectedRequestWereMade(env);
+
+    env.addRequestExpectation(API_CREATE_INSTANCE, {
+      auth: "Bearer another_token",
+      request: {
+        id: "new_instance_id",
+      } as MerchantBackend.Instances.InstanceConfigurationMessage,
+    });
+
+    result.current.admin.createInstance({
+      id: "new_instance_id",
+    } as MerchantBackend.Instances.InstanceConfigurationMessage);
+
+    assertJustExpectedRequestWereMade(env);
+  });
+
+  it("should use new token after updating the instance token in the settings 
as admin", async () => {
+    const env = new AxiosMockEnvironment();
+
+    const { result, waitForNextUpdate } = renderHook(
+      () => {
+        const instance = useInstanceAPI();
+        const management = useManagementAPI("default");
+        const admin = useAdminAPI();
+
+        return { instance, management, admin };
+      },
+      { wrapper: AdminTestingContext }
+    );
+
+    if (!result.current) {
+      expect(result.current).toBeDefined();
+      return;
+    }
+
+    env.addRequestExpectation(API_UPDATE_CURRENT_INSTANCE_AUTH, {
+      request: {
+        method: "token",
+        token: "another_token",
+      },
+      response: {
+        name: "instance_name",
+      } as MerchantBackend.Instances.QueryInstancesResponse,
+    });
+
+    await act(async () => {
+      await result.current?.instance.setNewToken("another_token");
+    });
+
+    // await waitForNextUpdate({ timeout: 1 });
+
+    assertJustExpectedRequestWereMade(env);
+
+    env.addRequestExpectation(API_CREATE_INSTANCE, {
+      auth: "Bearer another_token",
+      request: {
+        id: "new_instance_id",
+      } as MerchantBackend.Instances.InstanceConfigurationMessage,
+    });
+
+    result.current.admin.createInstance({
+      id: "new_instance_id",
+    } as MerchantBackend.Instances.InstanceConfigurationMessage);
+
+    assertJustExpectedRequestWereMade(env);
+  });
+});

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]