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

fix: URI components are not encoded correctly (#1640) #1641

Merged
merged 5 commits into from
May 3, 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
6 changes: 6 additions & 0 deletions docs/guides/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ A few Stylelint rules were changed and the `.scss` files were adapted.
In addition empty comments in `.scss` files are no longer allowed and removed.
In migration projects either keep the Stylelint rules with the old settings or migrate all your styling files accordingly running `npm run format`.

The method [`encodeResourceID`](../../src/app/core/utils/url-resource-ids.ts) is now used to encode all dynamic resource IDs in any REST API call.
This was previously only done for the login but is now consistently used for all resource IDs.
For ICM 7.10 and ICM 11 this is done by default with a duplicated `encodeURIComponent` encoding.
For ICM 12 and newer this needs to be changed to a single `encodeURIComponent` with additional `+` character handling.
The ICM 12 handling is already prepared in [`encodeResourceID`](../../src/app/core/utils/url-resource-ids.ts) and needs to be activated in the source code if the Intershop PWA is used together with ICM 12.

## From 5.0 to 5.1

The OrderListComponent is strictly presentational, components using it have to supply the data.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import { CostCenterBase, CostCenterBuyer } from 'ish-core/models/cost-center/cos
import { Customer } from 'ish-core/models/customer/customer.model';
import { ApiService } from 'ish-core/services/api/api.service';
import { getLoggedInCustomer } from 'ish-core/store/customer/user';
import { encodeResourceID } from 'ish-core/utils/url-resource-ids';

import { CostCentersService } from './cost-centers.service';

describe('Cost Centers Service', () => {
let apiService: ApiService;
let costCentersService: CostCentersService;
const millersEMail = 'pmiller@test.intershop.de';
const millersCostcenterEndpoint = `customers/4711/costcenters/123/buyers/${encodeResourceID(millersEMail)}`;

beforeEach(() => {
apiService = mock(ApiService);
Expand Down Expand Up @@ -107,23 +110,19 @@ describe('Cost Centers Service', () => {
});

it('should call the updateCostCenterBuyer of the customer API for updating a cost center buyer', done => {
const costCenterBuyer = { login: 'pmiller@test.intershop.de' } as CostCenterBuyer;
const costCenterBuyer = { login: millersEMail } as CostCenterBuyer;

costCentersService.updateCostCenterBuyer('123', costCenterBuyer).subscribe(() => {
verify(apiService.patch(anything(), anything())).once();
expect(capture(apiService.patch).last()[0]).toMatchInlineSnapshot(
`"customers/4711/costcenters/123/buyers/pmiller%2540test.intershop.de"`
);
expect(capture(apiService.patch).last()[0]).toMatchInlineSnapshot(`"${millersCostcenterEndpoint}"`);
done();
});
});

it('should call the deleteCostCenterBuyer of the customer API for deleting a cost center buyer', done => {
costCentersService.deleteCostCenterBuyer('123', 'pmiller@test.intershop.de').subscribe(() => {
costCentersService.deleteCostCenterBuyer('123', millersEMail).subscribe(() => {
verify(apiService.delete(anything())).once();
expect(capture(apiService.delete).last()[0]).toMatchInlineSnapshot(
`"customers/4711/costcenters/123/buyers/pmiller%2540test.intershop.de"`
);
expect(capture(apiService.delete).last()[0]).toMatchInlineSnapshot(`"${millersCostcenterEndpoint}"`);
done();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class CostCentersService {
getCostCenters(): Observable<CostCenter[]> {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService.get(`customers/${customer.customerNo}/costcenters`).pipe(
this.apiService.get(`customers/${encodeResourceID(customer.customerNo)}/costcenters`).pipe(
this.apiService.resolveLinks<CostCenterData>(),
map(ccData => ccData.map(CostCenterMapper.fromData))
)
Expand All @@ -47,7 +47,9 @@ export class CostCentersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.get<CostCenterData>(`customers/${customer.customerNo}/costcenters/${costCenterId}`)
.get<CostCenterData>(
`customers/${encodeResourceID(customer.customerNo)}/costcenters/${encodeResourceID(costCenterId)}`
)
.pipe(map(CostCenterMapper.fromData))
)
);
Expand All @@ -67,7 +69,7 @@ export class CostCentersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.post<CostCenterData>(`customers/${customer.customerNo}/costcenters`, costCenter)
.post<CostCenterData>(`customers/${encodeResourceID(customer.customerNo)}/costcenters`, costCenter)
.pipe(concatMap(() => this.getCostCenter(costCenter.costCenterId)))
)
);
Expand All @@ -87,7 +89,10 @@ export class CostCentersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.patch<CostCenterData>(`customers/${customer.customerNo}/costcenters/${costCenter.id}`, costCenter)
.patch<CostCenterData>(
`customers/${encodeResourceID(customer.customerNo)}/costcenters/${encodeResourceID(costCenter.id)}`,
costCenter
)
.pipe(map(CostCenterMapper.fromData))
)
);
Expand All @@ -104,7 +109,9 @@ export class CostCentersService {
}

return this.currentCustomer$.pipe(
switchMap(customer => this.apiService.delete(`customers/${customer.customerNo}/costcenters/${id}`))
switchMap(customer =>
this.apiService.delete(`customers/${encodeResourceID(customer.customerNo)}/costcenters/${encodeResourceID(id)}`)
)
);
}

Expand All @@ -127,7 +134,10 @@ export class CostCentersService {
concatMap(customer =>
forkJoin(
buyers.map(buyer =>
this.apiService.post(`customers/${customer.customerNo}/costcenters/${costCenterId}/buyers`, buyer)
this.apiService.post(
`customers/${encodeResourceID(customer.customerNo)}/costcenters/${encodeResourceID(costCenterId)}/buyers`,
buyer
)
)
).pipe(concatMap(() => this.getCostCenter(costCenterId)))
)
Expand All @@ -153,7 +163,9 @@ export class CostCentersService {
switchMap(customer =>
this.apiService
.patch(
`customers/${customer.customerNo}/costcenters/${costCenterId}/buyers/${encodeResourceID(buyer.login)}`,
`customers/${encodeResourceID(customer.customerNo)}/costcenters/${encodeResourceID(
costCenterId
)}/buyers/${encodeResourceID(buyer.login)}`,
buyer
)
.pipe(concatMap(() => this.getCostCenter(costCenterId)))
Expand All @@ -179,7 +191,11 @@ export class CostCentersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.delete(`customers/${customer.customerNo}/costcenters/${costCenterId}/buyers/${encodeResourceID(login)}`)
.delete(
`customers/${encodeResourceID(customer.customerNo)}/costcenters/${encodeResourceID(
costCenterId
)}/buyers/${encodeResourceID(login)}`
)
.pipe(concatMap(() => this.getCostCenter(costCenterId)))
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AppFacade } from 'ish-core/facades/app.facade';
import { Customer } from 'ish-core/models/customer/customer.model';
import { ApiService } from 'ish-core/services/api/api.service';
import { getLoggedInCustomer } from 'ish-core/store/customer/user';
import { encodeResourceID } from 'ish-core/utils/url-resource-ids';

import { B2bRoleData } from '../../models/b2b-role/b2b-role.interface';
import { B2bUser } from '../../models/b2b-user/b2b-user.model';
Expand All @@ -18,6 +19,8 @@ describe('Users Service', () => {
let apiService: ApiService;
let usersService: UsersService;
let appFacade: AppFacade;
const millersEMail = 'pmiller@test.intershop.de';
const millersEndpoint = `customers/4711/users/${encodeResourceID(millersEMail)}`;

beforeEach(() => {
apiService = mock(ApiService);
Expand Down Expand Up @@ -59,31 +62,31 @@ describe('Users Service', () => {
});

it('should call the getUser of customer API when fetching user', done => {
usersService.getUser('pmiller@test.intershop.de').subscribe(() => {
usersService.getUser(millersEMail).subscribe(() => {
verify(apiService.get(anything())).once();
expect(capture(apiService.get).last()).toMatchInlineSnapshot(`
[
"customers/4711/users/pmiller%2540test.intershop.de",
"${millersEndpoint}",
]
`);
done();
});
});

it('should call delete method of customer API when delete user', done => {
usersService.deleteUser('pmiller@test.intershop.de').subscribe(() => {
usersService.deleteUser(millersEMail).subscribe(() => {
verify(apiService.delete(anything())).once();
expect(capture(apiService.delete).last()).toMatchInlineSnapshot(`
[
"customers/4711/users/pmiller%2540test.intershop.de",
"${millersEndpoint}",
]
`);
done();
});
});

it('should call the addUser for creating a new b2b user', done => {
const user = { login: 'pmiller@test.intershop.de' } as B2bUser;
const user = { login: millersEMail } as B2bUser;

usersService.addUser(user).subscribe(() => {
verify(apiService.post(anything(), anything())).once();
Expand All @@ -93,21 +96,19 @@ describe('Users Service', () => {
});

it('should call the updateUser for updating a b2b user', done => {
const user = { login: 'pmiller@test.intershop.de' } as B2bUser;
const user = { login: millersEMail } as B2bUser;

usersService.updateUser(user).subscribe(() => {
verify(apiService.put(anything(), anything())).once();
expect(capture(apiService.put).last()[0]).toMatchInlineSnapshot(
`"customers/4711/users/pmiller%2540test.intershop.de"`
);
expect(capture(apiService.put).last()[0]).toMatchInlineSnapshot(`"${millersEndpoint}"`);
done();
});
});

it('should put the roles onto user when calling setUserRoles', done => {
when(apiService.put(anyString(), anything())).thenReturn(of({ userRoles: [{ roleID: 'BUYER' }] as B2bRoleData[] }));

usersService.setUserRoles('pmiller@test.intershop.de', []).subscribe(data => {
usersService.setUserRoles(millersEMail, []).subscribe(data => {
expect(data).toMatchInlineSnapshot(`
[
"BUYER",
Expand All @@ -116,7 +117,7 @@ describe('Users Service', () => {
verify(apiService.put(anything(), anything())).once();
expect(capture(apiService.put).last()).toMatchInlineSnapshot(`
[
"customers/4711/users/pmiller%2540test.intershop.de/roles",
"${millersEndpoint}/roles",
{
"userRoles": [],
},
Expand All @@ -130,7 +131,7 @@ describe('Users Service', () => {
when(apiService.put(anyString(), anything())).thenReturn(of({}));

usersService
.setUserBudget('pmiller@test.intershop.de', {
.setUserBudget(millersEMail, {
orderSpentLimit: undefined,
budget: { value: 2000, currency: 'USD' },
budgetPeriod: 'monthly',
Expand All @@ -140,7 +141,7 @@ describe('Users Service', () => {
verify(apiService.put(anything(), anything())).once();
expect(capture(apiService.put).last()).toMatchInlineSnapshot(`
[
"customers/4711/users/pmiller%2540test.intershop.de/budgets",
"${millersEndpoint}/budgets",
{
"budget": {
"currency": "USD",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class UsersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.get(`customers/${customer.customerNo}/users`)
.get(`customers/${encodeResourceID(customer.customerNo)}/users`)
.pipe(unpackEnvelope(), map(B2bUserMapper.fromListData))
)
);
Expand All @@ -53,7 +53,7 @@ export class UsersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.get(`customers/${customer.customerNo}/users/${encodeResourceID(login)}`)
.get(`customers/${encodeResourceID(customer.customerNo)}/users/${encodeResourceID(login)}`)
.pipe(map(B2bUserMapper.fromData))
)
);
Expand All @@ -74,7 +74,7 @@ export class UsersService {
withLatestFrom(this.appFacade.currentLocale$),
switchMap(([customer, currentLocale]) =>
this.apiService
.post<B2bUser>(`customers/${customer.customerNo}/users`, {
.post<B2bUser>(`customers/${encodeResourceID(customer.customerNo)}/users`, {
elements: [
{
...customer,
Expand Down Expand Up @@ -120,7 +120,7 @@ export class UsersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.put(`customers/${customer.customerNo}/users/${encodeResourceID(user.login)}`, {
.put(`customers/${encodeResourceID(customer.customerNo)}/users/${encodeResourceID(user.login)}`, {
...customer,
...user,
preferredInvoiceToAddress: { urn: user.preferredInvoiceToAddressUrn },
Expand All @@ -147,14 +147,16 @@ export class UsersService {
}

return this.currentCustomer$.pipe(
switchMap(customer => this.apiService.delete(`customers/${customer.customerNo}/users/${encodeResourceID(login)}`))
switchMap(customer =>
this.apiService.delete(`customers/${encodeResourceID(customer.customerNo)}/users/${encodeResourceID(login)}`)
)
);
}

getAvailableRoles(): Observable<B2bRole[]> {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService.get(`customers/${customer.customerNo}/roles`).pipe(
this.apiService.get(`customers/${encodeResourceID(customer.customerNo)}/roles`).pipe(
unpackEnvelope<B2bRoleData>('userRoles'),
map(data => this.b2bRoleMapper.fromData(data))
)
Expand All @@ -171,7 +173,9 @@ export class UsersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService
.put(`customers/${customer.customerNo}/users/${encodeResourceID(login)}/roles`, { userRoles })
.put(`customers/${encodeResourceID(customer.customerNo)}/users/${encodeResourceID(login)}/roles`, {
userRoles,
})
.pipe(
unpackEnvelope<B2bRoleData>('userRoles'),
map(data => data.map(r => r.roleID))
Expand All @@ -195,7 +199,7 @@ export class UsersService {
return this.currentCustomer$.pipe(
switchMap(customer =>
this.apiService.put<UserBudget>(
`customers/${customer.customerNo}/users/${encodeResourceID(login)}/budgets`,
`customers/${encodeResourceID(customer.customerNo)}/users/${encodeResourceID(login)}/budgets`,
budget
)
)
Expand Down
14 changes: 10 additions & 4 deletions src/app/core/services/address/address.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AddressMapper } from 'ish-core/models/address/address.mapper';
import { Address } from 'ish-core/models/address/address.model';
import { Link } from 'ish-core/models/link/link.model';
import { ApiService, unpackEnvelope } from 'ish-core/services/api/api.service';
import { encodeResourceID } from 'ish-core/utils/url-resource-ids';

/**
* The Address Service handles the interaction with the REST API concerning addresses.
Expand All @@ -25,7 +26,7 @@ export class AddressService {
return this.appFacade.customerRestResource$.pipe(
first(),
concatMap(restResource =>
this.apiService.get(`${restResource}/${customerId}/addresses`).pipe(
this.apiService.get(`${restResource}/${encodeResourceID(customerId)}/addresses`).pipe(
unpackEnvelope<Link>(),
this.apiService.resolveLinks<Address>(),
map(addressesData => addressesData.map(AddressMapper.fromData))
Expand All @@ -51,7 +52,7 @@ export class AddressService {
first(),
concatMap(restResource =>
this.apiService
.post(`${restResource}/${customerId}/addresses`, customerAddress)
.post(`${restResource}/${encodeResourceID(customerId)}/addresses`, customerAddress)
.pipe(this.apiService.resolveLink<Address>(), map(AddressMapper.fromData))
)
);
Expand All @@ -73,7 +74,10 @@ export class AddressService {
first(),
concatMap(restResource =>
this.apiService
.put(`${restResource}/${customerId}/addresses/${address.id}`, customerAddress)
.put(
`${restResource}/${encodeResourceID(customerId)}/addresses/${encodeResourceID(address.id)}`,
customerAddress
)
.pipe(map(AddressMapper.fromData))
)
);
Expand All @@ -90,7 +94,9 @@ export class AddressService {
return this.appFacade.customerRestResource$.pipe(
first(),
concatMap(restResource =>
this.apiService.delete(`${restResource}/${customerId}/addresses/${addressId}`).pipe(map(() => addressId))
this.apiService
.delete(`${restResource}/${encodeResourceID(customerId)}/addresses/${encodeResourceID(addressId)}`)
.pipe(map(() => addressId))
)
);
}
Expand Down
Loading
Loading