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

src/composables: add composable useApiPostSubsidiary for sending create subsidiary request #761

Merged
merged 9 commits into from
Dec 17, 2024
Merged
41 changes: 41 additions & 0 deletions src/adapters/subsidiaryAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// types
import type { FormCompanyAddressFields } from '../components/types/Form';
import type {
SubsidiaryPostApiPayload,
SubsidiaryPostApiResponse,
} from '../components/types/ApiSubsidiary';

/**
* Adapter for converting between API and form subsidiary data formats
*/
export const subsidiaryAdapter = {
/**
* Convert form data to API payload format
*/
toApiPayload(formData: FormCompanyAddressFields): SubsidiaryPostApiPayload {
return {
address: {
street: formData.street,
street_number: formData.houseNumber,
recipient: formData.department,
city: formData.city,
psc: formData.zip,
},
city_id: formData.cityChallenge,
};
},

/**
* Convert API response to form data format
*/
toFormData(apiData: SubsidiaryPostApiResponse): FormCompanyAddressFields {
return {
street: apiData.address.street,
houseNumber: apiData.address.street_number,
city: apiData.address.city,
zip: String(apiData.address.psc),
cityChallenge: apiData.city_id,
department: apiData.address.recipient,
};
},
};
23 changes: 23 additions & 0 deletions src/components/types/ApiSubsidiary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* API types for subsidiary-related operations
*/

export interface SubsidiaryPostApiAddress {
street: string;
street_number: string;
recipient: string;
city: string;
psc: string | number;
}

export interface SubsidiaryPostApiPayload {
address: SubsidiaryPostApiAddress;
city_id: number | null;
}

export interface SubsidiaryPostApiResponse {
id: number;
city_id: number | null;
active: boolean;
address: SubsidiaryPostApiAddress;
}
94 changes: 94 additions & 0 deletions src/composables/useApiPostSubsidiary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// libraries
import { ref, Ref } from 'vue';

// adapters
import { subsidiaryAdapter } from '../adapters/subsidiaryAdapter';

// composables
import { useApi } from './useApi';

// config
import { rideToWorkByBikeConfig } from '../boot/global_vars';

// stores
import { useLoginStore } from '../stores/login';

// types
import type { FormCompanyAddressFields } from '../components/types/Form';
import type { Logger } from '../components/types/Logger';
import type { SubsidiaryPostApiResponse } from '../components/types/ApiSubsidiary';

// utils
import { requestDefaultHeader, requestTokenHeader } from '../utils';

interface UseApiPostSubsidiaryReturn {
isLoading: Ref<boolean>;
createSubsidiary: (
organizationId: number,
subsidiaryData: FormCompanyAddressFields,
) => Promise<FormCompanyAddressFields | null>;
}

/**
* Post subsidiary composable
* Used to enable calling the API to create organization subsidiary
* @param logger - Logger
* @returns {UseApiPostSubsidiaryReturn}
*/
export const useApiPostSubsidiary = (
logger: Logger | null,
): UseApiPostSubsidiaryReturn => {
const isLoading = ref<boolean>(false);
const loginStore = useLoginStore();
const { apiFetch } = useApi();

/**
* Create subsidiary
* Creates a new subsidiary under specified organization
* @param {number} organizationId - Organization Id to create subsidiary under
* @param {FormCompanyAddressFields} subsidiaryData - Subsidiary data to create
* @returns {Promise<FormCompanyAddressFields | null>} - Promise
*/
const createSubsidiary = async (
organizationId: number,
subsidiaryData: FormCompanyAddressFields,
): Promise<FormCompanyAddressFields | null> => {
// convert form data to API payload format
logger?.debug(
`Creating subsidiary payload from data <${JSON.stringify(subsidiaryData, null, 2)}>.`,
);
const subsidiaryPayload = subsidiaryAdapter.toApiPayload(subsidiaryData);
logger?.debug(
`Created subsidiary payload <${JSON.stringify(subsidiaryPayload, null, 2)}>.`,
);
logger?.info('Create new subsidiary.');
isLoading.value = true;

// append access token into HTTP header
const requestTokenHeader_ = { ...requestTokenHeader };
requestTokenHeader_.Authorization += loginStore.getAccessToken;

// post subsidiary
const { data } = await apiFetch<SubsidiaryPostApiResponse>({
endpoint: `${rideToWorkByBikeConfig.urlApiOrganizations}${organizationId}/${rideToWorkByBikeConfig.urlApiSubsidiaries}`,
method: 'post',
translationKey: 'createSubsidiary',
headers: Object.assign(requestDefaultHeader(), requestTokenHeader_),
payload: subsidiaryPayload,
logger,
});

isLoading.value = false;
const formData = data ? subsidiaryAdapter.toFormData(data) : data;
logger?.debug(
'Convert newly created and returned subsidiary API data to form data' +
` <${JSON.stringify(formData, null, 2)}>.`,
);
return formData;
};

return {
isLoading,
createSubsidiary,
};
};
5 changes: 5 additions & 0 deletions src/i18n/cs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ apiMessageError = "Vytvoření týmu selhalo."
apiMessageErrorWithMessage = "Vytvoření týmu selhalo. Chyba: {error}"
apiMessageSuccess = "Tým byl úspěšně vytvořen."

[createSubsidiary]
apiMessageError = "Vytvoření pobočky selhalo."
apiMessageErrorWithMessage = "Vytvoření pobočky selhalo. Chyba: {error}"
apiMessageSuccess = "Pobočka byla úspěšně vytvořena."

[drawerMenu]
buttonParticipation = "Účast"
buttonCityAdministration = "Správa města"
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ apiMessageError = "Team creation failed."
apiMessageErrorWithMessage = "Team creation failed. Error: {error}"
apiMessageSuccess = "Team was successfully created."

[createSubsidiary]
apiMessageError = "Subsidiary creation failed."
apiMessageErrorWithMessage = "Subsidiary creation failed. Error: {error}"
apiMessageSuccess = "Subsidiary was successfully created."

[drawerMenu]
buttonParticipation = "Participation"
buttonCityAdministration = "City administration"
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/sk.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ apiMessageError = "Vytvorenie tímu zlyhalo."
apiMessageErrorWithMessage = "Vytvorenie tímu zlyhalo. Chyba: {error}"
apiMessageSuccess = "Tím bol úspešne vytvorený."

[createSubsidiary]
apiMessageError = "Vytvorenie pobočky zlyhalo."
apiMessageErrorWithMessage = "Vytvorenie pobočky zlyhalo. Chyba: {error}"
apiMessageSuccess = "Pobočka bola úspešne vytvorená."

[drawerMenu]
buttonParticipation = "Účasť"
buttonCityAdministration = "Správa mesta"
Expand Down
10 changes: 10 additions & 0 deletions test/cypress/fixtures/apiPostSubsidiaryRequest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"address": {
"street": "Subsidiary Street",
"street_number": "1",
"recipient": "Subsidiary Recipient",
"city": "Subsidiary City",
"psc": "12345"
},
"city_id": 4
}
12 changes: 12 additions & 0 deletions test/cypress/fixtures/apiPostSubsidiaryResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": 5351,
"city_id": 4,
"active": true,
"address": {
"street": "Test Subsidiary Street 4",
"street_number": "004",
"recipient": "Recipient 4",
"psc": 50004,
"city": "City 4"
}
}
47 changes: 47 additions & 0 deletions test/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,53 @@ Cypress.Commands.add('waitForTeamPostApi', () => {
});
});

/*
* Intercept subsidiary POST API call
* Provides `@postSubsidiary` alias
* @param {object} config - App global config
* @param {Object|String} i18n - i18n instance or locale lang string e.g. en
* @param {number} organizationId - Organization ID
*/
Cypress.Commands.add(
'interceptSubsidiaryPostApi',
(config, i18n, organizationId) => {
const { apiBase, apiDefaultLang, urlApiOrganizations, urlApiSubsidiaries } =
config;
const apiBaseUrl = getApiBaseUrlWithLang(
null,
apiBase,
apiDefaultLang,
i18n,
);
const urlApiSubsidiaryLocalized = `${apiBaseUrl}${urlApiOrganizations}${organizationId}/${urlApiSubsidiaries}`;
cy.fixture('apiPostSubsidiaryResponse').then((subsidiaryResponse) => {
cy.intercept('POST', urlApiSubsidiaryLocalized, {
statusCode: httpSuccessfullStatus,
body: subsidiaryResponse,
}).as('postSubsidiary');
});
},
);

/*
* Wait for intercept subsidiary POST API call and compare request/response object
* Wait for `@postSubsidiary` intercept
*/
Cypress.Commands.add('waitForSubsidiaryPostApi', () => {
cy.fixture('apiPostSubsidiaryRequest').then((subsidiaryRequest) => {
cy.fixture('apiPostSubsidiaryResponse').then((subsidiaryResponse) => {
cy.wait('@postSubsidiary').then(({ request, response }) => {
expect(request.headers.authorization).to.include(bearerTokeAuth);
expect(request.body).to.deep.equal(subsidiaryRequest);
if (response) {
expect(response.statusCode).to.equal(httpSuccessfullStatus);
expect(response.body).to.deep.equal(subsidiaryResponse);
}
});
});
});
});

/**
* Intercept merchandise GET API call
* Provides `@getMerchandise` and `@getMerchandiseNextPage` aliases
Expand Down
Loading