From 96f772b8b59a1b90712a3a9050d2d184f6effce7 Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Sat, 6 Feb 2021 14:37:11 +0100 Subject: [PATCH 1/7] initial commit --- .../api-client/src/index.server.ts | 42 ++++++------ .../commercetools/composables/nuxt/index.js | 15 ++-- .../commercetools/composables/src/index.ts | 12 ++++ packages/commercetools/theme/.gitignore | 1 + packages/commercetools/theme/nuxt.config.js | 31 ++++++--- packages/core/core/package.json | 3 +- .../core/src/factories/apiClientFactory.ts | 32 +++++---- packages/core/core/src/server/index.ts | 23 +------ packages/core/core/src/server/memory/index.ts | 20 ++++++ packages/core/core/src/types.ts | 16 ++--- packages/core/core/src/utils/context/index.ts | 14 ++-- packages/core/middleware/nuxt/index.js | 13 +++- packages/core/middleware/package.json | 8 ++- packages/core/middleware/rollup.config.js | 4 ++ packages/core/middleware/src/createServer.ts | 68 +++++++++++++++++++ packages/core/middleware/src/index.ts | 1 + packages/core/middleware/tsconfig.json | 19 ++++++ packages/core/nuxt-module/lib/module.js | 5 ++ yarn.lock | 10 +++ 19 files changed, 248 insertions(+), 89 deletions(-) create mode 100644 packages/core/core/src/server/memory/index.ts create mode 100644 packages/core/middleware/rollup.config.js create mode 100644 packages/core/middleware/src/createServer.ts create mode 100644 packages/core/middleware/src/index.ts create mode 100644 packages/core/middleware/tsconfig.json diff --git a/packages/commercetools/api-client/src/index.server.ts b/packages/commercetools/api-client/src/index.server.ts index a7a9bea18aa..c3fb6ee772e 100644 --- a/packages/commercetools/api-client/src/index.server.ts +++ b/packages/commercetools/api-client/src/index.server.ts @@ -58,29 +58,31 @@ const parseToken = (rawToken) => { } }; -const tokenExtension: ApiClientExtension = (req, res) => { - const rawCurrentToken = req.cookies['vsf-commercetools-token']; - const currentToken = parseToken(rawCurrentToken); +const tokenExtension: ApiClientExtension = { + lifecycle: (req, res) => { + const rawCurrentToken = req.cookies['vsf-commercetools-token']; + const currentToken = parseToken(rawCurrentToken); - return { - beforeCreate: (config) => ({ - ...config, - auth: { - onTokenChange: (newToken) => { - if (!currentToken || currentToken.access_token !== newToken.access_token) { - res.cookie('vsf-commercetools-token', JSON.stringify(newToken)); + return { + beforeCreate: ({ config }) => ({ + ...config, + auth: { + onTokenChange: (newToken) => { + if (!currentToken || currentToken.access_token !== newToken.access_token) { + res.cookie('vsf-commercetools-token', JSON.stringify(newToken)); + } + }, + onTokenRead: () => { + res.cookie('vsf-commercetools-token', rawCurrentToken); + return currentToken; + }, + onTokenRemove: () => { + delete req.cookies['vsf-commercetools-token']; } - }, - onTokenRead: () => { - res.cookie('vsf-commercetools-token', rawCurrentToken); - return currentToken; - }, - onTokenRemove: () => { - delete req.cookies['vsf-commercetools-token']; } - } - }) - }; + }) + }; + } }; const { createApiClient } = apiClientFactory({ diff --git a/packages/commercetools/composables/nuxt/index.js b/packages/commercetools/composables/nuxt/index.js index 627e1387d23..6d72df1dfb9 100644 --- a/packages/commercetools/composables/nuxt/index.js +++ b/packages/commercetools/composables/nuxt/index.js @@ -1,5 +1,4 @@ import path from 'path'; -import { createMiddleware } from '@vue-storefront/core/server'; const mapI18nSettings = (i18n) => ({ locale: i18n.defaultLocale, @@ -18,8 +17,6 @@ const getMissingFields = (options) => .filter(o => options[o] === undefined); export default function (moduleOptions) { - const { middleware } = createMiddleware(moduleOptions); - const options = isNuxtI18nUsed(moduleOptions) ? { ...moduleOptions, ...mapI18nSettings(this.options.i18n) } : moduleOptions; @@ -30,6 +27,15 @@ export default function (moduleOptions) { throw new Error(`Please provide missing i18n fields: (${missingFields.join(', ')})`); } + this.nuxt.registerIntegration('ct', { + apiClientPackage: '@vue-storefront/commercetools-api/server', + modulePackage: '@vue-storefront/commercetools/nuxt', + configuration: options, + extensions: [ + '@vue-storefront/commercetools' + ] + }); + this.extendBuild(config => { config.resolve.alias['@vue-storefront/commercetools-api$'] = require.resolve('@vue-storefront/commercetools-api'); }); @@ -39,7 +45,4 @@ export default function (moduleOptions) { options }); - if (moduleOptions.apiMiddleware !== false) { - this.addServerMiddleware(middleware); - } } diff --git a/packages/commercetools/composables/src/index.ts b/packages/commercetools/composables/src/index.ts index 8405c4cc7be..1e792a1b9a0 100644 --- a/packages/commercetools/composables/src/index.ts +++ b/packages/commercetools/composables/src/index.ts @@ -15,7 +15,19 @@ import { track } from '@vue-storefront/core'; track('VSFCommercetools'); +const extensions = [ + { + extendApi: { + testFunction2: async (context) => { + console.log('test function2 called', context); + return { test: 2 }; + } + } + } +]; + export { + extensions, useCategory, useProduct, useCart, diff --git a/packages/commercetools/theme/.gitignore b/packages/commercetools/theme/.gitignore index 931abe20328..0688d885742 100644 --- a/packages/commercetools/theme/.gitignore +++ b/packages/commercetools/theme/.gitignore @@ -67,6 +67,7 @@ typings/ # nuxt.js build output .nuxt +.vsf # theme _theme diff --git a/packages/commercetools/theme/nuxt.config.js b/packages/commercetools/theme/nuxt.config.js index c54d6602bd8..91c6e9a17bc 100644 --- a/packages/commercetools/theme/nuxt.config.js +++ b/packages/commercetools/theme/nuxt.config.js @@ -81,6 +81,28 @@ export default { ['@vue-storefront/nuxt-theme'], project-only-end */ ['@vue-storefront/commercetools/nuxt', { + middleware: { + extensions: [ + { + extendApi: { + // eslint-disable-next-line + testFunction: async (context) => { + console.log('test function called'); + return { test: 1 }; + } + }, + // eslint-disable-next-line + lifecycle: (req, res) => ({ beforeCall: ({ callName, args }) => { + + if (callName === 'getCategory') { + console.log(args); + } + + return args; + } }) + } + ] + }, api: { uri: 'https://api.commercetools.com/vsf-ct-dev/graphql', authHost: 'https://auth.sphere.io', @@ -109,14 +131,7 @@ export default { 'nuxt-i18n', 'cookie-universal-nuxt', 'vue-scrollto/nuxt', - ['@vue-storefront/middleware/nuxt', { - integrations: { - ct: { - api: '@vue-storefront/commercetools-api/server', - module: '@vue-storefront/commercetools/nuxt' - } - } - }] + ['@vue-storefront/middleware/nuxt'] ], i18n: { currency: 'USD', diff --git a/packages/core/core/package.json b/packages/core/core/package.json index 0880a686a1f..937d6ef416d 100644 --- a/packages/core/core/package.json +++ b/packages/core/core/package.json @@ -18,7 +18,8 @@ "vue": "^2.6.11", "lodash-es": "^4.17.15", "express": "^4.17.1", - "axios": "0.21.1" + "axios": "0.21.1", + "fs-extra": "^9.1.0" }, "devDependencies": { "@vue/test-utils": "^1.0.0-beta.30", diff --git a/packages/core/core/src/factories/apiClientFactory.ts b/packages/core/core/src/factories/apiClientFactory.ts index eacaa2a5b71..f0e81232a6b 100644 --- a/packages/core/core/src/factories/apiClientFactory.ts +++ b/packages/core/core/src/factories/apiClientFactory.ts @@ -4,34 +4,38 @@ import { Logger } from './../utils'; const apiClientFactory = (factoryParams: ApiClientFactoryParams): ApiClientFactory => { function createApiClient (config: any, customApi: any = {}): ApiInstance { - const extensions = factoryParams.extensions && this && this.middleware - // eslint-disable-next-line - ? Object.values(factoryParams.extensions).map((extensionFn) => extensionFn(this.middleware.req, this.middleware.res)) - : []; - - const _config = extensions + const predefinedExtensions = factoryParams.extensions || []; + const incommingExtensions = this?.middleware?.extensions || []; + const rawExtensions = [...predefinedExtensions, ...incommingExtensions]; + const lifecycles = Object.values(rawExtensions) + .filter(ext => typeof ext.lifecycle === 'function') + .map(({ lifecycle }) => lifecycle(this?.middleware?.req, this?.middleware?.res)); + const extendedApis = Object.keys(rawExtensions) + .reduce((prev, curr) => ({ ...prev, ...rawExtensions[curr].extendApi }), customApi); + + const _config = lifecycles .filter(ext => ext.beforeCreate) - .reduce((prev, curr) => curr.beforeCreate(prev), config); + .reduce((prev, curr) => curr.beforeCreate({ config: prev }), config); const settings = factoryParams.onCreate ? factoryParams.onCreate(_config) : { config, client: config.client }; Logger.debug('apiClientFactory.create', settings); - settings.config = extensions + settings.config = lifecycles .filter(ext => ext.afterCreate) - .reduce((prev, curr) => curr.afterCreate(prev), settings.config); + .reduce((prev, curr) => curr.afterCreate({ config: prev }), settings.config); const extensionHooks = { - before: (args) => extensions + before: (params) => lifecycles .filter(e => e.beforeCall) - .reduce((prev, e) => e.beforeCall(prev), args), - after: (resp) => extensions + .reduce((args, e) => e.beforeCall({ ...params, args}), params.args), + after: (params) => lifecycles .filter(e => e.afterCall) - .reduce((prev, e) => e.afterCall(prev), resp) + .reduce((response, e) => e.afterCall({ ...params, response }), params.response) }; const api = applyContextToApi( - { ...factoryParams.api, ...customApi }, + { ...factoryParams.api, ...extendedApis }, settings, extensionHooks ); diff --git a/packages/core/core/src/server/index.ts b/packages/core/core/src/server/index.ts index d8833d56e73..3854f9c4eef 100644 --- a/packages/core/core/src/server/index.ts +++ b/packages/core/core/src/server/index.ts @@ -1,22 +1 @@ -import express, { json } from 'express'; - -const app = express(); -app.use(json()); - -const extend = (fn) => fn(app); - -const createMiddleware = ({ apiMiddleware }) => { - if (apiMiddleware && apiMiddleware.extend) { - extend(apiMiddleware.extend); - } - - return { - middleware: { - path: '/api', - handler: app - }, - extend - }; -}; - -export { createMiddleware }; +export * from './memory'; diff --git a/packages/core/core/src/server/memory/index.ts b/packages/core/core/src/server/memory/index.ts new file mode 100644 index 00000000000..63ae418a073 --- /dev/null +++ b/packages/core/core/src/server/memory/index.ts @@ -0,0 +1,20 @@ +import fs from 'fs-extra'; +import merge from 'lodash-es/merge'; + +const createMemory = (rootDir) => { + const memoryFile = rootDir + '/.vsf/mem.json'; + + fs.ensureFileSync(memoryFile); + + const read = () => fs.readJsonSync(memoryFile, { throws: false }); + + const save = (content) => { + const prevContent = read(); + + fs.outputJsonSync(memoryFile, merge(prevContent, content)); + }; + + return { read, save }; +}; + +export { createMemory }; diff --git a/packages/core/core/src/types.ts b/packages/core/core/src/types.ts index 39cd74cd00d..0fabd6ad4cc 100644 --- a/packages/core/core/src/types.ts +++ b/packages/core/core/src/types.ts @@ -583,17 +583,17 @@ export interface FactoryParams { provide?: (context: Context) => any; } -// TODO: Implement proper typing -// https://github.com/vuestorefront/vue-storefront/issues/5431 export interface ApiClientExtensionLifecycle { - beforeCreate?: (config, headers?: Record) => any; - afterCreate?: ({ config, client }, headers?: Record) => { config; client }; - beforeCall?: ({ config, functionName, params }) => any; - afterCall?: ({ config, functionName, params }) => any; + beforeCreate?: ({ config }) => any; + afterCreate?: ({ config }) => any; + beforeCall?: ({ config, callName, args }) => any; + afterCall?: ({ config, callName, args }) => any; } -export type ApiClientExtension = (req: any, res: any) => ApiClientExtensionLifecycle; - +export interface ApiClientExtension { + extendApi?: Record; + lifecycle?: (req: any, res: any) => ApiClientExtensionLifecycle; +} export interface ApiClientFactoryParams { api: F; isProxy?: boolean; diff --git a/packages/core/core/src/utils/context/index.ts b/packages/core/core/src/utils/context/index.ts index aaf49e7b504..dd41433aca6 100644 --- a/packages/core/core/src/utils/context/index.ts +++ b/packages/core/core/src/utils/context/index.ts @@ -5,8 +5,8 @@ interface ContextConfiguration { } interface ApplyingContextHooks { - before: (args: any[]) => any[]; - after: (response: any) => any; + before: ({ callName, args }) => any[]; + after: ({ callName, args, response }) => any; } let useVSFContext = () => ({}) as Context; @@ -28,9 +28,15 @@ const applyContextToApi = ( hooks: ApplyingContextHooks = { before: NOP, after: NOP } ) => Object.entries(api) - .reduce((prev, [key, fn]: any) => ({ + .reduce((prev, [callName, fn]: any) => ({ ...prev, - [key]: async (...args) => hooks.after(await fn(context, ...hooks.before(args))) + [callName]: async (...args) => { + const transformedArgs = hooks.before({ callName, args }); + const response = await fn(context, ...transformedArgs); + const transformedResponse = hooks.after({ callName, args, response }); + + return transformedResponse; + } }), {}); const generateContext = (factoryParams) => { diff --git a/packages/core/middleware/nuxt/index.js b/packages/core/middleware/nuxt/index.js index c40dba256e0..5cfc95fa141 100644 --- a/packages/core/middleware/nuxt/index.js +++ b/packages/core/middleware/nuxt/index.js @@ -1,5 +1,12 @@ -const createProxyEndpoint = require('./../middleware'); +const { createMemory } = require('@vue-storefront/core/server'); +const { createServer } = require('@vue-storefront/middleware'); -module.exports = function VueStorefrontMiddleware (moduleOptions) { - this.addServerMiddleware(createProxyEndpoint(moduleOptions, this.options)); +module.exports = function VueStorefrontMiddleware () { + const { read } = createMemory(this.nuxt.options.buildDir + '/../'); + const integrations = read(); + const handler = createServer.bind(this)({ integrations }); + + const serverMiddleware = {path: '/api', handler }; + + this.addServerMiddleware(serverMiddleware); }; diff --git a/packages/core/middleware/package.json b/packages/core/middleware/package.json index 354942d6f76..6072250e132 100644 --- a/packages/core/middleware/package.json +++ b/packages/core/middleware/package.json @@ -1,10 +1,12 @@ { "name": "@vue-storefront/middleware", - "version": "2.1.1-rc.1", + "version": "2.1.1", "description": "", - "main": "module.js", + "main": "lib/index.cjs.js", + "module": "lib/index.es.js", + "types": "lib/src/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "rimraf lib && rollup -c" }, "author": "Vue Storefront", "license": "MIT", diff --git a/packages/core/middleware/rollup.config.js b/packages/core/middleware/rollup.config.js new file mode 100644 index 00000000000..10dc02f5e26 --- /dev/null +++ b/packages/core/middleware/rollup.config.js @@ -0,0 +1,4 @@ +import pkg from './package.json'; +import { generateBaseConfig } from '../../rollup.base.config'; + +export default generateBaseConfig(pkg); diff --git a/packages/core/middleware/src/createServer.ts b/packages/core/middleware/src/createServer.ts new file mode 100644 index 00000000000..6aa14e10e32 --- /dev/null +++ b/packages/core/middleware/src/createServer.ts @@ -0,0 +1,68 @@ +import express from 'express'; +import cookieParser from 'cookie-parser'; + +const app = express(); +app.use(express.json()); +app.use(cookieParser()); + +const lookUpConfiguration = (integrationConfig, nuxtInstance) => { + const integrationModule = nuxtInstance.options.buildModules.find(module => + Array.isArray(module) && module[0] === integrationConfig.modulePackage + ); + + return integrationModule ? integrationModule[1] : {}; +}; + +const registerIntegrations = (integrations, nuxtInstance) => + Object.entries(integrations).reduce((prev, [tag, integrationConfig]: any) => { + const { middleware, ...rest } = lookUpConfiguration(integrationConfig, nuxtInstance); + const configuration = { ...rest, ...integrationConfig.configuration }; + const rawExtensions = [...middleware.extensions || [], ...integrationConfig.extensions || []]; + const extensions = rawExtensions.reduce((prev, curr) => { + if (typeof curr === 'string') { + console.log('loading...', curr); + return [ + ...prev, + ...require(curr).extensions + ]; + } + + return [...prev, curr]; + }, []); + + return { + ...prev, + [tag]: { + apiClient: require(integrationConfig.apiClientPackage), + configuration, + extensions + } + }; + }, {}); + +const getApiClient = (apiClientPackage, { req, res, extensions }) => { + const context = { middleware: { req, res, extensions } }; + const createApiClient = apiClientPackage.createApiClient.bind(context); + + return { ...apiClientPackage, createApiClient }; +}; + +function createServer (config) { + const integrations = registerIntegrations(config.integrations, this); + + app.post('/:integrationName/:functionName', async (req, res) => { + const { integrationName, functionName } = req.params; + const { apiClient, configuration, extensions } = integrations[integrationName]; + const { createApiClient } = getApiClient(apiClient, { req, res, extensions }); + + const apiClientInstance = createApiClient(configuration); + const apiFunction = apiClientInstance.api[functionName]; + const platformResponse = await apiFunction(...req.body); + + res.send(platformResponse); + }); + + return app; +} + +export { createServer }; diff --git a/packages/core/middleware/src/index.ts b/packages/core/middleware/src/index.ts new file mode 100644 index 00000000000..f94bdb51cba --- /dev/null +++ b/packages/core/middleware/src/index.ts @@ -0,0 +1 @@ +export * from './createServer'; diff --git a/packages/core/middleware/tsconfig.json b/packages/core/middleware/tsconfig.json new file mode 100644 index 00000000000..5f31c268ff0 --- /dev/null +++ b/packages/core/middleware/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "./lib", + "esModuleInterop": true, + "target": "es5", + "module": "ES2015", + "moduleResolution": "node", + "importHelpers": true, + "noEmitHelpers": true, + "sourceMap": true, + "declaration": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": "./", + "lib": ["es6", "es7", "dom"], + "strict": false, + }, + "exclude": ["node_modules", "**/*.spec.ts"] +} diff --git a/packages/core/nuxt-module/lib/module.js b/packages/core/nuxt-module/lib/module.js index 4f5f2a21efa..4d537a6d594 100644 --- a/packages/core/nuxt-module/lib/module.js +++ b/packages/core/nuxt-module/lib/module.js @@ -7,8 +7,13 @@ const resolveDependency = require('./helpers/resolveDependency'); const performanceModule = require('./modules/performance'); const storefrontUiModule = require('./modules/storefront-ui'); const rawSourcesModule = require('./modules/raw-sources-loader'); +const { createMemory } = require('@vue-storefront/core/server'); module.exports = function VueStorefrontNuxtModule (moduleOptions) { + const { save, read } = createMemory(this.nuxt.options.buildDir + '/../') + this.nuxt.registerIntegration = (tag, config) => save({ [tag]: config }) + this.nuxt.readMemory = read + const defaultOptions = { coreDevelopment: false, performance : { diff --git a/yarn.lock b/yarn.lock index 18d1229bfdc..b06e7f62c78 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7381,6 +7381,16 @@ fs-extra@^9.0.0, fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^1.0.0" +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-memo@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/fs-memo/-/fs-memo-1.2.0.tgz#a2ec3be606b902077adbb37ec529c5ec5fb2e037" From 2d08725d8703b3073f27921bf6753182e6feda2a Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Tue, 9 Feb 2021 13:20:26 +0100 Subject: [PATCH 2/7] separate middleware, config --- .../commercetools/composables/nuxt/index.js | 9 --- .../commercetools/theme/middleware.config.js | 62 +++++++++++++++++++ packages/commercetools/theme/middleware.js | 8 +++ packages/commercetools/theme/nuxt.config.js | 48 ++------------ packages/core/core/src/utils/nuxt/index.ts | 8 ++- packages/core/middleware/nuxt/index.js | 7 +-- packages/core/middleware/package.json | 3 +- packages/core/middleware/src/createServer.ts | 21 ++----- packages/core/nuxt-module/lib/module.js | 5 -- yarn.lock | 12 +++- 10 files changed, 102 insertions(+), 81 deletions(-) create mode 100644 packages/commercetools/theme/middleware.config.js create mode 100644 packages/commercetools/theme/middleware.js diff --git a/packages/commercetools/composables/nuxt/index.js b/packages/commercetools/composables/nuxt/index.js index 6d72df1dfb9..c60622c40a0 100644 --- a/packages/commercetools/composables/nuxt/index.js +++ b/packages/commercetools/composables/nuxt/index.js @@ -27,15 +27,6 @@ export default function (moduleOptions) { throw new Error(`Please provide missing i18n fields: (${missingFields.join(', ')})`); } - this.nuxt.registerIntegration('ct', { - apiClientPackage: '@vue-storefront/commercetools-api/server', - modulePackage: '@vue-storefront/commercetools/nuxt', - configuration: options, - extensions: [ - '@vue-storefront/commercetools' - ] - }); - this.extendBuild(config => { config.resolve.alias['@vue-storefront/commercetools-api$'] = require.resolve('@vue-storefront/commercetools-api'); }); diff --git a/packages/commercetools/theme/middleware.config.js b/packages/commercetools/theme/middleware.config.js new file mode 100644 index 00000000000..411a84a32a6 --- /dev/null +++ b/packages/commercetools/theme/middleware.config.js @@ -0,0 +1,62 @@ +module.exports = { + integrations: { + ct: { + apiClientPackage: '@vue-storefront/commercetools-api/server', + modulePackage: '@vue-storefront/commercetools/nuxt', + extensions: [ + // '@vue-storefront/commercetools', + { + extendApi: { + // eslint-disable-next-line + testFunction: async (context) => { + console.log('test function called'); + return { test: 1 }; + } + }, + // eslint-disable-next-line + lifecycle: (req, res) => ({ beforeCall: ({ callName, args }) => { + + if (callName === 'getCategory') { + console.log(args); + } + + return args; + } }) + } + ], + configuration: { + api: { + uri: 'https://api.commercetools.com/vsf-ct-dev/graphql', + authHost: 'https://auth.sphere.io', + projectKey: 'vsf-ct-dev', + clientId: 'RT4iJGDbDzZe4b2E6RyeNe9s', + clientSecret: '5eBt3yfZJWw1j7V6kXjfKXpuFP-YQXpg', + scopes: [ + 'manage_products:vsf-ct-dev', + 'create_anonymous_token:vsf-ct-dev', + 'manage_my_profile:vsf-ct-dev', + 'manage_customer_groups:vsf-ct-dev', + 'view_categories:vsf-ct-dev', + 'introspect_oauth_tokens:vsf-ct-dev', + 'manage_my_payments:vsf-ct-dev', + 'manage_my_orders:vsf-ct-dev', + 'manage_my_shopping_lists:vsf-ct-dev', + 'view_published_products:vsf-ct-dev' + ] + }, + currency: 'USD', + country: 'US', + countries: [ + { name: 'US', label: 'United States' }, + { name: 'AT', label: 'Austria' }, + { name: 'DE', label: 'Germany' }, + { name: 'NL', label: 'Netherlands' } + ], + currencies: [ + { name: 'EUR', label: 'Euro' }, + { name: 'USD', label: 'Dollar' } + ] + } + } + } +}; diff --git a/packages/commercetools/theme/middleware.js b/packages/commercetools/theme/middleware.js new file mode 100644 index 00000000000..694622d079b --- /dev/null +++ b/packages/commercetools/theme/middleware.js @@ -0,0 +1,8 @@ +const { createServer } = require('@vue-storefront/middleware'); +const { integrations } = require('./middleware.config'); + +const app = createServer({ integrations }); + +app.listen(8181, () => { + console.log('Middleware started'); +}); diff --git a/packages/commercetools/theme/nuxt.config.js b/packages/commercetools/theme/nuxt.config.js index 91c6e9a17bc..ca60324785e 100644 --- a/packages/commercetools/theme/nuxt.config.js +++ b/packages/commercetools/theme/nuxt.config.js @@ -1,6 +1,9 @@ import webpack from 'webpack'; export default { + publicRuntimeConfig: { + middlewareUrl: 'http://172.21.127.16:8181/' + }, mode: 'universal', server: { port: 3000, @@ -81,47 +84,6 @@ export default { ['@vue-storefront/nuxt-theme'], project-only-end */ ['@vue-storefront/commercetools/nuxt', { - middleware: { - extensions: [ - { - extendApi: { - // eslint-disable-next-line - testFunction: async (context) => { - console.log('test function called'); - return { test: 1 }; - } - }, - // eslint-disable-next-line - lifecycle: (req, res) => ({ beforeCall: ({ callName, args }) => { - - if (callName === 'getCategory') { - console.log(args); - } - - return args; - } }) - } - ] - }, - api: { - uri: 'https://api.commercetools.com/vsf-ct-dev/graphql', - authHost: 'https://auth.sphere.io', - projectKey: 'vsf-ct-dev', - clientId: 'RT4iJGDbDzZe4b2E6RyeNe9s', - clientSecret: '5eBt3yfZJWw1j7V6kXjfKXpuFP-YQXpg', - scopes: [ - 'manage_products:vsf-ct-dev', - 'create_anonymous_token:vsf-ct-dev', - 'manage_my_profile:vsf-ct-dev', - 'manage_customer_groups:vsf-ct-dev', - 'view_categories:vsf-ct-dev', - 'introspect_oauth_tokens:vsf-ct-dev', - 'manage_my_payments:vsf-ct-dev', - 'manage_my_orders:vsf-ct-dev', - 'manage_my_shopping_lists:vsf-ct-dev', - 'view_published_products:vsf-ct-dev' - ] - }, i18n: { useNuxtI18nConfig: true } @@ -130,8 +92,8 @@ export default { modules: [ 'nuxt-i18n', 'cookie-universal-nuxt', - 'vue-scrollto/nuxt', - ['@vue-storefront/middleware/nuxt'] + 'vue-scrollto/nuxt' + // ['@vue-storefront/middleware/nuxt'] ], i18n: { currency: 'USD', diff --git a/packages/core/core/src/utils/nuxt/index.ts b/packages/core/core/src/utils/nuxt/index.ts index 38cb75cdca4..d3351080378 100644 --- a/packages/core/core/src/utils/nuxt/index.ts +++ b/packages/core/core/src/utils/nuxt/index.ts @@ -10,8 +10,14 @@ export const integrationPlugin = (pluginFn: NuxtPlugin) => (nuxtCtx: NuxtContext const configure = (tag, configuration) => { const injectInContext = createAddIntegrationToCtx({ tag, nuxtCtx, inject }); const config = getIntegrationConfig(nuxtCtx, configuration); + const { middlewareUrl } = (nuxtCtx as any).$config; + + if (middlewareUrl) { + config.axios.baseURL = middlewareUrl; + } + const client = axios.create(config.axios); - const api = createProxiedApi({ givenApi: configuration.api, client, tag }); + const api = createProxiedApi({ givenApi: configuration.api || {}, client, tag }); injectInContext({ api, client, config }); }; diff --git a/packages/core/middleware/nuxt/index.js b/packages/core/middleware/nuxt/index.js index 5cfc95fa141..79bd0cc093d 100644 --- a/packages/core/middleware/nuxt/index.js +++ b/packages/core/middleware/nuxt/index.js @@ -1,11 +1,8 @@ -const { createMemory } = require('@vue-storefront/core/server'); const { createServer } = require('@vue-storefront/middleware'); module.exports = function VueStorefrontMiddleware () { - const { read } = createMemory(this.nuxt.options.buildDir + '/../'); - const integrations = read(); - const handler = createServer.bind(this)({ integrations }); - + const { integrations } = require(this.nuxt.options.rootDir + '/middleware.config.js'); + const handler = createServer({ integrations }); const serverMiddleware = {path: '/api', handler }; this.addServerMiddleware(serverMiddleware); diff --git a/packages/core/middleware/package.json b/packages/core/middleware/package.json index 6072250e132..d94b1a96897 100644 --- a/packages/core/middleware/package.json +++ b/packages/core/middleware/package.json @@ -13,7 +13,8 @@ "dependencies": { "express": "^4.17.1", "cookie-parser": "^1.4.5", - "is-https": "^3.0.2" + "is-https": "^3.0.2", + "cors": "^2.8.5" }, "publishConfig": { "access": "public" diff --git a/packages/core/middleware/src/createServer.ts b/packages/core/middleware/src/createServer.ts index 6aa14e10e32..9840a92a9ef 100644 --- a/packages/core/middleware/src/createServer.ts +++ b/packages/core/middleware/src/createServer.ts @@ -1,23 +1,15 @@ import express from 'express'; import cookieParser from 'cookie-parser'; +import cors from 'cors'; const app = express(); app.use(express.json()); app.use(cookieParser()); +app.use(cors()); -const lookUpConfiguration = (integrationConfig, nuxtInstance) => { - const integrationModule = nuxtInstance.options.buildModules.find(module => - Array.isArray(module) && module[0] === integrationConfig.modulePackage - ); - - return integrationModule ? integrationModule[1] : {}; -}; - -const registerIntegrations = (integrations, nuxtInstance) => +const registerIntegrations = (integrations) => Object.entries(integrations).reduce((prev, [tag, integrationConfig]: any) => { - const { middleware, ...rest } = lookUpConfiguration(integrationConfig, nuxtInstance); - const configuration = { ...rest, ...integrationConfig.configuration }; - const rawExtensions = [...middleware.extensions || [], ...integrationConfig.extensions || []]; + const rawExtensions = integrationConfig.extensions || []; const extensions = rawExtensions.reduce((prev, curr) => { if (typeof curr === 'string') { console.log('loading...', curr); @@ -34,7 +26,7 @@ const registerIntegrations = (integrations, nuxtInstance) => ...prev, [tag]: { apiClient: require(integrationConfig.apiClientPackage), - configuration, + configuration: integrationConfig.configuration, extensions } }; @@ -48,13 +40,12 @@ const getApiClient = (apiClientPackage, { req, res, extensions }) => { }; function createServer (config) { - const integrations = registerIntegrations(config.integrations, this); + const integrations = registerIntegrations(config.integrations); app.post('/:integrationName/:functionName', async (req, res) => { const { integrationName, functionName } = req.params; const { apiClient, configuration, extensions } = integrations[integrationName]; const { createApiClient } = getApiClient(apiClient, { req, res, extensions }); - const apiClientInstance = createApiClient(configuration); const apiFunction = apiClientInstance.api[functionName]; const platformResponse = await apiFunction(...req.body); diff --git a/packages/core/nuxt-module/lib/module.js b/packages/core/nuxt-module/lib/module.js index 4d537a6d594..4f5f2a21efa 100644 --- a/packages/core/nuxt-module/lib/module.js +++ b/packages/core/nuxt-module/lib/module.js @@ -7,13 +7,8 @@ const resolveDependency = require('./helpers/resolveDependency'); const performanceModule = require('./modules/performance'); const storefrontUiModule = require('./modules/storefront-ui'); const rawSourcesModule = require('./modules/raw-sources-loader'); -const { createMemory } = require('@vue-storefront/core/server'); module.exports = function VueStorefrontNuxtModule (moduleOptions) { - const { save, read } = createMemory(this.nuxt.options.buildDir + '/../') - this.nuxt.registerIntegration = (tag, config) => save({ [tag]: config }) - this.nuxt.readMemory = read - const defaultOptions = { coreDevelopment: false, performance : { diff --git a/yarn.lock b/yarn.lock index b06e7f62c78..fd181ea4815 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5463,6 +5463,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + cosmiconfig@^5.0.0, cosmiconfig@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -11145,7 +11153,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -15447,7 +15455,7 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -vary@^1.1.2, vary@~1.1.2: +vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= From 45174002beea6d30623ba0632e715e5c020ee6f8 Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Wed, 10 Feb 2021 14:24:40 +0100 Subject: [PATCH 3/7] separate middleware, config --- package.json | 3 +- .../api-client/src/index.server.ts | 7 +-- .../commercetools/theme/middleware.config.js | 45 ++++------------ packages/commercetools/theme/nuxt.config.js | 7 +-- .../core/src/factories/apiClientFactory.ts | 22 ++++---- packages/core/core/src/types.ts | 13 ++--- packages/core/middleware/middleware.js | 54 ------------------- packages/core/middleware/package.json | 1 + packages/core/middleware/src/createServer.ts | 47 ++++++---------- packages/core/middleware/src/integrations.ts | 38 +++++++++++++ yarn.lock | 5 ++ 11 files changed, 97 insertions(+), 145 deletions(-) delete mode 100644 packages/core/middleware/middleware.js create mode 100644 packages/core/middleware/src/integrations.ts diff --git a/package.json b/package.json index a9668e26aee..809b734c591 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build:docs": "cd packages/core/docs && yarn build", "dev:docs": "cd packages/core/docs && yarn dev", "build:core": "cd packages/core/core && yarn build", + "build:middleware": "cd packages/core/middleware && yarn build", "build:ayc:api-client": "cd packages/about-you/api-client && yarn build", "build:ayc:composables": "cd packages/about-you/composables && yarn build", "build:ayc:theme": "cd packages/about-you/theme && yarn build", @@ -28,7 +29,7 @@ "dev:bp": "cd packages/boilerplate/theme && yarn dev", "build:ct:api-client": "cd packages/commercetools/api-client && yarn build", "build:ct:composables": "cd packages/commercetools/composables && yarn build", - "build:ct:tools": "yarn build:core && yarn build:ct:api-client && yarn build:ct:composables", + "build:ct:tools": "yarn build:core && yarn build:middleware && yarn build:ct:api-client && yarn build:ct:composables", "build:ct:theme": "cd packages/commercetools/theme && yarn build", "build:ct": "yarn build:core && yarn build:ct:tools && yarn build:ct:theme", "build:sp:tools": "yarn build:core && yarn build:sp:api-client && yarn build:sp:composables", diff --git a/packages/commercetools/api-client/src/index.server.ts b/packages/commercetools/api-client/src/index.server.ts index c3fb6ee772e..8c40bcd9b49 100644 --- a/packages/commercetools/api-client/src/index.server.ts +++ b/packages/commercetools/api-client/src/index.server.ts @@ -59,13 +59,14 @@ const parseToken = (rawToken) => { }; const tokenExtension: ApiClientExtension = { - lifecycle: (req, res) => { + name: 'tokenExtension', + hooks: (req, res) => { const rawCurrentToken = req.cookies['vsf-commercetools-token']; const currentToken = parseToken(rawCurrentToken); return { - beforeCreate: ({ config }) => ({ - ...config, + beforeCreate: ({ configuration }) => ({ + ...configuration, auth: { onTokenChange: (newToken) => { if (!currentToken || currentToken.access_token !== newToken.access_token) { diff --git a/packages/commercetools/theme/middleware.config.js b/packages/commercetools/theme/middleware.config.js index 411a84a32a6..491786e31c8 100644 --- a/packages/commercetools/theme/middleware.config.js +++ b/packages/commercetools/theme/middleware.config.js @@ -1,29 +1,7 @@ module.exports = { integrations: { ct: { - apiClientPackage: '@vue-storefront/commercetools-api/server', - modulePackage: '@vue-storefront/commercetools/nuxt', - extensions: [ - // '@vue-storefront/commercetools', - { - extendApi: { - // eslint-disable-next-line - testFunction: async (context) => { - console.log('test function called'); - return { test: 1 }; - } - }, - // eslint-disable-next-line - lifecycle: (req, res) => ({ beforeCall: ({ callName, args }) => { - - if (callName === 'getCategory') { - console.log(args); - } - - return args; - } }) - } - ], + location: '@vue-storefront/commercetools-api/server', configuration: { api: { uri: 'https://api.commercetools.com/vsf-ct-dev/graphql', @@ -45,18 +23,17 @@ module.exports = { ] }, currency: 'USD', - country: 'US', - countries: [ - { name: 'US', label: 'United States' }, - { name: 'AT', label: 'Austria' }, - { name: 'DE', label: 'Germany' }, - { name: 'NL', label: 'Netherlands' } - ], - currencies: [ - { name: 'EUR', label: 'Euro' }, - { name: 'USD', label: 'Dollar' } - ] + country: 'US' } } } }; + +/** +extensions -> function: (extensions) => extensions + +extendApi -> extendApiMethods +lifecycle: before / after: config -> configuration +apiClientPackage -> location +add `name` do extension type +lifecycle -> hooks +*/ diff --git a/packages/commercetools/theme/nuxt.config.js b/packages/commercetools/theme/nuxt.config.js index ca60324785e..24dfcc321fc 100644 --- a/packages/commercetools/theme/nuxt.config.js +++ b/packages/commercetools/theme/nuxt.config.js @@ -1,9 +1,6 @@ import webpack from 'webpack'; export default { - publicRuntimeConfig: { - middlewareUrl: 'http://172.21.127.16:8181/' - }, mode: 'universal', server: { port: 3000, @@ -92,8 +89,8 @@ export default { modules: [ 'nuxt-i18n', 'cookie-universal-nuxt', - 'vue-scrollto/nuxt' - // ['@vue-storefront/middleware/nuxt'] + 'vue-scrollto/nuxt', + '@vue-storefront/middleware/nuxt' ], i18n: { currency: 'USD', diff --git a/packages/core/core/src/factories/apiClientFactory.ts b/packages/core/core/src/factories/apiClientFactory.ts index f0e81232a6b..2b44b27c4e0 100644 --- a/packages/core/core/src/factories/apiClientFactory.ts +++ b/packages/core/core/src/factories/apiClientFactory.ts @@ -1,21 +1,19 @@ -import { ApiClientFactoryParams, ApiClientConfig, ApiInstance, ApiClientFactory } from './../types'; +import { ApiClientFactoryParams, ApiClientConfig, ApiInstance, ApiClientFactory, ApiClientExtension } from './../types'; import { applyContextToApi } from './../utils/context'; import { Logger } from './../utils'; const apiClientFactory = (factoryParams: ApiClientFactoryParams): ApiClientFactory => { function createApiClient (config: any, customApi: any = {}): ApiInstance { - const predefinedExtensions = factoryParams.extensions || []; - const incommingExtensions = this?.middleware?.extensions || []; - const rawExtensions = [...predefinedExtensions, ...incommingExtensions]; + const rawExtensions: ApiClientExtension[] = this?.middleware?.extensions || []; const lifecycles = Object.values(rawExtensions) - .filter(ext => typeof ext.lifecycle === 'function') - .map(({ lifecycle }) => lifecycle(this?.middleware?.req, this?.middleware?.res)); + .filter(ext => typeof ext.hooks === 'function') + .map(({ hooks }) => hooks(this?.middleware?.req, this?.middleware?.res)); const extendedApis = Object.keys(rawExtensions) - .reduce((prev, curr) => ({ ...prev, ...rawExtensions[curr].extendApi }), customApi); + .reduce((prev, curr) => ({ ...prev, ...rawExtensions[curr].extendApiMethods }), customApi); const _config = lifecycles .filter(ext => ext.beforeCreate) - .reduce((prev, curr) => curr.beforeCreate({ config: prev }), config); + .reduce((prev, curr) => curr.beforeCreate({ configuration: prev }), config); const settings = factoryParams.onCreate ? factoryParams.onCreate(_config) : { config, client: config.client }; @@ -23,15 +21,15 @@ const apiClientFactory = (f settings.config = lifecycles .filter(ext => ext.afterCreate) - .reduce((prev, curr) => curr.afterCreate({ config: prev }), settings.config); + .reduce((prev, curr) => curr.afterCreate({ configuration: prev }), settings.config); const extensionHooks = { before: (params) => lifecycles .filter(e => e.beforeCall) - .reduce((args, e) => e.beforeCall({ ...params, args}), params.args), + .reduce((args, e) => e.beforeCall({ ...params, configuration: settings.config, args}), params.args), after: (params) => lifecycles .filter(e => e.afterCall) - .reduce((response, e) => e.afterCall({ ...params, response }), params.response) + .reduce((response, e) => e.afterCall({ ...params, configuration: settings.config, response }), params.response) }; const api = applyContextToApi( @@ -47,6 +45,8 @@ const apiClientFactory = (f }; } + (createApiClient as any)._predefinedExtensions = factoryParams.extensions || []; + return { createApiClient }; }; diff --git a/packages/core/core/src/types.ts b/packages/core/core/src/types.ts index 0fabd6ad4cc..911b92cf2ca 100644 --- a/packages/core/core/src/types.ts +++ b/packages/core/core/src/types.ts @@ -584,15 +584,16 @@ export interface FactoryParams { } export interface ApiClientExtensionLifecycle { - beforeCreate?: ({ config }) => any; - afterCreate?: ({ config }) => any; - beforeCall?: ({ config, callName, args }) => any; - afterCall?: ({ config, callName, args }) => any; + beforeCreate?: ({ configuration }) => any; + afterCreate?: ({ configuration }) => any; + beforeCall?: ({ configuration, callName, args }) => any; + afterCall?: ({ configuration, callName, args }) => any; } export interface ApiClientExtension { - extendApi?: Record; - lifecycle?: (req: any, res: any) => ApiClientExtensionLifecycle; + name: string; + extendApiMethods?: Record; + hooks?: (req: any, res: any) => ApiClientExtensionLifecycle; } export interface ApiClientFactoryParams { api: F; diff --git a/packages/core/middleware/middleware.js b/packages/core/middleware/middleware.js deleted file mode 100644 index 69f1571512f..00000000000 --- a/packages/core/middleware/middleware.js +++ /dev/null @@ -1,54 +0,0 @@ -const express = require('express'); -const cookieParser = require('cookie-parser'); - -const app = express(); -app.use(express.json()); -app.use(cookieParser()); - -function getIntegrationConfigFromModule (moduleName, nuxtOptions) { - const integrationModule = nuxtOptions.buildModules.find(module => - Array.isArray(module) && module[0] === moduleName - ); - - return integrationModule ? integrationModule[1] : {}; -} - -function loadApiClient (apiClientPackageName, { req, res }) { - const apiClientPackage = require(apiClientPackageName); - const context = { middleware: { req, res } }; - const createApiClient = apiClientPackage.createApiClient.bind(context); - - return { ...apiClientPackage, createApiClient }; -} - -function createProxyEndpoint (moduleOptions, nuxtOptions) { - app.post('/:integrationName/:functionName', async (req, res) => { - const { integrationName, functionName } = req.params; - const integration = moduleOptions.integrations[integrationName]; - - const initialConifguration = getIntegrationConfigFromModule(integration.module, nuxtOptions); - - const { createApiClient } = loadApiClient(integration.api, { req, res }); - - const apiClient = createApiClient({ - ...initialConifguration, - locale: 'en', - country: 'US', - currency: 'USD' - }); - - const apiFunction = apiClient.api[functionName]; - - const platformResponse = await apiFunction(...req.body); - - res.send(platformResponse); - }); - - return { - path: '/api', - handler: app - }; -} - -module.exports = createProxyEndpoint; - diff --git a/packages/core/middleware/package.json b/packages/core/middleware/package.json index d94b1a96897..c1e4ab2afbb 100644 --- a/packages/core/middleware/package.json +++ b/packages/core/middleware/package.json @@ -11,6 +11,7 @@ "author": "Vue Storefront", "license": "MIT", "dependencies": { + "consola": "2.15.3", "express": "^4.17.1", "cookie-parser": "^1.4.5", "is-https": "^3.0.2", diff --git a/packages/core/middleware/src/createServer.ts b/packages/core/middleware/src/createServer.ts index 9840a92a9ef..95787245baa 100644 --- a/packages/core/middleware/src/createServer.ts +++ b/packages/core/middleware/src/createServer.ts @@ -1,51 +1,34 @@ import express from 'express'; import cookieParser from 'cookie-parser'; import cors from 'cors'; +import consola from 'consola'; +import { registerIntegrations } from './integrations'; const app = express(); app.use(express.json()); app.use(cookieParser()); app.use(cors()); -const registerIntegrations = (integrations) => - Object.entries(integrations).reduce((prev, [tag, integrationConfig]: any) => { - const rawExtensions = integrationConfig.extensions || []; - const extensions = rawExtensions.reduce((prev, curr) => { - if (typeof curr === 'string') { - console.log('loading...', curr); - return [ - ...prev, - ...require(curr).extensions - ]; - } - - return [...prev, curr]; - }, []); - - return { - ...prev, - [tag]: { - apiClient: require(integrationConfig.apiClientPackage), - configuration: integrationConfig.configuration, - extensions - } - }; - }, {}); - -const getApiClient = (apiClientPackage, { req, res, extensions }) => { - const context = { middleware: { req, res, extensions } }; - const createApiClient = apiClientPackage.createApiClient.bind(context); - - return { ...apiClientPackage, createApiClient }; +const applyMiddlewreContext = (createApiClient, { req, res, extensions }) => { + const context = { + middleware: { req, res, extensions } + }; + + return createApiClient.bind(context); }; function createServer (config) { + consola.info('Middleware starting....'); + consola.info('Loading integartions...'); + const integrations = registerIntegrations(config.integrations); + consola.success('Integrations loaded!'); app.post('/:integrationName/:functionName', async (req, res) => { const { integrationName, functionName } = req.params; const { apiClient, configuration, extensions } = integrations[integrationName]; - const { createApiClient } = getApiClient(apiClient, { req, res, extensions }); + const middlewareContext = { req, res, extensions }; + const createApiClient = applyMiddlewreContext(apiClient.createApiClient, middlewareContext); const apiClientInstance = createApiClient(configuration); const apiFunction = apiClientInstance.api[functionName]; const platformResponse = await apiFunction(...req.body); @@ -53,6 +36,8 @@ function createServer (config) { res.send(platformResponse); }); + consola.success('Middleware created!'); + return app; } diff --git a/packages/core/middleware/src/integrations.ts b/packages/core/middleware/src/integrations.ts new file mode 100644 index 00000000000..715582f9969 --- /dev/null +++ b/packages/core/middleware/src/integrations.ts @@ -0,0 +1,38 @@ +import consola from 'consola'; + +const createRawExtensions = (apiClient, integrationConfig) => { + const extensionsCreateFn = integrationConfig.extensions; + const prederinedExtensions = apiClient.createApiClient._predefinedExtensions; + return extensionsCreateFn ? extensionsCreateFn(prederinedExtensions) : prederinedExtensions; +}; + +const lookUpExternal = (curr) => typeof curr === 'string' ? require(curr) : [curr]; + +const createExtensions = (rawExtensions) => rawExtensions + .reduce((prev, curr) => [...prev, ...lookUpExternal(curr)], []); + +const registerIntegrations = (integrations) => + Object.entries(integrations).reduce((prev, [tag, integrationConfig]: any) => { + consola.info(`- Loading: ${tag} ${integrationConfig.location}`); + + const apiClient = require(integrationConfig.location); + const rawExtensions = createRawExtensions(apiClient, integrationConfig); + const extensions = createExtensions(rawExtensions); + + extensions.forEach(({ name }) => { + consola.info(`- Loading: ${tag} extension: ${name}`); + }); + + consola.success(`- Integration: ${tag} loaded!`); + + return { + ...prev, + [tag]: { + apiClient, + configuration: integrationConfig.configuration, + extensions + } + }; + }, {}); + +export { registerIntegrations }; diff --git a/yarn.lock b/yarn.lock index fd181ea4815..e569624e8c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5217,6 +5217,11 @@ connect@^3.7.0: parseurl "~1.3.3" utils-merge "1.0.1" +consola@2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + consola@^1.4.5: version "1.4.5" resolved "https://registry.yarnpkg.com/consola/-/consola-1.4.5.tgz#09732d07cb50af07332e54e0f42fafb92b962c4a" From 73cf65cf04dc41cb8844b3899b3493b730b56043 Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Thu, 11 Feb 2021 09:18:03 +0100 Subject: [PATCH 4/7] fix test, remove unused filed --- .../factories/apiClientFactory.spec.ts | 16 +++++---- .../middleware/createMiddleware.spec.ts | 31 ----------------- packages/core/core/package.json | 6 ++-- packages/core/core/rollup-server.config.js | 33 ------------------- ...llup-client.config.js => rollup.config.js} | 0 packages/core/core/src/server/index.ts | 1 - packages/core/core/src/server/memory/index.ts | 20 ----------- packages/core/core/src/utils/context/index.ts | 6 ++-- 8 files changed, 15 insertions(+), 98 deletions(-) delete mode 100644 packages/core/core/__tests__/middleware/createMiddleware.spec.ts delete mode 100644 packages/core/core/rollup-server.config.js rename packages/core/core/{rollup-client.config.js => rollup.config.js} (100%) delete mode 100644 packages/core/core/src/server/index.ts delete mode 100644 packages/core/core/src/server/memory/index.ts diff --git a/packages/core/core/__tests__/factories/apiClientFactory.spec.ts b/packages/core/core/__tests__/factories/apiClientFactory.spec.ts index aed9841ed2a..d63269a83ef 100644 --- a/packages/core/core/__tests__/factories/apiClientFactory.spec.ts +++ b/packages/core/core/__tests__/factories/apiClientFactory.spec.ts @@ -48,11 +48,12 @@ describe('[CORE - factories] apiClientFactory', () => { }); it('Should run given extensions', () => { - const extensionFns = { - beforeCreate: jest.fn(a => a), - afterCreate: jest.fn(a => a) + const beforeCreate = jest.fn(a => a); + const afterCreate = jest.fn(a => a); + const extension = { + name: 'extTest', + hooks: () => ({ beforeCreate, afterCreate }) }; - const extension = () => extensionFns; const params = { onCreate: jest.fn((config) => ({ config })), @@ -61,10 +62,11 @@ describe('[CORE - factories] apiClientFactory', () => { }; const { createApiClient } = apiClientFactory(params as any); + const extensions = (createApiClient as any)._predefinedExtensions; - createApiClient.bind({ middleware: { req: null, res: null } })({}); + createApiClient.bind({ middleware: { req: null, res: null, extensions } })({}); - expect(extensionFns.beforeCreate).toHaveBeenCalled(); - expect(extensionFns.afterCreate).toHaveBeenCalled(); + expect(beforeCreate).toHaveBeenCalled(); + expect(afterCreate).toHaveBeenCalled(); }); }); diff --git a/packages/core/core/__tests__/middleware/createMiddleware.spec.ts b/packages/core/core/__tests__/middleware/createMiddleware.spec.ts deleted file mode 100644 index 8a364c87615..00000000000 --- a/packages/core/core/__tests__/middleware/createMiddleware.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { createMiddleware } from '../../src/server'; - -jest.mock('express', () => ({ - __esModule: true, - json: jest.fn(), - default: () => ({ - use: () => {} - }) -})); - -describe('[CORE - server] createMiddleware', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('creates middleware', () => { - const { middleware } = createMiddleware({} as any); - - expect(middleware.handler).toBeInstanceOf(Object); - expect(middleware.path).toEqual('/api'); - }); - - it('extends middleware', () => { - const extendApi = jest.fn(); - const { extend } = createMiddleware({ apiMiddleware: { extend: extendApi } }); - - extend(extendApi); - - expect(extendApi).toBeCalledTimes(2); - }); -}); diff --git a/packages/core/core/package.json b/packages/core/core/package.json index 937d6ef416d..cd9513c6c6c 100644 --- a/packages/core/core/package.json +++ b/packages/core/core/package.json @@ -7,10 +7,8 @@ "mainServer": "server/index.js", "types": "lib/src/index.d.ts", "scripts": { - "build:client": "rollup -c rollup-client.config.js", - "build:server": "rollup -c rollup-server.config.js", - "build": "yarn build:client && yarn build:server", - "dev": "rollup -c rollup-client.config.js -w", + "build": "rimraf lib && rollup -c rollup.config.js", + "dev": "rimraf lib && rollup -c rollup.config.js -w", "prepublish": "yarn build" }, "dependencies": { diff --git a/packages/core/core/rollup-server.config.js b/packages/core/core/rollup-server.config.js deleted file mode 100644 index 4258b445601..00000000000 --- a/packages/core/core/rollup-server.config.js +++ /dev/null @@ -1,33 +0,0 @@ -import pkg from './package.json'; -import typescript from 'rollup-plugin-typescript2'; -import replace from '@rollup/plugin-replace'; - -export function generateBaseConfig(pkg) { - return { - input: 'src/server/index.ts', - output: [ - { - file: pkg.mainServer, - format: 'cjs', - sourcemap: true - } - ], - external: [ - ...Object.keys(pkg.dependencies || {}) - ], - plugins: [ - typescript({ - // eslint-disable-next-line global-require - typescript: require('typescript') - }), - replace({ - __DEV__: process.env.NODE_ENV === 'development', - delimiters: ['', ''] - }) - ] - }; -} - -const baseConfig = generateBaseConfig(pkg); - -export default baseConfig; diff --git a/packages/core/core/rollup-client.config.js b/packages/core/core/rollup.config.js similarity index 100% rename from packages/core/core/rollup-client.config.js rename to packages/core/core/rollup.config.js diff --git a/packages/core/core/src/server/index.ts b/packages/core/core/src/server/index.ts deleted file mode 100644 index 3854f9c4eef..00000000000 --- a/packages/core/core/src/server/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './memory'; diff --git a/packages/core/core/src/server/memory/index.ts b/packages/core/core/src/server/memory/index.ts deleted file mode 100644 index 63ae418a073..00000000000 --- a/packages/core/core/src/server/memory/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from 'fs-extra'; -import merge from 'lodash-es/merge'; - -const createMemory = (rootDir) => { - const memoryFile = rootDir + '/.vsf/mem.json'; - - fs.ensureFileSync(memoryFile); - - const read = () => fs.readJsonSync(memoryFile, { throws: false }); - - const save = (content) => { - const prevContent = read(); - - fs.outputJsonSync(memoryFile, merge(prevContent, content)); - }; - - return { read, save }; -}; - -export { createMemory }; diff --git a/packages/core/core/src/utils/context/index.ts b/packages/core/core/src/utils/context/index.ts index dd41433aca6..4f936f6f495 100644 --- a/packages/core/core/src/utils/context/index.ts +++ b/packages/core/core/src/utils/context/index.ts @@ -15,7 +15,9 @@ const configureContext = (config: ContextConfiguration) => { useVSFContext = config.useVSFContext || useVSFContext; }; -const NOP = (x) => x; +const nopBefore = ({ args }) => args; +const nopAfter = ({ response }) => response; + const applyContextToApi = ( api: Record, context: any, @@ -25,7 +27,7 @@ const applyContextToApi = ( * It's useful in extensions, when someone don't want to inject into changing arguments or the response, * in that case, we use default function, to handle that scenario - NOP */ - hooks: ApplyingContextHooks = { before: NOP, after: NOP } + hooks: ApplyingContextHooks = { before: nopBefore, after: nopAfter } ) => Object.entries(api) .reduce((prev, [callName, fn]: any) => ({ From cb480fb9e2472897de78ca867d8c32d89c2bf287 Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Thu, 11 Feb 2021 09:23:41 +0100 Subject: [PATCH 5/7] cr --- packages/commercetools/composables/src/index.ts | 12 ------------ packages/commercetools/theme/.gitignore | 1 - packages/commercetools/theme/middleware.config.js | 9 --------- packages/core/core/package.json | 3 +-- packages/core/core/src/factories/apiClientFactory.ts | 12 +++++++----- packages/core/middleware/nuxt/index.js | 2 +- 6 files changed, 9 insertions(+), 30 deletions(-) diff --git a/packages/commercetools/composables/src/index.ts b/packages/commercetools/composables/src/index.ts index 1e792a1b9a0..8405c4cc7be 100644 --- a/packages/commercetools/composables/src/index.ts +++ b/packages/commercetools/composables/src/index.ts @@ -15,19 +15,7 @@ import { track } from '@vue-storefront/core'; track('VSFCommercetools'); -const extensions = [ - { - extendApi: { - testFunction2: async (context) => { - console.log('test function2 called', context); - return { test: 2 }; - } - } - } -]; - export { - extensions, useCategory, useProduct, useCart, diff --git a/packages/commercetools/theme/.gitignore b/packages/commercetools/theme/.gitignore index 0688d885742..931abe20328 100644 --- a/packages/commercetools/theme/.gitignore +++ b/packages/commercetools/theme/.gitignore @@ -67,7 +67,6 @@ typings/ # nuxt.js build output .nuxt -.vsf # theme _theme diff --git a/packages/commercetools/theme/middleware.config.js b/packages/commercetools/theme/middleware.config.js index 491786e31c8..834ffc13788 100644 --- a/packages/commercetools/theme/middleware.config.js +++ b/packages/commercetools/theme/middleware.config.js @@ -28,12 +28,3 @@ module.exports = { } } }; - -/** -extensions -> function: (extensions) => extensions + -extendApi -> extendApiMethods -lifecycle: before / after: config -> configuration -apiClientPackage -> location -add `name` do extension type -lifecycle -> hooks -*/ diff --git a/packages/core/core/package.json b/packages/core/core/package.json index cd9513c6c6c..8b8b44513da 100644 --- a/packages/core/core/package.json +++ b/packages/core/core/package.json @@ -16,8 +16,7 @@ "vue": "^2.6.11", "lodash-es": "^4.17.15", "express": "^4.17.1", - "axios": "0.21.1", - "fs-extra": "^9.1.0" + "axios": "0.21.1" }, "devDependencies": { "@vue/test-utils": "^1.0.0-beta.30", diff --git a/packages/core/core/src/factories/apiClientFactory.ts b/packages/core/core/src/factories/apiClientFactory.ts index 2b44b27c4e0..15f5ec5584e 100644 --- a/packages/core/core/src/factories/apiClientFactory.ts +++ b/packages/core/core/src/factories/apiClientFactory.ts @@ -2,17 +2,19 @@ import { ApiClientFactoryParams, ApiClientConfig, ApiInstance, ApiClientFactory, import { applyContextToApi } from './../utils/context'; import { Logger } from './../utils'; +const isFn = (x) => typeof x === 'function'; + const apiClientFactory = (factoryParams: ApiClientFactoryParams): ApiClientFactory => { function createApiClient (config: any, customApi: any = {}): ApiInstance { const rawExtensions: ApiClientExtension[] = this?.middleware?.extensions || []; const lifecycles = Object.values(rawExtensions) - .filter(ext => typeof ext.hooks === 'function') + .filter(ext => isFn(ext.hooks)) .map(({ hooks }) => hooks(this?.middleware?.req, this?.middleware?.res)); const extendedApis = Object.keys(rawExtensions) .reduce((prev, curr) => ({ ...prev, ...rawExtensions[curr].extendApiMethods }), customApi); const _config = lifecycles - .filter(ext => ext.beforeCreate) + .filter(ext => isFn(ext.beforeCreate)) .reduce((prev, curr) => curr.beforeCreate({ configuration: prev }), config); const settings = factoryParams.onCreate ? factoryParams.onCreate(_config) : { config, client: config.client }; @@ -20,15 +22,15 @@ const apiClientFactory = (f Logger.debug('apiClientFactory.create', settings); settings.config = lifecycles - .filter(ext => ext.afterCreate) + .filter(ext => isFn(ext.afterCreate)) .reduce((prev, curr) => curr.afterCreate({ configuration: prev }), settings.config); const extensionHooks = { before: (params) => lifecycles - .filter(e => e.beforeCall) + .filter(e => isFn(e.beforeCall)) .reduce((args, e) => e.beforeCall({ ...params, configuration: settings.config, args}), params.args), after: (params) => lifecycles - .filter(e => e.afterCall) + .filter(e => isFn(e.afterCall)) .reduce((response, e) => e.afterCall({ ...params, configuration: settings.config, response }), params.response) }; diff --git a/packages/core/middleware/nuxt/index.js b/packages/core/middleware/nuxt/index.js index 79bd0cc093d..9a96d0c7045 100644 --- a/packages/core/middleware/nuxt/index.js +++ b/packages/core/middleware/nuxt/index.js @@ -3,7 +3,7 @@ const { createServer } = require('@vue-storefront/middleware'); module.exports = function VueStorefrontMiddleware () { const { integrations } = require(this.nuxt.options.rootDir + '/middleware.config.js'); const handler = createServer({ integrations }); - const serverMiddleware = {path: '/api', handler }; + const serverMiddleware = { path: '/api', handler }; this.addServerMiddleware(serverMiddleware); }; From 640e0dafff762758ef0198e7dfff93ee08260c9a Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Thu, 11 Feb 2021 09:25:40 +0100 Subject: [PATCH 6/7] cr --- packages/core/middleware/src/integrations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/middleware/src/integrations.ts b/packages/core/middleware/src/integrations.ts index 715582f9969..2d71baf3590 100644 --- a/packages/core/middleware/src/integrations.ts +++ b/packages/core/middleware/src/integrations.ts @@ -2,8 +2,8 @@ import consola from 'consola'; const createRawExtensions = (apiClient, integrationConfig) => { const extensionsCreateFn = integrationConfig.extensions; - const prederinedExtensions = apiClient.createApiClient._predefinedExtensions; - return extensionsCreateFn ? extensionsCreateFn(prederinedExtensions) : prederinedExtensions; + const predefinedExtensions = apiClient.createApiClient._predefinedExtensions; + return extensionsCreateFn ? extensionsCreateFn(predefinedExtensions) : predefinedExtensions; }; const lookUpExternal = (curr) => typeof curr === 'string' ? require(curr) : [curr]; From 53b5e303a7f66b6c7c59e12aa97988593bb4f226 Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Thu, 11 Feb 2021 09:56:48 +0100 Subject: [PATCH 7/7] cr --- packages/commercetools/theme/package.json | 1 + packages/core/middleware/package.json | 2 +- yarn.lock | 10 ---------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/commercetools/theme/package.json b/packages/commercetools/theme/package.json index 7d12ef8b7c1..db27f26f044 100644 --- a/packages/commercetools/theme/package.json +++ b/packages/commercetools/theme/package.json @@ -20,6 +20,7 @@ "@vue-storefront/commercetools": "^1.1.2", "@vue-storefront/nuxt": "^2.2.1", "@vue-storefront/nuxt-theme": "^2.2.1", + "@vue-storefront/middleware": "^2.2.1", "cookie-universal-nuxt": "^2.1.3", "core-js": "^2.6.5", "nuxt": "^2.13.3", diff --git a/packages/core/middleware/package.json b/packages/core/middleware/package.json index c1e4ab2afbb..94fe5eb0107 100644 --- a/packages/core/middleware/package.json +++ b/packages/core/middleware/package.json @@ -1,6 +1,6 @@ { "name": "@vue-storefront/middleware", - "version": "2.1.1", + "version": "2.2.1", "description": "", "main": "lib/index.cjs.js", "module": "lib/index.es.js", diff --git a/yarn.lock b/yarn.lock index e569624e8c6..867cd46e1a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7394,16 +7394,6 @@ fs-extra@^9.0.0, fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^1.0.0" -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-memo@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/fs-memo/-/fs-memo-1.2.0.tgz#a2ec3be606b902077adbb37ec529c5ec5fb2e037"