Release notes v1.10.0
Introduction
In this release we:
- enabled configuration of cookie options in
middleware.config.js
file, - updated
@vue-storefront/core
and@vue-storefront/middleware
tov2.7.5
.
Migration guide
To configure cookie options, you should modify your middleware.config.js
file:
module.exports = {
integrations: {
ct: {
location: '@vsf-enterprise/commercetools-api/server',
configuration: {
+ cookie_options: {
+ 'vsf-commercetools-token': {
+ httpOnly: true,
+ secure: true,
+ sameSite: 'lax'
+ }
+ },
api: {
...
},
serverApi: {
...
},
currency: 'USD',
country: 'US',
languageMap: {
en: ['en', 'de'],
de: ['de', 'en']
}
},
...
}
}
};
By default, all cookie options are set to the most secure ones:
{
httpOnly: true,
secure: true,
sameSite: 'strict'
}
More information can be found in Localization and cookies guide.
Upgrading to 1.2.0
Introduction
This migration guide helps developers using our commercetools
integration to upgrade to version 2.3.0.
For more information about this version, refer to the Overview page.
Changes
- Added E2E (cypress) tests:
- use
yarn test:e2e
to start Cypress, - use
test:e2e:hl
to run Cypress tests in headless mode. Useful for running the tests in CI/CD workflows, - use
test:e2e:generate:report
to generate Cypress report.
- use
- BREAKING Fixed helpers used for search on the Category page to read
phrase
URL query string instead ofterm
. - Added
SfLoader
toVsfShippingProvider
to correctly handle the loading state. - Added expiration time to
vsf-commercetools-token
cookie matching token expiration time received from commercetools. Previously session cookies were used, which browsers delete when the current session ends. This forced users to log in more often than necessary. - Fixed an issue in the cart where API calls would result in
HTTP 413 Payload Too Large
error code due to large payload size. Cart method calls will now only include cartid
andversion
instead of the whole cart object.
We also made changes to the following files:
- updated
components/Checkout/CartPreview.vue
, - updated
components/Checkout/UserBillingAddresses.vue
, - updated
components/Checkout/UserShippingAddresses.vue
, - updated
components/Checkout/VsfPaymentProviderMock.vue
, - updated
components/Checkout/VsfShippingProvider.vue
, - updated
components/MyAccount/BillingAddressForm.vue
, - updated
components/MyAccount/PasswordResetForm.vue
, - updated
components/MyAccount/ProfileUpdateForm.vue
, - updated
components/MyAccount/ShippingAddressForm.vue
, - updated
pages/Checkout/Billing.vue
, - updated
pages/Checkout/Payment.vue
, - updated
pages/Checkout/Shipping.vue
, - updated
pages/Checkout/ThankYou.vue
.
Upgrading to 1.2.0-rc.2
Introduction
This migration guide helps developers using our commercetools
integration to upgrade to version 2.3.0-rc.2.
For more information about this version, refer to the Overview page.
Changes
We made changes to the following files:
- added new composable
useBilling
, - added new composable
useShipping
, - added new composable
useShippingProvider
- added new component
components/Checkout/VsfShippingProvider
, - added new component
components/Checkout/VsfPaymentProviderMock
, - added new components
pages/Checkout/Billing.vue
, - added new helper
helpers/Checkout/getShippingMethodPrice.ts
, - added new middleware
middleware/is-authenticated.js
, - added
middleware.config.js
, - updated
components/Checkout/UserBillingAddresses.vue
, - updated
components/Checkout/UserShippingAddresses.vue
, - updated
composables/useUiHelpers/index.ts
:- renamed
changeSearchTerm
tosetTermForUrl
, - added
getSearchTermFromUrl
,
- renamed
- updated translations in
theme/lang
, - updated
middleware/checkout.js
, - updated
pages/Checkout.vue
, - updated
pages/Checkout/Payment.vue
, - updated
pages/Checkout/Shipping.vue
, - updated icons in
static/icons
, - updated
nuxt.config.js
, - updated
package.json
, - deleted component
pages/Checkout/OrderReview.vue
, - deleted component
pages/Checkout/PersonalDetails.vue
.
Upgrading to 1.2.0-rc.3
Introduction
This migration guide helps developers using our commercetools
integration to upgrade to version 2.3.0-rc.3.
For more information about this version, refer to the Overview page.
Changes
- Fixed mapping for product attributes with type
set
. - Fixed cart getters to not throw errors when some properties are missing.
- Fixed error thrown by the Composition API plugin when importing
useWishlist
.
We also made changes to the following files:
- updated
components/Checkout/VsfShippingProvider.vue
, - deleted unused
packages/commercetools/theme/helpers/filters/getFiltersForUrl.js
, - deleted unused
packages/commercetools/theme/helpers/filters/getFiltersFromUrl.js
.
Upgrading to 1.3.0
Introduction
In 1.3.0 release, we introduce Forgot Password functionality and basic support for Stores and Channels in commercetools - you can now select a specific store and its corresponding channel, e.g., to display prices for a selected channel. We also fix some bugs related to Checkout forms and add new getters.
Changes
- BREAKING Enable the purchase of an item with selected supply channel and distribution channel
- Added
storeGetters
,useStore
composable and plugin for creating locale, currency, and country cookies - Added
forgotPasswordGetters
anduseForgotPassword
composable - Added
getProductSku
getter toproductGetters
- Extended
middleware.config.js
withinventoryMode
optional property - when added to the configuration, Inventory Mode is set on cart creation - Added smartphone only promo code input
- Removed hardcoded link to the category in
SearchResults.vue
- Replaced mocked email address in MyProfile password change tab to active user email
- Fixed bug with input characters limits on Checkout forms preventing submitting the form with a street name less than five characters long and apartment number with less than two characters long
- Added e2e tests for Checkout
We also made changes to the following files:
- Added
pages/MyAccount/MyProfile.vue
. - Updated
components/MyAccount/BillingAddressForm.vue
. - Updated
components/MyAccount/ShippingAddressForm.vue
. - Updated
pages/Checkout/Billing.vue
. - Updated
pages/Checkout/Payment.vue
. - Updated
pages/Checkout/Shipping.vue
. - Updated
lang/de.js
andlang/en.js
.
Upgrading to 1.3.2
Introduction
In the 1.3.2 release, we focused on improving security by limiting permissions given to the customers to the bare minimum. This release doesn't include breaking changes, but we HIGHLY recommend applying the changes described below to your projects.
Changes
In the past, all requests sent to the commercetools included customer's access token created with scopes defined in the middleware.config.js
file. If the customer didn't have permission to access a given resource, commercetools would return an error. It sounds logical - you can't access or operate the data if you don't have permission.
Unfortunately, some operations require manage_*
scopes. For example:
- resetting user password requires
manage_customers
scope, - adding product reviews requires
manage_products
scope.
Giving these permissions to the customer would allow them to access and modify all customers and products in your commercetools project.
For this reason, we introduced a new key named serverApi
to the commercetools middleware configuration. You can use it to pass new server-specific API client credentials that middleware will use instead of customers API client for selected operations that require manage_*
permissions.
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
api: {
uri: 'https://api.commercetools.com/PROJECT_KEY/graphql',
authHost: 'https://auth.sphere.io',
projectKey: 'PROJECT_KEY',
clientId: 'CLIENT_ID', // Customer API client ID
clientSecret: 'CLIENT_SECRET', // Customer API client secret
scopes: [
'create_anonymous_token:PROJECT_KEY',
'manage_my_profile:PROJECT_KEY',
'view_categories:PROJECT_KEY',
'manage_my_payments:PROJECT_KEY',
'manage_my_orders:PROJECT_KEY',
'manage_my_shopping_lists:PROJECT_KEY',
'view_published_products:PROJECT_KEY',
'view_stores:PROJECT_KEY'
]
},
serverApi: {
clientId: 'SERVER_ID', // Server API client ID
clientSecret: 'SERVER_SECRET', // Server API client secret
scopes: [
'manage_customers:PROJECT_KEY',
'manage_products:PROJECT_KEY'
]
}
}
}
}
};
API client used in the serverApi
section can only have the manage_customers
and manage_products
scopes.
Alternatively, you can use just the manage_project
scope, but keep in mind that this gives Alokai unlimited access to everything in your project. When dealing with permissions, less is always better ;)
Use two separate API clients for api
and serverApi
While it's possible to use the same API client in api
and serverApi
, but will different scopes, we recommend creating two separate API clients.
Recommended customer scopes
The image below shows a list of recommended scopes for the API client configured in the api
section should have.
Recommended server scopes
The image below shows a list of recommended scopes for the API client configured in the serverApi
section should have.
Upgrading to 1.3.3
Introduction
In the 1.3.3 release, we added new options to the serverApi
introduced in the 1.3.2 release.
Changes
In the 1.3.2 release, we introduced a new key named serverApi
to the commercetools middleware configuration. It stores API client used to generate access tokens for selected operations. However, we quickly noticed the need to allow adding other operations that will use these access tokens. That's why in this release we added new operations
option to the serverApi
configuration.
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
// irrelevant configuration was omitted for readability
serverApi: {
clientId: 'SERVER_ID',
clientSecret: 'SERVER_SECRET',
scopes: [
'manage_customers:PROJECT_KEY',
'manage_products:PROJECT_KEY'
],
operations: []
}
}
}
}
};
Custom operations might require additional scopes
Remember that custom operations added to the operations
array might require additional scopes.
Example
Let's assume you have custom GraphQL that adds new mutation like shown below:
mutation AddProductType(
$draft: ProductTypeDraft!
) {
productType: createProductType(draft: $draft) {
name
description
key
}
}
In this case, you need to add createProductType
to the operations
array:
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
// irrelevant configuration was omitted for readability
serverApi: {
operations: [
'createProductType'
]
}
}
}
}
};
Upgrading to 1.4.0
Introduction
Start by updating the core packages
This guide only includes commercetools-specific changes. Follow the Migrating projects to 2.5.0 guide to update the core before you proceed with applying changes from this guide.
In this release, we focused on improving security and performance and updating GraphQL types.
This release includes some breaking changes. However, adjusting your projects shouldn't take too long and only requires a few minor changes.
Updated GraphQL types
You might have noticed that the @vue-storefront/commercetools-api
package exports TypeScript types for GraphQL data returned by commercetools. We didn't update them for quite a while and decided that it was time to do it. There are plenty of new types as well as some breaking changes to the old types. Depending on the types you use, you might need to update your project.
Handling access tokens
We completely rewrote internals for handling authentication because of the poor performance of the old solution. Previously, the application requested a new access token several times for every user visiting the page if they didn't have the token cookie. This approach didn't make much sense, especially because all guests share the same access token until their session become unique in any way, for example, by logging in or adding an item to the cart or wishlist.
From now on, the application will request one access token for server operations (introduced in the previous release) and one for all guests. The server stores these tokens and doesn't send them out to the users. It will only create session cookies only for anonymous and logged-in users.
What is an anonymous session in commercetools?
You might be wondering what an "anonymous" session is and how it's different from the "guest" session. In commercetools there are three types of sessions:
- guest - user that didn't log in nor perform any visitor-specific operation,
- anonymous - user that didn't log in but performed some visitor-specific operation, for example, added item to the cart or wishlist,
- customer - user that logged into the application.
This change by itself doesn't require any changes in existing projects. However, we used this opportunity to improve the security of the cookies storing the user session. See the next section for more details.
Improving security of the session cookie
The commercetools integration creates the vsf-commercetools-token
cookie to store the user session. In the past, this cookie didn't include the secure
and httpOnly
attributes. The lack of these attributes meant that the cookie could be sent with unsecured HTTP and be accessed using JavaScript, which exposed the cookie to security risks.
From now on, a session cookie will always have the httpOnly
attribute. It will also have the secure
attribute, but only if the request was HTTPS. For this reason, you don't have to use HTTPS in your development environments.
However, because the session cookie can no longer be accessed using JavaScript, we needed a new way of checking if the user is logged in. For this reason, we introduced a new isLoggedIn
endpoint. You need to update the middleware/is-authenticated.js
file, which protects /my-account
routes from unauthorized access and all other parts of the application that read the session cookie.
// middleware/is-authenticated.js
import { Logger } from '@vue-storefront/core';
export default async ({ $vsf, route, redirect }) => {
try {
const isLoggedIn = await $vsf.$ct.api.isLoggedIn();
if (!isLoggedIn) {
throw new Error(`"${ route.fullPath }" route is only available to logged-in customers`);
}
} catch (error) {
Logger.warn(error.toString());
return redirect('/');
}
};
In components, you can use the same approach by using the useVSFContext
helper to access the $ct
context.
// Vue components
import { useVSFContext } from '@vue-storefront/core';
export default {
setup() {
const { $ct } = useVSFContext();
async function isLoggedIn() {
return await $ct.api.isLoggedIn();
}
}
};
Other changes
Below is the list of the template files that we updated since the last release. You can generate a new project using our CLI and compare the files listed below with your existing project to see if they need updating.
components/AppHeader.vue
,components/CategoryPageHeader.vue
,components/MyAccount/ProfileUpdateForm.vue
,components/StoreLocaleSelector.vue
,composables/useUiHelpers/index.ts
,lang/de.js
,lang/en.js
,middleware/is-authenticated.js
,nuxt.config.js
,package.json
,pages/Checkout.vue
,pages/Checkout/Billing.vue
,pages/Checkout/Payment.vue
,pages/Checkout/Shipping.vue
,pages/Checkout/ThankYou.vue
,pages/MyAccount/OrderHistory.vue
,pages/Product.vue
.
If you are using E2E tests included in the projects, you might also need to update the following files.
tests/e2e/api/requests.ts
,tests/e2e/fixtures/responses/getOrders.json
,tests/e2e/fixtures/responses/getOrdersWithOffset.json
,tests/e2e/fixtures/test-data/e2e-add-to-cart.json
,tests/e2e/fixtures/test-data/e2e-carts-merging.json
,tests/e2e/fixtures/test-data/e2e-my-account-order-history.json
,tests/e2e/fixtures/test-data/e2e-product-page.json
,tests/e2e/fixtures/test-data/e2e-remove-from-cart.json
,tests/e2e/fixtures/test-data/e2e-update-cart.json
,tests/e2e/integration/e2e-add-to-cart.spec.ts
,tests/e2e/integration/e2e-carts-merging.spec.ts
,tests/e2e/integration/e2e-checkout-order-summary.spec.ts
,tests/e2e/integration/e2e-my-account-order-history.spec.ts
,tests/e2e/integration/e2e-my-account.spec.ts
,tests/e2e/integration/e2e-place-order.spec.ts
,tests/e2e/integration/e2e-user-login.spec.ts
,tests/e2e/integration/e2e-user-registration.spec.ts
,tests/e2e/pages/category.ts
,tests/e2e/pages/checkout.ts
,tests/e2e/pages/components/breadcrumbs.ts
,tests/e2e/pages/components/cart-sidebar.ts
,tests/e2e/pages/components/header.ts
,tests/e2e/pages/components/login-modal.ts
,tests/e2e/pages/my-account.ts
,tests/e2e/utils/network.ts
.
Upgrading to 1.5.0
Introduction
In this release, we focused on improving the implementation of faceting. To be up-to-date with the latest features offered by commercetools, it no longer uses REST API but GraphQL API instead. It's also no longer a separate integration and is part of the commercetools integration.
We've also changed the organization's name under which we release the commercetools integration from @vue-storefront
to @vsf-enterprise
.
This release includes some breaking changes. However, adjusting your projects shouldn't take too long and only requires a few minor changes.
Update dependencies
Rename old dependencies
As stated above, we changed the organization's name under which we release commercetools integration. Open the package.json
file and rename the following dependencies:
@vue-storefront/commercetools
to@vsf-enterprise/commercetools
@vue-storefront/commercetools-api
to@vsf-enterprise/commercetools-api
Add new and remove old packages
Firstly add @vsf-enterprise/commercetools-api
package (if it's not already installed).
yarn add @vsf-enterprise/commercetools-api
Starting this release, we require a new dependency:
vue-lazy-hydration
in version2.0.0-beta.4
.
We also no longer use the @vue-storefront/nuxt-theme
dependency.
To update your project, run the following commands:
yarn add vue-lazy-hydration@~2.0.0-beta.4
yarn remove @vue-storefront/nuxt-theme
Update configuration files
middleware.config.js
- Open
middleware.config.js
file and replace@vue-storefront/commercetools-api/server
with@vsf-enterprise/commercetools-api/server
for location insidect
section.
// middleware.config.js
module.exports = {
integrations: {
ct: {
- location: '@vue-storefront/commercetools-api/server',
+ location: '@vsf-enterprise/commercetools-api/server',
configuration: {
// irrelevant configuration was omitted for readability
}
},
}
};
- Remove
extensions
line fromct
section.
// middleware.config.js
module.exports = {
integrations: {
ct: {
location: '@vsf-enterprise/commercetools-api/server',
- extensions: existing => existing.concat('@vsf-enterprise/commercetools/extensions'),
configuration: {
// irrelevant configuration was omitted for readability
}
},
- Remove
ctf
section. It will no longer be used as faceting is now part of commercetools integration.
// middleware.config.js
module.exports = {
integrations: {
// ct integration config
- ctf: {
- location: '@vsf-enterprise/ct-faceting/server',
- configuration: {
- api: {
- authHost: 'https://auth.sphere.io',
- projectKey: '<PROJECT_KEY>',
- clientId: '<CLIENT_ID>',
- clientSecret: '<CLIENT_SECRET>',
- scopes: [
- /* scope rules */
- ]
- },
- faceting: {
- host: 'https://api.commercetools.com'
- }
- }
- }
}
};
nuxt.config.js
- Default configuration for faceting is no longer provided by external package
@vsf-enterprise/ct-faceting
. Now it is placed innuxt.config.js
underbuildModules
section and in@vsf-enterprise/commercetools/nuxt
module. If you don't have default configuration or you need more details please refer to Configuration section of theuseFacet
composable.
Context configuration access
Since we're removing $ctf
object from the middleware configuration, and still referring to the application context with it, now all the faceting configuration data will be available from the $ct
key/object.
Example:
// before
const { $ctf: { config }} = useVSFContext();
// now
const { $ct: { config: { faceting } }} = useVSFContext();
Please be aware that with defining facets your commercetools data structure might be different, and reaching for specific objects might need some adjustments.
- We no longer use the
@vue-storefront/nuxt-theme
package which registered default routing paths (we will remove it after). They need to be configured in thenuxt.config.js
file.
To do it, use the extendRoutes
function in the nuxt.config.js
.
// nuxt.config.js
router: {
// other configuration options
extendRoutes(routes, resolve) {
routes.push({
name: 'home',
path: '/',
component: resolve(__dirname, 'pages/Home.vue')
},
{
name: 'product',
path: '/p/:id/:slug/',
component: resolve(__dirname, 'pages/Product.vue')
},
{
name: 'category',
path: '/c/:slug_1/:slug_2?/:slug_3?/:slug_4?/:slug_5?',
component: resolve(__dirname, 'pages/Category.vue')
},
{
name: 'my-account',
path: '/my-account/:pageName?',
component: resolve(__dirname, 'pages/MyAccount.vue')
},
{
name: 'checkout',
path: '/checkout',
component: resolve(__dirname, 'pages/Checkout.vue'),
children: [
{
path: 'shipping',
name: 'shipping',
component: resolve(__dirname, 'pages/Checkout/Shipping.vue')
},
{
path: 'billing',
name: 'billing',
component: resolve(__dirname, 'pages/Checkout/Billing.vue')
},
{
path: 'payment',
name: 'payment',
component: resolve(__dirname, 'pages/Checkout/Payment.vue')
},
{
path: 'thank-you',
name: 'thank-you',
component: resolve(__dirname, 'pages/Checkout/ThankYou.vue')
}
]
},
{
name: 'reset-password',
path: '/reset-password',
component: resolve(__dirname, 'pages/ResetPassword.vue')
});
}
},
Routes configuration can be moved to a separate file and imported into the nuxt.config.js
file to increase the readability.
@vue-storefront/nuxt-theme
package needs to be removed frombuildModules
section.
buildModules: [
- ['@vue-storefront/nuxt-theme'],
- As faceting is now part of commercetools integration, the configuration inside
buildModules
is no longer needed and has to be removed.
buildModules: [
- ['@vsf-enterprise/ct-faceting/nuxt', {
- apiConfigModule: '@vsf-enterprise/commercetools/nuxt'
- }]
- Update configuration of the
@vue-storefront/nuxt
module by replacing@vue-storefront/commercetools
with@vsf-enterprise/commercetools
inuseRawSource
object.
['@vue-storefront/nuxt', {
useRawSource: {
dev: [
'@vue-storefront/core',
- '@vue-storefront/commercetools'
+ '@vsf-enterprise/commercetools'
],
prod: [
'@vue-storefront/core',
- '@vue-storefront/commercetools'
+ '@vsf-enterprise/commercetools'
]
}
}]
Update imports
The final step is to replace ALL @vue-storefront/commercetools
imports with @vsf-enterprise/commercetools
in *.vue template file.
- import { useUser } from '@vue-storefront/commercetools';
+ import { useUser } from '@vsf-enterprise/commercetools';
The same applies to @vue-storefront/commercetools-api
, if it's used in the project. It has to be replaced with @vsf-enterprise/commercetools-api
.
- import { Category } from '@vue-storefront/commercetools-api';
+ import { Category } from '@vsf-enterprise/commercetools-api';
Optional: Redis cache
In this version, we've added support for Redis cache out of the box for new projects. If you want to use it, please follow the instructions below. If not, you can skip this step.
You can read more about our implementation of Redis cache on SSR Cache page.
Add new packages
Start by installing required dependencies:
@vue-storefront/cache
in version2.5.3
@vsf-enterprise/redis-cache
in version1.3.0
To update your project, run the following commands:
yarn add @vue-storefront/cache@~2.5.3
yarn add @vsf-enterprise/redis-cache@~1.3.0
Add body parser server middleware
Create a body-parser.js
file inside of the serverMiddleware
directory with the following code:
// ./serverMiddleware/body-parser.js
const bodyParser = require('body-parser')
const app = require('express')()
app.use(bodyParser.json())
module.exports = app
Add the location of the body-parser.js
file to the serverMiddleware
array in nuxt.config.js
.
serverMiddleware: [
'~/serverMiddleware/body-parser.js'
],
Extend configuration
In the nuxt.config.js
file, update the modules
section by adding the @vue-storefront/cache/nuxt
module with the following configuration.
modules: [
// other modules
['@vue-storefront/cache/nuxt', {
enabled: process.env.VSF_REDIS_ENABLED === 'true',
invalidation: {
endpoint: process.env.CACHE_INVALIDATE_URL,
key: process.env.CACHE_INVALIDATE_KEY,
handlers: [
'@vue-storefront/cache/defaultHandler'
]
},
driver: [
'@vsf-enterprise/redis-cache',
{
// docs: https://github.com/luin/ioredis/blob/master/API.md#new-redisport-host-options
redis: {
keyPrefix: process.env.VSF_REDIS_KEY_PREFIX,
host: process.env.VSF_REDIS_HOST,
port: process.env.VSF_REDIS_PORT || 6379,
password: process.env.VSF_REDIS_PASSWORD
}
}
]
}]
],
Update template file
As the last step, update template files that use useCache
composable. Start by adding an import statement:
import { useCache, CacheTagPrefix } from '@vue-storefront/cache';
Then, use the useCache
composable in the setup
function to add proper tags. Below is a code example of handling tags on the Category page.
setup() {
const { addTags } = useCache();
onSSR(() => {
const tags = [{ prefix: CacheTagPrefix.View, value: 'category' }];
const productTags = products.value.map((prod) => {
return { prefix: CacheTagPrefix.Product, value: prod._id };
});
const categoriesTags = categoryTree.value.items.map((cat) => {
return { prefix: CacheTagPrefix.Category, value: cat.id };
});
addTags(tags.concat(productTags, categoriesTags));
});
}
Upgrading to 1.6.0
Introduction
In this release, we've migrated from the old internationalization package called nuxt-i18n
to the new one called nuxtjs/i18n
. Follow its Migration guide to upgrade your project. We've also refactored the useUserBilling
and useUserShipping
composables.
Updating the useUserBilling
and useUserShipping
implementations
The billing
and shipping
properties from the useUserBilling
and useUserShipping
composables reference the user
object provided by the useUser composable.
For this reason, we are deprecating the load
methods from the useUserBilling
and useUserShipping
composables. To populate all the billing
and shipping
properties with user data, call the useUser.load
method.
import { onSSR } from '@vue-storefront/core'
import {
useUser,
useUserBilling,
useUserShipping
} from '@vsf-enterprise/commercetools';
export default {
setup() {
const { load: loadUser } = useUser();
- const { load: loadBilling, billing } = useUserBilling();
- const { load: loadShipping, shipping } = useUserShipping();
+ const { billing } = useUserBilling();
+ const { shipping } = useUserShipping();
onSSR(async () => {
await loadUser();
- await loadBilling();
- await loadShipping();
});
return {
billing,
shipping
};
}
};
Calling the useUserBilling.load
and useUserShipping.load
methods is no longer necessary and will result in a deprecation warning.
Upgrading to 1.7.0
Introduction
Hi. With this version, we're introducing some significant performance optimization like new improved typings, the new Node version or conditional rendering removal, finally build modern mode for client.
Upgrade to Node 12 or 14
We have recommended using Node 12 or higher for a few months now. It's mandatory starting with this release because @vsf-enterprise/commercetools-api
and @vsf-enterprise/commercetools
packages are now targeting ES2019 syntax, supported fully in Node 12+.
At this moment, we don't support Node versions higher than 14.
Update import paths of types generated from commercetools GraphQL API
You might have noticed that some of the types exported from the @vsf-enterprise/commercetools-api
represent data accepted and returned from the commercetools GraphQL API. We thought it would be easier to differentiate our types from those generated from the API if they come from two different packages.
For this reason, we introduced the new @vsf-enterprise/commercetools-types
package. It only contains types generated from the commercetools GraphQL API and nothing more.
The @vsf-enterprise/commercetools-api
package only includes types for the Server Middleware API, such as parameters accepted and data returned from API endpoints and default GraphQL queries sent to the commercetools GraphQL API.
You can refer to the API Reference for more details.
- import type { Customer } from '@vsf-enterprise/commercetools-api`;
+ import type { Customer } from '@vsf-enterprise/commercetools-types`;
Update deprecated types
We refactored most types in @vsf-enterprise/commercetools-api
and @vsf-enterprise/commercetools
packages to improve coding suggestions. We marked most of the types we no longer use as deprecated with comments indicating the new type that you should use instead. Most IDEs should highlight deprecated types — you can click them to go to the type declaration file for more information.
We will remove deprecated types in version 1.7.0.
Upgrade @storefront-ui/vue
package to version 0.13.0
With the new Storefront UI version, we bring some performance improvements however, you need to make some changes to your components.
# components/CartSidebar
<SfSidebar
class="sf-sidebar--right"
v-e2e="'sidebar-cart'"
:visible="isCartSidebarOpen"
:title="$t('My Cart')"
@close="toggleCartSidebar"
:persistent="hasNotifications"
+ position="right"
>
# components/WishlistSidebar
<SfSidebar
class="sf-sidebar--right"
v-e2e="'sidebar-wishlist'"
:visible="isWishlistSidebarOpen"
:title="$t('My wishlist')"
@close="toggleWishlistSidebar"
:persistent="hasNotifications"
+ position="right"
>
Remove mobileObserver
With this version we're getting rid of all the conditional rendering. This means that we're not using mobileObserver
utility anymore. It was removed along with the new Storefront UI version.
Please follow these steps to cover all the necessary changes. This contributes to our full CDN optimization.
# components/AppFooter
data() {
return {
- isMobile: false,
- desktopMin: 1024
}
}
# components/AppHeader
<template #navigation>
- <HeaderNavigation :isMobile="isMobile">
+ <HeaderNavigation>
</template>
...
- import {
- mapMobileObserver,
- unMapMobileObserver
- } from '@storefront-ui/vue/src/utilities/mobile-observer.js';
...
- const isMobile = ref(mapMobileObserver().isMobile.get());
...
watch(() => term.value, (newVal, oldVal) => {
- const shouldSearchBeOpened = (!isMobile.value && term.value.length > 0) && ((!oldVal && newVal) || (newVal.length !== oldVal.length && isSearchOpen.value === false));
- if (shouldSearchBeOpened) {
+ if (term.value.length > 0 && ((!oldVal && newVal) || (newVal.length !== oldVal.length && isSearchOpen.value === false))) {
isSearchOpen.value = true;
}
});
...
- onBeforeUnmount(() => {
- unMapMobileObserver();
- });
...
return {
...
- isMobile
}
# components/Headernavigation
<template>
- <div class="sf-header__navigation desktop" v-if="!isMobile">
- <SfHeaderNavigationItem
- v-for="category in categories"
- :key="category.id"
- class="nav-item"
- v-e2e="`app-header-url_${category.slug}`"
- :label="category.name"
- :link="localePath(`/c/${category.slug}`)"
- />
- </div>
- <SfModal v-else :visible="isMobileMenuOpen">
+ <div class="header-navigation">
+ <div class="sf-header__navigation desktop-only">
+ <SfHeaderNavigationItem
+ v-for="category in categories"
+ :key="category.id"
+ class="nav-item"
+ v-e2e="`app-header-url_${category.slug}`"
+ :label="category.name"
+ :link="localePath(`/c/${category.slug}`)"
+ />
+ </div>
+ <SfModal class="smartphone-only" :visible="isMobileMenuOpen">
<SfList>
<SfListItem
v-for="category in categories"
:key="category.id"
class="nav-item sf-header-navigation-item"
v-e2e="`app-header-url_${category.slug}`"
>
<SfMenuItem
:label="category.name"
class="sf-header-navigation-item__menu-item"
:link="localePath(`/c/${category.slug}`)"
@click.native="toggleMobileMenu"
/>
</SfListItem>
</SfList>
</SfModal>
+ </div>
</template>
...
- props: {
- isMobile: {
- type: Boolean,
- default: false
- }
- },
# components/InstagramFeed
<template>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageAm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageAd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageAm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageAd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageBm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageCd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageBm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageCd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageCm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageBd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageCm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageBd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
...
- <SfImage v-if="isMobile" :src="'/homepage/imageDm.webp' | addBasePathFilter" alt="katherina_trn" :width="160" :height="160">katherina_trn</SfImage>
- <SfImage v-else :src="'/homepage/imageDd.webp' | addBasePathFilter" alt="katherina_trn" :width="470" :height="470">katherina_trn</SfImage>
+ <SfImage
+ class="smartphone-only"
+ :src="'/homepage/imageDm.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="160"
+ :height="160"
+ >
+ katherina_trn
+ </SfImage>
+ <SfImage
+ class="desktop-only"
+ :src="'/homepage/imageDd.webp' | addBasePathFilter"
+ alt="katherina_trn"
+ :width="470"
+ :height="470"
+ >
+ katherina_trn
+ </SfImage>
</template>
...
- import {
- mapMobileObserver,
- unMapMobileObserver
- } from '@storefront-ui/vue/src/utilities/mobile-observer.js';
...
- computed: {
- ...mapMobileObserver()
- },
- beforeDestroy() {
- unMapMobileObserver();
- }
# pages/MyAccount
- import { computed, onBeforeUnmount, useContext, useRouter, useRoute } from '@nuxtjs/composition-api';
+ import { computed, useContext, useRouter, useRoute } from '@nuxtjs/composition-api';
- import {
- mapMobileObserver,
- unMapMobileObserver
- } from '@storefront-ui/vue/src/utilities/mobile-observer.js';
...
- const isMobile = computed(() => mapMobileObserver().isMobile.get());
const activePage = computed(() => {
const { pageName } = route.value.params;
if (pageName) {
return (pageName.charAt(0).toUpperCase() + pageName.slice(1)).replace('-', ' ');
}
- else {
- return !isMobile.value ? i18n.t('My profile') : '';
- }
});
- onBeforeUnmount(() => {
- unMapMobileObserver();
- });
Use --modern=client
mode
From now on, with application build we're using Nuxt modern mode just for client side. More about this you can find here.
# package.json
- "build": "nuxt build -m",
- "build:analyze": "nuxt build -a -m",
- "start": "nuxt start",
+ "build": "nuxt build --modern=client",
+ "build:analyze": "nuxt build -a --modern=client",
+ "start": "nuxt start --modern=client",
Upgrading to 1.8.5
Introduction
In the 1.8.5 release, we changed the response of productsSearch
to return the raw data whenever customQuery
is passed as parameter.
Changes
Previously, productsSearch
needed the enhanceProduct
parameter to handle customQuery
responses but this fail to work due to the implementation of the productsSearch
method that returns enhancedProductResponse.data
.
return (enhancedProductResponse.data as any)._variants;
This would fail if the enhanceProduct
parameter did not properly format the data needed to create the response of the productsSearch
which is not documented correctly.
With this we created this change to directly return the raw response coming from context.$ct.api.getProduct(<searchParams>, <customQuery>)
The change will not require everyone to pass in enhanceProduct
and just allow them to modify or process the response after they received it.
Usage
Before
Previously, enhanceProduct
was part of productsSearch
and modify the data prior to being returned.
const { search, products } = useProduct()
// Which will fail cause of the required format of response of enhanceProduct
await search({
customQuery: { products: 'my-custom-query' },
enhanceProduct: (productResponse) => productResponse
})
Now
Now, since the productsSearch
will now return raw response whenever there's a customQuery
present. You may be able to add a method like enhanceProduct
below that can transform the raw response to what's needed.
const { search, products } = useProduct()
const enhanceProduct = (productResponse) => productResponse
await search({
customQuery: { products: 'my-custom-query' }
})
products.value = enhanceProduct(products.value)
Upgrading to 1.9.0
Introduction
The main and the most important change was implementation of the init
function for a performance boost and rework of
the token flow logic, which was a necessary step after the usage of the init
function.
Init function
Previously, Apollo Client was created on every request which was not a very good solution in terms of performance.
Utilizing the new core mechanism we implemented the init
function to bootstrap the client once on the build time.
File | What was changed |
---|---|
! packages/api-client/src/index.server.ts | Move client creation logic from the onCreate to the init function |
For a reference please check getInitConfig in the middleware package.
Token flow
Due to tokens leaks and security issues after the implementation of the init function, token flow was reworked.
File | What was changed |
---|---|
! packages/api-client/src/token/TokenManagerFactory.ts | Creates object with helpers for reading and creating access token cookies and accessing current token provider |
! packages/api-client/src/token/tokenParser.ts | Parses raw token string into an object |
! packages/api-client/src/links/** | All Apollo Client links related factories and handlers |
! packages/api-client/src/links/createLinks.ts | Main entry point for links creation |