├── .all-contributorsrc ├── .changeset ├── README.md └── config.json ├── .config ├── .eslintignore ├── .eslintrc.json ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── 1.bug-report.yml │ ├── 2.documentation-issue.yml │ ├── 3.feature-request.yml │ ├── 4.question.yml │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md ├── actions │ └── install-dependencies │ │ └── action.yml └── workflows │ ├── continuous-delivery.yml │ ├── continuous-integration.yml │ └── deploy-docs.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .ncurc.json ├── .node-version ├── .prettierrc ├── .vscode └── settings.json ├── .yarn └── releases │ └── yarn-1.19.0.cjs ├── .yarnrc ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── License.md ├── README.md ├── alokai-odoo.png ├── api-extractor.base.json ├── commitlint.config.js ├── docs ├── .gitignore ├── .nuxt │ ├── app.config.mjs │ ├── components.d.ts │ ├── imports.d.ts │ ├── manifest │ │ └── latest.json │ ├── mdc-configs.mjs │ ├── mdc-highlighter.mjs │ ├── mdc-imports.mjs │ ├── module │ │ ├── nuxt-robots.d.ts │ │ ├── nuxt-schema-org.d.ts │ │ ├── nuxt-seo-experiments.assets.d.ts │ │ └── nuxt-seo-experiments.d.ts │ ├── nuxt.d.ts │ ├── nuxt.json │ ├── schema │ │ ├── nuxt.schema.d.ts │ │ └── nuxt.schema.json │ ├── tsconfig.json │ ├── tsconfig.server.json │ └── types │ │ ├── app.config.d.ts │ │ ├── content.d.ts │ │ ├── imports.d.ts │ │ ├── layouts.d.ts │ │ ├── middleware.d.ts │ │ ├── nitro-config.d.ts │ │ ├── nitro-imports.d.ts │ │ ├── nitro-nuxt.d.ts │ │ ├── nitro-routes.d.ts │ │ ├── nitro.d.ts │ │ ├── plugins.d.ts │ │ ├── schema.d.ts │ │ └── vue-shim.d.ts ├── content │ ├── 1.index.md │ ├── 2.assistant.md │ ├── 2.general │ │ ├── 1.index.md │ │ ├── 2.testing-local.md │ │ ├── 3.new-project.md │ │ ├── _dir.yml │ │ └── img │ │ │ └── demo.png │ ├── 3.middleware │ │ ├── 1.index.md │ │ ├── _dir.yml │ │ └── img │ │ │ ├── extensions │ │ │ └── data-flow.svg │ │ │ └── overview │ │ │ └── architecture.svg │ ├── 4.sdk │ │ ├── 1.index.md │ │ └── _dir.yml │ ├── _dir.yml │ └── contributing │ │ ├── 1.index.md │ │ └── _dir.yml ├── nuxt.config.ts ├── package.json ├── tailwind.config.js ├── tsconfig.json └── yarn.lock ├── jest.base.config.js ├── lerna.json ├── package.json ├── packages ├── nuxt-layer │ ├── .editorconfig │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .nuxtrc │ ├── README.md │ ├── app.config.ts │ ├── app.vue │ ├── nuxt.config.ts │ ├── package.json │ ├── server │ │ ├── api │ │ │ └── odoo │ │ │ │ ├── mutation.post.ts │ │ │ │ └── query.post.ts │ │ ├── middleware │ │ │ └── apolloClient.ts │ │ ├── mutations │ │ │ ├── ChangePasswordMutation.ts │ │ │ ├── CreateNewAccountMutation.ts │ │ │ ├── LoginMutation.ts │ │ │ ├── LogoutMutation.ts │ │ │ ├── SendResetPasswordMutation.ts │ │ │ ├── UpdatePasswordMutation.ts │ │ │ └── index.ts │ │ ├── queries │ │ │ ├── LoadUserQuery.ts │ │ │ ├── ProductVariantQuery.ts │ │ │ ├── api │ │ │ │ └── cartRedis │ │ │ │ │ ├── helper.ts │ │ │ │ │ ├── redisAddItemToCart.ts │ │ │ │ │ ├── redisLoadCart.ts │ │ │ │ │ ├── redisRemoveItem.ts │ │ │ │ │ ├── redisSyncCartToOdoo.ts │ │ │ │ │ └── redisUpdateItemQty.ts │ │ │ ├── fragments │ │ │ │ ├── index.ts │ │ │ │ └── partnerFragment.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── utils │ │ │ └── getCached.ts │ └── tsconfig.json ├── sdk-api-client │ ├── .gitignore │ ├── .npmignore │ ├── __mocks__ │ │ ├── context.mock.ts │ │ ├── customQueries │ │ │ ├── customQueryCategoryListWithoutChild.ts │ │ │ ├── customQueryCategoryWithoutChild.ts │ │ │ ├── customQueryCountryListWithoutState.ts │ │ │ ├── customQueryCountryWithoutState.ts │ │ │ ├── customQueryFullProductTemplate.ts │ │ │ ├── customQueryFullProductTemplateListWithoutPrice.ts │ │ │ └── customQueryProductVariant.ts │ │ ├── data │ │ │ ├── address.json │ │ │ ├── category.json │ │ │ ├── categoryList.json │ │ │ ├── country.json │ │ │ ├── countryList.json │ │ │ ├── customQueryCategoryListWithChilds.json │ │ │ ├── customQueryCategoryWithoutChild.json │ │ │ ├── customQueryCountryListWithoutState.json │ │ │ ├── customQueryFullProductTemplate.json │ │ │ ├── customQueryProductTemplateWithoutPrice.json │ │ │ ├── customQueryProductVariant.json │ │ │ ├── mailingContacts.json │ │ │ ├── mailingLists.json │ │ │ ├── orders.json │ │ │ ├── productTemplate.json │ │ │ ├── productTemplateList.json │ │ │ ├── productVariant.json │ │ │ └── user.json │ │ ├── handlers.ts │ │ ├── server.ts │ │ └── setupTests.ts │ ├── __tests__ │ │ └── api │ │ │ ├── mutation.spec.ts │ │ │ └── query.spec.ts │ ├── api-extractor.json │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── api │ │ │ ├── index.ts │ │ │ ├── mutation │ │ │ │ └── index.ts │ │ │ └── query │ │ │ │ └── index.ts │ │ ├── composables │ │ │ └── useCartRedis │ │ │ │ └── index.ts │ │ ├── extensions │ │ │ └── sessionHeaderExtension .ts │ │ ├── index.server.ts │ │ ├── index.ts │ │ ├── setup │ │ │ ├── clientSetup.ts │ │ │ └── logBuilder.ts │ │ └── types │ │ │ ├── api │ │ │ ├── endpoints.ts │ │ │ └── index.ts │ │ │ ├── config │ │ │ └── index.ts │ │ │ ├── context │ │ │ └── index.ts │ │ │ └── index.ts │ └── tsconfig.json └── sdk │ ├── .gitignore │ ├── .npmignore │ ├── __tests__ │ ├── integration │ │ ├── __config__ │ │ │ ├── README.md │ │ │ ├── jest.const.ts │ │ │ ├── jest.setup.global.ts │ │ │ ├── jest.setup.ts │ │ │ ├── jest.teardown.global.ts │ │ │ └── sdk.config.ts │ │ ├── __nock-fixtures__ │ │ │ ├── [Integration-Boilerplate-SDK][integration]-exampleMethod-makes-a-request-to-the-middleware │ │ │ ├── [Integration-Boilerplate-SDK][unit]-exampleMethod-makes-a-call-to-API-Middleware-with-the-right-params │ │ │ ├── [Integration-Boilerplate-SDK][unit]-exampleMethod-makes-a-single-call-to-API-Middleware │ │ │ └── [Integration-Boilerplate-SDK][unit]-exampleMethod-throws-an-exception-in-case-of-network-error │ │ └── exampleMethod.integration.spec.ts │ └── unit │ │ └── exampleMethod.unit.spec.ts │ ├── api-extractor.json │ ├── jest.config.integration.js │ ├── jest.config.unit.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ ├── api-extractor.data.ts │ ├── client │ │ └── index.ts │ ├── connector.ts │ ├── index.ts │ ├── methods │ │ ├── index.ts │ │ ├── mutation │ │ │ └── index.ts │ │ ├── query-no-cache │ │ │ └── index.ts │ │ └── query │ │ │ └── index.ts │ └── types │ │ ├── MethodOptions.ts │ │ ├── index.ts │ │ └── options.ts │ └── tsconfig.json ├── playground-nuxt └── app │ ├── .env.example │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── app.vue │ ├── assets │ └── css │ │ └── tailwind.css │ ├── codegen.ts │ ├── components │ ├── Breadcrumb.vue │ ├── Card.vue │ ├── Footer.vue │ ├── Grid.vue │ ├── MainBanner.vue │ ├── MegaMenu.vue │ ├── Navbar.vue │ ├── Newsletter.vue │ ├── NumberTitle.vue │ ├── ProductCard.vue │ ├── TabBrowser.vue │ ├── TheContent.vue │ ├── cart │ │ ├── Drawer.vue │ │ └── Product.vue │ └── category │ │ └── FilterSidebar.vue │ ├── graphql │ ├── gql │ │ ├── fragment-masking.ts │ │ ├── gql.ts │ │ ├── graphql.ts │ │ └── index.ts │ ├── index.ts │ └── types.ts │ ├── layouts │ └── default.vue │ ├── nuxt.config.ts │ ├── package.json │ ├── pages │ ├── category │ │ └── [id].vue │ └── index.vue │ ├── plugins │ ├── 1.winston.server.ts │ ├── 2.getImage.ts │ └── 3.sdk.ts │ ├── public │ ├── favicon.ico │ └── img │ │ └── logo.svg │ ├── server │ ├── api │ │ └── odoo │ │ │ ├── mutation.post.ts │ │ │ └── query.post.ts │ ├── middleware │ │ └── apolloClient.ts │ ├── mutations │ │ ├── ChangePasswordMutation.ts │ │ ├── CreateNewAccountMutation.ts │ │ ├── LoginMutation.ts │ │ ├── LogoutMutation.ts │ │ ├── SendResetPasswordMutation.ts │ │ ├── UpdatePasswordMutation.ts │ │ └── index.ts │ ├── queries │ │ ├── GetCategories.ts │ │ ├── GetProductTemplateList.ts │ │ ├── LoadUserQuery.ts │ │ ├── ProductVariantQuery.ts │ │ ├── fragments │ │ │ ├── index.ts │ │ │ └── partnerFragment.ts │ │ └── index.ts │ ├── tsconfig.json │ └── utils │ │ └── getCached.ts │ ├── tailwind.config.ts │ └── tsconfig.json ├── playground-other └── server │ ├── .eslintrc.js │ ├── middleware.config.ts │ ├── mutations │ ├── LoginMutation.ts │ └── index.ts │ ├── nodemon.json │ ├── package.json │ ├── queries │ ├── ProductVariantQuery.ts │ └── index.ts │ └── src │ ├── index.ts │ └── types.ts ├── rollup.base.config.js ├── scripts ├── lib │ └── publishNpm.js ├── publishApi.js └── publishComposable.js └── tsconfig.base.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "odoo", 3 | "projectOwner": "vuestorefront", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 80, 10 | "commit": false, 11 | "commitConvention": "angular", 12 | "contributors": [ 13 | { 14 | "login": "cpintofonseca", 15 | "name": "cpintofonseca", 16 | "avatar_url": "https://avatars.githubusercontent.com/u/21957046?v=4", 17 | "profile": "http://www.promptequation.com/", 18 | "contributions": [ 19 | "code" 20 | ] 21 | }, 22 | { 23 | "login": "SDMonteiro", 24 | "name": "SDMonteiro", 25 | "avatar_url": "https://avatars.githubusercontent.com/u/68434298?v=4", 26 | "profile": "https://github.com/SDMonteiro", 27 | "contributions": [ 28 | "code" 29 | ] 30 | }, 31 | { 32 | "login": "brunoodoogap", 33 | "name": "brunoodoogap", 34 | "avatar_url": "https://avatars.githubusercontent.com/u/84967663?v=4", 35 | "profile": "https://github.com/brunoodoogap", 36 | "contributions": [ 37 | "code" 38 | ] 39 | }, 40 | { 41 | "login": "dduarte-odoogap", 42 | "name": "Diogo Duarte", 43 | "avatar_url": "https://avatars.githubusercontent.com/u/18329970?v=4", 44 | "profile": "https://github.com/dduarte-odoogap", 45 | "contributions": [ 46 | "code" 47 | ] 48 | } 49 | ], 50 | "contributorsPerLine": 5 51 | } 52 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.config: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront-community/odoo/c06fd20e9bb5e7c203f09d86ca06df397ffc016a/.config -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/**/* 2 | **/lib/* 3 | packages/theme/_theme 4 | packages/theme/.nuxt 5 | **/tests/* -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:@typescript-eslint/eslint-recommended", 6 | "plugin:@typescript-eslint/recommended" 7 | ], 8 | "parser": "vue-eslint-parser", 9 | "parserOptions": { 10 | "parser": "@typescript-eslint/parser", 11 | "ecmaVersion": 2017, 12 | "sourceType": "module" 13 | }, 14 | "plugins": [ 15 | "vue", 16 | "@typescript-eslint" 17 | ], 18 | "env": { 19 | "browser": true, 20 | "commonjs": true, 21 | "node": true, 22 | "jest": true 23 | }, 24 | "globals": { 25 | "Promise": true, 26 | "process": true, 27 | "console": true, 28 | "Set": true, 29 | "Intl": true 30 | }, 31 | "rules": { 32 | "eqeqeq": 2, 33 | "no-use-before-define": [ 34 | 1, 35 | { 36 | "functions": false 37 | } 38 | ], 39 | "no-undef": 2, 40 | "no-unused-vars": 0, 41 | "brace-style": 2, 42 | "no-mixed-spaces-and-tabs": 2, 43 | "no-empty-pattern": 0, 44 | "key-spacing": 2, 45 | "comma-spacing": 2, 46 | "array-bracket-spacing": 2, 47 | "space-in-parens": 2, 48 | "no-trailing-spaces": 2, 49 | "comma-dangle": 2, 50 | "comma-style": 2, 51 | "space-infix-ops": 2, 52 | "keyword-spacing": 2, 53 | "space-before-blocks": 2, 54 | "spaced-comment": 2, 55 | "no-multiple-empty-lines": [ 56 | 2, 57 | { 58 | "max": 1 59 | } 60 | ], 61 | "complexity": 2, 62 | "max-depth": [ 63 | 2, 64 | { 65 | "max": 3 66 | } 67 | ], 68 | "default-case": 0, 69 | "dot-notation": 2, 70 | "no-alert": 2, 71 | "no-empty-function": 0, 72 | "no-eval": 2, 73 | "no-extend-native": 2, 74 | "no-extra-bind": 2, 75 | "no-implicit-coercion": 2, 76 | "no-multi-spaces": 2, 77 | "no-useless-return": 2, 78 | "no-console": 0, 79 | "global-require": 1, 80 | "camelcase": 2, 81 | "computed-property-spacing": 2, 82 | "consistent-this": 2, 83 | "func-call-spacing": 2, 84 | "func-names": 2, 85 | "func-name-matching": 2, 86 | "func-style": [ 87 | 2, 88 | "declaration", 89 | { 90 | "allowArrowFunctions": true 91 | } 92 | ], 93 | "indent": [ 94 | 2, 95 | 2, 96 | { 97 | "SwitchCase": 1 98 | } 99 | ], 100 | "line-comment-position": 2, 101 | "linebreak-style": [ 102 | 2, 103 | "unix" 104 | ], 105 | "lines-around-comment": 2, 106 | "max-statements-per-line": 2, 107 | "no-lonely-if": 2, 108 | "prefer-const": 2, 109 | "no-mixed-operators": 2, 110 | "no-multi-assign": 2, 111 | "no-unneeded-ternary": 2, 112 | "object-property-newline": [ 113 | 2, 114 | { 115 | "allowAllPropertiesOnSameLine": true 116 | } 117 | ], 118 | "operator-linebreak": 2, 119 | "quote-props": [ 120 | 2, 121 | "as-needed" 122 | ], 123 | "quotes": [ 124 | 2, 125 | "single" 126 | ], 127 | "semi": 2, 128 | "semi-spacing": 2, 129 | "one-var": [ 130 | 2, 131 | "never" 132 | ], 133 | "eol-last": 2, 134 | "newline-after-var": 0, 135 | "no-var": 2, 136 | "@typescript-eslint/no-empty-function": 0, 137 | "no-case-declarations": 0, 138 | "@typescript-eslint/no-var-requires": 0, 139 | "@typescript-eslint/no-explicit-any": 0, 140 | "@typescript-eslint/explicit-function-return-type": 0, 141 | "@typescript-eslint/no-unused-vars": 0, 142 | "@typescript-eslint/ban-ts-ignore": 0, 143 | "@typescript-eslint/explicit-module-boundary-types": "off" 144 | }, 145 | "overrides": [ 146 | { 147 | "files": "*.ts", 148 | "rules": { 149 | "no-undef": "off", 150 | "no-unused-vars": "off" 151 | } 152 | }, 153 | { 154 | "files": [ 155 | "*.ts", 156 | "*.tsx" 157 | ], 158 | "rules": { 159 | "@typescript-eslint/explicit-module-boundary-types": [ 160 | "error" 161 | ] 162 | } 163 | } 164 | ] 165 | } -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @dduarte-odoogap 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to @vuestorefront/odoo 2 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 3 | 4 | - Reporting a bug 5 | - Discussing the current state of the code 6 | - Submitting a fix 7 | - Proposing new features 8 | - Becoming a maintainer 9 | 10 | ## We Develop with Github 11 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 12 | 13 | ## Any contributions you make will be under the MIT Software License 14 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. 15 | 16 | ## Report bugs using Github's [issues](https://github.com/vuestorefront-community/odoo/issues) 17 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/vuestorefront-community/odoo/issues/new); it's that easy! 18 | 19 | ## Write bug reports with detail, background, and sample code 20 | **Great Bug Reports** tend to have: 21 | 22 | - A quick summary and/or background 23 | - Steps to reproduce 24 | - Be specific! 25 | - Give sample code if you can. 26 | - What you expected would happen 27 | - What actually happens 28 | - The environment you're running the application 29 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 30 | 31 | People *love* thorough bug reports. I'm not even kidding. 32 | 33 | ## License 34 | By contributing, you agree that your contributions will be licensed under its MIT License. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1.bug-report.yml: -------------------------------------------------------------------------------- 1 | name: "🐛 Bug report" 2 | description: Report errors or unexpected behavior 3 | labels: 4 | - bug 5 | - triage-needed 6 | title: '[Bug]: ' 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this bug report, please make sure to [search for existing issues](https://github.com/vuestorefront-community/odoo/issues) before filing a new one! 12 | - type: textarea 13 | id: whattoexpect 14 | attributes: 15 | label: Expected Behavior 16 | placeholder: What were you expecting? 17 | validations: 18 | required: false 19 | - type: textarea 20 | id: whathappened 21 | attributes: 22 | label: Actual Behavior 23 | placeholder: What happened instead?? 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: solution 28 | attributes: 29 | label: Possible Solution 30 | description: Also, if possible provide the information on how to implement the solution. 31 | placeholder: Do you have any possible solution or fix for this bug? 32 | validations: 33 | required: false 34 | - type: textarea 35 | id: reproduce 36 | attributes: 37 | label: Steps to reproduce 38 | description: Please provide detailed instructions on how to reproduce. 39 | placeholder: How we can reproduce this bug? 40 | validations: 41 | required: false 42 | - type: input 43 | attributes: 44 | label: What version of odoo integration are you using? 45 | description: 'For example: 1.0.0' 46 | validations: 47 | required: true 48 | - type: input 49 | attributes: 50 | label: What version of Node.js are you using? 51 | description: 'For example: 12.0.0' 52 | validations: 53 | required: true 54 | - type: input 55 | attributes: 56 | label: What browser (and version) are you using? 57 | description: 'For example: Chrome, Safari' 58 | validations: 59 | required: true 60 | - type: input 61 | attributes: 62 | label: What operating system (and version) are you using? 63 | description: 'For example: macOS, Windows' 64 | validations: 65 | required: true 66 | - type: textarea 67 | id: logs 68 | attributes: 69 | label: Relevant log output 70 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 71 | render: shell 72 | - type: checkboxes 73 | id: terms 74 | attributes: 75 | label: Code of Conduct 76 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/vuestorefront-community/odoo/blob/master/CODE_OF_CONDUCT.md) 77 | options: 78 | - label: I agree to follow this project's Code of Conduct 79 | required: true 80 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2.documentation-issue.yml: -------------------------------------------------------------------------------- 1 | name: "📚 Documentation Issue" 2 | description: | 3 | Report issues in our documentation 4 | labels: 5 | - documentation 6 | - triage-needed 7 | body: 8 | - type: textarea 9 | attributes: 10 | label: Provide a description of requested docs changes 11 | placeholder: Briefly describe which document needs to be corrected. 12 | validations: 13 | required: true 14 | - type: checkboxes 15 | id: fixthebug 16 | attributes: 17 | label: Able to fix / change the documentation? 18 | description: Can you handle this change and create a Pull Request? 19 | options: 20 | - label: 'Yes' 21 | required: false 22 | - label: 'No' 23 | required: false 24 | - type: checkboxes 25 | id: terms 26 | attributes: 27 | label: Code of Conduct 28 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/vuestorefront-community/odoo/blob/master/CODE_OF_CONDUCT.md) 29 | options: 30 | - label: I agree to follow this project's Code of Conduct 31 | required: true 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3.feature-request.yml: -------------------------------------------------------------------------------- 1 | name: "🚀 Feature Request" 2 | description: Sugest a new feature request or improvement on the project 3 | title: '[Feature]: ' 4 | labels: 5 | - feature 6 | - enhancement 7 | - triage-needed 8 | 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | Please, provide as many information, and knowledge so the feature can be correctly designed and developed. 14 | - type: textarea 15 | id: suggestion 16 | attributes: 17 | label: How the project can be improved? 18 | description: What is the motivation for adding / enhancing this feature? Can you describe a concrete use case for this feature or why one of current ones should be enhanced. 19 | placeholder: Describe the motivation or the concrete use case 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: acceptcriterea 24 | attributes: 25 | label: What are the acceptance criteria? 26 | description: List the acceptance criteria for this task in a form of a list. 27 | value: '- [ ]' 28 | - type: textarea 29 | id: additionalinfo 30 | attributes: 31 | label: Additional information 32 | description: If you think that any additional information would be useful please provide them here. 33 | - type: checkboxes 34 | id: terms 35 | attributes: 36 | label: Code of Conduct 37 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/vuestorefront-community/odoo/blob/master/CODE_OF_CONDUCT.md) 38 | options: 39 | - label: I agree to follow this project's Code of Conduct 40 | required: true 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/4.question.yml: -------------------------------------------------------------------------------- 1 | name: "❓ Question / Basic Issue" 2 | description: | 3 | Do you have a question on the implementation or a basic issue 4 | labels: 5 | - triage-needed 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: If you are not sure how something works or want discuss something just describe your doubts. 10 | - type: textarea 11 | attributes: 12 | label: What is your question / Please describe your issue 13 | validations: 14 | required: true 15 | - type: input 16 | attributes: 17 | label: What version of odoo integration are you using? 18 | description: 'For example: 1.0.0' 19 | validations: 20 | required: true 21 | - type: checkboxes 22 | id: terms 23 | attributes: 24 | label: Code of Conduct 25 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/vuestorefront-community/vue-storefront/blob/master/CODE_OF_CONDUCT.md) 26 | options: 27 | - label: I agree to follow this project's Code of Conduct 28 | required: true 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord Chat 4 | url: https://discord.vuestorefront.io/ 5 | about: Ask questions and discuss with other Vue Storefront users in real time. 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Related Issue 7 | 8 | 9 | 10 | 11 | 12 | ## Motivation and Context 13 | 14 | 15 | ## How Has This Been Tested? 16 | 17 | 18 | 19 | 20 | ## Screenshots (if appropriate): 21 | 22 | ## Types of changes 23 | 24 | - [ ] Bug fix (non-breaking change which fixes an issue) 25 | - [ ] New feature (non-breaking change which adds functionality) 26 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 27 | 28 | ## Checklist: 29 | 30 | 31 | - [ ] My code follows the code style of this project. 32 | - [ ] My change requires a change to the documentation. 33 | - [ ] I have updated the documentation accordingly. 34 | - [ ] I have read the **CONTRIBUTING** document. 35 | - [ ] I have added tests to cover my changes. 36 | - [ ] All new and existing tests passed. 37 | -------------------------------------------------------------------------------- /.github/actions/install-dependencies/action.yml: -------------------------------------------------------------------------------- 1 | name: Install dependencies 2 | description: Install and cache all project dependencies 3 | runs: 4 | using: 'composite' 5 | steps: 6 | - name: Checkout code 7 | uses: actions/checkout@v3 8 | 9 | - name: Setup node 10 | uses: actions/setup-node@v1 11 | with: 12 | node-version: '20' 13 | 14 | - name: Get node modules cache 15 | id: yarn-cache 16 | uses: actions/cache@v3 17 | with: 18 | path: '**/node_modules' 19 | key: ${{ runner.os }}-yarn-new-${{ hashFiles('**/yarn.lock') }} 20 | 21 | - name: Install dependencies 22 | shell: bash 23 | if: steps.yarn-cache.outputs.cache-hit != 'true' 24 | run: | 25 | yarn --frozen-lockfile 26 | -------------------------------------------------------------------------------- /.github/workflows/continuous-delivery.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | NPM_USER: 7 | description: "repository NPM_USER secret passed on" 8 | required: false 9 | NPM_PASS: 10 | description: "repository NPM_PASS secret passed on" 11 | required: false 12 | NPM_EMAIL: 13 | description: "repository NPM_EMAIL secret passed on" 14 | required: false 15 | inputs: 16 | enterprise: 17 | description: "Flag to use enterprise registry" 18 | type: boolean 19 | required: false 20 | default: false 21 | 22 | defaults: 23 | run: 24 | shell: bash 25 | 26 | env: 27 | GCP_PROJECT_ID: 81559754996 28 | GCP_PROJECT_NAME: sf-artifacts-prod 29 | 30 | jobs: 31 | changelog: 32 | name: Changelog PR or Release 33 | runs-on: ubuntu-latest 34 | permissions: 35 | contents: write 36 | id-token: write 37 | pull-requests: write 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | 42 | - name: Setup Node 43 | uses: actions/setup-node@v3 44 | with: 45 | node-version-file: '.node-version' 46 | 47 | - if: ${{ inputs.enterprise }} 48 | id: auth 49 | name: Authenticate to Google Cloud 50 | uses: google-github-actions/auth@v1 51 | with: 52 | workload_identity_provider: projects/${{ env.GCP_PROJECT_ID }}/locations/global/workloadIdentityPools/sf-artifacts-prod-01/providers/sf-artifacts-prod-01 53 | service_account: workload-identity-federation@sf-artifacts-prod.iam.gserviceaccount.com 54 | token_format: access_token 55 | # The credentials file is created in repo root as an untracked file in Git 56 | # Changesets commits all files in the repo root when creating an automated bot PR 57 | # This causes plaintext credentials to show up in automated PRs 58 | # So let's not create this file at all 59 | create_credentials_file: false 60 | 61 | - if: ${{ inputs.enterprise }} 62 | name: Set Artifact Registry & Verdaccio tokens 63 | run: | 64 | # set token for Artifact Registry 65 | npm config set //europe-west1-npm.pkg.dev/${{ env.GCP_PROJECT_NAME }}/npm/:_authToken=${{ steps.auth.outputs.access_token }} 66 | # set token for Verdaccio 67 | npx npm-cli-login -u ${{ secrets.NPM_USER }} -p ${{ secrets.NPM_PASS }} -e ${{ secrets.NPM_EMAIL }} -r https://registrynpm.storefrontcloud.io 68 | 69 | - if: ${{ !inputs.enterprise }} 70 | run: npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_RELEASE_TOKEN }} 71 | 72 | - name: Install dependencies 73 | run: HUSKY=0 yarn --frozen-lockfile 74 | 75 | - name: Create Release Pull Request or Publish 76 | id: changesets 77 | uses: changesets/action@v1 78 | with: 79 | commit: "ci: release" 80 | title: "ci: release" 81 | env: 82 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 83 | 84 | - if: ${{ (inputs.enterprise == true) && (steps.changesets.outputs.hasChangesets == 'false') }} 85 | name: Publish in Artifact Registry 86 | run: | 87 | yarn build && \ 88 | npm config --location project set @vsf-enterprise:registry=https://europe-west1-npm.pkg.dev/${{ env.GCP_PROJECT_NAME }}/npm/ && \ 89 | yarn changeset publish 90 | 91 | # Remove this step once Verdaccio is removed by the Cloud Team for good. 92 | # It's possible to use `continue-on-error: true` here for max futureproofing, 93 | # but this also makes publishing failures show as successful despite errors, so I didn't do that 94 | - if: ${{ (inputs.enterprise == true) && (steps.changesets.outputs.hasChangesets == 'false') }} 95 | name: Publish in Verdaccio 96 | run: | 97 | npm config --location project set @vsf-enterprise:registry=https://registrynpm.storefrontcloud.io/ && \ 98 | yarn changeset publish 99 | 100 | 101 | - if: ${{ (inputs.enterprise == false) && (steps.changesets.outputs.hasChangesets == 'false') }} 102 | name: Publish 103 | run: yarn build && yarn changeset publish 104 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration.yml: -------------------------------------------------------------------------------- 1 | name: Continous Integration 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | enterprise: 7 | description: 'Flag to use enterprise registry' 8 | type: boolean 9 | required: false 10 | default: false 11 | 12 | node_version: 13 | description: 'Node versions to test' 14 | type: string 15 | required: false 16 | default: "['16']" 17 | 18 | defaults: 19 | run: 20 | shell: bash 21 | env: 22 | GCP_PROJECT_ID: 81559754996 23 | GCP_PROJECT_NAME: sf-artifacts-prod 24 | 25 | jobs: 26 | run-ci: 27 | name: Run CI 28 | runs-on: ubuntu-latest 29 | strategy: 30 | matrix: 31 | node_version: ${{ fromJson(inputs.node_version) }} 32 | permissions: 33 | contents: read 34 | id-token: write 35 | steps: 36 | - name: Expose github environment as shell variables 37 | env: 38 | SECRETS_CONTEXT: ${{ toJson(secrets) }} 39 | VARS_CONTEXT: ${{ toJson(vars) }} 40 | run: | 41 | # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable 42 | # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings 43 | EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) 44 | to_envs() { jq -r "to_entries[] | \"\(.key)<<$EOF\n\(.value)\n$EOF\n\""; } 45 | echo "$SECRETS_CONTEXT" | to_envs >> $GITHUB_ENV 46 | - name: Checkout code 🛎️ 47 | uses: actions/checkout@v3 48 | 49 | - name: Setup node 🏗️ 50 | uses: actions/setup-node@v3 51 | with: 52 | node-version: ${{ matrix.node_version }} 53 | 54 | - name: Get cache 🗄️ 55 | id: cache 56 | uses: actions/cache@v3 57 | with: 58 | path: '**/node_modules' 59 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} 60 | 61 | # IN-3457 62 | - id: auth 63 | name: Authenticate to Google Cloud 64 | if: ${{ inputs.enterprise }} 65 | uses: google-github-actions/auth@v1 66 | with: 67 | workload_identity_provider: projects/${{ env.GCP_PROJECT_ID }}/locations/global/workloadIdentityPools/sf-artifacts-prod-01/providers/sf-artifacts-prod-01 68 | service_account: workload-identity-federation@sf-artifacts-prod.iam.gserviceaccount.com 69 | token_format: access_token 70 | create_credentials_file: false 71 | 72 | - if: ${{ inputs.enterprise }} 73 | run: | 74 | npm config set //europe-west1-npm.pkg.dev/${{ env.GCP_PROJECT_NAME }}/npm/:_authToken=${{ steps.auth.outputs.access_token }} 75 | npm config set //registrynpm.storefrontcloud.io/:_authToken=${{ secrets.NPM_DEFAULT_ENTERPRISE_TOKEN }} 76 | 77 | - if: ${{ !inputs.enterprise }} 78 | run: npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_RELEASE_TOKEN }} 79 | 80 | - name: Install dependencies 81 | shell: bash 82 | if: steps.cache.outputs.cache-hit != true 83 | run: HUSKY=0 yarn --frozen-lockfile 84 | 85 | - name: Detect circular dependencies 🔄 86 | uses: vuestorefront/engineering-toolkit/github-actions/circular-dependencies@main 87 | with: 88 | filesPath: 'packages/**/*.{ts,vue}' 89 | 90 | - name: Check licenses 🧪 91 | uses: vuestorefront/engineering-toolkit/github-actions/check-licenses@main 92 | with: 93 | projectPath: ${{ github.workspace }} 94 | 95 | - name: Build project 96 | run: yarn build 97 | 98 | - name: Run tests 99 | run: yarn test 100 | 101 | - name: Upload test coverage 102 | uses: actions/upload-artifact@v3 103 | with: 104 | name: coverage-${{ runner.os }}-${{ github.event.pull_request.head.sha }} 105 | path: | 106 | coverage 107 | packages/**/coverage 108 | 109 | - name: Lint project 110 | run: yarn lint 111 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to Netlify 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/workflows/deploy-docs.yml' 9 | - 'docs/**/*' 10 | jobs: 11 | curl-job: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Deploy to Netlify 16 | run: | 17 | response=$(curl -o /dev/null -w "%{http_code}" -X POST https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_KEY }}?clear_cache=true) 18 | if [ "$response" -ne 200 ]; then 19 | echo "Failed with status $response" 20 | exit 1 21 | fi 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules 3 | .history 4 | # PyCache 5 | *.pyc 6 | 7 | # Logs 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # NPM config 14 | .npmrc 15 | 16 | # Yarn Integrity file 17 | .yarn-integrity 18 | 19 | # Rollup generate output 20 | lib 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | 25 | # Editor directories and files 26 | .idea 27 | .vscode 28 | 29 | # OS generated files 30 | .DS_STORE 31 | 32 | !scripts/lib 33 | 34 | # service workers 35 | sw.* 36 | 37 | build-docs-dd.sh -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: node:lts 2 | 3 | stages: 4 | - test 5 | - deploy_dev 6 | 7 | cache: 8 | paths: 9 | - node_modules/ 10 | 11 | test: 12 | stage: test 13 | script: 14 | - yarn 15 | - yarn build 16 | - yarn test 17 | 18 | deploy_dev: 19 | stage: deploy_dev 20 | only: 21 | - dev 22 | script: 23 | - yarn 24 | - yarn build 25 | 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "theme"] 2 | path = theme 3 | url = https://github.com/vuestorefront-community/template-odoo 4 | branch = dev 5 | [submodule "packages/theme"] 6 | path = packages/theme 7 | url = https://github.com/vuestorefront-community/template-odoo 8 | branch = dev 9 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx commitlint --edit 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm test 5 | -------------------------------------------------------------------------------- /.ncurc.json: -------------------------------------------------------------------------------- 1 | { 2 | "reject": [ 3 | "typescript" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 20 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": true, 4 | "singleQuote": true, 5 | "arrowParens": "always", 6 | "tabWidth": 2, 7 | "bracketSpacing": true 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[typescript]": { 3 | "editor.formatOnPaste": false, 4 | "editor.formatOnSave": false, 5 | }, 6 | "editor.formatOnPaste": true, 7 | "editor.formatOnSave": true, 8 | "search.exclude": { 9 | "**/docs/**": true, 10 | "**/old-api-client/**": true, 11 | }, 12 | } -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | yarn-path ".yarn/releases/yarn-1.19.0.cjs" 6 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @vuestorefront-community/vsfcloud 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Yeay! You want to contribute to @vuestorefront/odoo. That's amazing! To smoothen everyone's experience involved with the project please take note of the following guidelines and rules. 4 | 5 | 6 | ## Found an Issue? 7 | 8 | Thank you for reporting any issues you find. We do our best to test and make @vuestorefront/odoo as solid as possible, but any reported issue is a real help. 9 | 10 | Please follow these guidelines when reporting issues: 11 | 12 | - Provide a title in the format of ` when ` 13 | - Tag your issue with the tag `bug` 14 | - Provide a short summary of what you are trying to do 15 | - Provide the log of the encountered error if applicable 16 | - Provide the exact version of @vuestorefront/odoo. 17 | - Be awesome and consider contributing a [pull request](#want-to-contribute) 18 | 19 | ## Want to contribute? 20 | 21 | Please consider these guidelines when filing a pull request: 22 | 23 | > @vuestorefront/odoo pull requests 24 | 25 | - Follow the [Coding Rules](#coding-rules) 26 | - Follow the [Commit Rules](#commit-rules) 27 | - Make sure you rebased the current master branch when filing the pull request 28 | - Squash your commits when filing the pull request 29 | - Provide a short title with a maximum of 100 characters 30 | - Provide a more detailed description containing 31 | _ What you want to achieve 32 | _ What you changed 33 | _ What you added 34 | _ What you removed 35 | 36 | ## Coding Rules 37 | 38 | To keep the code base of @vuestorefront/odoo neat and tidy the following rules apply to every change 39 | 40 | > Coding standards 41 | 42 | - `eslint` is king 43 | - Favor micro library over swiss army knives (rimraf, ncp vs. fs-extra) 44 | - Be awesome 45 | 46 | ## Commit Rules 47 | 48 | To help everyone with understanding the commit history of commitlint the following commit rules are enforced. 49 | To make your life easier @vuestorefront/odoo is commitizen-friendly and provides the npm run-script `commit`. 50 | 51 | > Commit standards 52 | 53 | - [conventional-changelog](https://github.com/conventional-changelog) 54 | - husky commit message hook available 55 | - present tense 56 | - maximum of 100 characters 57 | - message format of `$type($scope): $message` 58 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Vue Storefront 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /alokai-odoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront-community/odoo/c06fd20e9bb5e7c203f09d86ca06df397ffc016a/alokai-odoo.png -------------------------------------------------------------------------------- /api-extractor.base.json: -------------------------------------------------------------------------------- 1 | /** 2 | * Config file for API Extractor. For more info, please visit: https://api-extractor.com 3 | */ 4 | { 5 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", 6 | "projectFolder": ".", 7 | "compiler": { 8 | "tsconfigFilePath": "/tsconfig.base.json" 9 | }, 10 | "docModel": { 11 | "enabled": true 12 | }, 13 | "dtsRollup": { 14 | "enabled": true 15 | }, 16 | "tsdocMetadata": { 17 | "enabled": false 18 | }, 19 | "apiReport": { 20 | "enabled": false 21 | }, 22 | "messages": { 23 | "compilerMessageReporting": { 24 | "default": { 25 | "logLevel": "warning" 26 | } 27 | }, 28 | "extractorMessageReporting": { 29 | "default": { 30 | "logLevel": "none", 31 | "addToApiReportFile": false 32 | }, 33 | "ae-extra-release-tag": { 34 | "logLevel": "none", 35 | "addToApiReportFile": false 36 | }, 37 | "ae-forgotten-export": { 38 | "logLevel": "none" 39 | } 40 | }, 41 | "tsdocMessageReporting": { 42 | "default": { 43 | "logLevel": "none", 44 | "addToApiReportFile": false 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'subject-case': [2, 'never', ['sentence-case', 'start-case', 'upper-case']], 5 | }, 6 | } -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | pids 2 | logs 3 | node_modules 4 | npm-debug.log 5 | coverage/ 6 | run 7 | dist 8 | .DS_Store 9 | .nyc_output 10 | .basement 11 | config.local.js 12 | basement_dist 13 | .nuxt 14 | -------------------------------------------------------------------------------- /docs/.nuxt/app.config.mjs: -------------------------------------------------------------------------------- 1 | 2 | import { updateAppConfig } from '#app' 3 | import { defuFn } from '/home/leonardo/Documents/PROJETOS/odoo/node_modules/defu/dist/defu.mjs' 4 | 5 | const inlineConfig = { 6 | "nuxt": { 7 | "buildId": "test" 8 | } 9 | } 10 | 11 | // Vite - webpack is handled directly in #app/config 12 | if (import.meta.hot) { 13 | import.meta.hot.accept((newModule) => { 14 | updateAppConfig(newModule.default) 15 | }) 16 | } 17 | 18 | 19 | 20 | export default /* #__PURE__ */ defuFn(inlineConfig) 21 | -------------------------------------------------------------------------------- /docs/.nuxt/manifest/latest.json: -------------------------------------------------------------------------------- 1 | {"id":"test","timestamp":1723499968114} -------------------------------------------------------------------------------- /docs/.nuxt/mdc-configs.mjs: -------------------------------------------------------------------------------- 1 | let configs 2 | export function getMdcConfigs () { 3 | if (!configs) { 4 | configs = Promise.all([ 5 | ]) 6 | } 7 | return configs 8 | } -------------------------------------------------------------------------------- /docs/.nuxt/mdc-imports.mjs: -------------------------------------------------------------------------------- 1 | import _Highlight from '/home/leonardo/Documents/PROJETOS/odoo/docs/node_modules/@nuxtjs/mdc/dist/runtime/highlighter/rehype-nuxt.js' 2 | 3 | export const remarkPlugins = { 4 | } 5 | 6 | export const rehypePlugins = { 7 | 'highlight': { instance: _Highlight, options: {} }, 8 | } 9 | 10 | export const highlight = {"theme":"one-dark-pro"} -------------------------------------------------------------------------------- /docs/.nuxt/module/nuxt-robots.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by nuxt-robots 2 | 3 | declare module 'nitropack' { 4 | interface NitroApp { 5 | _robots: { 6 | ctx: import('../../node_modules/@nuxtjs/robots/dist/runtime/types').HookRobotsConfigContext 7 | nuxtContentUrls: Set 8 | }, 9 | _robotsRuleMactcher: (url: string) => string 10 | } 11 | interface NitroRouteRules { 12 | /** 13 | * @deprecated Use `robots: ` instead. 14 | */ 15 | index?: boolean 16 | robots?: boolean | string | { 17 | indexable: boolean 18 | rule: string 19 | } 20 | } 21 | interface NitroRouteConfig { 22 | /** 23 | * @deprecated Use `robots: ` instead. 24 | */ 25 | index?: boolean 26 | robots?: boolean | string | { 27 | indexable: boolean 28 | rule: string 29 | } 30 | } 31 | interface NitroRuntimeHooks { 32 | 'robots:config': (ctx: import('../../node_modules/@nuxtjs/robots/dist/runtime/types').HookRobotsConfigContext) => void | Promise 33 | 'robots:robots-txt': (ctx: import('../../node_modules/@nuxtjs/robots/dist/runtime/types').HookRobotsTxtContext) => void | Promise 34 | } 35 | } 36 | declare module 'h3' { 37 | interface H3EventContext { 38 | robots: { 39 | rule: string 40 | indexable: boolean 41 | } 42 | } 43 | } 44 | 45 | export {} 46 | -------------------------------------------------------------------------------- /docs/.nuxt/module/nuxt-schema-org.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by nuxt-schema-org 2 | 3 | declare module '@nuxt/schema' { 4 | export interface RuntimeNuxtHooks { 5 | 'schema-org:meta': (meta: import('../../node_modules/nuxt-schema-org/dist/runtime/types').MetaInput) => void | Promise 6 | } 7 | } 8 | declare module '#app' { 9 | export interface RuntimeNuxtHooks { 10 | 'schema-org:meta': (meta: import('../../node_modules/nuxt-schema-org/dist/runtime/types').MetaInput) => void | Promise 11 | } 12 | } 13 | 14 | export {} 15 | -------------------------------------------------------------------------------- /docs/.nuxt/module/nuxt-seo-experiments.assets.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by nuxt-seo-experiments.assets 2 | 3 | declare module '#app' { 4 | import { HeadEntry, HeadTag } from '@unhead/schema' 5 | 6 | interface RuntimeNuxtHooks { 7 | 'head:tags': (tag: HeadTag[]) => Promise | void 8 | 'head:entries': (entries: HeadEntry[]) => Promise | void 9 | } 10 | } 11 | declare module '@unhead/schema' { 12 | 13 | type PublicFiles = (string & Record) 14 | type JsFiles = (string & Record) 15 | 16 | interface SchemaAugmentations { 17 | link: import('@unhead/schema').UserTagConfigWithoutInnerContent & { 18 | href: PublicFiles 19 | } 20 | script: import('@unhead/schema').TagUserProperties & { 21 | src: JsFiles 22 | } 23 | } 24 | } 25 | export {} 26 | -------------------------------------------------------------------------------- /docs/.nuxt/module/nuxt-seo-experiments.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by nuxt-seo-experiments 2 | 3 | declare module 'nitropack' { 4 | interface NitroRouteRules { 5 | seoMeta?: import('@unhead/schema').UseSeoMetaInput 6 | head?: import('@unhead/schema').Head 7 | } 8 | interface NitroRouteConfig { 9 | seoMeta?: import('@unhead/schema').UseSeoMetaInput 10 | head?: import('@unhead/schema').Head 11 | } 12 | } 13 | 14 | declare module '@nuxt/schema' { 15 | interface NuxtAppConfig { seoMeta?: import('@unhead/schema').UseSeoMetaInput } 16 | interface NuxtConfig { app?: ConfigSchema['app'] & { seoMeta?: import('@unhead/schema').UseSeoMetaInput } } 17 | interface NuxtOptions { app: ConfigSchema['app'] & { seoMeta?: import('@unhead/schema').UseSeoMetaInput } } 18 | } 19 | 20 | declare module 'nuxt/schema' { 21 | interface NuxtAppConfig { seoMeta?: import('@unhead/schema').UseSeoMetaInput } 22 | interface NuxtConfig { app?: import('@nuxt/schema').ConfigSchema['app'] & { seoMeta?: import('@unhead/schema').UseSeoMetaInput } } 23 | interface NuxtOptions { app: import('@nuxt/schema').ConfigSchema['app'] & { seoMeta?: import('@unhead/schema').UseSeoMetaInput } } 24 | } 25 | 26 | export {} 27 | -------------------------------------------------------------------------------- /docs/.nuxt/nuxt.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by nuxi 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | 36 | export {} 37 | -------------------------------------------------------------------------------- /docs/.nuxt/nuxt.json: -------------------------------------------------------------------------------- 1 | { 2 | "_hash": "V2jRX0moaJ", 3 | "project": { 4 | "rootDir": "/home/leonardo/Documents/PROJETOS/odoo/docs" 5 | }, 6 | "versions": { 7 | "nuxt": "3.8.0" 8 | } 9 | } -------------------------------------------------------------------------------- /docs/.nuxt/schema/nuxt.schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface NuxtCustomSchema { 2 | appConfig?: { 3 | /** 4 | * Nuxt Icon 5 | * 6 | * Configure the defaults of Nuxt Icon 7 | * 8 | */ 9 | nuxtIcon?: { 10 | /** 11 | * Icon Size 12 | * 13 | * Set the default icon size. Set to false to disable the sizing of icon in style. 14 | * 15 | * @default "1em" 16 | * 17 | * @studioIcon material-symbols:format-size-rounded 18 | */ 19 | size?: string | false, 20 | 21 | /** 22 | * CSS Class 23 | * 24 | * Set the default CSS class 25 | * 26 | * @default "" 27 | * 28 | * @studioIcon material-symbols:css 29 | */ 30 | class?: string, 31 | 32 | /** 33 | * Icon aliases 34 | * 35 | * Define Icon aliases to update them easily without code changes. 36 | * 37 | * 38 | * @studioIcon material-symbols:star-rounded 39 | * 40 | * @studioInputObjectValueType icon 41 | */ 42 | aliases?: { [alias: string]: string }, 43 | }, 44 | }, 45 | } 46 | export type CustomAppConfig = Exclude 47 | type _CustomAppConfig = CustomAppConfig 48 | 49 | declare module '@nuxt/schema' { 50 | interface NuxtConfig extends Omit {} 51 | interface NuxtOptions extends Omit {} 52 | interface CustomAppConfig extends _CustomAppConfig {} 53 | } 54 | 55 | declare module 'nuxt/schema' { 56 | interface NuxtConfig extends Omit {} 57 | interface NuxtOptions extends Omit {} 58 | interface CustomAppConfig extends _CustomAppConfig {} 59 | } 60 | -------------------------------------------------------------------------------- /docs/.nuxt/schema/nuxt.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "#", 3 | "properties": { 4 | "appConfig": { 5 | "id": "#appConfig", 6 | "properties": { 7 | "nuxtIcon": { 8 | "title": "Nuxt Icon", 9 | "description": "Configure the defaults of Nuxt Icon", 10 | "id": "#appConfig/nuxtIcon", 11 | "properties": { 12 | "size": { 13 | "title": "Icon Size", 14 | "description": "Set the default icon size. Set to false to disable the sizing of icon in style.", 15 | "tags": [ 16 | "@studioIcon material-symbols:format-size-rounded" 17 | ], 18 | "tsType": "string | false", 19 | "id": "#appConfig/nuxtIcon/size", 20 | "default": "1em", 21 | "type": "string" 22 | }, 23 | "class": { 24 | "title": "CSS Class", 25 | "description": "Set the default CSS class", 26 | "tags": [ 27 | "@studioIcon material-symbols:css" 28 | ], 29 | "id": "#appConfig/nuxtIcon/class", 30 | "default": "", 31 | "type": "string" 32 | }, 33 | "aliases": { 34 | "title": "Icon aliases", 35 | "description": "Define Icon aliases to update them easily without code changes.", 36 | "tags": [ 37 | "@studioIcon material-symbols:star-rounded", 38 | "@studioInputObjectValueType icon" 39 | ], 40 | "tsType": "{ [alias: string]: string }", 41 | "id": "#appConfig/nuxtIcon/aliases", 42 | "default": {}, 43 | "type": "object" 44 | } 45 | }, 46 | "type": "object", 47 | "default": { 48 | "size": "1em", 49 | "class": "", 50 | "aliases": {} 51 | } 52 | } 53 | }, 54 | "type": "object", 55 | "default": { 56 | "nuxtIcon": { 57 | "size": "1em", 58 | "class": "", 59 | "aliases": {} 60 | } 61 | } 62 | } 63 | }, 64 | "type": "object", 65 | "default": { 66 | "appConfig": { 67 | "nuxtIcon": { 68 | "size": "1em", 69 | "class": "", 70 | "aliases": {} 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /docs/.nuxt/types/app.config.d.ts: -------------------------------------------------------------------------------- 1 | 2 | import type { CustomAppConfig } from 'nuxt/schema' 3 | import type { Defu } from 'defu' 4 | 5 | 6 | declare const inlineConfig = { 7 | "nuxt": { 8 | "buildId": "test" 9 | } 10 | } 11 | type ResolvedAppConfig = Defu 12 | type IsAny = 0 extends 1 & T ? true : false 13 | 14 | type MergedAppConfig, Custom extends Record> = { 15 | [K in keyof (Resolved & Custom)]: K extends keyof Custom 16 | ? unknown extends Custom[K] 17 | ? Resolved[K] 18 | : IsAny extends true 19 | ? Resolved[K] 20 | : Custom[K] extends Record 21 | ? Resolved[K] extends Record 22 | ? MergedAppConfig 23 | : Exclude 24 | : Exclude 25 | : Resolved[K] 26 | } 27 | 28 | declare module 'nuxt/schema' { 29 | interface AppConfig extends MergedAppConfig { } 30 | } 31 | declare module '@nuxt/schema' { 32 | interface AppConfig extends MergedAppConfig { } 33 | } 34 | -------------------------------------------------------------------------------- /docs/.nuxt/types/content.d.ts: -------------------------------------------------------------------------------- 1 | declare module '#content/server' { 2 | const serverQueryContent: typeof import('/home/leonardo/Documents/PROJETOS/odoo/docs/node_modules/@nuxt/content/dist/runtime/legacy/types').serverQueryContent 3 | const parseContent: typeof import('/home/leonardo/Documents/PROJETOS/odoo/docs/node_modules/@nuxt/content/dist/runtime/server').parseContent 4 | } -------------------------------------------------------------------------------- /docs/.nuxt/types/layouts.d.ts: -------------------------------------------------------------------------------- 1 | import { ComputedRef, MaybeRef } from 'vue' 2 | export type LayoutKey = "default" | "fullscreen" 3 | declare module "../../../node_modules/nuxt/dist/pages/runtime/composables" { 4 | interface PageMeta { 5 | layout?: MaybeRef | ComputedRef 6 | } 7 | } -------------------------------------------------------------------------------- /docs/.nuxt/types/middleware.d.ts: -------------------------------------------------------------------------------- 1 | import type { NavigationGuard } from 'vue-router' 2 | export type MiddlewareKey = string 3 | declare module "../../../node_modules/nuxt/dist/pages/runtime/composables" { 4 | interface PageMeta { 5 | middleware?: MiddlewareKey | NavigationGuard | Array 6 | } 7 | } -------------------------------------------------------------------------------- /docs/.nuxt/types/nitro-config.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by nitro 2 | 3 | // App Config 4 | import type { Defu } from 'defu' 5 | 6 | 7 | 8 | type UserAppConfig = Defu<{}, []> 9 | 10 | declare module 'nitropack' { 11 | interface AppConfig extends UserAppConfig {} 12 | } 13 | 14 | export {} -------------------------------------------------------------------------------- /docs/.nuxt/types/nitro-nuxt.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /// 3 | 4 | import type { RuntimeConfig } from 'nuxt/schema' 5 | import type { H3Event } from 'h3' 6 | import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from 'nuxt/dist/core/runtime/nitro/renderer' 7 | 8 | declare module 'nitropack' { 9 | interface NitroRuntimeConfigApp { 10 | buildAssetsDir: string 11 | cdnURL: string 12 | } 13 | interface NitroRuntimeConfig extends RuntimeConfig {} 14 | interface NitroRouteConfig { 15 | ssr?: boolean 16 | experimentalNoScripts?: boolean 17 | } 18 | interface NitroRouteRules { 19 | ssr?: boolean 20 | experimentalNoScripts?: boolean 21 | } 22 | interface NitroRuntimeHooks { 23 | 'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise 24 | 'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/.nuxt/types/nitro-routes.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by nitro 2 | import type { Serialize, Simplify } from 'nitropack' 3 | declare module 'nitropack' { 4 | type Awaited = T extends PromiseLike ? Awaited : T 5 | interface InternalApi { 6 | '/api/fetch': { 7 | 'post': Simplify>>> 8 | } 9 | '/__nuxt_error': { 10 | 'default': Simplify>>> 11 | } 12 | '/api/_mdc/highlight': { 13 | 'default': Simplify>>> 14 | } 15 | '/robots.txt': { 16 | 'default': Simplify>>> 17 | } 18 | '/__robots__/nuxt-content.json': { 19 | 'default': Simplify>>> 20 | } 21 | '/__robots__/debug.json': { 22 | 'default': Simplify>>> 23 | } 24 | '/__robots__/debug-path.json': { 25 | 'default': Simplify>>> 26 | } 27 | '/__schema-org__/debug.json': { 28 | 'default': Simplify>>> 29 | } 30 | '/api/_content/query/:qid/**:params': { 31 | 'get': Simplify>>> 32 | } 33 | '/api/_content/query/:qid': { 34 | 'get': Simplify>>> 35 | } 36 | '/api/_content/query': { 37 | 'get': Simplify>>> 38 | } 39 | '/api/_content/cache.json': { 40 | 'get': Simplify>>> 41 | } 42 | '/api/_content/navigation/:qid/**:params': { 43 | 'get': Simplify>>> 44 | } 45 | '/api/_content/navigation/:qid': { 46 | 'get': Simplify>>> 47 | } 48 | '/api/_content/navigation': { 49 | 'get': Simplify>>> 50 | } 51 | } 52 | } 53 | export {} -------------------------------------------------------------------------------- /docs/.nuxt/types/nitro.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// -------------------------------------------------------------------------------- /docs/.nuxt/types/plugins.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by Nuxt' 2 | import type { Plugin } from '#app' 3 | 4 | type Decorate> = { [K in keyof T as K extends string ? `$${K}` : never]: T[K] } 5 | 6 | type InjectionType = A extends Plugin ? Decorate : unknown 7 | 8 | type NuxtAppInjections = 9 | InjectionType & 10 | InjectionType & 11 | InjectionType & 12 | InjectionType & 13 | InjectionType & 14 | InjectionType & 15 | InjectionType & 16 | InjectionType & 17 | InjectionType & 18 | InjectionType & 19 | InjectionType & 20 | InjectionType & 21 | InjectionType & 22 | InjectionType & 23 | InjectionType & 24 | InjectionType & 25 | InjectionType & 26 | InjectionType & 27 | InjectionType & 28 | InjectionType & 29 | InjectionType & 30 | InjectionType & 31 | InjectionType & 32 | InjectionType & 33 | InjectionType 34 | 35 | declare module '#app' { 36 | interface NuxtApp extends NuxtAppInjections { } 37 | } 38 | 39 | declare module 'vue' { 40 | interface ComponentCustomProperties extends NuxtAppInjections { } 41 | } 42 | 43 | export { } 44 | -------------------------------------------------------------------------------- /docs/.nuxt/types/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } -------------------------------------------------------------------------------- /docs/content/1.index.md: -------------------------------------------------------------------------------- 1 | # Demonstration 2 | 3 | This documentation is about Odoo integration with Alokai. This integration has been developed and maintained by ERPGAP / PromptEQUATION ❤️ 4 | 5 | Before anything, you should take a look in our live demo: https://alokai.labs.erpgap.com/ _it's a dev server so could be down sometimes_ :man_shrugging: 6 | 7 | ::: warning 8 | We need to make clear that this hole documentation is about **Alokai + Odoo - Front End**, not about Odoo Back End. So, take a look in following explanations to better understand how it works. 9 | ::: 10 | 11 | ## Alokai Community Projects explanation 12 | 13 | We are Alokai integrators partners, so we make part of Alokai open source community. We keep our available projects in Github Alokai Community (https://github.com/vuestorefront-community) to keep everyting working: 14 | 15 | - **[Odoo Alokai Modules](https://github.com/erpgap/alokai-odoo.git)**: This is where we maintain the Odoo modules that you need to install on your Odoo server. This will expose some GraphQL endpoints that will be used for Alokai server to serve and cache your Odoo information. 16 | 17 | - **[Storefront Boilerplate](https://github.com/erpgap/storefront-ui.git)**: This is the project to start a new Alokai-odoo project. It's our main theme. So, if you want to start your own project, you'll need this repository to get started with your Nuxt3 project. 18 | 19 | - **[Docker Compose Project](https://github.com/erpgap/alokai-docker.git)**: This is the fastest way to test Alokai and Odoo togeather. It contains the following containers: Postgresql Database, Odoo Server, Alokai Server and Redis Server. 20 | 21 | When we start a new project from **Storefront Boilerplate**, the npm package with odoo composables and apis will be injected. 22 | -------------------------------------------------------------------------------- /docs/content/2.assistant.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: fullscreen 3 | navigation: false 4 | --- 5 | 6 | # AI Assistant (Alpha) 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/content/2.general/2.testing-local.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing local 3 | layout: default 4 | --- 5 | 6 | # Integration test 7 | 8 | _In this section you will find everything you need to build our entire environment to do a deeper test on Alokai + ODOO and make sure if our integration is what you has been looking for. We hope it is :innocent:_ 9 | 10 | # Video tutorial 11 | 12 | Now you can watch and follow the steps in this video tutorial. Take your time :thumbsup: 13 | 14 | 15 | 16 | ## Before you start 17 | 18 | Pre-requisites: Docker, Node.js (>=14.19.0), npm or similar and Git. 19 | 20 | Last odoo **version** released: Clone the repo from (https://github.com/odoogap/vuestorefront-docker) 21 | 22 | ### Inside the repo 23 | 24 | Its really convenient to test all the ecosystem Vuestorefront - Odoo Integration working in your local machine. We have prepared a docker-compose file that will help you to get started in a few minutes. 25 | 26 | Inside the repo downloaded you can find the 17.0 package that contains a startup file _startup.sh_ bash script. It basically detects if _mount/extra-addons/graphql_vuestorefront_ exists or not then its going to clone the odoo alokai modules that you can also find at https://github.com/odoogap/storefront-ui 27 | 28 | ## Installation 29 | 30 | First of all we should understand what's inside the docker-compose file. We have 4 services: 31 | _:dart: You can go deeper [Configuration > docker-compose](/configuration/docker-compose.md#links) ._ 32 | 33 | - **Redis**: Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It's used by Alokai to store the cache. 34 | 35 | - **db**: PostgreSQL database used by Odoo. 36 | 37 | - **odoo**: Odoo service. This is our backend service that you could use to manage your products, orders, etc. 38 | 39 | - **Alokai**: Here is our front end integrated to Alokai Storefront. 40 | 41 | ```bash 42 | docker-compose up --build -d 43 | # You might want to check what happens under the hood 44 | docker-compose logs -f 45 | ``` 46 | 47 | :dart: _If you use Docker Desktop on Windows, some .sh files are executed during instalation, so we recommend you to read_ [Docker Desktop Documentation](https://docs.docker.com/docker-for-windows/wsl/) . 48 | 49 | ## After installation 50 | 51 | Now just open http://localhost:3000 for Alokai and http://localhost:8069 for local Odoo (credentials admin/admin) 52 | 53 | ::: warning 54 | You might not see the top categories (MEN/WOMEN) until the Odoo server is initialized (database init takes time to install all modules) 55 | ::: 56 | 57 | ## Tips 58 | 59 | **How to stop the services?** 60 | 61 | ```bash 62 | docker-compose stop 63 | ``` 64 | 65 | **How to clear Odoo database?** 66 | 67 | Running this command below it will stop all docker services and clears the Odoo local database. 68 | 69 | ```bash 70 | docker-compose down -v 71 | ``` 72 | 73 | **Handle caching with Redis** 74 | 75 | The `REDIS_ENABLED=true` into .env file ensure that Redis is enabled to be used. It is used to cache all odoo datas that has been accessed by the browser. Running this command below it will clear the Redis database. 76 | 77 | ```bash 78 | docker exec -it redis redis-cli 79 | 127.0.0.1:6379> flushall 80 | OK 81 | ``` 82 | -------------------------------------------------------------------------------- /docs/content/2.general/3.new-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: New project 3 | layout: default 4 | --- 5 | 6 | # Starting new project 7 | 8 | We already explained how to test our integration in your local machine. In this point we're going to show how you can start your own new project using our integration. It's really simple to get start. Just follow the steps below. 9 | 10 | ## Running in your local machine 11 | 12 | 1. Type the command to get the project: `git clone https://github.com/vuestorefront-community/template-odoo.git` 13 | 14 | 2. Copy .env.example to .env 15 | 16 | :triangular*flag_on_post: \_Do not commit .env file for security reasons* 17 | 18 | 3. Install the dependences: `yarn install` 19 | 20 | :dart: _This script will get all categories and products previously entered into odoo database and generate Nuxt custom routes. It creates a Custom Routes folder with all routes retrieved from odoo_ 21 | 22 | 4. Build for production and launch server: `yarn build` 23 | 24 | 5. Build local: `yarn build:local` 25 | 26 | ::: tip Docker 27 | You can use Docker instead. Type: 28 | `docker-compose up --build -d` 29 | 30 | You might need to: Type: 31 | `docker-compose restart odoo nginx` 32 | ::: 33 | 34 | ## After installation 35 | 36 | Now just open http://localhost:3000 for Alokai and http://localhost:8069 for local Odoo (credentials admin/admin) 37 | -------------------------------------------------------------------------------- /docs/content/2.general/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Getting Started 2 | navigation: 3 | icon: ri:book-2-fill 4 | -------------------------------------------------------------------------------- /docs/content/2.general/img/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront-community/odoo/c06fd20e9bb5e7c203f09d86ca06df397ffc016a/docs/content/2.general/img/demo.png -------------------------------------------------------------------------------- /docs/content/3.middleware/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | --- 4 | # Middleware 5 | 6 | Alokai's **Server Middleware** is an Express.js application that provides a single place for you to connect to the multiple services that you need to run your storefront. 7 | 8 | It acts as a layer between your frontend application and the various services that you need to connect to, such as a commerce backend, CMS, or payment gateway. 9 | 10 | Our different [integrations](/integrations) add onto the middleware to provide additional API Clients specific for a certain integration that you can interact with using the [SDK](/sdk). 11 | 12 | ## Features 13 | 14 | ::list{type="success"} 15 | - Connect multiple services using different technologies and libraries 16 | - Create and [extend](/middleware/guides/extensions) integrations to add new capabilities or modify their behavior 17 | - Control of the requests sent to the integration platform and responses sent back to the Nuxt.js application 18 | - Securely store credentials on the server without exposing them to the end-users of your application 19 | - Improve site performance by moving logic to the server abd shipping less code to the browser 20 | :: 21 | 22 | ## Getting Started 23 | 24 | If you're using our storefront, the middleware is set up for you. Our storefronts come with a middleware and frontend app already configured, so you can get started right away. 25 | 26 | But if you're building your Alokai application from scratch, you'll need to set up the middleware to connect to your backend services. 27 | 28 | ::grid{:columns="2"} 29 | #section-1 30 | :card{to="/middleware/guides/extensions" title="See Available Storefronts" description="Get started with one of our ready-to-customize storefronts." icon="material-symbols:storefront"} 31 | #section-2 32 | :card{to="/middleware/guides/extensions" title="Start From Scratch" description="Set up your server middleware, configure it, and more." icon="gridicons:customize"} 33 | :: 34 | 35 | ## Architecture 36 | 37 | The easiest way to explain the Server Middleware architecture is to base the explanation on the Alokai Integration context. 38 | 39 | In the container, the server application uses the integration that can communicate with the external service provider (e.g. commerce backend). The integration can be extended by the integration extensions. Middleware config is provided for both integration and its extensions. 40 | 41 | Middleware Data Flow 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/content/3.middleware/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Middleware 2 | navigation.icon: 'fa6-solid:layer-group' 3 | -------------------------------------------------------------------------------- /docs/content/4.sdk/_dir.yml: -------------------------------------------------------------------------------- 1 | navigation.title: 'SDK' 2 | navigation.icon: 'ri:terminal-box-fill' 3 | -------------------------------------------------------------------------------- /docs/content/_dir.yml: -------------------------------------------------------------------------------- 1 | sidebarRoot: true 2 | navigation: 3 | title: 'Odoo' 4 | icon: IconOdoo 5 | -------------------------------------------------------------------------------- /docs/content/contributing/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: 'fullscreen' 3 | navigation: 4 | title: "Overview" 5 | --- 6 | 7 | # Contributing 8 | 9 | ::grid{:columns="3"} 10 | #section-1 11 | :card{to="/contributing/integrations" title="Integrations" description="Help make our open-source integrations better or build your own integrations" icon="fluent:puzzle-cube-piece-20-filled"} 12 | 13 | #section-2 14 | :card{to="/contributing/docs" title="Docs" description="Help improve our documentation for any of our projects" icon="ic:baseline-mode-edit-outline"} 15 | #section-3 16 | :card{to="https://discord.com/invite/vuestorefront" title="Discord" description="Join the conversation and talk with our team members" icon="logos:discord-icon"} 17 | :: -------------------------------------------------------------------------------- /docs/content/contributing/_dir.yml: -------------------------------------------------------------------------------- 1 | title: 'Contributing' 2 | 'navigation.icon': 'clarity:group-solid' 3 | -------------------------------------------------------------------------------- /docs/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | extends: ["sf-docs-base"], 4 | }); 5 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuestorefront-odoo-api-docs", 3 | "version": "2.0.0", 4 | "description": "This project is a Odoo integration documentation", 5 | "main": "index.js", 6 | "authors": { 7 | "name": "Ivan Ferreira", 8 | "email": "iferreira@odoogap.com" 9 | }, 10 | "repository": "https://github.com/vuestorefront-community/odoo", 11 | "scripts": { 12 | "build": "nuxt build", 13 | "dev": "nuxt dev", 14 | "generate": "nuxt generate", 15 | "preview": "nuxt preview", 16 | "api-extract": "cd ../ && yarn build && cd docs/ && yarn middleware-ref && yarn multistore-ref && yarn sdk-ref", 17 | "middleware-ref": "cd ../packages/middleware && api-extractor run --local", 18 | "multistore-ref": "cd ../packages/multistore && api-extractor run --local", 19 | "sdk-ref": "cd ../packages/sdk && api-extractor run --local", 20 | "add-changelogs:middleware": "cp ../packages/middleware/CHANGELOG.md ./content/3.middleware/4.reference/change-log.md", 21 | "add-changelogs:multistore": "cp ../packages/multistore/CHANGELOG.md ./content/3.middleware/4.reference/multistore/change-log.md", 22 | "add-changelogs:sdk": "cp ../packages/sdk/CHANGELOG.md ./content/4.sdk/5.reference/change-log.md", 23 | "add-changelogs": "yarn add-changelogs:middleware && yarn add-changelogs:multistore && yarn add-changelogs:sdk" 24 | }, 25 | "devDependencies": { 26 | "@microsoft/api-documenter": "^7.13.30", 27 | "@microsoft/api-extractor": "7.18.1", 28 | "concat-md": "^0.3.5", 29 | "handlebars": "^4.7.7", 30 | "typedoc": "^0.20.20", 31 | "typedoc-plugin-markdown": "^3.4.5", 32 | "typescript": "^3.6.4" 33 | }, 34 | "dependencies": { 35 | "@stackblitz/sdk": "^1.9.0", 36 | "sf-docs-base": "latest" 37 | }, 38 | "resolutions": { 39 | "@nuxt/content": "^2.8.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: "class", 4 | content: ["./**/*.{vue,md}"], 5 | purge: false, 6 | theme: { 7 | container: { 8 | padding: { 9 | DEFAULT: "1em", 10 | sm: "2em", 11 | lg: "4em", 12 | xl: "5em", 13 | "2xl": "6em", 14 | }, 15 | }, 16 | extend: { 17 | colors: { 18 | green: { 19 | DEFAULT: "#00C652", 20 | 50: "#7FFFB4", 21 | 100: "#6AFFA8", 22 | 200: "#41FF90", 23 | 300: "#19FF78", 24 | 400: "#00EF63", 25 | 500: "#00C652", 26 | 600: "#008E3B", 27 | 700: "#005624", 28 | 800: "#001E0C", 29 | 900: "#000000", 30 | }, 31 | purple: { 32 | DEFAULT: "#531ED3", 33 | 50: "#C7B4F4", 34 | 100: "#BAA2F2", 35 | 200: "#9F7FED", 36 | 300: "#845BE8", 37 | 400: "#6937E3", 38 | 500: "#531ED3", 39 | 600: "#4017A2", 40 | 700: "#2C1071", 41 | 800: "#190940", 42 | 900: "#06020F", 43 | }, 44 | }, 45 | }, 46 | }, 47 | plugins: [], 48 | }; 49 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /jest.base.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | transform: { 6 | '^.+\\.(ts)$': 'ts-jest' 7 | }, 8 | coverageDirectory: './coverage/', 9 | collectCoverageFrom: [ 10 | 'src/**/*.ts' 11 | ], 12 | testMatch: ['/**/__tests__/**/*spec.[jt]s?(x)'] 13 | }; 14 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "useWorkspaces": true, 3 | "npmClient": "yarn", 4 | "command": { 5 | "publish": { 6 | "message": "chore(release): %s", 7 | "conventionalCommits": true 8 | } 9 | }, 10 | "packages": [ 11 | "packages/**/*", 12 | "playground-nuxt/**/*" 13 | ], 14 | "version": "1.0.0" 15 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce-integration-odoo", 3 | "private": true, 4 | "license": "MIT", 5 | "scripts": { 6 | "build": "lerna run build", 7 | "dev": "concurrently --names \"Frontend\" \"npm run dev:app\"", 8 | "dev:app": "cd playground-nuxt/app && npm run dev", 9 | "dev:server": "cd playground-nuxt/server && npm run dev", 10 | "test": "lerna run test", 11 | "changesets:version": "yarn changeset version && YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install && yarn prepare:docs", 12 | "changesets:publish": "yarn build && yarn changeset publish", 13 | "prepare:docs": "cd docs && yarn install && yarn api-extract && yarn copy-changelog", 14 | "commit": "git-cz" 15 | }, 16 | "config": { 17 | "commitizen": { 18 | "path": "@commitlint/prompt" 19 | } 20 | }, 21 | "devDependencies": { 22 | "-": "^0.0.1", 23 | "@babel/core": "^7.10.5", 24 | "@changesets/cli": "^2.26.2", 25 | "@commitlint/config-conventional": "^17.6.7", 26 | "@loopmode/crosslink": "^0.4.0", 27 | "@rollup/plugin-babel": "^6.0.3", 28 | "@rollup/plugin-node-resolve": "^13.0.6", 29 | "@rollup/plugin-replace": "^2.3.3", 30 | "@types/jest": "^27.4.0", 31 | "@types/node": "^12.12.14", 32 | "@types/supertest": "~2.0.12", 33 | "commitizen": "^4.3.0", 34 | "concurrently": "^8.0.1", 35 | "cross-env": "^6.0.3", 36 | "husky": "^8.0.0", 37 | "jest": "^27.4.7", 38 | "lerna": "^6.5.1", 39 | "lint-staged": "^10.0.7", 40 | "msw": "^0.49.1", 41 | "nodemon": "^2.0.22", 42 | "rimraf": "^3.0.2", 43 | "rollup": "^2.59.0", 44 | "rollup-plugin-terser": "^7.0.2", 45 | "rollup-plugin-typescript2": "^0.34.1", 46 | "supertest": "~6.2.4", 47 | "ts-jest": "^27.1.3", 48 | "ts-node": "^8.4.1", 49 | "tslib": "^2.1.0", 50 | "typescript": "^4.2.2", 51 | "webpack-bundle-analyzer": "^3.5.2" 52 | }, 53 | "engines": { 54 | "node": ">=16.x" 55 | }, 56 | "workspaces": [ 57 | "packages/*", 58 | "playground-nuxt/*" 59 | ], 60 | "dependencies": { 61 | "@commitlint/prompt": "^17.6.7", 62 | "@nuxt/devtools": "^0.7.0", 63 | "@vue-storefront/cli": "^4.3.0", 64 | "react": "18.2.0", 65 | "react-dom": "18.2.0" 66 | }, 67 | "overrides": { 68 | "react-json-view": { 69 | "react": "$react", 70 | "react-dom": "$react-dom" 71 | } 72 | }, 73 | "version": "0.0.0" 74 | } 75 | -------------------------------------------------------------------------------- /packages/nuxt-layer/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /packages/nuxt-layer/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ["@nuxt/eslint-config"], 4 | }; 5 | -------------------------------------------------------------------------------- /packages/nuxt-layer/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .nuxt 4 | nuxt.d.ts 5 | .output 6 | .data 7 | .env 8 | package-lock.json 9 | framework 10 | dist 11 | .DS_Store 12 | 13 | # Yarn 14 | .yarn/cache 15 | .yarn/*state* 16 | 17 | # Local History 18 | .history 19 | 20 | # VSCode 21 | .vscode/ 22 | -------------------------------------------------------------------------------- /packages/nuxt-layer/.nuxtrc: -------------------------------------------------------------------------------- 1 | typescript.includeWorkspace = true 2 | -------------------------------------------------------------------------------- /packages/nuxt-layer/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt Layer Starter 2 | 3 | Create Nuxt extendable layer with this GitHub template. 4 | 5 | ## Setup 6 | 7 | Make sure to install the dependencies: 8 | 9 | ```bash 10 | pnpm install 11 | ``` 12 | 13 | ## Working on your theme 14 | 15 | Your theme is at the root of this repository, it is exactly like a regular Nuxt project, except you can publish it on NPM. 16 | 17 | The `.playground` directory should help you on trying your theme during development. 18 | 19 | Running `pnpm dev` will prepare and boot `.playground` directory, which imports your theme itself. 20 | 21 | ## Distributing your theme 22 | 23 | Your Nuxt layer is shaped exactly the same as any other Nuxt project, except you can publish it on NPM. 24 | 25 | To do so, you only have to check if `files` in `package.json` are valid, then run: 26 | 27 | ```bash 28 | npm publish --access public 29 | ``` 30 | 31 | Once done, your users will only have to run: 32 | 33 | ```bash 34 | npm install --save your-theme 35 | ``` 36 | 37 | Then add the dependency to their `extends` in `nuxt.config`: 38 | 39 | ```ts 40 | defineNuxtConfig({ 41 | extends: 'your-theme' 42 | }) 43 | ``` 44 | 45 | ## Development Server 46 | 47 | Start the development server on http://localhost:3000 48 | 49 | ```bash 50 | pnpm dev 51 | ``` 52 | 53 | ## Production 54 | 55 | Build the application for production: 56 | 57 | ```bash 58 | pnpm build 59 | ``` 60 | 61 | Or statically generate it with: 62 | 63 | ```bash 64 | pnpm generate 65 | ``` 66 | 67 | Locally preview production build: 68 | 69 | ```bash 70 | pnpm preview 71 | ``` 72 | 73 | Checkout the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 74 | -------------------------------------------------------------------------------- /packages/nuxt-layer/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | }) 3 | 4 | declare module '@nuxt/schema' { 5 | interface AppConfigInput { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/nuxt-layer/app.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /packages/nuxt-layer/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | devtools: { enabled: true }, 4 | build: { 5 | transpile: ['tslib', '@apollo/client', '@apollo/client/core', '@vue/apollo-composable', '@vue/apollo-option', 'ts-invariant', 'vue-toastification', '@erpgap/odoo-sdk-api-client'] 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/nuxt-layer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erpgap/odoo-nuxt-layer", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "main": "./nuxt.config.ts", 6 | "files": [ 7 | "server" 8 | ], 9 | "scripts": { 10 | "dev": "nuxi dev", 11 | "build": "nuxt build", 12 | "generate": "nuxt generate", 13 | "preview": "nuxt preview", 14 | "lint": "eslint .", 15 | "postinstall": "nuxt prepare" 16 | }, 17 | "devDependencies": { 18 | "@erpgap/odoo-sdk-api-client": "*", 19 | "@nuxt/eslint-config": "^0.1.1", 20 | "eslint": "^8.28.0", 21 | "nuxt": "^3.8.0", 22 | "typescript": "^4.9.3" 23 | } 24 | } -------------------------------------------------------------------------------- /packages/nuxt-layer/server/api/odoo/mutation.post.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints } from '@erpgap/odoo-sdk-api-client'; 2 | 3 | export default defineEventHandler(async (event) => { 4 | const body = await readBody(event); 5 | 6 | const api: Endpoints = event.context.apolloClient.api; 7 | 8 | const response = await api.mutation(body?.[0], body?.[1]); 9 | 10 | if ((response.data as any)?.cookie) { 11 | appendResponseHeader(event, 'Set-cookie', (response.data as any)?.cookie); 12 | } 13 | 14 | if (response.errors) { 15 | throw createError({ statusCode: 400, data: response.errors }); 16 | } 17 | 18 | return response.data; 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/api/odoo/query.post.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints } from '@erpgap/odoo-sdk-api-client'; 2 | import { getCached } from '~/server/utils/getCached'; 3 | 4 | export default defineEventHandler(async (event) => { 5 | const body = await readBody(event); 6 | const api: Endpoints = event.context.apolloClient.api; 7 | 8 | // const data = await getCached(event, body); 9 | const response = await api.query(body?.[0], body?.[1]); 10 | 11 | if ((response.data as any)?.cookie) { 12 | appendResponseHeader(event, 'Set-cookie', (response.data as any)?.cookie); 13 | } 14 | 15 | if (response.errors) { 16 | throw createError(response.errors[0].message); 17 | } 18 | 19 | return response.data; 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/middleware/apolloClient.ts: -------------------------------------------------------------------------------- 1 | import { createApiClient, MiddlewareConfig } from '@erpgap/odoo-sdk-api-client/server'; 2 | import { Queries } from '~/server/queries'; 3 | import { Mutations } from '~/server/mutations'; 4 | 5 | export default defineEventHandler((event) => { 6 | 7 | const config : MiddlewareConfig = { 8 | odooGraphqlUrl: `${process.env.ODOO_BASE_URL}graphql/vsf`, 9 | queries: { ...Queries, ...Mutations }, 10 | headers: { 11 | 'REAL-IP': getRequestIP(event) || '', 12 | Cookie: `session_id=${parseCookies(event).session_id}`, 13 | 'resquest-host': getRequestHost(event) 14 | } 15 | }; 16 | 17 | event.context.apolloClient = createApiClient(config); 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/mutations/ChangePasswordMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($newPassword: String!, $token: String!) { 4 | changePassword(newPassword: $newPassword, token: $token) { 5 | id 6 | name 7 | email 8 | } 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/mutations/CreateNewAccountMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | 3 | export default gql` 4 | mutation register($companyName: String!, $firstName: String!, $lastName: String!, $email: String!, $location : String!$password: String!, $phoneNumber: String!) { 5 | register(companyName: $companyName, firstName: $firstName, lastName: $lastName, email: $email, location: $location, password: $password, phoneNumber: $phoneNumber) { 6 | partner{ 7 | id 8 | name 9 | street 10 | street2 11 | city 12 | state 13 | { 14 | id 15 | } 16 | country 17 | { 18 | id 19 | } 20 | email 21 | phone 22 | } 23 | } 24 | }`; 25 | 26 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/mutations/LoginMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($email: String!, $password: String!) { 4 | login(email: $email, password: $password) { 5 | partner{ 6 | id 7 | name 8 | street 9 | street2 10 | city 11 | state 12 | { 13 | id 14 | } 15 | country 16 | { 17 | id 18 | } 19 | email 20 | phone 21 | } 22 | } 23 | }`; 24 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/mutations/LogoutMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation { 4 | logout 5 | } 6 | `; 7 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/mutations/SendResetPasswordMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($email: String!) { 4 | resetPassword(email: $email) { 5 | id 6 | name 7 | email 8 | } 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/mutations/UpdatePasswordMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($currentPassword: String!, $newPassword: String!){ 4 | updatePassword(currentPassword: $currentPassword, newPassword: $newPassword) { 5 | id 6 | } 7 | } 8 | `; 9 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client'; 2 | import LoginMutation from './LoginMutation'; 3 | import LogoutMutation from './LogoutMutation'; 4 | import CreateNewAccountMutation from './CreateNewAccountMutation'; 5 | import SendResetPasswordMutation from './SendResetPasswordMutation'; 6 | import UpdatePasswordMutation from './UpdatePasswordMutation'; 7 | import ChangePasswordMutation from './ChangePasswordMutation'; 8 | 9 | enum MutationName { 10 | LoginMutation = 'LoginMutation', 11 | LogoutMutation = 'LogoutMutation', 12 | CreateNewAccountMutation = 'CreateNewAccountMutation', 13 | SendResetPasswordMutation = 'SendResetPasswordMutation', 14 | UpdatePasswordMutation = 'UpdatePasswordMutation', 15 | ChangePasswordMutation = 'ChangePasswordMutation' 16 | } 17 | 18 | const Mutations : Record = { 19 | LoginMutation, 20 | LogoutMutation, 21 | CreateNewAccountMutation, 22 | SendResetPasswordMutation, 23 | UpdatePasswordMutation, 24 | ChangePasswordMutation 25 | }; 26 | 27 | export { 28 | Mutations, 29 | MutationName 30 | }; 31 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/LoadUserQuery.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | import {partnerFragment} from './fragments'; 3 | export default gql` 4 | query LoadUser { 5 | ${partnerFragment} 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/ProductVariantQuery.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | query ProductVariant($productTemplateId: Int, $combinationId: [Int]) { 4 | productVariant( productTemplateId: $productTemplateId combinationId: $combinationId) { 5 | product { 6 | id 7 | image 8 | variantPrice 9 | variantPriceAfterDiscount 10 | variantHasDiscountedPrice 11 | alternativeProducts{ 12 | id 13 | typeId 14 | visibility 15 | status 16 | name 17 | displayName 18 | sku 19 | barcode 20 | description 21 | } 22 | } 23 | productTemplateId 24 | displayName 25 | price 26 | listPrice 27 | hasDiscountedPrice 28 | } 29 | }`; 30 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/api/cartRedis/helper.ts: -------------------------------------------------------------------------------- 1 | import { IRedisCart } from '../../types/'; 2 | 3 | export const populateCartRedis = (cartRedis: IRedisCart) : void => { 4 | cartRedis.totalItemsInCart = cartRedis.orderLines.length; 5 | cartRedis.totalItemsInCartWithQuantity = cartRedis.orderLines.reduce((acc, item) => acc + item.quantity, 0); 6 | cartRedis.amountTotal = cartRedis.orderLines.reduce((acc, item) => acc + (item.product?.combinationInfo?.price * item.quantity), 0); 7 | }; 8 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/api/cartRedis/redisAddItemToCart.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { Context } from '@vue-storefront/core'; 3 | import { populateCartRedis } from './helper'; 4 | 5 | export default async function redisAddItemToCart(context: Context, product: any, quantity = 1, updateOdoo?: boolean): Promise { 6 | if (!context.req.session?.cart) { 7 | context.req.session.cart = { 8 | orderLines: [] 9 | }; 10 | } 11 | 12 | // Clean unnecessary attributes for cart 13 | delete product.attributeValues; 14 | delete product.variantAttributeValues; 15 | delete product.categories; 16 | delete product.jsonLd; 17 | delete product.schemaAttributeValues; 18 | 19 | const previousAddedItem = context.req.session.cart.orderLines?.find(item => item.product.id === product.id); 20 | if (previousAddedItem) { 21 | previousAddedItem.quantity += quantity; 22 | 23 | populateCartRedis(context.req.session.cart); 24 | 25 | return { data: context.req.session.cart }; 26 | } 27 | 28 | context.req.session.cart.orderLines.push({ quantity: quantity, product, id: context.req.session.cart.orderLines.length + 1 }); 29 | 30 | populateCartRedis(context.req.session.cart); 31 | 32 | return { data: context.req.session.cart }; 33 | } 34 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/api/cartRedis/redisLoadCart.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { Context } from '@vue-storefront/core'; 3 | import { populateCartRedis } from './helper'; 4 | 5 | export default async function redisLoadCart(context: Context) { 6 | if (!context.req.session?.cart) { 7 | context.req.session.cart = { 8 | orderLines: [] 9 | }; 10 | } 11 | 12 | populateCartRedis(context.req.session.cart); 13 | 14 | return { data: context.req.session.cart }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/api/cartRedis/redisRemoveItem.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { Context } from '@vue-storefront/core'; 3 | import { Logger } from 'winston'; 4 | import { populateCartRedis } from './helper'; 5 | import cartRemoveItem from '../cartRemoveItem'; 6 | 7 | export default async function redisRemoveItem(context: Context, orderId: number, odooOrderLineId?: number): Promise { 8 | const logger : Logger = (process as any).winstonLog; 9 | 10 | if (!orderId) { 11 | logger.warn('Missing order id'); 12 | return { data: context.req.session.cart }; 13 | } 14 | 15 | const previousAddedItemIndex = context.req.session.cart.orderLines?.findIndex(item => item.id === orderId); 16 | 17 | if (previousAddedItemIndex !== -1) { 18 | context.req.session.cart.orderLines.splice(previousAddedItemIndex, 1); 19 | // Reorder ids 20 | context.req.session.cart.orderLines.forEach((element, index) => { 21 | element.id = index + 1; 22 | }); 23 | 24 | populateCartRedis(context.req.session.cart); 25 | if (odooOrderLineId) { 26 | await cartRemoveItem(context, { lineId: odooOrderLineId }); 27 | } 28 | 29 | return { data: context.req.session.cart }; 30 | } 31 | 32 | logger.warn(`Order Item not found for id ${orderId}`); 33 | return { data: context.req.session.cart }; 34 | } 35 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/api/cartRedis/redisSyncCartToOdoo.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { Context } from '@vue-storefront/core'; 3 | import cartAddMultipleItems from '../cartAddMultipleItems'; 4 | import cartUpdateItemQty from '../cartUpdateItemQty'; 5 | import cartRemoveMultipleItems from '../cartRemoveMultipleItems'; 6 | import cartLoad from '../cartLoad'; 7 | import { Logger } from 'winston'; 8 | 9 | export default async function redisSyncCartToOdoo(context: Context): Promise { 10 | const logger : Logger = (process as any).winstonLog; 11 | const redisOrderLines = context.req.session.cart.orderLines; 12 | 13 | logger.info('Start sync redis cart to odoo'); 14 | 15 | const { data } = await cartLoad(context); 16 | if (data.cart.order.orderLines?.length > 0) { 17 | logger.info('Find cart in odoo with items. Updating products:'); 18 | 19 | const orderIdsToRemove = []; 20 | 21 | data.cart.order.orderLines.forEach(async (orderLine) => { 22 | const redisOrderLine = redisOrderLines.find((redisOrderLine: any) => redisOrderLine.product.id === orderLine.product.id); 23 | if (redisOrderLine && redisOrderLine.quantity !== orderLine.quantity) { 24 | await cartUpdateItemQty(context, { lineId: orderLine.id, quantity: redisOrderLine.quantity }); 25 | } 26 | if (!redisOrderLine) { 27 | orderIdsToRemove.push(orderLine.id); 28 | } 29 | }); 30 | 31 | await cartRemoveMultipleItems(context, {lineIds: orderIdsToRemove }); 32 | 33 | // Add item in redis that are not in odoo cart 34 | redisOrderLines.forEach(async (redisOrderLine: any) => { 35 | const productAlreadyInOdooCart = data.cart.order.orderLines.some((orderLine: any) => redisOrderLine.product.id === orderLine.product.id); 36 | if (!productAlreadyInOdooCart) { 37 | await cartAddMultipleItems(context, { products: [{ 38 | id: redisOrderLine.product.id, 39 | quantity: redisOrderLine.quantity 40 | }]}); 41 | } 42 | }); 43 | 44 | return await cartLoad(context); 45 | } 46 | 47 | logger.info('No cart found in odoo. Adding products:'); 48 | const productsToAdd = redisOrderLines.map((orderLine: any) => ({ 49 | id: orderLine.product.id, 50 | quantity: orderLine.quantity 51 | })); 52 | 53 | await cartAddMultipleItems(context, { products: productsToAdd}); 54 | 55 | return await cartLoad(context); 56 | } 57 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/api/cartRedis/redisUpdateItemQty.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import { Context } from '@vue-storefront/core'; 3 | import { Logger } from 'winston'; 4 | import { populateCartRedis } from './helper'; 5 | import cartUpdateItemQty from '../cartUpdateItemQty'; 6 | 7 | export default async function redisUpdateItemQty(context: Context, orderId: number, quantity: 1, odooOrderLineId?: number): Promise { 8 | const logger : Logger = (process as any).winstonLog; 9 | 10 | if (!orderId) { 11 | logger.warn('Missing order id'); 12 | return { data: context.req.session.cart, error: 'Missing order id' }; 13 | } 14 | 15 | const previousAddedItem = context.req.session.cart.orderLines?.find(item => item.id === orderId); 16 | 17 | if (previousAddedItem) { 18 | previousAddedItem.quantity = quantity; 19 | 20 | populateCartRedis(context.req.session.cart); 21 | 22 | if (odooOrderLineId) { 23 | await cartUpdateItemQty(context, { lineId: odooOrderLineId, quantity }); 24 | } 25 | 26 | return { data: context.req.session.cart }; 27 | } 28 | 29 | logger.warn(`Order Item not found for id ${orderId}`); 30 | return { data: context.req.session.cart, error: `Order Item not found for id ${orderId}` }; 31 | } 32 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/fragments/index.ts: -------------------------------------------------------------------------------- 1 | export { default as partnerFragment } from './partnerFragment'; 2 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/fragments/partnerFragment.ts: -------------------------------------------------------------------------------- 1 | export default ` 2 | partner{ 3 | id 4 | name 5 | street 6 | street2 7 | city 8 | state 9 | { 10 | id 11 | } 12 | country 13 | { 14 | id 15 | } 16 | email 17 | phone 18 | } 19 | `; 20 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/queries/index.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client'; 2 | import ProductVariantQuery from './ProductVariantQuery'; 3 | import LoadUserQuery from './LoadUserQuery'; 4 | 5 | enum QueryName { 6 | ProductVariantQuery = 'ProductVariantQuery', 7 | LoadUserQuery = 'LoadUserQuery' 8 | } 9 | 10 | const Queries : Record = { 11 | ProductVariantQuery, 12 | LoadUserQuery 13 | }; 14 | 15 | export { 16 | Queries, 17 | QueryName 18 | }; 19 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json", 3 | "compilerOptions": { 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/nuxt-layer/server/utils/getCached.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints } from '@erpgap/odoo-sdk-api-client'; 2 | import { H3Event } from 'h3'; 3 | 4 | export const getCached = cachedFunction(async (event: H3Event, body: any) => { 5 | const api: Endpoints = event.context.apolloClient.api; 6 | 7 | return await api.query(body?.[0], body?.[1]); 8 | 9 | }, { swr: true, maxAge: 300 }); 10 | 11 | -------------------------------------------------------------------------------- /packages/nuxt-layer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.playground/.nuxt/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/sdk-api-client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | lib 4 | server 5 | -------------------------------------------------------------------------------- /packages/sdk-api-client/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/context.mock.ts: -------------------------------------------------------------------------------- 1 | import buildClient from '../src/setup/clientSetup'; 2 | import { MiddlewareConfig } from '../src/types'; 3 | 4 | const apolloClient = buildClient({ 5 | odooGraphqlUrl: 'http://localhost:5000/api/graphql', 6 | fetchOptions: {} 7 | }); 8 | 9 | export const contextMock = { 10 | config: { 11 | queries: { 12 | 13 | } 14 | } as MiddlewareConfig, 15 | client: apolloClient, 16 | api: jest.fn() as any 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/customQueries/customQueryCategoryListWithoutChild.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query CustomQueryCategoryListWithoutChild{ 4 | categories{ 5 | categories{ 6 | id 7 | name 8 | slug 9 | childs{ 10 | id 11 | name 12 | } 13 | } 14 | } 15 | }`; 16 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/customQueries/customQueryCategoryWithoutChild.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query CustomQueryCategoryWithoutChild($slug: String, $id: Int) { 4 | category(slug: $slug, id: $id) { 5 | id 6 | name 7 | slug 8 | } 9 | }`; 10 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/customQueries/customQueryCountryListWithoutState.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query CustomQueryCountryListWithoutState($filter: CountryFilterInput, $currentPage: Int, $pageSize: Int, $search: String, $sort: CountrySortInput) { 4 | countries(filter: $filter, currentPage: $currentPage, pageSize: $pageSize, search: $search, sort: $sort) { 5 | countries{ 6 | code 7 | id 8 | imageUrl 9 | name 10 | } 11 | } 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/customQueries/customQueryCountryWithoutState.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query customQueryCountryWithoutState($id: Int){ 4 | country ($id: Int) { 5 | code 6 | id 7 | imageUrl 8 | name 9 | } 10 | }`; -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/customQueries/customQueryFullProductTemplate.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query CustomQueryFullProductTemplate($id: Int, $slug: String, $barcode: String) { 4 | product(id: $id, slug: $slug, barcode: $barcode) { 5 | id 6 | } 7 | }`; 8 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/customQueries/customQueryFullProductTemplateListWithoutPrice.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query CustomQueryProductTemplateWithoutPrice { 4 | products{ 5 | products{ 6 | id 7 | name 8 | } 9 | } 10 | }`; 11 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/customQueries/customQueryProductVariant.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query CustomQueryProductVariant($productTemplateId: Int, $combinationId: [Int]) { 4 | productVariant( productTemplateId: $productTemplateId combinationId: $combinationId) { 5 | product { 6 | id 7 | image 8 | variantPrice 9 | variantPriceAfterDiscount 10 | variantHasDiscountedPrice 11 | } 12 | } 13 | }`; 14 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/address.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 672, 4 | "name": "home", 5 | "city": "Rio de Janeiro", 6 | "street": "Rua Pereira de Figueiredo", 7 | "street2": null, 8 | "state": { 9 | "id": 1, 10 | "name": "Australian Capital Territory", 11 | "code": "ACT" 12 | }, 13 | "zip": "21341030", 14 | "email": null, 15 | "country": { 16 | "id": 1, 17 | "name": "Andorra", 18 | "code": "AD" 19 | }, 20 | "phone": "987654", 21 | "contacts": null 22 | } 23 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/category.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "name": "Desks", 4 | "slug": "false", 5 | "childs": [ 6 | { 7 | "id": 10, 8 | "name": "Components", 9 | "slug": "false", 10 | "childs": null 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/categoryList.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Desks", 5 | "slug": "false" 6 | }, 7 | { 8 | "id": 10, 9 | "name": "Components", 10 | "slug": "false" 11 | }, 12 | { 13 | "id": 2, 14 | "name": "Furnitures", 15 | "slug": "false" 16 | }, 17 | { 18 | "id": 3, 19 | "name": "Chairs", 20 | "slug": "false" 21 | }, 22 | { 23 | "id": 4, 24 | "name": "Tables", 25 | "slug": "false" 26 | }, 27 | { 28 | "id": 5, 29 | "name": "Sofas", 30 | "slug": "false" 31 | }, 32 | { 33 | "id": 6, 34 | "name": "Beds", 35 | "slug": "false" 36 | }, 37 | { 38 | "id": 7, 39 | "name": "Wardrobes", 40 | "slug": "false" 41 | }, 42 | { 43 | "id": 8, 44 | "name": "Accessories", 45 | "slug": "false" 46 | }, 47 | { 48 | "id": 9, 49 | "name": "Lighting", 50 | "slug": "false" 51 | } 52 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/country.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": "AE", 3 | "id": 2, 4 | "imageUrl": "/base/static/img/country_flags/ae.png", 5 | "name": "United Arab Emirates", 6 | "states": [ 7 | { 8 | "id": 547 9 | }, 10 | { 11 | "id": 546 12 | }, 13 | { 14 | "id": 548 15 | }, 16 | { 17 | "id": 549 18 | }, 19 | { 20 | "id": 550 21 | }, 22 | { 23 | "id": 551 24 | }, 25 | { 26 | "id": 552 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/countryList.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "code": "AD", 4 | "id": 1, 5 | "imageUrl": "/base/static/img/country_flags/ad.png", 6 | "name": "Andorra", 7 | "states": null 8 | }, 9 | { 10 | "code": "AE", 11 | "id": 2, 12 | "imageUrl": "/base/static/img/country_flags/ae.png", 13 | "name": "United Arab Emirates", 14 | "states": [ 15 | { 16 | "id": 547, 17 | "name": "Ajman", 18 | "code": "AJ" 19 | }, 20 | { 21 | "id": 546, 22 | "name": "Abu Dhabi", 23 | "code": "AZ" 24 | }, 25 | { 26 | "id": 548, 27 | "name": "Dubai", 28 | "code": "DU" 29 | }, 30 | { 31 | "id": 549, 32 | "name": "Fujairah", 33 | "code": "FU" 34 | }, 35 | { 36 | "id": 550, 37 | "name": "Ras al-Khaimah", 38 | "code": "RK" 39 | }, 40 | { 41 | "id": 551, 42 | "name": "Sharjah", 43 | "code": "SH" 44 | }, 45 | { 46 | "id": 552, 47 | "name": "Umm al-Quwain", 48 | "code": "UQ" 49 | } 50 | ] 51 | }, 52 | { 53 | "code": "AF", 54 | "id": 3, 55 | "imageUrl": "/base/static/img/country_flags/af.png", 56 | "name": "Afghanistan", 57 | "states": null 58 | }, 59 | { 60 | "code": "AG", 61 | "id": 4, 62 | "imageUrl": "/base/static/img/country_flags/ag.png", 63 | "name": "Antigua and Barbuda", 64 | "states": null 65 | }, 66 | { 67 | "code": "AI", 68 | "id": 5, 69 | "imageUrl": "/base/static/img/country_flags/ai.png", 70 | "name": "Anguilla", 71 | "states": null 72 | } 73 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/customQueryCategoryListWithChilds.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Desks", 5 | "slug": "false", 6 | "childs": [ 7 | { 8 | "id": 10, 9 | "name": "Components" 10 | } 11 | ] 12 | }, 13 | { 14 | "id": 10, 15 | "name": "Components", 16 | "slug": "false", 17 | "childs": null 18 | }, 19 | { 20 | "id": 2, 21 | "name": "Furnitures", 22 | "slug": "false", 23 | "childs": [ 24 | { 25 | "id": 11, 26 | "name": "Chairs" 27 | }, 28 | { 29 | "id": 12, 30 | "name": "Couches" 31 | } 32 | ] 33 | }, 34 | { 35 | "id": 11, 36 | "name": "Chairs", 37 | "slug": "false", 38 | "childs": null 39 | } 40 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/customQueryCategoryWithoutChild.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 2, 3 | "name": "Desks", 4 | "slug": "false" 5 | } -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/customQueryCountryListWithoutState.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "code": "AD", 4 | "id": 1, 5 | "imageUrl": "/base/static/img/country_flags/ad.png", 6 | "name": "Andorra" 7 | }, 8 | { 9 | "code": "AE", 10 | "id": 2, 11 | "imageUrl": "/base/static/img/country_flags/ae.png", 12 | "name": "United Arab Emirates" 13 | }, 14 | { 15 | "code": "AF", 16 | "id": 3, 17 | "imageUrl": "/base/static/img/country_flags/af.png", 18 | "name": "Afghanistan" 19 | }, 20 | { 21 | "code": "AG", 22 | "id": 4, 23 | "imageUrl": "/base/static/img/country_flags/ag.png", 24 | "name": "Antigua and Barbuda" 25 | }, 26 | { 27 | "code": "AI", 28 | "id": 5, 29 | "imageUrl": "/base/static/img/country_flags/ai.png", 30 | "name": "Anguilla" 31 | }, 32 | { 33 | "code": "AL", 34 | "id": 6, 35 | "imageUrl": "/base/static/img/country_flags/al.png", 36 | "name": "Albania" 37 | }, 38 | { 39 | "code": "AM", 40 | "id": 7, 41 | "imageUrl": "/base/static/img/country_flags/am.png", 42 | "name": "Armenia" 43 | }, 44 | { 45 | "code": "AO", 46 | "id": 8, 47 | "imageUrl": "/base/static/img/country_flags/ao.png", 48 | "name": "Angola" 49 | }, 50 | { 51 | "code": "AQ", 52 | "id": 9, 53 | "imageUrl": null, 54 | "name": "Antarctica" 55 | }, 56 | { 57 | "code": "AR", 58 | "id": 10, 59 | "imageUrl": "/base/static/img/country_flags/ar.png", 60 | "name": "Argentina" 61 | }, 62 | { 63 | "code": "AS", 64 | "id": 11, 65 | "imageUrl": "/base/static/img/country_flags/as.png", 66 | "name": "American Samoa" 67 | }, 68 | { 69 | "code": "AT", 70 | "id": 12, 71 | "imageUrl": "/base/static/img/country_flags/at.png", 72 | "name": "Austria" 73 | }, 74 | { 75 | "code": "AU", 76 | "id": 13, 77 | "imageUrl": "/base/static/img/country_flags/au.png", 78 | "name": "Australia" 79 | }, 80 | { 81 | "code": "AW", 82 | "id": 14, 83 | "imageUrl": "/base/static/img/country_flags/aw.png", 84 | "name": "Aruba" 85 | }, 86 | { 87 | "code": "AX", 88 | "id": 15, 89 | "imageUrl": "/base/static/img/country_flags/ax.png", 90 | "name": "Åland Islands" 91 | }, 92 | { 93 | "code": "AZ", 94 | "id": 16, 95 | "imageUrl": "/base/static/img/country_flags/az.png", 96 | "name": "Azerbaijan" 97 | }, 98 | { 99 | "code": "BA", 100 | "id": 17, 101 | "imageUrl": "/base/static/img/country_flags/ba.png", 102 | "name": "Bosnia and Herzegovina" 103 | }, 104 | { 105 | "code": "BB", 106 | "id": 18, 107 | "imageUrl": "/base/static/img/country_flags/bb.png", 108 | "name": "Barbados" 109 | }, 110 | { 111 | "code": "BD", 112 | "id": 19, 113 | "imageUrl": "/base/static/img/country_flags/bd.png", 114 | "name": "Bangladesh" 115 | }, 116 | { 117 | "code": "BE", 118 | "id": 20, 119 | "imageUrl": "/base/static/img/country_flags/be.png", 120 | "name": "Belgium" 121 | } 122 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/customQueryProductTemplateWithoutPrice.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Restaurant Expenses" 5 | }, 6 | { 7 | "id": 2, 8 | "name": "Hotel Accommodation" 9 | }, 10 | { 11 | "id": 3, 12 | "name": "Virtual Interior Design" 13 | }, 14 | { 15 | "id": 4, 16 | "name": "Virtual Home Staging" 17 | } 18 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/customQueryProductVariant.json: -------------------------------------------------------------------------------- 1 | { 2 | "product": { 3 | "id": 74, 4 | "image": "/web/image/product.product/74/image_1920", 5 | "variantPrice": 148.75, 6 | "variantPriceAfterDiscount": 148.75, 7 | "variantHasDiscountedPrice": false 8 | } 9 | } -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/mailingContacts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "email": "admin@yourcompany.example.com", 5 | "companyName": null, 6 | "subscriptionList": [ 7 | { 8 | "id": 1, 9 | "mailingList": { 10 | "id": 1, 11 | "name": "Newsletter" 12 | }, 13 | "optOut": false 14 | } 15 | ] 16 | } 17 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/mailingLists.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Newsletter" 5 | }, 6 | { 7 | "id": 2, 8 | "name": "Imported Contacts" 9 | } 10 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/productTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1 3 | } -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/productTemplateList.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Restaurant Expenses", 5 | "price": 14 6 | }, 7 | { 8 | "id": 2, 9 | "name": "Hotel Accommodation", 10 | "price": 400 11 | }, 12 | { 13 | "id": 3, 14 | "name": "Virtual Interior Design", 15 | "price": 30.75 16 | }, 17 | { 18 | "id": 4, 19 | "name": "Virtual Home Staging", 20 | "price": 38.25 21 | } 22 | ] -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/productVariant.json: -------------------------------------------------------------------------------- 1 | { 2 | "product": { 3 | "id": 74, 4 | "image": "/web/image/product.product/74/image_1920", 5 | "variantPrice": 148.75, 6 | "variantPriceAfterDiscount": 148.75, 7 | "variantHasDiscountedPrice": false 8 | }, 9 | "productTemplateId": 46, 10 | "displayName": "[578902-00] Shirt Himons multi (40)", 11 | "price": 148.75, 12 | "listPrice": "148.75", 13 | "hasDiscountedPrice": false 14 | } -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/data/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 3, 3 | "name": "Mitchell Admin", 4 | "street": "215 Vine St", 5 | "street2": null, 6 | "city": "Scranton", 7 | "state": { 8 | "id": 47 9 | }, 10 | "country": { 11 | "id": 233 12 | }, 13 | "email": "admin@yourcompany.example.com", 14 | "phone": "+1 555-555-5555" 15 | } -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/server.ts: -------------------------------------------------------------------------------- 1 | import { setupServer } from 'msw/node' 2 | import { handlers } from './handlers' 3 | 4 | export const server = setupServer(...handlers) -------------------------------------------------------------------------------- /packages/sdk-api-client/__mocks__/setupTests.ts: -------------------------------------------------------------------------------- 1 | import { server } from './server' 2 | 3 | beforeAll(() => server.listen()) 4 | 5 | afterEach(() => server.resetHandlers()) 6 | 7 | afterAll(() => server.close()) 8 | 9 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__tests__/api/mutation.spec.ts: -------------------------------------------------------------------------------- 1 | import { mutation } from '../../src/api'; 2 | import { contextMock } from '../../__mocks__/context.mock'; 3 | import FullProduct from '../../__mocks__/customQueries/customQueryFullProductTemplateListWithoutPrice' 4 | 5 | describe('[ODOO-API] mutation', () => { 6 | 7 | it('throw exception because mutation object is not given', async () => { 8 | try { 9 | await mutation(contextMock, null as any); 10 | }catch(e) { 11 | expect(e).toBe('Developer Error: mutationName is required') 12 | } 13 | }); 14 | 15 | it('throw exception because mutation name is not given', async () => { 16 | try { 17 | await mutation(contextMock, { mutationName: '' }); 18 | }catch(e) { 19 | expect(e).toBe('Developer Error: mutationName is required') 20 | } 21 | }); 22 | 23 | it('throw exception because mutations are not configured in middleware', async () => { 24 | try { 25 | await mutation(contextMock, { mutationName: 'LoginMutation' }); 26 | }catch(e) { 27 | expect(e).toBe('Developer Error: mutations must be configured (MiddlewareConfig.queries)') 28 | } 29 | }); 30 | 31 | it('throw exception because mutations are not configured in middleware', async () => { 32 | contextMock.config.queries = { 33 | GetProduct: FullProduct 34 | } 35 | 36 | try { 37 | await mutation(contextMock, { mutationName: 'LogoutMutation' }); 38 | }catch(e) { 39 | expect(e).toBe('Developer Error: mutation LogoutMutation is not configured in middleware') 40 | } 41 | }); 42 | 43 | // it('works', async () => { 44 | // contextMock.config.queries = { 45 | // GetProduct: FullProduct, 46 | // GetVariant: customQueryCountryListWithoutState 47 | // } 48 | 49 | // const response = await query(contextMock, { queryName: 'GetVariant' }); 50 | 51 | // expect(response).toEqual({}) 52 | // }); 53 | 54 | }); 55 | -------------------------------------------------------------------------------- /packages/sdk-api-client/__tests__/api/query.spec.ts: -------------------------------------------------------------------------------- 1 | import { query } from '../../src/api'; 2 | import { contextMock } from '../../__mocks__/context.mock'; 3 | import FullProduct from '../../__mocks__/customQueries/customQueryFullProductTemplateListWithoutPrice' 4 | import customQueryCountryListWithoutState from '../../__mocks__/customQueries/customQueryCountryListWithoutState' 5 | 6 | describe('[ODOO-API] query', () => { 7 | 8 | 9 | it('throw exception because query object is not given', async () => { 10 | try { 11 | await query(contextMock, null as any); 12 | }catch(e) { 13 | expect(e).toBe('Developer Error: queryName is required') 14 | } 15 | }); 16 | 17 | it('throw exception because query name is not given', async () => { 18 | try { 19 | await query(contextMock, { queryName: '' }); 20 | }catch(e) { 21 | expect(e).toBe('Developer Error: queryName is required') 22 | } 23 | }); 24 | 25 | it('throw exception because queries are not configured in middleware', async () => { 26 | try { 27 | await query(contextMock, { queryName: 'GetProduct' }); 28 | }catch(e) { 29 | expect(e).toBe('Developer Error: queries must be configured (MiddlewareConfig.queries)') 30 | } 31 | }); 32 | 33 | it('throw exception because queries are not configured in middleware', async () => { 34 | contextMock.config.queries = { 35 | GetProduct: FullProduct 36 | } 37 | 38 | try { 39 | await query(contextMock, { queryName: 'GetVariant' }); 40 | }catch(e) { 41 | expect(e).toBe('Developer Error: query GetVariant is not configured in middleware') 42 | } 43 | }); 44 | 45 | // it('works', async () => { 46 | // contextMock.config.queries = { 47 | // GetProduct: FullProduct, 48 | // GetVariant: customQueryCountryListWithoutState 49 | // } 50 | 51 | // const response = await query(contextMock, { queryName: 'GetVariant' }); 52 | 53 | // expect(response).toEqual({}) 54 | // }); 55 | 56 | }); 57 | -------------------------------------------------------------------------------- /packages/sdk-api-client/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.base.json", 3 | "mainEntryPointFilePath": "./lib/index.d.ts", 4 | "dtsRollup": { 5 | "untrimmedFilePath": "./lib/.d.ts" 6 | }, 7 | "docModel": { 8 | "apiJsonFilePath": "/docs/reference/api/.api.json" 9 | } 10 | } -------------------------------------------------------------------------------- /packages/sdk-api-client/jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./../../jest.base.config'); 2 | 3 | const apiClientJestConfig = { 4 | setupFilesAfterEnv: ['./__mocks__/setupTests.ts'], 5 | ...baseConfig 6 | }; 7 | 8 | apiClientJestConfig.collectCoverageFrom = [ 9 | 'src/**/*.ts', 10 | '!src/types/**', 11 | '!src/extensions/**', 12 | '!src/gql/**', 13 | '!src/index.ts' 14 | ]; 15 | 16 | module.exports = apiClientJestConfig; 17 | -------------------------------------------------------------------------------- /packages/sdk-api-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erpgap/odoo-sdk-api-client", 3 | "version": "2.0.4", 4 | "private": false, 5 | "sideEffects": false, 6 | "server": "server/index.js", 7 | "main": "lib/index.cjs.js", 8 | "module": "lib/index.es.js", 9 | "types": "lib/index.d.ts", 10 | "license": "VSFEL", 11 | "engines": { 12 | "node": ">=16.x" 13 | }, 14 | "scripts": { 15 | "build": "rimraf lib server && rollup -c", 16 | "dev": "rollup -c -w", 17 | "test": "cross-env APP_ENV=test jest", 18 | "test:coverage": "jest --coverage --passWithNoTests", 19 | "test:watch": "jest --watch", 20 | "prepublish": "yarn build", 21 | "codegen": "yarn graphql-codegen", 22 | "lint": "eslint . --ext .ts,.vue" 23 | }, 24 | "dependencies": { 25 | "@apollo/client": "^3.9.9", 26 | "@vue-storefront/middleware": "3.5.1", 27 | "axios": "^0.21.1", 28 | "consola": "^3.0.0", 29 | "cross-fetch": "^4.0.0", 30 | "https-proxy-agent": "^7.0.4", 31 | "node-fetch": "^3.3.2", 32 | "request-ip": "^3.3.0", 33 | "winston": "^3.9.0" 34 | }, 35 | "devDependencies": { 36 | "eslint": "^8.45.0", 37 | "jsdom": "^17.0.0", 38 | "typescript": "^5.1.6" 39 | }, 40 | "files": [ 41 | "lib/**/*", 42 | "server/**/*", 43 | "src/**/*" 44 | ], 45 | "publishConfig": { 46 | "access": "public" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/sdk-api-client/rollup.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from '@rollup/plugin-node-resolve'; 2 | import typescript from 'rollup-plugin-typescript2'; 3 | import pkg from './package.json'; 4 | import { generateBaseConfig } from '../../rollup.base.config'; 5 | 6 | const extensions = ['.ts', '.js']; 7 | 8 | const server = { 9 | input: 'src/index.server.ts', 10 | output: [ 11 | { 12 | file: pkg.server, 13 | format: 'cjs', 14 | sourcemap: true 15 | } 16 | ], 17 | external: [ 18 | ...Object.keys(pkg.dependencies || {}), 19 | ...Object.keys(pkg.peerDependencies || {}) 20 | ], 21 | plugins: [ 22 | nodeResolve({ 23 | extensions 24 | }), 25 | typescript({ 26 | // eslint-disable-next-line global-require 27 | typescript: require('typescript'), 28 | objectHashIgnoreUnknownHack: false 29 | }) 30 | ] 31 | }; 32 | 33 | export default [ 34 | generateBaseConfig(pkg), // It's required for other packages using api-client's types 35 | server 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/api/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export { mutation } from './mutation'; 3 | export { query } from './query'; 4 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/api/mutation/index.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints, OdooIntegrationContext, MutationMetadataParams } from '../../types'; 2 | import consola from 'consola'; 3 | 4 | export const mutation: Endpoints['mutation'] = async (context: OdooIntegrationContext, metadata: MutationMetadataParams, params?: ApiParams) => { 5 | 6 | if(!metadata || !metadata.mutationName) { 7 | const msg = 'Developer Error: mutationName is required' 8 | consola.error(msg); 9 | throw msg; 10 | } 11 | 12 | if(!context.config.queries || Object.keys(context.config?.queries)?.length == 0) { 13 | const msg = 'Developer Error: mutations must be configured (MiddlewareConfig.queries)' 14 | consola.error(msg); 15 | throw msg; 16 | } 17 | 18 | const mutation = context.config.queries[metadata.mutationName]; 19 | 20 | if(!mutation) { 21 | const msg = `Developer Error: mutation ${metadata.mutationName} is not configured in middleware` 22 | consola.error(msg); 23 | throw msg; 24 | } 25 | 26 | 27 | const response = await context.client.mutate({ 28 | variables: params, 29 | mutation, 30 | fetchPolicy: 'no-cache', 31 | errorPolicy: 'all' 32 | }); 33 | 34 | return response; 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/api/query/index.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints, OdooIntegrationContext, QueryMetadataParams } from '../../types'; 2 | import consola from 'consola'; 3 | 4 | export const query: Endpoints['query'] = async (context: OdooIntegrationContext, metadata: QueryMetadataParams, params?: ApiParams) => { 5 | 6 | if (!metadata || !metadata.queryName) { 7 | const msg = 'Developer Error: queryName is required'; 8 | consola.error(msg); 9 | throw msg; 10 | } 11 | 12 | if (!context.config.queries || Object.keys(context.config?.queries)?.length == 0) { 13 | const msg = 'Developer Error: queries must be configured (MiddlewareConfig.queries)'; 14 | consola.error(msg); 15 | throw msg; 16 | } 17 | 18 | const query = context.config.queries[metadata.queryName]; 19 | 20 | if (!query) { 21 | const msg = `Developer Error: query ${metadata.queryName} is not configured in middleware`; 22 | consola.error(msg); 23 | throw msg; 24 | } 25 | 26 | const response = await context.client.query({ 27 | variables: params, 28 | query, 29 | fetchPolicy: 'no-cache', 30 | errorPolicy: 'all' 31 | }); 32 | 33 | return response; 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/extensions/sessionHeaderExtension .ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { ApiClientExtension } from '@vue-storefront/middleware'; 3 | import type { Request, Response } from 'express'; 4 | import requestIp from 'request-ip'; 5 | 6 | const sessionHeaderExtension: ApiClientExtension = { 7 | name: 'sessionHeaderExtension ', 8 | hooks: (req: Request, res: Response) => { 9 | return { 10 | beforeCreate: ({ configuration }) => { 11 | const clientIp = requestIp.getClientIp(req); 12 | 13 | return { 14 | ...configuration, 15 | sessionAuth: req.headers.cookie, 16 | requestHost: req.headers.host, 17 | realIp: clientIp 18 | }; 19 | }, 20 | beforeCall: ({ configuration, callName, args }) => args, 21 | afterCall: ({ configuration, callName, response }) => { 22 | if ((response as any)?.data?.cookie) { 23 | res.setHeader('Set-cookie', (response as any)?.data?.cookie); 24 | } 25 | 26 | return response; 27 | } 28 | 29 | }; 30 | } 31 | }; 32 | 33 | export default sessionHeaderExtension; 34 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/index.server.ts: -------------------------------------------------------------------------------- 1 | import buildClient from './setup/clientSetup'; 2 | import { apiClientFactory } from '@vue-storefront/middleware'; 3 | import { MiddlewareConfig } from './index'; 4 | import sessionHeaderExtension from './extensions/sessionHeaderExtension '; 5 | import * as apiEndpoints from './api'; 6 | 7 | const onCreate = (settings: MiddlewareConfig) => { 8 | const client = buildClient(settings); 9 | 10 | return { 11 | config: settings, 12 | client 13 | }; 14 | }; 15 | 16 | const { createApiClient } = apiClientFactory({ 17 | onCreate, 18 | api: apiEndpoints, 19 | extensions: [ 20 | sessionHeaderExtension 21 | ] 22 | }); 23 | 24 | export { createApiClient }; 25 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `api-client` for Vue Storefront 2 integration bolierplate. 3 | * 4 | * @remarks 5 | * In here you can find all references to the integration API Client. 6 | * 7 | * @packageDocumentation 8 | */ 9 | 10 | export * from './types'; 11 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/setup/clientSetup.ts: -------------------------------------------------------------------------------- 1 | 2 | import { ApolloClient, ApolloLink, InMemoryCache, createHttpLink } from '@apollo/client'; 3 | import { MiddlewareConfig } from '../index'; 4 | 5 | import fetch from 'cross-fetch'; 6 | 7 | const buildClient = (settings: MiddlewareConfig) => { 8 | 9 | const httpLink = createHttpLink({ 10 | uri: settings.odooGraphqlUrl, 11 | credentials: 'include', 12 | fetch, 13 | fetchOptions: settings.fetchOptions, 14 | headers: settings.headers 15 | }); 16 | 17 | const afterwareLink = new ApolloLink((operation, forward) => { 18 | return forward(operation).map((response) => { 19 | const context = operation.getContext(); 20 | const authHeader = context.response.headers.get('set-cookie'); 21 | 22 | if (response.data) { 23 | response.data.cookie = authHeader; 24 | } 25 | 26 | return response; 27 | }); 28 | }); 29 | 30 | const apolloLink = ApolloLink.from([ 31 | afterwareLink.concat(httpLink) 32 | ]); 33 | 34 | return new ApolloClient({ 35 | link: apolloLink, 36 | cache: new InMemoryCache(), 37 | ssrMode: true, 38 | credentials: 'include', 39 | defaultOptions: { 40 | query: { 41 | errorPolicy: 'all', 42 | fetchPolicy: 'no-cache' 43 | }, 44 | mutate: { 45 | errorPolicy: 'all' 46 | } 47 | } 48 | }); 49 | }; 50 | 51 | export default buildClient; 52 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/setup/logBuilder.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | import consola from 'consola'; 3 | 4 | interface LooseObject { 5 | label?: string, 6 | message?: string | Error, 7 | location?: string, 8 | path?: any, 9 | query?: string, 10 | variables?: string, 11 | locations?: any, 12 | operation?: any, 13 | } 14 | 15 | function getGqlString(doc: any) { 16 | return doc.loc && doc.loc?.source?.body?.replaceAll('\n', ''); 17 | } 18 | 19 | export default ({ label, message, locations, path, operation }: LooseObject) : void=> { 20 | 21 | const log : LooseObject = { label, message: message, path: path }; 22 | 23 | if (locations) { 24 | log.location = locations?.map(item => `line: ${item.line} | column: ${item.column}`).join(' '); 25 | } 26 | 27 | log.query = getGqlString(operation.query); 28 | log.variables = operation.variables; 29 | 30 | consola.error(log); 31 | }; 32 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/types/api/endpoints.ts: -------------------------------------------------------------------------------- 1 | import { ApolloQueryResult, FetchResult } from '@apollo/client'; 2 | import { OdooIntegrationContext } from '..'; 3 | 4 | export interface QueryMetadataParams { 5 | queryName: string; 6 | cacheKey?: string 7 | } 8 | export interface MutationMetadataParams { 9 | mutationName: string; 10 | } 11 | 12 | /** 13 | * Definition of all API-client methods available in {@link https://docs.vuestorefront.io/v2/advanced/context.html#context-api | context}. 14 | */ 15 | export interface Endpoints { 16 | 17 | /** 18 | * Here you can find an example endpoint definition. Based on this example, you should define how your endpoint will look like. 19 | * This description will appear in the API extractor, so try to document all endpoints added here. 20 | */ 21 | 22 | query(context: OdooIntegrationContext, metadata: QueryMetadataParams, params?: ApiParams): Promise>; 23 | mutation(context: OdooIntegrationContext, metadata: MutationMetadataParams, params?: ApiParams): Promise>; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/types/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './endpoints'; 2 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/types/config/index.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client'; 2 | 3 | /** 4 | * Settings to be provided in the `middleware.config.js` file. 5 | */ 6 | export interface MiddlewareConfig { 7 | odooGraphqlUrl: string; 8 | proxy?: string; 9 | fetchOptions?: any; 10 | fetch?: any; 11 | headers?: Record 12 | queries?: Record 13 | 14 | } 15 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/types/context/index.ts: -------------------------------------------------------------------------------- 1 | import { IntegrationContext, ExtendQuery } from '@vue-storefront/middleware'; 2 | import { MiddlewareConfig, ContextualizedEndpoints } from '../index'; 3 | import { ApolloClient } from '@apollo/client'; 4 | 5 | /** 6 | * Runtime integration context, which includes API client instance, settings, and endpoints that will be passed via middleware server. 7 | **/ 8 | export type OdooIntegrationContext = IntegrationContext< 9 | ApolloClient, 10 | MiddlewareConfig, 11 | ContextualizedEndpoints 12 | >; 13 | 14 | /** 15 | * Global context of the application which includes runtime integration context. 16 | **/ 17 | export interface Context { 18 | $odoo: OdooIntegrationContext; 19 | } 20 | -------------------------------------------------------------------------------- /packages/sdk-api-client/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints, QueryMetadataParams } from './api'; 2 | 3 | /** 4 | * All available API Endpoints without first argument - `context`, because this prop is set automatically. 5 | */ 6 | export type ContextualizedEndpoints = { 7 | [T in keyof Endpoints]: Endpoints[T] extends ( 8 | x: any, 9 | ...args: infer P 10 | ) => infer R 11 | ? (...args: P) => R 12 | : never; 13 | }; 14 | 15 | export type TODO = any; 16 | 17 | export { QueryMetadataParams }; 18 | export * from './api'; 19 | export * from './config'; 20 | export * from './context'; 21 | -------------------------------------------------------------------------------- /packages/sdk-api-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src"], 4 | "exclude": ["__mocks__", "__tests__"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/sdk/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | lib 4 | server 5 | .env 6 | -------------------------------------------------------------------------------- /packages/sdk/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__config__/jest.const.ts: -------------------------------------------------------------------------------- 1 | /** Setup variables */ 2 | export const NOCK_FIXTURES_CATALOG_NAME = '__nock-fixtures__'; 3 | export const NOCK_MODE = 'record'; 4 | export const NOCK_EXCLUDED_SCOPE = 'localhost'; 5 | -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__config__/jest.setup.global.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from '@vue-storefront/middleware'; 2 | 3 | const middlewareConfig = { 4 | integrations: { 5 | odoo: { 6 | location: '@erpgap/odoo-sdk-api-client/server', 7 | configuration: {}, 8 | }, 9 | } 10 | }; 11 | 12 | export default async () => { 13 | const app = await createServer(middlewareConfig); 14 | const server = await runMiddleware(app); 15 | 16 | // eslint-disable-next-line 17 | (globalThis as any).__MIDDLEWARE__ = server; 18 | }; 19 | 20 | async function runMiddleware (app: any) { 21 | return new Promise(resolve => { 22 | const server = app.listen(8181, async () => { 23 | resolve(server); 24 | }); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__config__/jest.setup.ts: -------------------------------------------------------------------------------- 1 | import nock from 'nock'; 2 | import path from 'path'; 3 | import { 4 | NOCK_MODE, 5 | NOCK_FIXTURES_CATALOG_NAME, 6 | NOCK_EXCLUDED_SCOPE, 7 | } from './jest.const'; 8 | 9 | let nockDone; 10 | /** 11 | * Sets Nock mode, fixture name and directory. Removes recorded requests to API Middleware 12 | * so that integration tests send them every time. 13 | */ 14 | async function setupNock (customFixtureName?) { 15 | nock.back.setMode(NOCK_MODE); 16 | nock.back.fixtures = path.join(__dirname, '../', NOCK_FIXTURES_CATALOG_NAME); 17 | 18 | const fixtureName = customFixtureName ?? expect.getState().currentTestName.split(' ').join('-'); 19 | const afterRecord = (recordings) => recordings.filter( 20 | recording => !recording.scope.toString().includes(NOCK_EXCLUDED_SCOPE) 21 | ); 22 | const { nockDone } = await nock.back(fixtureName, { afterRecord }); 23 | 24 | nock.enableNetConnect(); 25 | 26 | return nockDone; 27 | } 28 | 29 | beforeEach(async () => { 30 | nockDone = await setupNock(); 31 | }); 32 | 33 | afterEach(() => { 34 | nockDone(); 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__config__/jest.teardown.global.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | // eslint-disable-next-line 3 | (globalThis as any).__MIDDLEWARE__.close(); 4 | }; 5 | -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__config__/sdk.config.ts: -------------------------------------------------------------------------------- 1 | import { initSDK, buildModule } from '@vue-storefront/sdk'; 2 | import { OdooModule, OdooModuleType } from '../../../src'; 3 | 4 | const sdkConfig = { 5 | odooModule: buildModule(OdooModule, { 6 | apiUrl: 'http://localhost:8181/boilerplate', 7 | }), 8 | }; 9 | 10 | export const sdk = initSDK(sdkConfig); 11 | -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__nock-fixtures__/[Integration-Boilerplate-SDK][integration]-exampleMethod-makes-a-request-to-the-middleware: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__nock-fixtures__/[Integration-Boilerplate-SDK][unit]-exampleMethod-makes-a-call-to-API-Middleware-with-the-right-params: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__nock-fixtures__/[Integration-Boilerplate-SDK][unit]-exampleMethod-makes-a-single-call-to-API-Middleware: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/__nock-fixtures__/[Integration-Boilerplate-SDK][unit]-exampleMethod-throws-an-exception-in-case-of-network-error: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/sdk/__tests__/integration/exampleMethod.integration.spec.ts: -------------------------------------------------------------------------------- 1 | import { sdk } from './__config__/sdk.config'; 2 | 3 | describe('[Integration Boilerplate SDK][integration] exampleMethod', () => { 4 | it('makes a request to the middleware', async () => { 5 | const EXPECTED_RESPONSE = {}; 6 | 7 | const res = await sdk.boilerplate.getProductTemplateList({ }); 8 | 9 | expect(res).toEqual(EXPECTED_RESPONSE); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/sdk/__tests__/unit/exampleMethod.unit.spec.ts: -------------------------------------------------------------------------------- 1 | import { client } from '../../src/client'; 2 | 3 | /** SETUP */ 4 | const API_METHOD_NAME = 'getProductTemplateList'; 5 | const PARAMS_MOCK = { }; 6 | const RESPONSE_MOCK = { }; 7 | const ERROR_MOCK = new Error('error'); 8 | 9 | jest.mock('../../src/client', () => ({ 10 | client: { 11 | post: jest.fn(() => RESPONSE_MOCK) 12 | } 13 | })); 14 | 15 | /** TESTS */ 16 | describe('[Integration Boilerplate SDK][unit] exampleMethod', () => { 17 | 18 | beforeEach(() => { 19 | jest.clearAllMocks(); 20 | }) 21 | 22 | it('makes a single call to API Middleware', async () => { 23 | }); 24 | 25 | it('makes a call to API Middleware with the right params', async () => { 26 | }); 27 | 28 | it('throws an exception in case of network error', async () => { 29 | 30 | }); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /packages/sdk/api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../api-extractor.base.json", 3 | "mainEntryPointFilePath": "./lib/api-extractor.data.d.ts", 4 | "dtsRollup": { 5 | "untrimmedFilePath": "./lib/.d.ts" 6 | }, 7 | "docModel": { 8 | "apiJsonFilePath": "/docs/reference/api/.api.json" 9 | } 10 | } -------------------------------------------------------------------------------- /packages/sdk/jest.config.integration.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./../../jest.base.config'); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | preset: 'ts-jest', 6 | transform: { 7 | '^.+\\.(j|t)s$': 'ts-jest', 8 | }, 9 | globalSetup: './__tests__/integration/__config__/jest.setup.global.ts', 10 | globalTeardown: './__tests__/integration/__config__/jest.teardown.global.ts', 11 | setupFilesAfterEnv: ['./__tests__/integration/__config__/jest.setup.ts'] 12 | }; -------------------------------------------------------------------------------- /packages/sdk/jest.config.unit.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./../../jest.base.config'); 2 | 3 | module.exports = { ...baseConfig }; 4 | -------------------------------------------------------------------------------- /packages/sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erpgap/odoo-sdk", 3 | "version": "2.0.4", 4 | "private": false, 5 | "main": "lib/index.cjs.js", 6 | "module": "lib/index.es.js", 7 | "types": "lib/index.d.ts", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "build": "rimraf lib && rollup -c", 13 | "dev": "rimraf lib && rollup -c -w", 14 | "lint": "eslint . --ext .ts", 15 | "test": "yarn test:unit", 16 | "test:unit": "jest ./unit -c ./jest.config.unit.js", 17 | "test:integration": "jest ./integration -c ./jest.config.integration.js --runInBand", 18 | "test:integration:init": "rm -rf __tests__/integration/__nock-fixtures__ && jest ./integration -c ./jest.config.integration.js --runInBand", 19 | "test:watch": "jest --watch", 20 | "test:coverage": "jest --coverage --passWithNoTests", 21 | "prepublish": "yarn build" 22 | }, 23 | "dependencies": { 24 | "@apollo/client": "^3.7.16", 25 | "axios": "^1.4.0", 26 | "ofetch": "^1.3.3" 27 | }, 28 | "devDependencies": { 29 | "@erpgap/odoo-sdk-api-client": "*", 30 | "@rollup/plugin-babel": "^6.0.3", 31 | "@rollup/plugin-replace": "^2.3.3", 32 | "@vue-storefront/sdk": "1.1.2", 33 | "babel-jest": "^29.6.1", 34 | "msw": "^0.47.3", 35 | "nock": "^13.2.9", 36 | "rollup-plugin-typescript2": "^0.34.1" 37 | }, 38 | "engines": { 39 | "node": ">=16.x" 40 | }, 41 | "publishConfig": { 42 | "access": "public" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/sdk/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | import pkg from './package.json'; 3 | 4 | export default [ 5 | { 6 | input: 'src/index.ts', 7 | output: [ 8 | { 9 | file: pkg.main, 10 | format: 'cjs', 11 | sourcemap: true, 12 | }, 13 | { 14 | file: pkg.module, 15 | format: 'es', 16 | sourcemap: true, 17 | }, 18 | ], 19 | external: [...Object.keys(pkg.dependencies || {})], 20 | plugins: [ 21 | typescript({ 22 | // eslint-disable-next-line global-require 23 | typescript: require('typescript'), 24 | }), 25 | ], 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/sdk/src/api-extractor.data.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `sdk-connector` for Vue Storefront 2 integration bolierplate. 3 | * 4 | * @remarks 5 | * In here you can find all references to the integration SDK connector. 6 | * 7 | * @packageDocumentation 8 | */ 9 | export * from './types'; 10 | export * from './methods'; 11 | -------------------------------------------------------------------------------- /packages/sdk/src/client/index.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | const axiosInstance = axios.create(); 3 | axiosInstance.defaults.withCredentials = true; 4 | 5 | export const client = axiosInstance; 6 | -------------------------------------------------------------------------------- /packages/sdk/src/connector.ts: -------------------------------------------------------------------------------- 1 | import { client } from './client'; 2 | import { Options } from './types'; 3 | import * as methods from './methods/index'; 4 | import { MutationMetadataParams } from '../../sdk-api-client'; 5 | import { QueryMetadataParams } from '../../sdk-api-client'; 6 | import hash from 'object-hash'; 7 | 8 | /** 9 | * Connector methods. 10 | */ 11 | export type Methods = typeof methods; 12 | 13 | /** 14 | * Initialize the Odoo connector. 15 | */ 16 | export const odooConnector = (options: Options): Methods => { 17 | let mutation = null; 18 | let query = null; 19 | let queryNoCache = null; 20 | 21 | mutation = async (metadata: MutationMetadataParams, params?: ApiParams): Promise => { 22 | return await client.post('mutation', [metadata, params]); 23 | }; 24 | 25 | query = async (metadata: QueryMetadataParams, params?: ApiParams): Promise =>{ 26 | return await client.post('query', [metadata, params]); 27 | }; 28 | 29 | queryNoCache = async (metadata: QueryMetadataParams, params?: ApiParams): Promise =>{ 30 | return await client.post('query=no-cache', [metadata, params]); 31 | }; 32 | 33 | if (options.ofetch) { 34 | mutation = async (metadata: MutationMetadataParams, params?: ApiParams): Promise => { 35 | return options.ofetch('/api/odoo/mutation', { method: 'POST', body: [metadata, params], cache: 'no-cache', watch: false, ...options.ofetchOptions } as any); 36 | }; 37 | 38 | query = async (metadata: QueryMetadataParams, params?: ApiParams): Promise =>{ 39 | const cacheKey = metadata.cacheKey || hash({ ...metadata, ...params }); 40 | return options.ofetch('/api/odoo/query', { method: 'POST', body: [metadata, params], cache: 'no-cache', watch: false, key: cacheKey, ...options.ofetchOptions } as any); 41 | }; 42 | 43 | queryNoCache = async (metadata: QueryMetadataParams, params?: ApiParams): Promise =>{ 44 | const cacheKey = metadata.cacheKey || hash({ ...metadata, ...params }); 45 | return options.ofetch('/api/odoo/query-no-cache', { method: 'POST', body: [metadata, params], cache: 'no-cache', watch: false, key: cacheKey, ...options.ofetchOptions } as any); 46 | }; 47 | } 48 | 49 | client.defaults.baseURL = options.apiUrl; 50 | 51 | return { query, mutation, queryNoCache }; 52 | }; 53 | -------------------------------------------------------------------------------- /packages/sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | import { odooConnector } from './connector'; 2 | import type { Options } from './types'; 3 | import type { Module } from '@vue-storefront/sdk'; 4 | 5 | /** 6 | * Boulerplate module type. 7 | */ 8 | export interface OdooModuleType extends Module { 9 | 10 | /** 11 | * The connector of the Odoo module. 12 | */ 13 | connector: ReturnType; 14 | } 15 | 16 | /** 17 | * Odoo module. 18 | */ 19 | export const OdooModule = (options: Options): OdooModuleType => ({ 20 | connector: odooConnector({ 21 | apiUrl: options.apiUrl, 22 | ofetch: options.ofetch 23 | }), 24 | utils: {}, 25 | subscribers: {} 26 | }); 27 | 28 | export { client } from './client'; 29 | 30 | export * from './types'; 31 | -------------------------------------------------------------------------------- /packages/sdk/src/methods/index.ts: -------------------------------------------------------------------------------- 1 | export * from './query'; 2 | export * from './query-no-cache'; 3 | export * from './mutation'; 4 | -------------------------------------------------------------------------------- /packages/sdk/src/methods/mutation/index.ts: -------------------------------------------------------------------------------- 1 | import { FetchResult } from '@apollo/client'; 2 | import { MutationMetadataParams } from '@erpgap/odoo-sdk-api-client'; 3 | import { client } from '../../index'; 4 | 5 | /** 6 | * Make the query 7 | * 8 | * @example 9 | */ 10 | export async function mutation(metadata: MutationMetadataParams, params?: ApiParams): Promise { 11 | return await client.post('mutation', [metadata, params]); 12 | } 13 | -------------------------------------------------------------------------------- /packages/sdk/src/methods/query-no-cache/index.ts: -------------------------------------------------------------------------------- 1 | import { ApolloQueryResult } from '@apollo/client'; 2 | import { QueryMetadataParams, Endpoints } from '@erpgap/odoo-sdk-api-client'; 3 | import { client } from '../../client'; 4 | 5 | /** 6 | * Make the query 7 | * 8 | * @example 9 | */ 10 | export async function queryNoCache(metadata: QueryMetadataParams, params?: ApiParams): Promise { 11 | return await client.post('query-no-cache', [metadata, params]); 12 | } 13 | -------------------------------------------------------------------------------- /packages/sdk/src/methods/query/index.ts: -------------------------------------------------------------------------------- 1 | import { QueryMetadataParams, Endpoints } from '@erpgap/odoo-sdk-api-client'; 2 | import { client } from '../../client'; 3 | 4 | /** 5 | * Make the query 6 | * 7 | * @example 8 | */ 9 | export async function query(metadata: QueryMetadataParams, params?: ApiParams): Promise { 10 | return await client.post('query', [metadata, params]); 11 | } 12 | -------------------------------------------------------------------------------- /packages/sdk/src/types/MethodOptions.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from 'axios'; 2 | 3 | /** 4 | * Definition of the MethodOptions parameter. 5 | */ 6 | export interface MethodOptions { 7 | 8 | /** 9 | * {@link https://axios-http.com/docs/req_config | AxiosRequestConfig} object 10 | * You can use it to override Axios request configuration 11 | */ 12 | axiosRequestConfig?: Readonly; 13 | 14 | /** 15 | * Additional optional fields. Its usage depends on the custom implementation. 16 | */ 17 | [key: string]: any; 18 | } 19 | -------------------------------------------------------------------------------- /packages/sdk/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type TODO = any; 2 | 3 | export type { MethodOptions } from './MethodOptions'; 4 | export type { Options } from './options'; 5 | -------------------------------------------------------------------------------- /packages/sdk/src/types/options.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance } from 'axios'; 2 | import { $Fetch, FetchOptions } from 'ofetch'; 3 | 4 | /** Options for the SDK module */ 5 | export interface Options { 6 | 7 | /** The API URL of the client-side environment */ 8 | apiUrl: string; 9 | 10 | /** Custom fetch instance */ 11 | ofetch?: $Fetch; 12 | ofetchOptions?: FetchOptions; 13 | } 14 | -------------------------------------------------------------------------------- /packages/sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src"], 4 | "exclude": ["__mocks__", "__tests__"] 5 | } 6 | -------------------------------------------------------------------------------- /playground-nuxt/app/.env.example: -------------------------------------------------------------------------------- 1 | ODOO_BASE_URL=https://vsfdemo15.labs.odoogap.com/graphql/vsf 2 | 3 | NUXT_PUBLIC_MIDDLEWARE_URL=http://localhost:3000/ 4 | NUXT_PUBLIC_MIDDLEWARE_PORT=8443 5 | 6 | NODE_ENV=dev 7 | NUXT_NODE_LOCALE=en-EN 8 | NUXT_PUBLIC_VSF_PORT=3000 9 | 10 | REDIS_ENABLED=false 11 | REDIS_HOST=127.0.0.1 12 | REDIS_PORT=6379 13 | REDIS_PASSWORD=pass 14 | # Used for invalidating cache 15 | NUXT_REDIS_INVALIDATION_KEY=123 16 | 17 | NUXT_TELEMETRY_DISABLED=1 18 | GOOGLE_TAG_MANAGER_ID=1 -------------------------------------------------------------------------------- /playground-nuxt/app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | .nuxt 4 | .nitro 5 | .cache 6 | .output 7 | .env 8 | dist 9 | .DS_Store 10 | .env -------------------------------------------------------------------------------- /playground-nuxt/app/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Vue Storefront 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /playground-nuxt/app/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt Playground for Vue Storefront Integration Boilerplate 2 | 3 | ## Description 4 | 5 | Welcome to the Nuxt Playground! This repository serves as a real-world application setting that allows integrators to test, explore, and understand the practical implementation of their integrations with Vue Storefront's integration boilerplate. 6 | 7 | ***This is meant to be used as part of the intregation-boilerplate.*** 8 | 9 | For comprehensive information, guides, and details of the integration boilerplate, please refer to the parent project: [Vue Storefront Integration Boilerplate](https://github.com/vuestorefront/integration-boilerplate). 10 | 11 | ## Prerequisites 12 | 13 | To make the best use of this playground, you should have a basic understanding of the following technologies: 14 | 15 | - [Node.js](https://nodejs.org/) 16 | - [Nuxt.js](https://nuxt.com/) 17 | - [Vue Storefront Integration Boilerplate](https://github.com/vuestorefront/integration-boilerplate) 18 | 19 | ## Getting Started 20 | 21 | See: [Vue Storefront Integration Boilerplate](https://github.com/vuestorefront/integration-boilerplate). 22 | 23 | Remember, this playground is designed to put your integrations to work in a real application scenario. Use it to test, improve and showcase your solutions built upon Vue Storefront's integration boilerplate. 24 | 25 | Happy coding! 26 | -------------------------------------------------------------------------------- /playground-nuxt/app/app.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /playground-nuxt/app/assets/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /playground-nuxt/app/codegen.ts: -------------------------------------------------------------------------------- 1 | import { CodegenConfig } from '@graphql-codegen/cli' 2 | 3 | const config: CodegenConfig = { 4 | schema: [ 5 | { 6 | 'https://vsfdemo15.labs.odoogap.com/graphql/vsf': { 7 | }, 8 | }, 9 | ], 10 | ignoreNoDocuments: true, 11 | generates: { 12 | './graphql/gql/': { 13 | documents: ['graphql/**/*.tsx'], 14 | preset: 'client' 15 | }, 16 | }, 17 | } 18 | 19 | export default config 20 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/Breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 86 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/Card.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 28 | 29 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/Grid.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/MainBanner.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 81 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/Newsletter.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 84 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/NumberTitle.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 31 | 32 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/ProductCard.vue: -------------------------------------------------------------------------------- 1 | 22 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/TabBrowser.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 45 | 46 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/TheContent.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/cart/Drawer.vue: -------------------------------------------------------------------------------- 1 | 13 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /playground-nuxt/app/components/cart/Product.vue: -------------------------------------------------------------------------------- 1 | 30 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /playground-nuxt/app/graphql/gql/fragment-masking.ts: -------------------------------------------------------------------------------- 1 | import { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core'; 2 | import { FragmentDefinitionNode } from 'graphql'; 3 | import { Incremental } from './graphql'; 4 | 5 | 6 | export type FragmentType> = TDocumentType extends DocumentTypeDecoration< 7 | infer TType, 8 | any 9 | > 10 | ? [TType] extends [{ ' $fragmentName'?: infer TKey }] 11 | ? TKey extends string 12 | ? { ' $fragmentRefs'?: { [key in TKey]: TType } } 13 | : never 14 | : never 15 | : never; 16 | 17 | // return non-nullable if `fragmentType` is non-nullable 18 | export function useFragment( 19 | _documentNode: DocumentTypeDecoration, 20 | fragmentType: FragmentType> 21 | ): TType; 22 | // return nullable if `fragmentType` is nullable 23 | export function useFragment( 24 | _documentNode: DocumentTypeDecoration, 25 | fragmentType: FragmentType> | null | undefined 26 | ): TType | null | undefined; 27 | // return array of non-nullable if `fragmentType` is array of non-nullable 28 | export function useFragment( 29 | _documentNode: DocumentTypeDecoration, 30 | fragmentType: ReadonlyArray>> 31 | ): ReadonlyArray; 32 | // return array of nullable if `fragmentType` is array of nullable 33 | export function useFragment( 34 | _documentNode: DocumentTypeDecoration, 35 | fragmentType: ReadonlyArray>> | null | undefined 36 | ): ReadonlyArray | null | undefined; 37 | export function useFragment( 38 | _documentNode: DocumentTypeDecoration, 39 | fragmentType: FragmentType> | ReadonlyArray>> | null | undefined 40 | ): TType | ReadonlyArray | null | undefined { 41 | return fragmentType as any; 42 | } 43 | 44 | 45 | export function makeFragmentData< 46 | F extends DocumentTypeDecoration, 47 | FT extends ResultOf 48 | >(data: FT, _fragment: F): FragmentType { 49 | return data as FragmentType; 50 | } 51 | export function isFragmentReady( 52 | queryNode: DocumentTypeDecoration, 53 | fragmentNode: TypedDocumentNode, 54 | data: FragmentType, any>> | null | undefined 55 | ): data is FragmentType { 56 | const deferredFields = (queryNode as { __meta__?: { deferredFields: Record } }).__meta__ 57 | ?.deferredFields; 58 | 59 | if (!deferredFields) return true; 60 | 61 | const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined; 62 | const fragName = fragDef?.name?.value; 63 | 64 | const fields = (fragName && deferredFields[fragName]) || []; 65 | return fields.length > 0 && fields.every(field => data && field in data); 66 | } 67 | -------------------------------------------------------------------------------- /playground-nuxt/app/graphql/gql/gql.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as types from './graphql'; 3 | import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; 4 | 5 | const documents = []; 6 | /** 7 | * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. 8 | * 9 | * 10 | * @example 11 | * ```ts 12 | * const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`); 13 | * ``` 14 | * 15 | * The query argument is unknown! 16 | * Please regenerate the types. 17 | */ 18 | export function graphql(source: string): unknown; 19 | 20 | export function graphql(source: string) { 21 | return (documents as any)[source] ?? {}; 22 | } 23 | 24 | export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; -------------------------------------------------------------------------------- /playground-nuxt/app/graphql/gql/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fragment-masking"; 2 | export * from "./gql"; -------------------------------------------------------------------------------- /playground-nuxt/app/graphql/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./gql/fragment-masking"; 2 | export * from "./gql/gql"; 3 | export * from "./gql/graphql"; 4 | export * from "./types"; -------------------------------------------------------------------------------- /playground-nuxt/app/graphql/types.ts: -------------------------------------------------------------------------------- 1 | import { _AsyncData } from 'nuxt/dist/app/composables/asyncData'; 2 | import { H3Error } from 'h3'; 3 | import { Partner, ProductVariant } from '~/graphql'; 4 | 5 | export type SalesPersonResponse = _AsyncData< 6 | { 7 | productVariant: ProductVariant 8 | }, 9 | H3Error 10 | >; 11 | 12 | export type LoginMutationResponse = { 13 | login: { 14 | partner: Partner 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /playground-nuxt/app/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /playground-nuxt/app/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | modules: ['@nuxtjs/tailwindcss', '@nuxt/devtools'], 4 | runtimeConfig:{ 5 | public: { 6 | middlewareUrl: '', 7 | middlewarePort: 8181, 8 | odooImageUrl: '' 9 | } 10 | }, 11 | build: { 12 | transpile: ['tslib', '@apollo/client', '@apollo/client/core', '@vue/apollo-composable', '@vue/apollo-option', 'ts-invariant', 'vue-toastification', '@erpgap/odoo-sdk-api-client'] 13 | }, 14 | devtools: { 15 | enabled: true 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /playground-nuxt/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare", 11 | "predev": "yarn codegen", 12 | "codegen": "graphql-codegen" 13 | }, 14 | "dependencies": { 15 | "@erpgap/odoo-sdk-api-client": "*", 16 | "@nuxtjs/tailwindcss": "^6.6.5", 17 | "clipboard": "^2.0.11", 18 | "vue3-json-viewer": "^2.2.2" 19 | }, 20 | "devDependencies": { 21 | "@graphql-codegen/cli": "^4.0.1", 22 | "@graphql-codegen/client-preset": "^4.0.1", 23 | "@graphql-codegen/typescript": "^4.0.1", 24 | "@graphql-codegen/typescript-generic-sdk": "^3.1.0", 25 | "@graphql-typed-document-node/core": "^3.2.0", 26 | "@nuxtjs/tailwindcss": "^6.6.5", 27 | "@storefront-ui/typography": "^2.0.0", 28 | "@storefront-ui/vue": "^2.2.0", 29 | "@typescript-eslint/eslint-plugin": "^6.1.0", 30 | "nuxt": "^3.8.0" 31 | }, 32 | "resolutions": { 33 | "consola": "^3.0.0" 34 | } 35 | } -------------------------------------------------------------------------------- /playground-nuxt/app/pages/category/[id].vue: -------------------------------------------------------------------------------- 1 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /playground-nuxt/app/pages/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /playground-nuxt/app/plugins/1.winston.server.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtPlugin } from '#app'; 2 | import { createLogger, format, transports } from 'winston'; 3 | const { combine, timestamp, label, prettyPrint } = format; 4 | 5 | export default defineNuxtPlugin(async (nuxtApp) => { 6 | 7 | let loggerWinston: any; 8 | 9 | loggerWinston = createLogger({ 10 | format: combine( 11 | timestamp(), 12 | prettyPrint() 13 | ), 14 | transports: [ 15 | new transports.Console() 16 | ] 17 | }); 18 | 19 | (process as any).winstonLog = loggerWinston 20 | 21 | return { 22 | provide: { 23 | logger: () => { 24 | return loggerWinston; 25 | }, 26 | }, 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /playground-nuxt/app/plugins/2.getImage.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtPlugin(async (nuxtApp) => { 2 | const config = useRuntimeConfig() 3 | 4 | return { 5 | provide: { 6 | getImage: (imagePath: string, width: number, heigth: number, name: string) => { 7 | const resolution = `${width}x${heigth}`; 8 | return `${config.public.odooImageUrl}${imagePath?.replace('/', '')}/${resolution}/${name}_${resolution}`; 9 | } 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /playground-nuxt/app/plugins/3.sdk.ts: -------------------------------------------------------------------------------- 1 | import { initSDK, buildModule } from '@vue-storefront/sdk'; 2 | import { OdooModule, OdooModuleType } from '../../../packages/sdk/src'; 3 | 4 | export default defineNuxtPlugin(async (nuxtApp) => { 5 | const config = useRuntimeConfig(); 6 | 7 | const sdkConfig = { 8 | odoo: buildModule(OdooModule, { 9 | apiUrl: `${config.public.middlewareUrl}api/odoo/`, 10 | ofetch: useFetch 11 | }) 12 | }; 13 | 14 | return { 15 | provide: { 16 | sdk: () => initSDK(sdkConfig) 17 | } 18 | }; 19 | }); 20 | -------------------------------------------------------------------------------- /playground-nuxt/app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuestorefront-community/odoo/c06fd20e9bb5e7c203f09d86ca06df397ffc016a/playground-nuxt/app/public/favicon.ico -------------------------------------------------------------------------------- /playground-nuxt/app/server/api/odoo/mutation.post.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints } from '@erpgap/odoo-sdk-api-client'; 2 | 3 | export default defineEventHandler(async (event) => { 4 | const body = await readBody(event); 5 | 6 | const api: Endpoints = event.context.apolloClient.api; 7 | 8 | const response = await api.mutation(body?.[0], body?.[1]); 9 | 10 | if ((response.data as any)?.cookie) { 11 | appendResponseHeader(event, 'Set-cookie', (response.data as any)?.cookie); 12 | } 13 | 14 | if (response.errors) { 15 | throw createError({ statusCode: 400, data: response.errors }); 16 | } 17 | 18 | return response.data; 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/api/odoo/query.post.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints } from '@erpgap/odoo-sdk-api-client'; 2 | import { getCached } from '~/server/utils/getCached'; 3 | 4 | export default defineEventHandler(async (event) => { 5 | const body = await readBody(event); 6 | const api: Endpoints = event.context.apolloClient.api; 7 | 8 | // const data = await getCached(event, body); 9 | const response = await api.query(body?.[0], body?.[1]); 10 | 11 | if ((response.data as any)?.cookie) { 12 | appendResponseHeader(event, 'Set-cookie', (response.data as any)?.cookie); 13 | } 14 | 15 | if (response.errors) { 16 | throw createError(response.errors[0].message); 17 | } 18 | 19 | return response.data; 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/middleware/apolloClient.ts: -------------------------------------------------------------------------------- 1 | import { createApiClient } from '../../../../packages/sdk-api-client/src/index.server'; 2 | import { MiddlewareConfig } from '../../../../packages/sdk-api-client/src/index'; 3 | import { Queries } from '~/server/queries'; 4 | import { Mutations } from '~/server/mutations'; 5 | 6 | export default defineEventHandler((event) => { 7 | 8 | if(event.method == 'POST') { 9 | const config : MiddlewareConfig = { 10 | odooGraphqlUrl: `${process.env.ODOO_BASE_URL}graphql/vsf`, 11 | queries: { ...Queries, ...Mutations }, 12 | headers: { 13 | Cookie: `session_id=${parseCookies(event).session_id}`, 14 | 'resquest-host': getRequestHost(event), 15 | 'REAL-IP': getRequestIP(event) || '' 16 | } 17 | }; 18 | 19 | event.context.apolloClient = createApiClient(config); 20 | } 21 | 22 | 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/mutations/ChangePasswordMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($newPassword: String!, $token: String!) { 4 | changePassword(newPassword: $newPassword, token: $token) { 5 | id 6 | name 7 | email 8 | } 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/mutations/CreateNewAccountMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | 3 | export default gql` 4 | mutation register($companyName: String!, $firstName: String!, $lastName: String!, $email: String!, $location : String!$password: String!, $phoneNumber: String!) { 5 | register(companyName: $companyName, firstName: $firstName, lastName: $lastName, email: $email, location: $location, password: $password, phoneNumber: $phoneNumber) { 6 | partner{ 7 | id 8 | name 9 | street 10 | street2 11 | city 12 | state 13 | { 14 | id 15 | } 16 | country 17 | { 18 | id 19 | } 20 | email 21 | phone 22 | } 23 | } 24 | }`; 25 | 26 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/mutations/LoginMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($email: String!, $password: String!) { 4 | login(email: $email, password: $password) { 5 | partner{ 6 | id 7 | name 8 | street 9 | street2 10 | city 11 | state 12 | { 13 | id 14 | } 15 | country 16 | { 17 | id 18 | } 19 | email 20 | phone 21 | } 22 | } 23 | }`; 24 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/mutations/LogoutMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation { 4 | logout 5 | } 6 | `; 7 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/mutations/SendResetPasswordMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($email: String!) { 4 | resetPassword(email: $email) { 5 | id 6 | name 7 | email 8 | } 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/mutations/UpdatePasswordMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | mutation($currentPassword: String!, $newPassword: String!){ 4 | updatePassword(currentPassword: $currentPassword, newPassword: $newPassword) { 5 | id 6 | } 7 | } 8 | `; 9 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client'; 2 | import LoginMutation from './LoginMutation'; 3 | import LogoutMutation from './LogoutMutation'; 4 | import CreateNewAccountMutation from './CreateNewAccountMutation'; 5 | import SendResetPasswordMutation from './SendResetPasswordMutation'; 6 | import UpdatePasswordMutation from './UpdatePasswordMutation'; 7 | import ChangePasswordMutation from './ChangePasswordMutation'; 8 | 9 | enum MutationName { 10 | LoginMutation = 'LoginMutation', 11 | LogoutMutation = 'LogoutMutation', 12 | CreateNewAccountMutation = 'CreateNewAccountMutation', 13 | SendResetPasswordMutation = 'SendResetPasswordMutation', 14 | UpdatePasswordMutation = 'UpdatePasswordMutation', 15 | ChangePasswordMutation = 'ChangePasswordMutation' 16 | } 17 | 18 | const Mutations : Record = { 19 | LoginMutation, 20 | LogoutMutation, 21 | CreateNewAccountMutation, 22 | SendResetPasswordMutation, 23 | UpdatePasswordMutation, 24 | ChangePasswordMutation 25 | }; 26 | 27 | export { 28 | Mutations, 29 | MutationName 30 | }; 31 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/queries/GetCategories.ts: -------------------------------------------------------------------------------- 1 | import { partnerFragment } from './fragments'; 2 | import { gql } from '@apollo/client/core'; 3 | 4 | export default gql` 5 | query( 6 | $search: String 7 | $filter: CategoryFilterInput 8 | $currentPage: Int 9 | $pageSize: Int 10 | $sort: CategorySortInput 11 | ) { 12 | categories( 13 | search: $search 14 | filter: $filter 15 | currentPage: $currentPage 16 | pageSize: $pageSize 17 | sort: $sort 18 | ) { 19 | categories { 20 | id 21 | name 22 | slug 23 | childs { 24 | id 25 | name 26 | slug 27 | childs { 28 | id 29 | name 30 | slug 31 | } 32 | } 33 | parent { 34 | id 35 | name 36 | slug 37 | } 38 | } 39 | } 40 | } 41 | `; 42 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/queries/GetProductTemplateList.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | 3 | export default gql` 4 | query( 5 | $filter: ProductFilterInput 6 | $currentPage: Int 7 | $pageSize: Int = 0 8 | $search: String 9 | $sort: ProductSortInput 10 | ) { 11 | products( 12 | filter: $filter 13 | currentPage: $currentPage 14 | pageSize: $pageSize 15 | search: $search 16 | sort: $sort 17 | ) { 18 | totalCount 19 | attributeValues { 20 | id 21 | name 22 | displayType 23 | name 24 | htmlColor 25 | search 26 | attribute{ 27 | id 28 | name 29 | } 30 | 31 | } 32 | products { 33 | id 34 | } 35 | } 36 | } 37 | `; 38 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/queries/LoadUserQuery.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | import {partnerFragment} from './fragments'; 3 | export default gql` 4 | query LoadUser { 5 | ${partnerFragment} 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/queries/ProductVariantQuery.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core'; 2 | export default gql` 3 | query ProductVariant($productTemplateId: Int, $combinationId: [Int]) { 4 | productVariant( productTemplateId: $productTemplateId combinationId: $combinationId) { 5 | product { 6 | id 7 | image 8 | variantPrice 9 | variantPriceAfterDiscount 10 | variantHasDiscountedPrice 11 | alternativeProducts{ 12 | id 13 | typeId 14 | visibility 15 | status 16 | name 17 | displayName 18 | sku 19 | barcode 20 | description 21 | } 22 | } 23 | productTemplateId 24 | displayName 25 | price 26 | listPrice 27 | hasDiscountedPrice 28 | } 29 | }`; 30 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/queries/fragments/index.ts: -------------------------------------------------------------------------------- 1 | export { default as partnerFragment } from './partnerFragment'; 2 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/queries/fragments/partnerFragment.ts: -------------------------------------------------------------------------------- 1 | export default ` 2 | partner{ 3 | id 4 | name 5 | street 6 | street2 7 | city 8 | state 9 | { 10 | id 11 | } 12 | country 13 | { 14 | id 15 | } 16 | email 17 | phone 18 | } 19 | `; 20 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/queries/index.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client'; 2 | import ProductVariantQuery from './ProductVariantQuery'; 3 | import LoadUserQuery from './LoadUserQuery'; 4 | import GetCategories from './GetCategories'; 5 | import GetProductTemplateList from './GetProductTemplateList'; 6 | 7 | enum QueryName { 8 | ProductVariantQuery = 'ProductVariantQuery', 9 | LoadUserQuery = 'LoadUserQuery', 10 | GetCategories = 'GetCategories', 11 | GetProductTemplateList = 'GetProductTemplateList' 12 | } 13 | 14 | const Queries : Record = { 15 | ProductVariantQuery, 16 | LoadUserQuery, 17 | GetCategories, 18 | GetProductTemplateList 19 | }; 20 | 21 | export { 22 | Queries, 23 | QueryName 24 | }; 25 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json", 3 | "compilerOptions": { 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /playground-nuxt/app/server/utils/getCached.ts: -------------------------------------------------------------------------------- 1 | import { Endpoints } from '@erpgap/odoo-sdk-api-client'; 2 | import { H3Event } from 'h3'; 3 | 4 | export const getCached = cachedFunction(async (event: H3Event, body: any) => { 5 | const api: Endpoints = event.context.apolloClient.api; 6 | 7 | return await api.query(body?.[0], body?.[1]); 8 | 9 | }, { swr: true, maxAge: 300 }); 10 | 11 | -------------------------------------------------------------------------------- /playground-nuxt/app/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | import { tailwindConfig } from '@storefront-ui/vue/tailwind-config'; 3 | import sfTypography from '@storefront-ui/typography'; 4 | 5 | export default { 6 | presets: [tailwindConfig], 7 | content: ['./**/*.vue', '../../node_modules/@storefront-ui/vue/**/*.{js,mjs}'], 8 | plugins: [sfTypography], 9 | css: ['~/assets/css/tailwind.css'], 10 | }; 11 | -------------------------------------------------------------------------------- /playground-nuxt/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json", 4 | "compilerOptions": { 5 | "verbatimModuleSyntax": false 6 | } 7 | } -------------------------------------------------------------------------------- /playground-other/server/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@vue-storefront/eslint-config' 3 | ], 4 | parserOptions: { 5 | project: './tsconfig.json', 6 | tsconfigRootDir: __dirname, 7 | sourceType: 'module', 8 | }, 9 | ignorePatterns: [' /*.*', 'lib/**/*.ts', '**/*.js'], 10 | rules: { 11 | 'unicorn/prefer-top-level-await': 'off', 12 | }, 13 | }; -------------------------------------------------------------------------------- /playground-other/server/middleware.config.ts: -------------------------------------------------------------------------------- 1 | import { Integration } from '@vue-storefront/middleware'; 2 | import { Queries } from './queries' 3 | import { Mutations } from './mutations' 4 | 5 | const odooIntegration: Integration = { 6 | location: '@erpgap/odoo-sdk-api-client/server', 7 | configuration: { 8 | odooGraphqlUrl: 'https://vsfdemo15.labs.odoogap.com/graphql/vsf', 9 | queries: { ...Queries, ...Mutations } 10 | } 11 | }; 12 | 13 | export default { 14 | integrations: { 15 | odoo: odooIntegration 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /playground-other/server/mutations/LoginMutation.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | mutation($email: String!, $password: String!) { 4 | login(email: $email, password: $password) { 5 | partner{ 6 | id 7 | name 8 | street 9 | street2 10 | city 11 | state 12 | { 13 | id 14 | } 15 | country 16 | { 17 | id 18 | } 19 | email 20 | phone 21 | } 22 | } 23 | }`; 24 | -------------------------------------------------------------------------------- /playground-other/server/mutations/index.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client'; 2 | import LoginMutation from './LoginMutation'; 3 | 4 | enum MutationName { 5 | LoginMutation = 'LoginMutation' 6 | } 7 | 8 | const Mutations : Record = { 9 | LoginMutation 10 | } 11 | 12 | export { 13 | Mutations, 14 | MutationName 15 | } -------------------------------------------------------------------------------- /playground-other/server/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "**/*" 4 | ], 5 | "ext": "ts", 6 | "exec": "ts-node-dev src/index.ts" 7 | } -------------------------------------------------------------------------------- /playground-other/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erp-gap/server", 3 | "private": true, 4 | "version": "0.1.0", 5 | "engines": { 6 | "node": ">=16.x" 7 | }, 8 | "scripts": { 9 | "dev": "nodemon", 10 | "start": "nodemon", 11 | "lint": "eslint . --ext .ts", 12 | "lint:fix": "eslint . --ext .ts --fix" 13 | }, 14 | "dependencies": { 15 | "@erpgap/odoo-sdk-api-client": "*", 16 | "@vue-storefront/middleware": "3.5.1", 17 | "consola": "^3.0.0", 18 | "cors": "^2.8.5", 19 | "graphql": "^16.8.1", 20 | "graphql-codegen": "^0.4.0", 21 | "nodemon": "^3.0.0", 22 | "ts-node-dev": "^2.0.0" 23 | }, 24 | "devDependencies": { 25 | "@graphql-codegen/cli": "^5.0.0", 26 | "@graphql-codegen/client-preset": "^4.0.1", 27 | "@graphql-codegen/typescript": "^4.0.1", 28 | "@graphql-codegen/typescript-generic-sdk": "^3.1.0", 29 | "@graphql-typed-document-node/core": "^3.2.0", 30 | "@types/express": "^4.17.13", 31 | "@typescript-eslint/eslint-plugin": "^6.1.0", 32 | "eslint": "^8.50.0", 33 | "eslint-plugin-prettier": "5.0.0" 34 | }, 35 | "files": [ 36 | "lib/**/*", 37 | "server/**/*" 38 | ] 39 | } -------------------------------------------------------------------------------- /playground-other/server/queries/ProductVariantQuery.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client'; 2 | export default gql` 3 | query ProductVariant($productTemplateId: Int, $combinationId: [Int]) { 4 | productVariant( productTemplateId: $productTemplateId combinationId: $combinationId) { 5 | product { 6 | id 7 | image 8 | variantPrice 9 | variantPriceAfterDiscount 10 | variantHasDiscountedPrice 11 | alternativeProducts{ 12 | id 13 | typeId 14 | visibility 15 | status 16 | name 17 | displayName 18 | sku 19 | barcode 20 | description 21 | } 22 | } 23 | productTemplateId 24 | displayName 25 | price 26 | listPrice 27 | hasDiscountedPrice 28 | } 29 | }`; 30 | -------------------------------------------------------------------------------- /playground-other/server/queries/index.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client'; 2 | import ProductVariantQuery from './ProductVariantQuery'; 3 | 4 | enum QueryName { 5 | ProductVariantQuery = 'ProductVariantQuery' 6 | } 7 | 8 | const Queries : Record = { 9 | ProductVariantQuery 10 | } 11 | 12 | export { 13 | Queries, 14 | QueryName 15 | } -------------------------------------------------------------------------------- /playground-other/server/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from '@vue-storefront/middleware'; 2 | import consola from 'consola'; 3 | import config from '../middleware.config'; 4 | 5 | (async () => { 6 | const app = await createServer({ integrations: config.integrations }); 7 | const host = process.argv[2] ?? '0.0.0.0'; 8 | const port = Number(process.argv[3]) || 8181; 9 | 10 | app.listen(port, host, () => { 11 | consola.success(`API server listening on http://${host}:${port}`); 12 | }); 13 | })(); -------------------------------------------------------------------------------- /playground-other/server/src/types.ts: -------------------------------------------------------------------------------- 1 | import { QueryName } from '../queries' 2 | import { MutationName} from '../mutations' 3 | 4 | export { 5 | QueryName, 6 | MutationName 7 | } 8 | 9 | -------------------------------------------------------------------------------- /rollup.base.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from '@rollup/plugin-node-resolve'; 2 | import typescript from 'rollup-plugin-typescript2'; 3 | import { terser } from 'rollup-plugin-terser'; 4 | 5 | const extensions = ['.ts', '.js']; 6 | 7 | export function generateBaseConfig(pkg) { 8 | return { 9 | input: 'src/index.ts', 10 | output: [ 11 | { 12 | file: pkg.main, 13 | format: 'cjs', 14 | sourcemap: true 15 | }, 16 | { 17 | file: pkg.module, 18 | format: 'es', 19 | sourcemap: true 20 | } 21 | ], 22 | external: [ 23 | ...Object.keys(pkg.dependencies || {}), 24 | ...Object.keys(pkg.peerDependencies || {}) 25 | ], 26 | plugins: [ 27 | nodeResolve({ 28 | extensions 29 | }), 30 | typescript({ 31 | // eslint-disable-next-line global-require 32 | typescript: require('typescript'), 33 | objectHashIgnoreUnknownHack: false 34 | }), 35 | terser() 36 | ] 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /scripts/lib/publishNpm.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/no-process-exit, unicorn/prefer-module */ 2 | const { exec } = require("child_process"); 3 | 4 | const publishPackages = (pkgPath, labels) => { 5 | return new Promise((_res, _rej) => { 6 | try { 7 | const command = `npm publish ${pkgPath} --access public --tag ${labels}`; 8 | 9 | console.log(command) 10 | 11 | exec(command, (error, stdout, stderr) => { 12 | if (error) { 13 | console.log(`error: ${error.message}`); 14 | return; 15 | } 16 | if (stderr) { 17 | console.log(`stderr: ${stderr}`); 18 | return; 19 | } 20 | console.log(`stdout: ${stdout}`); 21 | }); 22 | } catch (e) { 23 | console.error(e); 24 | } 25 | }); 26 | } 27 | 28 | module.exports = { 29 | publishPackages, 30 | } 31 | -------------------------------------------------------------------------------- /scripts/publishApi.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/no-process-exit, unicorn/prefer-module */ 2 | const path = require('path'); 3 | const { publishPackages } = require('./lib/publishNpm'); 4 | 5 | const myArgs = process.argv.slice(2); 6 | const labels = myArgs[0]; 7 | 8 | publishPackages(path.join(process.cwd(), 'packages', 'api-client'), labels) 9 | .then(console.log) 10 | .catch((e) => { 11 | console.error(e); 12 | }); 13 | -------------------------------------------------------------------------------- /scripts/publishComposable.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/prefer-module */ 2 | const path = require('path'); 3 | const { publishPackages } = require('./lib/publishNpm'); 4 | 5 | const myArgs = process.argv.slice(2); 6 | const labels = myArgs[0]; 7 | 8 | publishPackages(path.join(process.cwd(), 'packages', 'composables'), labels) 9 | .then(console.log) 10 | .catch((e) => { 11 | console.error(e); 12 | }); 13 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./lib", 4 | "esModuleInterop": true, 5 | "target": "ES2020", 6 | "module": "ESNext", 7 | "moduleResolution": "node", 8 | "importHelpers": true, 9 | "noEmitHelpers": true, 10 | "sourceMap": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "skipLibCheck": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "baseUrl": "./", 16 | "lib": ["ES2020", "dom"], 17 | "strict": false, 18 | "preserveSymlinks": true, 19 | "resolveJsonModule": true 20 | }, 21 | "exclude": ["node_modules", "**/*.spec.ts", "**/*.mock.ts"] 22 | } 23 | --------------------------------------------------------------------------------