How can I make spartacus to use a different field configuration? - spartacus-storefront

I would like to change the endpoint to use fields=FULL instead of the preconfigured fields.
In particular I want to change this endpoint of the storefinder:
https://spartacus-demo.eastus.cloudapp.azure.com:8443/occ/v2/electronics-spa/stores

Miroslaws comment pointed me to the official documentation and helped me to change my stores endpoint. You can do this for all endpoints.
#NgModule({
imports: [
B2sStorefrontModule.withConfig({
backend: {
occ: {
endpoints: {
stores: 'stores?fields=stores(FULL),pagination(DEFAULT),sorts(DEFAULT)'
}
}
}
})
]
})

Related

Integrating Mollie payments in NestJS backend

I am trying to integrate Mollie payments into my NestJS backend.
To make a connection with Mollie, I have to use their MollieClient function. However, when I try to use it in a service I get the error:
Nest can't resolve dependencies of the NewUserService (UserService, MailService, ConfigService, JwtService, ?). Please make sure that the argument Object at index [4] is available in the NewUserModule context.
I am pretty sure this means I have to add a Mollie module/service to the NewUserModule, but I think the package doesn't actually come with a module made for NestJS. So if I try to make a Mollie module/service or use the MollieClient in another service, it asks me to provide it whilst I don't have anything to provide.
I'm pretty new to NestJS and backend development in general, so am I mistaken? Or is there a module added in the installed package?
If there isn't a module, should I make one? What exactly should be in such a module? Is there some sort of guide for it?
I realise this might be a rather vague series of questions, but I'm not very sure how to approach this.
Edit:
Rewrote the question for clarification.
Thanks in advance!
The message means, that Nest does not now how to resolve the 5th constructor argument of your NewUserService. I assume this is something like a MollieService?
You need to add MollieService as Provider to your NewUserModule:
#Module({
imports: [...],
controllers: [...],
providers: [
...otherProviders,
MollieService
]
})
export class NewUserModule {}
Or you can create a separate MollieModule and import it in NewUserModule:
#Module({
providers: [ MollieService ],
exports: [ MollieService ] // export MollieService, so that other modules can use it
})
export class MollieModule {}
#Module({
imports: [MollieModule],
controllers: [...],
providers: [...] // no need to provide MollieService here, because it's imported from MollieModule
})
export class NewUserModule {}
Of course you must also implement the MollieService using their SDK.
A recommend to read the Documentation on Modules. They are a little hard to understand at a first sight, but a powerful concept!
EDIT:
I would suggest to wrap the MollySDK in a service. This way you're not dealing with molly at various places in your app and prevent leaking it's api and types into your services.
#Injectable()
export class MollyService {
private readonly mollyClient: WhateverType;
constructor() {
this.mollyClient = createMollieClient({ apiKey: 'test_dHar4XY7LxsDOtmnkVtjNVWXLSlXsM' });
}
createPayment() {
this.mollieClient.payments.create({...});
}
}
This service could be injected as usual.
However if you really want to use the molly-client directly, you have to use a custom provider
#Module({
providers: [{
provides: 'MOLLY_CLIENT',
useFactory: () => createMollieClient({ apiKey: 'test_dHar4XY7LxsDOtmnkVtjNVWXLSlXsM' }) // you could also use useValue instead of useFactory
}],
exports: [ 'MOLLY_CLIENT' ]
})
export class MollieModule {}
Then inject it into NewUsersService like so:
constructor(#Inject('MOLLY_CLIENT')private readonly mollyClient: WhateverType) {}

How to make Nuxt router more dynamic?

I am currently replacing a legacy-app with vue.
I have a complex route like /foo/:category/:sub/:match. Most of the time, the urls are just like in that route definition. For some popular url's SEO figured they want short urls like /veryPopularSub.
Is it possible to register an alias for that?
Something like:
{ name: 'generic', path: '/foo/:category/:sub/:match' },
{ path: '/aSpecificShortcut', alias: { name: 'generic', params: { category: 1, sub: 2, match: 3 } } }
When accessing aSpecificShortcut the browser should not redirect
You can use router-extras-module if you want to add more flexibility to your app while staying simple.
Can be used this way
<router>
{
alias: [
'cool-fancy-alias',
]
}
</router>
<template>
...
</template>
Unfortunately, this will not support dynamic path as told here: https://github.com/nuxt-community/router-extras-module/issues/132#issuecomment-725413491
A more in-depth configuration may be tried as explained in my answer here but even tho, I'm not sure that you can have anything dynamic like this
export default {
router: {
extendRoutes(routes, resolve) {
routes.push(
{
name: 'foo-category-sub-match',
path: '/foo/*/*/*',
component: resolve(__dirname, 'pages/data/index.vue'),
alias: 'fancy-path-name' // dynamic here?
}
)
}
}
}
There are some changes on vue-router#4, full list available here: https://next.router.vuejs.org/guide/migration/
Meanwhile, this will not be available in Nuxt until Nuxt3 (powered by Vue3) is released. Still, not sure if it may be supported even there.
There is this github issue, but not sure that this may be extrapolated to alias. Maybe ask it on the project there, #posva is in charge of the Router and doing awesome things there.

PDP Custom Routes defined in a module not working

I'm following the Spartacus bootcamp sample for routing https://github.com/SAP/spartacus-bootcamp/tree/77b7474c9538eaa1032062ad3c6d461fb1fc7517/src/app/features/routing
My problem is when I have configured the custom PDP
imports: [
CommonModule,
// dependent module for semantic URLs like cxUrl
UrlModule,
// standard non-spartacus routes
RouterModule.forChild(staticRoutes),
// configure product routes
ConfigModule.withConfig({
routing: {
routes: {
product: {
paths: [
'product/:manufacturer/:firstCategoryName/:productCode/:prettyName',
'product/:manufacturer/:productCode/:prettyName',
'product/:productCode/:name',
],
},
},
},
} as RoutingConfig),
//code mapping in the routes
ConfigModule.withConfig({
paramsMapping: {
productCode: 'code',
},
} as RouteConfig),
The new PDP routes are never used in the Storefront. I can see in the browser console that the custom product properties firstCategoryName and prettyName are properly settled from the normalizers/converters of the sample...
Any insight what can be going on?
Thanks!
Fernando
I think it will also depend on whether/not the manufacturer property is populated as well. By default the manufacturer field is not requested in the productSearch OCC call, so will not be populated on the product object (see default-occ-product-config.ts in the Spartacus code) - this means that on product listing pages (category & search) those two paths will not resolve, and are therefore ignored.

When providing pregenerated links for supporting multiple API specs Swagger UI doesn't load the specs

I have a service written in Go that also uses go templates for frontend. This service is used by our external third parties as a portal for looking up stuff and searching. There is another service that is a rest API for handling orders. The portal service has a page where you can look up API docs.
I had only one API version and I use SwaggerUI for showing API docs. I had to create a new endpoint and make it a part of a new API version. Now I want to show a new API version but also the old one for supporting old clients. Something like this:
So when a user clicks a button on the portal website to see documentation, the request is handled by this function in portal (Note: I already refactored this function to support multiple urls):
func getDocs(c echo.Context) error {
source := c.Get(auth.SourceName).(string)
key := c.Get(auth.KeyName).(string)
jsonURLs := []DocsURL{
{
url: fmt.Sprintf("%s/0.1/docs?source=%s", config.baseURL, key),
name: "0.1"
},
{
url: fmt.Sprintf("%s/1.0/docs?source=%s", config.baseURL, key),
name: "0.1"
},
}
return c.Render(http.StatusOK, "docs/index", map[string]interface{}{
"source": source,
"key": key,
"pageType": "docs",
"jsonURLs": jsonURLs,
})
}
And this is the script from the template where I use SwaggerUI:
window.onload = function() {
const ui = SwaggerUIBundle({
urls: {{ .jsonURLs }},
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
supportedSubmitMethods: []
})
window.ui = ui
$('.tryitout').prop('disabled', true);
}
I need to generate links depending on the environment (production, staging, local). Also it makes sense to generate data on the backend and feed it to the template to display. BUT THIS DOENS'T WORK!
However, if I hard-code the links in the SwaggerUIBundle it works and portal shows a correct drop-down menu and allows to switch between versions and loads docs for the corresponding version.
func getDocs(c echo.Context) error {
source := c.Get(auth.SourceName).(string)
key := c.Get(auth.KeyName).(string)
return c.Render(http.StatusOK, "docs/index", map[string]interface{}{
"source": source,
"key": key,
"pageType": "docs",
})
}
In the template:
window.onload = function() {
const ui = SwaggerUIBundle({
urls: [
{
url: "http://localhost:8088/0.1/docs?source=111111",
name: "0.1"
},
{
url: "http://localhost:8088/1.0/docs?source=111111",
name: "1.0"
}
],
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
supportedSubmitMethods: []
})
window.ui = ui
$('.tryitout').prop('disabled', true);
}
Why is that? And is there a way to make the first version of code work?
I need the links to be dynamic and preferably generated in the handler. Thank you!
There appear to be two mistakes:
First:
urls: {{ .jsonURLs }},
This will not write jsonURLs in JSON format for you. It will simply write a string representation of jsonURLs. Either you need to write the template to iterate elements of jsonURLs and print them out one by one, or marshal jsonURLs to json yourself:
jsonText,_:=json.Marshal(jsonURLs)
return c.Render(http.StatusOK, "docs/index", map[string]interface{}{
"source": source,
"key": key,
"pageType": "docs",
"jsonURLs": string(jsonText),
})
Second: it looks like you didn't export the member fields of DocsURL struct. Capitalize the field names and add json tags.
type DocsURL struct {
URL string `json:"url"`
Name string `json:"name"`
}

How to change BaseStore from SAP Spartacus storefront

As Spartacus is for B2C process there is no any option to change BaseStore from storefront. I have a drop down for different countries and now want to change BaseSite from it.
So finally I made it working. I am storing baseSite to session if its changed from dropdown and if user is coming back reading it first from session.
here what you have to do to make it working:
Override BaseSite service and change initialize method similar to the initialize method of LanguageService. (which check if baseStore is stored in session )
Listen to SET_ACTIVE_BASE_SITE action and set payload to session. (again similar like activeLanguage effect in LanguagesEffects)
Now in B2cStorefrontModule config add your other sites as
B2cStorefrontModule.withConfig({
context: {
baseSite: ['electronics','mystore2','mystore-uk', 'mystore-canada'],
language: ['en'],
currency: ['USD']
}
So the main solution is, you listen to basestore change action and store the value to session and on page load you read basestore from session
I think you are looking for this. Check full code on Building the Spartacus Storefront from Libraries
B2cStorefrontModule.withConfig({
backend: {
occ: {
baseUrl: 'https://localhost:9002',
prefix: '/rest/v2/',
legacy: false
}
},
authentication: {
client_id: 'mobile_android',
client_secret: 'secret'
},
context: {
baseSite: ['electronics']
},
i18n: {
resources: translations,
chunks: translationChunksConfig,
fallbackLang: 'en'
}
}),