├── .editorconfig ├── .github ├── oxid-esales │ └── graphql-configuration-access_light.yaml └── workflows │ ├── dispatch_module.yaml │ ├── sbom.yaml │ ├── schema_trigger.yaml │ └── trigger.yaml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets └── logo.png ├── composer.json ├── dependencies.yaml ├── deptrac.yaml ├── git-hooks └── init.sh ├── metadata.php ├── module_blocklist.yaml ├── recipes └── setup-development.sh ├── services.yaml ├── src ├── Module │ ├── Controller │ │ ├── ModuleActivationController.php │ │ ├── ModuleListController.php │ │ └── ModuleSettingController.php │ ├── DataType │ │ ├── ModuleDataType.php │ │ ├── ModuleDataTypeFactory.php │ │ ├── ModuleDataTypeFactoryInterface.php │ │ └── ModuleDataTypeInterface.php │ ├── Exception │ │ ├── ModuleActivationBlockedException.php │ │ ├── ModuleActivationException.php │ │ ├── ModuleDeactivationBlockedException.php │ │ ├── ModuleDeactivationException.php │ │ └── ModulesNotFoundException.php │ ├── Infrastructure │ │ ├── ModuleListInfrastructure.php │ │ └── ModuleListInfrastructureInterface.php │ ├── Service │ │ ├── ModuleActivationService.php │ │ ├── ModuleActivationServiceInterface.php │ │ ├── ModuleBlocklistService.php │ │ ├── ModuleBlocklistServiceInterface.php │ │ ├── ModuleListService.php │ │ ├── ModuleListServiceInterface.php │ │ ├── ModuleSettingService.php │ │ └── ModuleSettingServiceInterface.php │ └── services.yaml ├── Shared │ ├── DataType │ │ ├── AbstractComponentDataType.php │ │ ├── BooleanSetting.php │ │ ├── ComponentDataTypeInterface.php │ │ ├── ComponentFilters.php │ │ ├── ComponentFiltersInterface.php │ │ ├── FloatSetting.php │ │ ├── IntegerSetting.php │ │ ├── SettingType.php │ │ └── StringSetting.php │ ├── Enum │ │ └── FieldType.php │ ├── Exception │ │ ├── CollectionEncodingException.php │ │ ├── InvalidCollectionException.php │ │ ├── NoSettingsFoundException.php │ │ └── WrongSettingValueException.php │ ├── Infrastructure │ │ ├── LanguageWrapper.php │ │ ├── LanguageWrapperInterface.php │ │ └── services.yaml │ ├── Service │ │ ├── CollectionEncodingServiceInterface.php │ │ ├── ComponentFilterService.php │ │ ├── ComponentFilterServiceInterface.php │ │ ├── JsonCollectionEncodingService.php │ │ ├── LanguageService.php │ │ ├── LanguageServiceInterface.php │ │ ├── NamespaceMapper.php │ │ └── PermissionProvider.php │ └── services.yaml ├── Shop │ ├── Controller │ │ └── ShopSettingController.php │ ├── Exception │ │ ├── NoSettingsFoundForShopException.php │ │ └── WrongSettingTypeException.php │ ├── Infrastructure │ │ ├── ShopSettingRepository.php │ │ └── ShopSettingRepositoryInterface.php │ ├── Service │ │ ├── ShopSettingService.php │ │ └── ShopSettingServiceInterface.php │ └── services.yaml └── Theme │ ├── Controller │ ├── ThemeListController.php │ ├── ThemeSettingController.php │ └── ThemeSwitchController.php │ ├── DataType │ ├── ThemeDataType.php │ ├── ThemeDataTypeFactory.php │ ├── ThemeDataTypeFactoryInterface.php │ └── ThemeDataTypeInterface.php │ ├── Exception │ ├── NoSettingsFoundForThemeException.php │ ├── ThemeActivationException.php │ └── ThemesNotFound.php │ ├── Infrastructure │ ├── CoreThemeFactory.php │ ├── CoreThemeFactoryInterface.php │ ├── ThemeListInfrastructure.php │ ├── ThemeListInfrastructureInterface.php │ ├── ThemeSettingRepository.php │ ├── ThemeSettingRepositoryInterface.php │ ├── ThemeSwitchInfrastructure.php │ └── ThemeSwitchInfrastructureInterface.php │ ├── Service │ ├── ThemeListService.php │ ├── ThemeListServiceInterface.php │ ├── ThemeSettingService.php │ ├── ThemeSettingServiceInterface.php │ ├── ThemeSwitchService.php │ └── ThemeSwitchServiceInterface.php │ └── services.yaml └── tests ├── Codeception ├── Acceptance.suite.yml ├── Acceptance │ ├── BaseCest.php │ ├── Module │ │ ├── ModuleActivationCest.php │ │ ├── ModuleListCest.php │ │ ├── ModuleSettingBaseCest.php │ │ ├── ModuleSettingGettersCest.php │ │ ├── ModuleSettingListCest.php │ │ ├── ModuleSettingMutationsCest.php │ │ └── ModuleSwitchCest.php │ ├── NotAuthorizedAccessCest.php │ ├── Shop │ │ ├── ShopSettingGettersCest.php │ │ ├── ShopSettingListCest.php │ │ └── ShopSettingMutationsCest.php │ └── Theme │ │ ├── ThemeListCest.php │ │ ├── ThemeSettingGettersCest.php │ │ ├── ThemeSettingListCest.php │ │ ├── ThemeSettingMutationsCest.php │ │ └── ThemeSwitchCest.php ├── Config │ └── params.php ├── Data │ └── testdata.sql ├── Support │ ├── AcceptanceTester.php │ └── _generated │ │ └── .gitignore └── _output │ └── .gitignore ├── Fixtures ├── testdemodata_ce.sql └── testdemodata_ee.sql ├── Integration ├── Infrastructure │ ├── CoreThemeFactoryTest.php │ ├── ModuleListInfrastructureTest.php │ ├── ShopSettingRepositoryTest.php │ ├── ThemeListInfrastructureTest.php │ └── ThemeSettingRepositoryTest.php └── Service │ └── ModuleBlockListServiceTest.php ├── PhpMd ├── exclude-ruleset.xml └── standard.xml ├── PhpStan ├── phpstan-bootstrap.php └── phpstan.neon ├── Reports └── .gitkeep ├── Unit ├── Module │ ├── Controller │ │ ├── ModuleActivationControllerTest.php │ │ ├── ModuleListControllerTest.php │ │ └── ModuleSettingControllerTest.php │ ├── DataType │ │ ├── ModuleDataTypeFactoryTest.php │ │ └── ModuleDataTypeTest.php │ ├── Exception │ │ ├── ModuleActivationBlockedExceptionTest.php │ │ ├── ModuleActivationExceptionTest.php │ │ ├── ModuleDeactivationBlockedExceptionTest.php │ │ ├── ModuleDeactivationExceptionTest.php │ │ └── ModulesNotFoundExceptionTest.php │ ├── Infrastructure │ │ └── ModuleListInfrastructureTest.php │ └── Service │ │ ├── ModuleActivationsServiceTest.php │ │ ├── ModuleBlocklistServiceTest.php │ │ ├── ModuleListServiceTest.php │ │ └── ModuleSettingServiceTest.php ├── Shared │ ├── DataType │ │ ├── AbstractComponentDataTypeTest.php │ │ ├── BooleanSettingTest.php │ │ ├── ComponentFiltersTest.php │ │ ├── FloatSettingTest.php │ │ ├── IntegerSettingTest.php │ │ ├── SettingTypeTest.php │ │ └── StringSettingTest.php │ ├── Enum │ │ └── FieldTypeTest.php │ ├── Exception │ │ ├── CollectionEncodingExceptionTest.php │ │ ├── InvalidCollectionExceptionTest.php │ │ └── WrongSettingValueExceptionTest.php │ ├── Infrastructure │ │ ├── AbstractDatabaseSettingsRepositoryTestCase.php │ │ └── LanguageWrapperTest.php │ └── Service │ │ ├── ComponentFilterServiceTest.php │ │ ├── JsonCollectionEncodingServiceTest.php │ │ ├── LanguageServiceTest.php │ │ ├── NamespaceMapperTest.php │ │ └── PermissionProviderTest.php ├── Shop │ ├── Controller │ │ └── ShopSettingControllerTest.php │ ├── Exception │ │ ├── NoSettingsFoundForShopExceptionTest.php │ │ └── WrongSettingTypeExceptionTest.php │ ├── Infrastructure │ │ ├── AbstractShopSettingRepositoryTestCase.php │ │ ├── ShopSettingRepositoryGettersTest.php │ │ └── ShopSettingRepositorySettersTest.php │ └── Service │ │ └── ShopSettingServiceTest.php ├── Theme │ ├── Controller │ │ ├── ThemeListControllerTest.php │ │ ├── ThemeSettingControllerTest.php │ │ └── ThemeSwitchControllerTest.php │ ├── DataType │ │ ├── ThemeDataTypeFactoryTest.php │ │ └── ThemeDataTypeTest.php │ ├── Exception │ │ ├── NoSettingsFoundForThemeExceptionTest.php │ │ ├── ThemeActivationExceptionTest.php │ │ └── ThemesNotFoundTest.php │ ├── Infrastructure │ │ ├── AbstractThemeSettingRepositoryTestCase.php │ │ ├── ThemeListInfrastructureTest.php │ │ ├── ThemeSettingRepositoryGettersTest.php │ │ ├── ThemeSettingRepositorySettersTest.php │ │ └── ThemeSwitchInfrastructureTest.php │ └── Service │ │ ├── ThemeListServiceTest.php │ │ ├── ThemeSettingServiceTest.php │ │ └── ThemeSwitchServiceTest.php └── UnitTestCase.php ├── codeception.yml ├── phpcs.xml └── phpunit.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | indent_style = space 12 | indent_size = 4 13 | trim_trailing_whitespace = true 14 | 15 | # Unix-style newlines with a newline ending every file 16 | [{*.yml,*.yaml}] 17 | indent_size = 2 18 | 19 | # Tab indentation (no size specified) 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.github/oxid-esales/graphql-configuration-access_light.yaml: -------------------------------------------------------------------------------- 1 | install: 2 | cache: 3 | prepared_shop: false 4 | git: 5 | repository: 'OXID-eSales/graphql-configuration-access' 6 | ref: &ref '{{ .Github.RefName }}' 7 | shop_url: 'https://github.com/OXID-eSales/graphql-configuration-access.git' 8 | shop_ref: *ref 9 | composer: 10 | transform: | 11 | { 12 | "require": { 13 | "oxid-esales/oxideshop-ce": "{{ .Data.global.composer.dev_ref }}", 14 | "oxid-esales/twig-component": "{{ .Data.global.composer.dev_ref }}", 15 | "oxid-esales/twig-admin-theme": "{{ .Data.global.composer.dev_ref }}", 16 | "oxid-esales/apex-theme": "{{ .Data.global.composer.dev_ref }}", 17 | "oxid-esales/developer-tools": "{{ .Data.global.composer.dev_ref }}" 18 | } 19 | } 20 | custom_script_container: | 21 | perl -pi -e 'print "SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=\$1\n\n" if $. == 1' source/.htaccess 22 | 23 | vendor/bin/oe-console oe:database:reset --db-host=mysql --db-port=3306 --db-name=example --db-user=root --db-password=root --force 24 | vendor/bin/oe-console oe:module:install ./ 25 | vendor/bin/oe-eshop-doctrine_migration migrations:migrate 26 | vendor/bin/oe-eshop-db_views_generate 27 | 28 | vendor/bin/oe-console oe:module:activate oe_graphql_base 29 | vendor/bin/oe-console oe:module:activate oe_graphql_configuration_access 30 | vendor/bin/oe-console oe:theme:activate apex 31 | 32 | install_shop_with_modules: 33 | composer: 34 | root_url: '' 35 | 36 | runscript: &runscript 37 | matrix: 38 | script: | 39 | [ 40 | "configuration_module:phpunit", 41 | "configuration_module:phpintegration", 42 | "configuration_module:codeception" 43 | ] 44 | composer: 45 | early: true 46 | configuration_module: 47 | path: '' 48 | 49 | runslim: 50 | <<: *runscript 51 | matrix: 52 | script: | 53 | [ 54 | "configuration_module:phpcs-report", 55 | "configuration_module:phpstan-report", 56 | "configuration_module:phpmd-report", 57 | "configuration_module:deptrac", 58 | ] 59 | 60 | sonarcloud: 61 | matrix: 62 | testplan: '["-"]' 63 | strip_path: '/var/www/' 64 | project_key: 'OXID-eSales_graphql-configuration-access' 65 | project_name: 'oxid-esales/graphql-configuration-access' 66 | parameters: | 67 | -Dsonar.language=php \ 68 | -Dsonar.scm.provider=git \ 69 | -Dsonar.sources=src \ 70 | -Dsonar.tests=tests \ 71 | -Dsonar.php.phpstan.reportPaths=coverage-reports/phpstan.report.json 72 | 73 | finish: 74 | slack_title: 'Configuration Access module ({{ .Data.global.git.shop_ref }}) by {{ .Github.Actor }}' 75 | -------------------------------------------------------------------------------- /.github/workflows/dispatch_module.yaml: -------------------------------------------------------------------------------- 1 | name: Manual trigger for testing github actions development 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | limit: 7 | type: choice 8 | options: 9 | - 'no' 10 | - 'PHP8.2/MySQL5.7' 11 | - 'PHP8.2/MySQL8.0' 12 | - 'PHP8.2/MariaDb11' 13 | - 'PHP8.3/MySQL5.7' 14 | - 'PHP8.3/MySQL8.0' 15 | - 'PHP8.3/MariaDb11' 16 | - 'PHP8.4/MySQL8.0' 17 | - 'PHP8.4/MariaDb11' 18 | default: 'PHP8.2/MySQL5.7' 19 | description: 'Limit to one PHP/MySQL combination' 20 | 21 | jobs: 22 | build_testplan: 23 | runs-on: ubuntu-latest 24 | outputs: 25 | testplan: '${{ steps.build.outputs.testplan }}' 26 | steps: 27 | - name: 'Build testplan' 28 | id: build 29 | run: | 30 | # Build testplan 31 | # shellcheck disable=SC2088 # Tilde expansion happens in the workflow and not by bash 32 | case '${{inputs.limit}}' in 33 | "no") LIMIT='';; 34 | "PHP8.2/MySQL5.7") LIMIT='~/defaults/php8.2_mysql5.7_only.yaml,' ;; 35 | "PHP8.2/MySQL8.0") LIMIT='~/defaults/php8.2_mysql8.0_only.yaml,' ;; 36 | "PHP8.2/MariaDb11") LIMIT='~/defaults/php8.2_mariadb11_only.yaml,' ;; 37 | "PHP8.3/MySQL5.7") LIMIT='~/defaults/php8.3_mysql5.7_only.yaml,' ;; 38 | "PHP8.3/MySQL8.0") LIMIT='~/defaults/php8.3_mysql8.0_only.yaml,' ;; 39 | "PHP8.3/MariaDb11") LIMIT='~/defaults/php8.3_mariadb11_only.yaml,' ;; 40 | "PHP8.4/MySQL8.0") LIMIT='~/defaults/php8.4_mysql8.0_only.yaml,' ;; 41 | "PHP8.4/MariaDb11") LIMIT='~/defaults/php8.4_mariadb11_only.yaml,' ;; 42 | *) echo "Illegal choice, fix the workflow" 43 | exit 1 44 | ;; 45 | esac 46 | # shellcheck disable=SC2088 47 | TESTPLAN="~/defaults/7.3.x.yaml,${LIMIT}~/graphql-configuration-access_light.yaml" 48 | echo "testplan=${TESTPLAN}" | tee -a "${GITHUB_OUTPUT}" 49 | 50 | dispatch_stable: 51 | needs: build_testplan 52 | uses: oxid-eSales/github-actions/.github/workflows/universal_workflow_light.yaml@v4 53 | with: 54 | testplan: ${{ needs.build_testplan.outputs.testplan }} 55 | runs_on: '"ubuntu-latest"' 56 | defaults: 'v4' 57 | plan_folder: '.github/oxid-esales' 58 | secrets: 59 | DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} 60 | DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }} 61 | CACHE_ENDPOINT: ${{ secrets.CACHE_ENDPOINT }} 62 | CACHE_ACCESS_KEY: ${{ secrets.CACHE_ACCESS_KEY }} 63 | CACHE_SECRET_KEY: ${{ secrets.CACHE_SECRET_KEY }} 64 | enterprise_github_token: ${{ secrets.enterprise_github_token }} 65 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 66 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 67 | -------------------------------------------------------------------------------- /.github/workflows/sbom.yaml: -------------------------------------------------------------------------------- 1 | name: SBOM generation 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | 7 | jobs: 8 | generate_sbom: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: generate sbom 15 | uses: CycloneDX/gh-php-composer-generate-sbom@v1 16 | -------------------------------------------------------------------------------- /.github/workflows/schema_trigger.yaml: -------------------------------------------------------------------------------- 1 | name: Trigger schema workflow 2 | 3 | on: 4 | push: 5 | tags: [v2.1.*] 6 | 7 | jobs: 8 | documentation_schema: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Trigger schema generation in documentation 12 | uses: convictional/trigger-workflow-and-wait@v1.6.5 13 | with: 14 | owner: oxid-esales 15 | repo: oxapi-documentation 16 | github_user: ${{ secrets.CI_USER }} 17 | github_token: ${{ secrets.GH_CI_JENKINS_TOKEN }} 18 | workflow_file_name: schema.yaml 19 | ref: "11.0-en" 20 | -------------------------------------------------------------------------------- /.github/workflows/trigger.yaml: -------------------------------------------------------------------------------- 1 | name: Auto trigger on push or pull requests 2 | 3 | on: 4 | pull_request: {} 5 | push: {} 6 | 7 | jobs: 8 | configuration_module_php82_mysql80: 9 | uses: oxid-eSales/github-actions/.github/workflows/universal_workflow_light.yaml@v4 10 | with: 11 | testplan: '~/defaults/7.3.x.yaml,~/defaults/php8.2_mysql8.0_only.yaml,~/graphql-configuration-access_light.yaml' 12 | runs_on: '"ubuntu-latest"' 13 | defaults: 'v4' 14 | plan_folder: '.github/oxid-esales' 15 | secrets: 16 | DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} 17 | DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }} 18 | CACHE_ENDPOINT: ${{ secrets.CACHE_ENDPOINT }} 19 | CACHE_ACCESS_KEY: ${{ secrets.CACHE_ACCESS_KEY }} 20 | CACHE_SECRET_KEY: ${{ secrets.CACHE_SECRET_KEY }} 21 | enterprise_github_token: ${{ secrets.enterprise_github_token }} 22 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 23 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 24 | 25 | push_module_ee: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Trigger EE workflow and wait for results 29 | uses: convictional/trigger-workflow-and-wait@v1.6.5 30 | with: 31 | owner: oxid-esales 32 | repo: module-workflows 33 | github_user: ${{ secrets.CI_USER }} 34 | github_token: ${{ secrets.GH_CI_JENKINS_TOKEN }} 35 | workflow_file_name: configuration_access_workflow.yaml 36 | ref: "b-7.3.x" 37 | client_payload: "{\"limit\": \"PHP8.2/MySQL8.0\", \"edition\": \"ee\", \"ref\": \"${{ github.ref_name }}\"}" 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .php_cs.cache 3 | tests/.phpunit.result.cache 4 | .deptrac.cache 5 | composer.lock 6 | /vendor/ 7 | tests/reports/ 8 | tests/Codeception/_data/ 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "recipes/parts"] 2 | path = recipes/parts 3 | url = https://github.com/OXID-eSales/docker-eshop-sdk-recipe-parts 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [2.1.0-rc.1] - 2025-04-28 8 | 9 | ### Added 10 | - New `parentTheme` and `parentVersions` fields in the `ThemeDataType` 11 | - PHP 8.4 support 12 | 13 | ## [2.0.0] - 2025-04-25 14 | 15 | ### Changed 16 | - Move ModuleDataType generation to Infrastructure 17 | - Move ThemeDataType generation to Infrastructure 18 | 19 | ### Removed 20 | - `OxidEsales\GraphQL\ConfigurationAccess\Shared\Subscriber\BeforeModuleDeactivation` because de/activation is already handled by shop 21 | 22 | ## [1.2.0] - 2024-11-27 23 | This is the stable release of v1.2.0. No changes have been made since v1.2.0-rc.1. 24 | 25 | ## [1.2.0-rc.1] - 2024-11-06 26 | 27 | ### Added 28 | - Theme list and filtering option on basis of theme name and status 29 | - Module list and filtering option on basis of module name and status 30 | - Activation of given theme by themeId 31 | - Mutations to de/activate a module. 32 | - Prevention of de/activation of certain modules mentioned in modules_blocklist.yaml. 33 | 34 | ## [1.1.0] - 2024-07-05 35 | This is stable release for v1.1.0. No changes have been made since v1.1.0-rc.1. 36 | 37 | ## [1.1.0-rc.1] - 2024-05-30 38 | ### Added 39 | - PHP 8.2 support 40 | - Module activation dependency on GraphQL Base module 41 | 42 | ### Changed 43 | - PHPUnit upgraded to version 10.x 44 | - Codeception tests structure updated to Codeception 5 45 | 46 | ### Removed 47 | - PHP 8.0 support 48 | 49 | ## [1.0.0] - 2024-02-07 50 | 51 | - Initial release 52 | 53 | [2.1.0-rc.1]: https://github.com/OXID-eSales/graphql-configuration-access/compare/b-7.2.x...v2.1.0-rc.1 54 | [2.0.0]: https://github.com/OXID-eSales/graphql-configuration-access/compare/v1.2.0...v2.0.0 55 | [1.2.0]: https://github.com/OXID-eSales/graphql-configuration-access/compare/v1.2.0-rc.1...v1.2.0 56 | [1.2.0-rc.1]: https://github.com/OXID-eSales/graphql-configuration-access/compare/v1.1.0...v1.2.0-rc.1 57 | [1.1.0]: https://github.com/OXID-eSales/graphql-configuration-access/compare/v1.1.0-rc.1...v1.1.0 58 | [1.1.0-rc.1]: https://github.com/OXID-eSales/graphql-configuration-access/compare/v1.0.0...v1.1.0-rc.1 59 | [1.0.0]: https://github.com/OXID-eSales/graphql-configuration-access/releases/tag/v1.0.0 60 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the OXID eSales GraphQL Configuration Access Module 2 | 3 | First off: Thank you for taking the time to contribute! 👍 4 | 5 | ## Contributor License Agreement 6 | 7 | When contributing code for the first time, you must sign our [Contributor License Agreement](https://gist.github.com/OXID-Admin/6df6ed126d074a54507d). No need to do something now, CLA Assistant will contact you after opening your first pull request. You can find more information about it on the [OXID Contribution and Contributor Agreement FAQ](https://docs.oxid-esales.com/developer/en/latest/development/modules_components_themes/contribution.html). 8 | 9 | ## How Can I Contribute? 10 | 11 | ### Reporting Bugs 12 | 13 | So, you stumbled upon a 🐛, those are tracked as [GitHub Issues](https://github.com/OXID-eSales/graphql-configuration-access/issues). Create an issue and provide all necessary information by explaining the problem and include additional details to help maintainers reproduce the problem: 14 | 15 | - **Use a clear and descriptive title** for the issue to identify the problem 16 | - **Describe the exact steps which reproduce the problem** in as many details as possible 17 | - **Describe the behavior you observed** 18 | - **Explain which behavior you expected to see instead and why** 19 | - **State the shop and module version** you used while having the problem 20 | 21 | ### Suggesting Enhancements 22 | 23 | 📈 Enhancement suggestions are tracked as [GitHub Issues](https://github.com/OXID-eSales/graphql-configuration-access/issues). Create an issue and provide all necessary information to help maintainers: 24 | 25 | - **Use a clear and descriptive title** for the issue to identify the suggestion 26 | - **Describe the current behavior** and **explain which behavior you expected to see instead** 27 | - **Explain why this enhancement would be useful** 28 | - **State if you are willing to provide a pull request for the enhancement** 29 | 30 | ### Pull Requests 31 | 32 | The basic workflow and the only way of contributing code to the project is via GitHub Pull Requests. 33 | 34 | - **Fork the project** to your account 35 | - **Make your bug fix or enhancement** 36 | - **Add tests** for your new code 37 | - **Send the Pull Request** 38 | - Make sure **CI is 💚** 39 | 40 | We recommend you to create a topic branch to work on, this will come in handy when making multiple contributions or when you need to rebase your pull request onto another branch. Also we would like to encourage you to open up an issue before making your pull request. 41 | 42 | #### What branch to send a Pull Request to? 43 | 44 | - Backwards compatibility breaks must go to the `master` branch 45 | - New Features go to the latest major version branch 46 | - Bugfixes to the latest minor version branch 47 | 48 | If in doubt, open up an issue before writing code, so we can help you settle this and possible other questions. 49 | 50 | ## I Need Help! 51 | 52 | When you need technical help on Git and GitHub, the [GitHub Help Page](https://help.github.com/) has you covered. You may additionally find [other sources with good practices](http://codeinthehole.com/writing/pull-requests-and-other-good-practices-for-teams-using-github/). 53 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OXID-eSales/graphql-configuration-access/eeebcc1d69a4286f1d8efb75907eaf944c7a679d/assets/logo.png -------------------------------------------------------------------------------- /dependencies.yaml: -------------------------------------------------------------------------------- 1 | modules: 2 | - oe_graphql_base 3 | -------------------------------------------------------------------------------- /deptrac.yaml: -------------------------------------------------------------------------------- 1 | deptrac: 2 | paths: 3 | - ./src/ 4 | layers: 5 | - name: Controller 6 | collectors: 7 | - type: classLike 8 | value: OxidEsales\\.*GraphQL.*Controller\\.* 9 | - name: Service 10 | collectors: 11 | - type: classLike 12 | value: OxidEsales\\.*GraphQL.*Service\\.* 13 | - name: Infrastructure 14 | collectors: 15 | - type: classLike 16 | value: OxidEsales\\.*GraphQL.*Repository\\.* 17 | - type: classLike 18 | value: OxidEsales\\.*GraphQL.*Infrastructure\\.* 19 | - name: InternalService 20 | collectors: 21 | - type: classLike 22 | regex: OxidEsales\\Eshop.*\\Internal\\.* 23 | - name: Core 24 | collectors: 25 | - type: classLike 26 | regex: OxidEsales\\Eshop(\w+)?\\(?!Internal) 27 | 28 | ruleset: 29 | Controller: 30 | - Service 31 | Service: 32 | - InternalService 33 | - Infrastructure 34 | Infrastructure: 35 | - Core 36 | - InternalService 37 | Core: ~ 38 | InternalService: ~ 39 | 40 | -------------------------------------------------------------------------------- /git-hooks/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | function setup_git_hooks() 6 | { 7 | echo "Initialising git hooks..." 8 | 9 | MODULE_PATH=$(git rev-parse --show-toplevel) 10 | PRE_COMMIT_HOOK_FILE_PATH=$MODULE_PATH/.git/hooks/pre-commit 11 | 12 | git config --local core.hooksPath .git/hooks 13 | echo "Add hook command to pre-commit file $PRE_COMMIT_HOOK_FILE_PATH" 14 | 15 | if ! [ -f "${PRE_COMMIT_HOOK_FILE_PATH}" ];then 16 | echo $'#!/bin/bash\n' >> $PRE_COMMIT_HOOK_FILE_PATH 17 | fi 18 | 19 | COMPOSER_STATIC_COMMAND="docker compose exec -T --workdir $MODULE_PATH php composer static" 20 | 21 | if grep -q "$COMPOSER_STATIC_COMMAND" "$PRE_COMMIT_HOOK_FILE_PATH"; then 22 | echo "Command has already been added" 23 | return 24 | fi 25 | 26 | echo "$COMPOSER_STATIC_COMMAND" >> $PRE_COMMIT_HOOK_FILE_PATH 27 | chmod +x $PRE_COMMIT_HOOK_FILE_PATH 28 | } 29 | 30 | setup_git_hooks 31 | -------------------------------------------------------------------------------- /metadata.php: -------------------------------------------------------------------------------- 1 | 'oe_graphql_configuration_access', 18 | 'title' => 'GraphQL Configuration Access', 19 | 'description' => 'GraphQL Configuration Access', 20 | 'thumbnail' => 'logo.png', 21 | 'version' => '2.1.0-rc.1', 22 | 'author' => 'OXID eSales AG', 23 | 'url' => 'https://github.com/OXID-eSales/graphql-configuration-access', 24 | 'email' => 'info@oxid-esales.com', 25 | 'extend' => [ 26 | ], 27 | 'controllers' => [ 28 | ], 29 | 'templates' => [ 30 | ], 31 | 'events' => [ 32 | ], 33 | 'blocks' => [ 34 | ], 35 | 'settings' => [ 36 | ], 37 | ]; 38 | -------------------------------------------------------------------------------- /module_blocklist.yaml: -------------------------------------------------------------------------------- 1 | modules: 2 | - oe_graphql_base 3 | - oe_graphql_configuration_access 4 | -------------------------------------------------------------------------------- /recipes/setup-development.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Flags possible: 3 | # -e for shop edition. Possible values: CE/EE 4 | 5 | edition='EE' 6 | while getopts e: flag; do 7 | case "${flag}" in 8 | e) edition=${OPTARG} ;; 9 | *) ;; 10 | esac 11 | done 12 | 13 | SCRIPT_PATH=$(dirname ${BASH_SOURCE[0]}) 14 | 15 | cd $SCRIPT_PATH/../../ || exit 16 | 17 | # Prepare services configuration 18 | make setup 19 | make addbasicservices 20 | make file=services/adminer.yml addservice 21 | make file=services/selenium-chrome.yml addservice 22 | make file=services/node.yml addservice 23 | 24 | # Configure containers 25 | perl -pi\ 26 | -e 's#error_reporting = .*#error_reporting = E_ALL ^ E_WARNING ^ E_DEPRECATED#g;'\ 27 | containers/php/custom.ini 28 | 29 | perl -pi\ 30 | -e 's#/var/www/#/var/www/source/#g;'\ 31 | containers/httpd/project.conf 32 | 33 | perl -pi\ 34 | -e 's#PHP_VERSION=.*#PHP_VERSION=8.2#g;'\ 35 | .env 36 | 37 | docker compose up --build -d php 38 | 39 | docker compose exec -T php git config --global --add safe.directory /var/www 40 | 41 | $SCRIPT_PATH/parts/shared/require_shop_edition_packages.sh -e"${edition}" -v"dev-b-7.3.x" 42 | $SCRIPT_PATH/parts/shared/require_twig_components.sh -e"${edition}" -b"b-7.3.x" 43 | $SCRIPT_PATH/parts/shared/require.sh -n"oxid-esales/graphql-base" -g"https://github.com/OXID-eSales/graphql-base-module.git" -v"dev-b-7.3.x" 44 | $SCRIPT_PATH/parts/shared/require.sh -n"oxid-esales/developer-tools" -v"dev-b-7.3.x" 45 | $SCRIPT_PATH/parts/shared/require.sh -n"oxid-esales/oxideshop-doctrine-migration-wrapper" -v"dev-b-7.3.x" 46 | $SCRIPT_PATH/parts/shared/require_demodata_package.sh -e"${edition}" -b"master" 47 | $SCRIPT_PATH/parts/shared/require_theme.sh -t"apex" -b"b-7.3.x" 48 | 49 | docker compose exec -T php composer update --no-interaction 50 | 51 | make up 52 | 53 | perl -pi\ 54 | -e 'print "SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=\$1\n\n" if $. == 1'\ 55 | source/source/.htaccess 56 | 57 | $SCRIPT_PATH/parts/shared/setup_database.sh 58 | 59 | docker compose exec -T php vendor/bin/oe-console oe:module:install ./ 60 | docker compose exec -T php vendor/bin/oe-eshop-doctrine_migration migrations:migrate 61 | docker compose exec -T php vendor/bin/oe-eshop-db_views_generate 62 | docker compose exec -T php vendor/bin/oe-console oe:module:activate oe_graphql_base 63 | docker compose exec -T php vendor/bin/oe-console oe:module:activate oe_graphql_configuration_access 64 | docker compose exec -T php vendor/bin/oe-console oe:theme:activate apex 65 | 66 | $SCRIPT_PATH/parts/shared/create_admin.sh 67 | 68 | # Register all related project packages git repositories 69 | mkdir -p .idea; mkdir -p source/.idea; cp "${SCRIPT_PATH}/parts/bases/vcs.xml.base" .idea/vcs.xml 70 | perl -pi\ 71 | -e 's##\n #g;'\ 72 | -e 's##\n #g;'\ 73 | -e 's##\n #g;'\ 74 | -e 's##\n #g;'\ 75 | -e 's##\n #g;'\ 76 | .idea/vcs.xml 77 | -------------------------------------------------------------------------------- /services.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: src/Shared/services.yaml } 3 | - { resource: src/Module/services.yaml } 4 | - { resource: src/Shop/services.yaml } 5 | - { resource: src/Theme/services.yaml } 6 | 7 | services: 8 | 9 | _defaults: 10 | public: false 11 | autowire: true 12 | 13 | OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\NamespaceMapper: 14 | class: OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\NamespaceMapper 15 | tags: [ 'graphql_namespace_mapper' ] 16 | 17 | OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\PermissionProvider: 18 | class: OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\PermissionProvider 19 | tags: [ 'graphql_permission_provider' ] 20 | 21 | OxidEsales\GraphQL\ConfigurationAccess\DependencyCheckTrigger: 22 | alias: OxidEsales\GraphQL\Base\Framework\GraphQLQueryHandler 23 | public: true 24 | -------------------------------------------------------------------------------- /src/Module/Controller/ModuleActivationController.php: -------------------------------------------------------------------------------- 1 | moduleActivationService->activateModule(moduleId: $moduleId); 35 | } 36 | 37 | /** 38 | * Mutation of Configuration Access Module 39 | * @param string $moduleId 40 | * @return bool 41 | */ 42 | #[Mutation] 43 | #[Logged] 44 | #[Right('ACTIVATE_MODULE')] 45 | public function deactivateModule(string $moduleId): bool 46 | { 47 | return $this->moduleActivationService->deactivateModule(moduleId: $moduleId); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Module/Controller/ModuleListController.php: -------------------------------------------------------------------------------- 1 | moduleListService->getModuleList($filters ?? new ComponentFilters()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Module/DataType/ModuleDataType.php: -------------------------------------------------------------------------------- 1 | thumbnail; 41 | } 42 | 43 | #[Field] 44 | public function getAuthor(): string 45 | { 46 | return $this->author; 47 | } 48 | 49 | #[Field] 50 | public function getUrl(): string 51 | { 52 | return $this->url; 53 | } 54 | 55 | #[Field] 56 | public function getEmail(): string 57 | { 58 | return $this->email; 59 | } 60 | 61 | #[Field] 62 | public function getLang(): string 63 | { 64 | return $this->lang; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Module/DataType/ModuleDataTypeFactory.php: -------------------------------------------------------------------------------- 1 | getTitle(); 26 | $translatedTitle = $this->languageService->filterByLanguageAbbreviation( 27 | data: $titlesData, 28 | defaultLang: $moduleConfig->getLang() 29 | ); 30 | 31 | $description = $moduleConfig->getDescription(); 32 | $translatedDescription = $this->languageService->filterByLanguageAbbreviation( 33 | data: $description, 34 | defaultLang: $moduleConfig->getLang() 35 | ); 36 | 37 | return new ModuleDataType( 38 | id: $moduleConfig->getId(), 39 | title: $translatedTitle, 40 | version: $moduleConfig->getVersion(), 41 | description: $translatedDescription, 42 | active: $moduleConfig->isActivated(), 43 | thumbnail: $moduleConfig->getThumbnail(), 44 | author: $moduleConfig->getAuthor(), 45 | url: $moduleConfig->getUrl(), 46 | email: $moduleConfig->getEmail(), 47 | lang: $moduleConfig->getLang() 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Module/DataType/ModuleDataTypeFactoryInterface.php: -------------------------------------------------------------------------------- 1 | shopConfigurationDaoBridge->get(); 27 | $moduleConfigurations = $shopConfiguration->getModuleConfigurations(); 28 | 29 | if (empty($moduleConfigurations)) { 30 | throw new ModulesNotFoundException(); 31 | } 32 | 33 | $moduleDatatypes = []; 34 | foreach ($moduleConfigurations as $moduleConfig) { 35 | $moduleDatatypes[] = $this->moduleDataTypeFactory->createFromModuleConfiguration($moduleConfig); 36 | } 37 | return $moduleDatatypes; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Module/Infrastructure/ModuleListInfrastructureInterface.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public function getModuleList(): array; 20 | } 21 | -------------------------------------------------------------------------------- /src/Module/Service/ModuleActivationService.php: -------------------------------------------------------------------------------- 1 | moduleBlocklistService->isModuleBlocked($moduleId)) { 34 | throw new ModuleActivationBlockedException($moduleId); 35 | } 36 | 37 | $shopId = $this->context->getCurrentShopId(); 38 | 39 | try { 40 | $this->moduleActivationBridge->activate(moduleId: $moduleId, shopId: $shopId); 41 | } catch (\Exception $exception) { 42 | throw new ModuleActivationException(); 43 | } 44 | 45 | return true; 46 | } 47 | 48 | /** 49 | * @inheritDoc 50 | */ 51 | public function deactivateModule(string $moduleId): bool 52 | { 53 | if ($this->moduleBlocklistService->isModuleBlocked($moduleId)) { 54 | throw new ModuleDeactivationBlockedException($moduleId); 55 | } 56 | 57 | $shopId = $this->context->getCurrentShopId(); 58 | 59 | try { 60 | $this->moduleActivationBridge->deactivate(moduleId: $moduleId, shopId: $shopId); 61 | } catch (\Exception $exception) { 62 | throw new ModuleDeactivationException(); 63 | } 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Module/Service/ModuleActivationServiceInterface.php: -------------------------------------------------------------------------------- 1 | moduleBlocklistPath; 25 | $configWrapper = $this->projectYamlDao->loadDIConfigFile($resolvedPath); 26 | $blocklistData = $configWrapper->getConfigAsArray(); 27 | 28 | return in_array($moduleId, $blocklistData['modules'], true); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Module/Service/ModuleBlocklistServiceInterface.php: -------------------------------------------------------------------------------- 1 | moduleListInfrastructure->getModuleList(); 27 | 28 | return $this->componentFilterService->filterComponents($moduleConfigurations, $filters); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Module/Service/ModuleListServiceInterface.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public function getModuleList(ComponentFiltersInterface $filters): array; 19 | } 20 | -------------------------------------------------------------------------------- /src/Module/Service/ModuleSettingServiceInterface.php: -------------------------------------------------------------------------------- 1 | id; 31 | } 32 | 33 | #[Field] 34 | public function getTitle(): string 35 | { 36 | return $this->title; 37 | } 38 | 39 | #[Field] 40 | public function getVersion(): string 41 | { 42 | return $this->version; 43 | } 44 | 45 | #[Field] 46 | public function getDescription(): string 47 | { 48 | return $this->description; 49 | } 50 | 51 | #[Field] 52 | public function isActive(): bool 53 | { 54 | return $this->active; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Shared/DataType/BooleanSetting.php: -------------------------------------------------------------------------------- 1 | name; 31 | } 32 | 33 | /** 34 | * Field of Configuration Access module's BooleanSetting-Type 35 | */ 36 | #[Field] 37 | public function getValue(): bool 38 | { 39 | return $this->value; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Shared/DataType/ComponentDataTypeInterface.php: -------------------------------------------------------------------------------- 1 | titleFilter; 33 | if ($titleFilter !== null) { 34 | return $titleFilter->matches($title); 35 | } 36 | 37 | return true; 38 | } 39 | 40 | /** 41 | * TODO: This method can be refactored after the chain-of-responsibility-pattern 42 | * Therefore classes like TitleFilter or ActiveFilter with the Component as argument can check if a specific 43 | * property of the Component fits to a specific filter (String and BoolFilter). As a result we have an iterable list 44 | * with filters which checks different fields. 45 | */ 46 | private function filterComponentByStatus(bool $status): bool 47 | { 48 | $statusFilter = $this->activeFilter; 49 | if ($statusFilter !== null && $status !== $statusFilter->equals()) { 50 | return false; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | public function filterComponent(ComponentDataTypeInterface $component): bool 57 | { 58 | return $this->filterComponentByTitle($component->getTitle()) 59 | && $this->filterComponentByStatus($component->isActive()); 60 | } 61 | 62 | /** 63 | * @Factory(name="ComponentFilters", default=true) 64 | */ 65 | public static function createComponentFilters( 66 | ?StringFilter $title = null, 67 | ?BoolFilter $active = null 68 | ): self { 69 | return new self(titleFilter: $title, activeFilter: $active); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Shared/DataType/ComponentFiltersInterface.php: -------------------------------------------------------------------------------- 1 | name; 31 | } 32 | 33 | /** 34 | * Field of Configuration Access module's FloatSetting-Type 35 | */ 36 | #[Field] 37 | public function getValue(): float 38 | { 39 | return $this->value; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Shared/DataType/IntegerSetting.php: -------------------------------------------------------------------------------- 1 | name; 31 | } 32 | 33 | /** 34 | * Field of Configuration Access module's IntegerSetting-Type 35 | */ 36 | #[Field] 37 | public function getValue(): int 38 | { 39 | return $this->value; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Shared/DataType/SettingType.php: -------------------------------------------------------------------------------- 1 | name; 35 | } 36 | 37 | /** 38 | * Field of Configuration Access module's SettingType-Type. 39 | * Indicates if the type is supported by our queries and mutations. 40 | * @SuppressWarnings(PHPMD.StaticAccess) 41 | */ 42 | #[Field] 43 | public function isSupported(): bool 44 | { 45 | return FieldType::validateFieldType($this->getType()); 46 | } 47 | 48 | /** 49 | * Field of Configuration Access module's StringSetting-Type 50 | */ 51 | #[Field] 52 | public function getType(): string 53 | { 54 | return $this->type; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Shared/DataType/StringSetting.php: -------------------------------------------------------------------------------- 1 | name; 31 | } 32 | 33 | /** 34 | * Field of Configuration Access module's StringSetting-Type 35 | */ 36 | #[Field] 37 | public function getValue(): string 38 | { 39 | return $this->value; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Shared/Enum/FieldType.php: -------------------------------------------------------------------------------- 1 | language->getLanguageAbbr(); 24 | return $this->language->getLanguageAbbr(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Shared/Infrastructure/LanguageWrapperInterface.php: -------------------------------------------------------------------------------- 1 | filterComponent($module); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Shared/Service/ComponentFilterServiceInterface.php: -------------------------------------------------------------------------------- 1 | $components 13 | * 14 | * @return array 15 | */ 16 | public function filterComponents(array $components, ComponentFiltersInterface $filterList): array; 17 | } 18 | -------------------------------------------------------------------------------- /src/Shared/Service/JsonCollectionEncodingService.php: -------------------------------------------------------------------------------- 1 | language->getCurrentLanguageAbbr(); 24 | 25 | if (isset($data[$languageAbbr])) { 26 | return $data[$languageAbbr]; 27 | } 28 | 29 | if (isset($data[$defaultLang])) { 30 | return $data[$defaultLang]; 31 | } 32 | 33 | $dataReversed = array_reverse($data); 34 | return array_pop($dataReversed); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Shared/Service/LanguageServiceInterface.php: -------------------------------------------------------------------------------- 1 | __DIR__ . '/../../Module/Controller/', 22 | self::SPACE . 'Shop\\Controller' => __DIR__ . '/../../Shop/Controller/', 23 | self::SPACE . 'Theme\\Controller' => __DIR__ . '/../../Theme/Controller/', 24 | ]; 25 | } 26 | 27 | public function getTypeNamespaceMapping(): array 28 | { 29 | return [ 30 | self::SPACE . 'Shared\\DataType' => __DIR__ . '/../../Shared/DataType/', 31 | self::SPACE . 'Theme\\DataType' => __DIR__ . '/../../Theme/DataType/', 32 | self::SPACE . 'Module\\DataType' => __DIR__ . '/../../Module/DataType/', 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Shared/Service/PermissionProvider.php: -------------------------------------------------------------------------------- 1 | [ 20 | 'CHANGE_CONFIGURATION', 21 | 'LIST_THEMES', 22 | 'LIST_MODULES', 23 | 'ACTIVATE_MODULE' 24 | ], 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Shared/services.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: Infrastructure/services.yaml } 3 | 4 | services: 5 | _defaults: 6 | public: false 7 | autowire: true 8 | 9 | OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\: 10 | resource: 'Service/' 11 | public: true 12 | 13 | OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\CollectionEncodingServiceInterface: 14 | class: OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\JsonCollectionEncodingService 15 | 16 | OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\LanguageServiceInterface: 17 | class: \OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\LanguageService 18 | 19 | OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\ComponentFilterServiceInterface: 20 | class: \OxidEsales\GraphQL\ConfigurationAccess\Shared\Service\ComponentFilterService 21 | -------------------------------------------------------------------------------- /src/Shop/Exception/NoSettingsFoundForShopException.php: -------------------------------------------------------------------------------- 1 | 61 | */ 62 | public function getSettingsList(): array; 63 | 64 | /** 65 | * @throws WrongSettingTypeException 66 | */ 67 | public function saveIntegerSetting(string $name, int $value): void; 68 | 69 | /** 70 | * @throws WrongSettingTypeException 71 | */ 72 | public function saveFloatSetting(string $name, float $value): void; 73 | 74 | /** 75 | * @throws WrongSettingTypeException 76 | */ 77 | public function saveBooleanSetting(string $name, bool $value): void; 78 | 79 | /** 80 | * @throws WrongSettingTypeException 81 | */ 82 | public function saveStringSetting(string $name, string $value): void; 83 | 84 | /** 85 | * @throws WrongSettingTypeException 86 | */ 87 | public function saveSelectSetting(string $name, string $value): void; 88 | 89 | /** 90 | * @throws WrongSettingTypeException 91 | */ 92 | public function saveCollectionSetting(string $name, array $value): void; 93 | 94 | /** 95 | * @throws WrongSettingTypeException 96 | */ 97 | public function saveAssocCollectionSetting(string $name, array $value): void; 98 | } 99 | -------------------------------------------------------------------------------- /src/Shop/Service/ShopSettingServiceInterface.php: -------------------------------------------------------------------------------- 1 | themeListService->getThemeList($filters ?? new ComponentFilters()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Theme/Controller/ThemeSwitchController.php: -------------------------------------------------------------------------------- 1 | themeSwitchService->switchTheme($themeId); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Theme/DataType/ThemeDataType.php: -------------------------------------------------------------------------------- 1 | parentTheme; 35 | } 36 | 37 | /** 38 | * @return ?string[] 39 | */ 40 | #[Field] 41 | public function getParentVersions(): ?array 42 | { 43 | return $this->parentVersions; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Theme/DataType/ThemeDataTypeFactory.php: -------------------------------------------------------------------------------- 1 | getInfo('id'), 21 | title: $theme->getInfo('title'), 22 | version: $theme->getInfo('version'), 23 | description: $theme->getInfo('description'), 24 | active: $theme->getInfo('active'), 25 | parentTheme: $theme->getInfo('parentTheme'), 26 | parentVersions: $theme->getInfo('parentVersions'), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Theme/DataType/ThemeDataTypeFactoryInterface.php: -------------------------------------------------------------------------------- 1 | coreThemeFactory->create(); 29 | $themesList = $coreThemeService->getList(); 30 | 31 | if (empty($themesList)) { 32 | throw new ThemesNotFound(); 33 | } 34 | 35 | $themeDataTypes = []; 36 | foreach ($themesList as $theme) { 37 | $themeDataTypes[] = $this->themeDataTypeFactory->createFromCoreTheme(theme: $theme); 38 | } 39 | 40 | return $themeDataTypes; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Theme/Infrastructure/ThemeListInfrastructureInterface.php: -------------------------------------------------------------------------------- 1 | 60 | */ 61 | public function getSettingsList(string $themeId): array; 62 | 63 | /** 64 | * @throws NoSettingsFoundForThemeException 65 | */ 66 | public function saveIntegerSetting(string $name, int $value, string $themeId): void; 67 | 68 | /** 69 | * @throws NoSettingsFoundForThemeException 70 | */ 71 | public function saveFloatSetting(string $name, float $value, string $themeId): void; 72 | 73 | /** 74 | * @throws NoSettingsFoundForThemeException 75 | */ 76 | public function saveBooleanSetting(string $name, bool $value, string $themeId): void; 77 | 78 | /** 79 | * @throws NoSettingsFoundForThemeException 80 | */ 81 | public function saveStringSetting(string $name, string $value, string $themeId): void; 82 | 83 | /** 84 | * @throws NoSettingsFoundForThemeException 85 | */ 86 | public function saveSelectSetting(string $name, string $value, string $themeId): void; 87 | 88 | /** 89 | * @throws NoSettingsFoundForThemeException 90 | */ 91 | public function saveCollectionSetting(string $name, array $value, string $themeId): void; 92 | 93 | /** 94 | * @throws NoSettingsFoundForThemeException 95 | */ 96 | public function saveAssocCollectionSetting(string $name, array $value, string $themeId): void; 97 | } 98 | -------------------------------------------------------------------------------- /src/Theme/Infrastructure/ThemeSwitchInfrastructure.php: -------------------------------------------------------------------------------- 1 | coreThemeFactory->create(); 28 | if (!$coreThemeService->load($themeId)) { 29 | throw new ThemeActivationException(self::THEME_NOT_EXIST); 30 | } 31 | $coreThemeService->activate(); 32 | 33 | return true; 34 | } catch (ThemeActivationException | StandardException $e) { 35 | throw new ThemeActivationException($e->getMessage()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Theme/Infrastructure/ThemeSwitchInfrastructureInterface.php: -------------------------------------------------------------------------------- 1 | themeListInfrastructure->getThemes(); 27 | 28 | return $this->componentFilterService->filterComponents($themeDataTypes, $filters); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Theme/Service/ThemeListServiceInterface.php: -------------------------------------------------------------------------------- 1 | themeSwitchInfrastructure->switchTheme(themeId: $themeId); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Theme/Service/ThemeSwitchServiceInterface.php: -------------------------------------------------------------------------------- 1 | 22 | mysql --defaults-file=$mysql_config --default-character-set=utf8 $dbname < $module_dump 23 | - \OxidEsales\Codeception\Module\Database: 24 | config_key: 'fq45QS09_fqyx09239QQ' 25 | depends: Db 26 | - \OxidEsales\Codeception\Module\SelectTheme: 27 | theme_id: '%THEME_ID%' 28 | depends: 29 | - \OxidEsales\Codeception\Module\Database 30 | - Db 31 | - REST: 32 | url: '%SHOP_URL%' 33 | depends: PhpBrowser 34 | part: Json 35 | - PhpBrowser: 36 | url: '%SHOP_URL%' 37 | - \OxidEsales\GraphQL\Base\Tests\Codeception\Module\AcceptanceHelper: 38 | depends: 39 | - REST 40 | step_decorators: 41 | - \Codeception\Step\Retry 42 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/BaseCest.php: -------------------------------------------------------------------------------- 1 | logout(); 33 | } 34 | 35 | protected function getTestThemeName(): string 36 | { 37 | return self::TEST_THEME_ID; 38 | } 39 | 40 | protected function getAgentUsername(): string 41 | { 42 | return self::AGENT_USERNAME; 43 | } 44 | 45 | protected function getAgentPassword(): string 46 | { 47 | return self::AGENT_PASSWORD; 48 | } 49 | 50 | protected function getAdminUsername(): string 51 | { 52 | return self::ADMIN_USERNAME; 53 | } 54 | 55 | protected function getAdminPassword(): string 56 | { 57 | return self::ADMIN_PASSWORD; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Module/ModuleActivationCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 28 | 29 | $result = $this->runModuleMutation( 30 | I: $I, 31 | queryName: $example['queryName'], 32 | field: $example['field'] 33 | ); 34 | 35 | $I->assertNotSame('You do not have sufficient rights to access this field', $result['errors'][0]['message']); 36 | } 37 | 38 | private function runModuleMutation( 39 | AcceptanceTester $I, 40 | string $queryName, 41 | string $field 42 | ): array { 43 | $I->login($this->getAdminUsername(), $this->getAdminPassword()); 44 | $I->sendGQLQuery( 45 | 'mutation { 46 | ' . $queryName . '(' . $field . ': "' . self::TEST_MODULE_ID . '") 47 | }' 48 | ); 49 | 50 | $I->seeResponseIsJson(); 51 | return $I->grabJsonResponseAsArray(); 52 | } 53 | 54 | protected function moduleDeActivationDataProvider(): \Generator 55 | { 56 | yield ['queryName' => 'activateModule', 'field' => 'moduleId']; 57 | yield ['queryName' => 'deactivateModule', 'field' => 'moduleId']; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Module/ModuleListCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 24 | 25 | $result = $this->runModuleListQuery($I); 26 | $I->assertArrayNotHasKey('errors', $result); 27 | } 28 | 29 | public function runModuleListQuery(AcceptanceTester $I): array 30 | { 31 | $I->sendGQLQuery( 32 | 'query modulesList { 33 | modules( 34 | filters: { 35 | title: { 36 | contains: "' . self::TEST_MODULE_TITLE . '" 37 | } 38 | active: { 39 | equals: true 40 | } 41 | } 42 | ) { 43 | id 44 | version 45 | title 46 | description 47 | thumbnail 48 | author 49 | url 50 | email 51 | active 52 | } 53 | }' 54 | ); 55 | 56 | $I->seeResponseIsJson(); 57 | return $I->grabJsonResponseAsArray(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Module/ModuleSettingGettersCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 26 | 27 | $result = $this->runSettingGetterQuery($I, $example['queryName'], $example['settingName']); 28 | 29 | $I->assertArrayNotHasKey('errors', $result); 30 | 31 | $setting = $result['data'][$example['queryName']]; 32 | $I->assertSame($example['settingName'], $setting['name']); 33 | $I->assertSame($example['expectedValue'], $setting['value']); 34 | } 35 | 36 | protected function queryMethodsDataProvider(): \Generator 37 | { 38 | yield [ 39 | 'queryName' => 'moduleSettingInteger', 40 | 'settingName' => 'intSetting', 41 | 'expectedValue' => 123 42 | ]; 43 | 44 | yield [ 45 | 'queryName' => 'moduleSettingFloat', 46 | 'settingName' => 'floatSetting', 47 | 'expectedValue' => 1.23 48 | ]; 49 | 50 | yield [ 51 | 'queryName' => 'moduleSettingBoolean', 52 | 'settingName' => 'boolSetting', 53 | 'expectedValue' => false 54 | ]; 55 | 56 | yield [ 57 | 'queryName' => 'moduleSettingString', 58 | 'settingName' => 'stringSetting', 59 | 'expectedValue' => 'default' 60 | ]; 61 | 62 | yield [ 63 | 'queryName' => 'moduleSettingCollection', 64 | 'settingName' => 'arraySetting', 65 | 'expectedValue' => '["nice","values"]' 66 | ]; 67 | } 68 | 69 | private function runSettingGetterQuery(AcceptanceTester $I, string $queryName, string $settingName): array 70 | { 71 | $I->sendGQLQuery( 72 | 'query q($name: String!, $moduleId: String!){ 73 | ' . $queryName . '(name: $name, moduleId: $moduleId) { 74 | name 75 | value 76 | } 77 | }', 78 | [ 79 | 'name' => $settingName, 80 | 'moduleId' => self::TEST_MODULE_ID 81 | ] 82 | ); 83 | 84 | $I->seeResponseIsJson(); 85 | 86 | return $I->grabJsonResponseAsArray(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Module/ModuleSettingListCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 25 | 26 | $I->sendGQLQuery( 27 | 'query getSettings($moduleId: String!){ 28 | moduleSettings(moduleId: $moduleId) { 29 | name 30 | type 31 | supported 32 | } 33 | }', 34 | ['moduleId' => self::TEST_MODULE_ID] 35 | ); 36 | 37 | $I->seeResponseIsJson(); 38 | 39 | $result = $I->grabJsonResponseAsArray(); 40 | $I->assertArrayNotHasKey('errors', $result); 41 | 42 | $settingsList = $result['data']['moduleSettings']; 43 | $I->assertCount(5, $settingsList); 44 | $I->assertContains( 45 | ['name' => 'intSetting', 'type' => FieldType::NUMBER, 'supported' => true], 46 | $settingsList 47 | ); 48 | $I->assertContains( 49 | ['name' => 'floatSetting', 'type' => FieldType::NUMBER, 'supported' => true], 50 | $settingsList 51 | ); 52 | $I->assertContains( 53 | ['name' => 'boolSetting', 'type' => FieldType::BOOLEAN, 'supported' => true], 54 | $settingsList 55 | ); 56 | $I->assertContains( 57 | ['name' => 'stringSetting', 'type' => FieldType::STRING, 'supported' => true], 58 | $settingsList 59 | ); 60 | $I->assertContains( 61 | ['name' => 'arraySetting', 'type' => FieldType::ARRAY, 'supported' => true], 62 | $settingsList 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Module/ModuleSwitchCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 27 | 28 | $result = $this->runModuleMutation( 29 | I: $I, 30 | queryName: $example['queryName'], 31 | field: $example['field'] 32 | ); 33 | 34 | $I->assertArrayNotHasKey('errors', $result); 35 | $response = $result['data'][$example['queryName']]; 36 | $I->assertTrue($response); 37 | } 38 | 39 | private function runModuleMutation( 40 | AcceptanceTester $I, 41 | string $queryName, 42 | string $field 43 | ): array { 44 | $I->login($this->getAdminUsername(), $this->getAdminPassword()); 45 | $I->sendGQLQuery( 46 | 'mutation { 47 | ' . $queryName . '(' . $field . ': "' . self::TEST_MODULE_ID . '") 48 | }' 49 | ); 50 | 51 | $I->seeResponseIsJson(); 52 | return $I->grabJsonResponseAsArray(); 53 | } 54 | 55 | protected function moduleSwitchDataProvider(): \Generator 56 | { 57 | yield ['queryName' => 'activateModule', 'field' => 'moduleId']; 58 | yield ['queryName' => 'deactivateModule', 'field' => 'moduleId']; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Shop/ShopSettingGettersCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 27 | 28 | $result = $this->runSettingGetterQuery($I, $example['queryName'], $example['settingName']); 29 | 30 | $I->assertArrayNotHasKey('errors', $result); 31 | 32 | $setting = $result['data'][$example['queryName']]; 33 | $I->assertSame($example['settingName'], $setting['name']); 34 | $I->assertSame($example['expectedValue'], $setting['value']); 35 | } 36 | 37 | protected function queryMethodsDataProvider(): \Generator 38 | { 39 | yield [ 40 | 'queryName' => 'shopSettingInteger', 41 | 'settingName' => 'intSetting', 42 | 'expectedValue' => 123 43 | ]; 44 | 45 | yield [ 46 | 'queryName' => 'shopSettingFloat', 47 | 'settingName' => 'floatSetting', 48 | 'expectedValue' => 1.23 49 | ]; 50 | 51 | yield [ 52 | 'queryName' => 'shopSettingBoolean', 53 | 'settingName' => 'boolSetting', 54 | 'expectedValue' => false 55 | ]; 56 | 57 | yield [ 58 | 'queryName' => 'shopSettingString', 59 | 'settingName' => 'stringSetting', 60 | 'expectedValue' => 'default' 61 | ]; 62 | 63 | yield [ 64 | 'queryName' => 'shopSettingSelect', 65 | 'settingName' => 'selectSetting', 66 | 'expectedValue' => 'selectString' 67 | ]; 68 | 69 | yield [ 70 | 'queryName' => 'shopSettingCollection', 71 | 'settingName' => 'arraySetting', 72 | 'expectedValue' => '["10","20","50","100"]' 73 | ]; 74 | 75 | yield [ 76 | 'queryName' => 'shopSettingAssocCollection', 77 | 'settingName' => 'aarraySetting', 78 | 'expectedValue' => '{"first":"10","second":"20","third":"50"}' 79 | ]; 80 | } 81 | 82 | private function runSettingGetterQuery(AcceptanceTester $I, string $queryName, string $settingName): array 83 | { 84 | $I->sendGQLQuery( 85 | 'query q($name: String!){ 86 | ' . $queryName . '(name: $name) { 87 | name 88 | value 89 | } 90 | }', 91 | [ 92 | 'name' => $settingName 93 | ] 94 | ); 95 | 96 | $I->seeResponseIsJson(); 97 | 98 | return $I->grabJsonResponseAsArray(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Shop/ShopSettingListCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 26 | 27 | $I->sendGQLQuery( 28 | 'query{ 29 | shopSettings { 30 | name 31 | type 32 | supported 33 | } 34 | }' 35 | ); 36 | 37 | $I->seeResponseIsJson(); 38 | 39 | $result = $I->grabJsonResponseAsArray(); 40 | $I->assertArrayNotHasKey('errors', $result); 41 | 42 | $settingsList = $result['data']['shopSettings']; 43 | $I->assertContains( 44 | ['name' => 'intSetting', 'type' => FieldType::NUMBER, 'supported' => true], 45 | $settingsList 46 | ); 47 | $I->assertContains( 48 | ['name' => 'floatSetting', 'type' => FieldType::NUMBER, 'supported' => true], 49 | $settingsList 50 | ); 51 | $I->assertContains( 52 | ['name' => 'boolSetting', 'type' => FieldType::BOOLEAN, 'supported' => true], 53 | $settingsList 54 | ); 55 | $I->assertContains( 56 | ['name' => 'stringSetting', 'type' => FieldType::STRING, 'supported' => true], 57 | $settingsList 58 | ); 59 | $I->assertContains( 60 | ['name' => 'selectSetting', 'type' => FieldType::SELECT, 'supported' => true], 61 | $settingsList 62 | ); 63 | $I->assertContains( 64 | ['name' => 'arraySetting', 'type' => FieldType::ARRAY, 'supported' => true], 65 | $settingsList 66 | ); 67 | $I->assertContains( 68 | ['name' => 'aarraySetting', 'type' => FieldType::ASSOCIATIVE_ARRAY, 'supported' => true], 69 | $settingsList 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Theme/ThemeListCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 25 | 26 | $result = $this->runThemeListQuery($I); 27 | $I->assertArrayNotHasKey('errors', $result); 28 | } 29 | 30 | private function runThemeListQuery(AcceptanceTester $I): array 31 | { 32 | $I->sendGQLQuery( 33 | 'query themeList { 34 | themesList( 35 | filters: null 36 | ) { 37 | title 38 | id 39 | version 40 | description 41 | active 42 | parentTheme 43 | parentVersions 44 | } 45 | }' 46 | ); 47 | 48 | $I->seeResponseIsJson(); 49 | return $I->grabJsonResponseAsArray(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Codeception/Acceptance/Theme/ThemeSwitchCest.php: -------------------------------------------------------------------------------- 1 | login($this->getAdminUsername(), $this->getAdminPassword()); 25 | 26 | $result = $this->runThemeSwitchMutation($I); 27 | 28 | $I->assertArrayNotHasKey('errors', $result); 29 | } 30 | 31 | private function runThemeSwitchMutation(AcceptanceTester $I): array 32 | { 33 | $themeId = $this->getCurrentThemeId(); 34 | 35 | $I->sendGQLQuery( 36 | 'mutation switchThemeCest{ 37 | switchTheme(themeId: "' . $themeId . '") 38 | }' 39 | ); 40 | 41 | $I->seeResponseIsJson(); 42 | return $I->grabJsonResponseAsArray(); 43 | } 44 | 45 | private function getCurrentThemeId(): string 46 | { 47 | $theme = new Theme(); 48 | return $theme->getActiveThemeId(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Codeception/Config/params.php: -------------------------------------------------------------------------------- 1 | $facts->getShopUrl(), 22 | 'SHOP_SOURCE_PATH' => $facts->getSourcePath(), 23 | 'VENDOR_PATH' => $facts->getVendorPath(), 24 | 'DB_NAME' => $facts->getDatabaseName(), 25 | 'DB_USERNAME' => $facts->getDatabaseUserName(), 26 | 'DB_PASSWORD' => $facts->getDatabasePassword(), 27 | 'DB_HOST' => $facts->getDatabaseHost(), 28 | 'DB_PORT' => $facts->getDatabasePort(), 29 | 'DUMP_PATH' => getTestDataDumpFilePath(), 30 | 'MODULE_DUMP_PATH' => getModuleTestDataDumpFilePath(), 31 | 'FIXTURES_PATH' => getTestFixtureSqlFilePath(), 32 | 'MYSQL_CONFIG_PATH' => getMysqlConfigPath(), 33 | 'SELENIUM_SERVER_PORT' => getenv('SELENIUM_SERVER_PORT') ?: '4444', 34 | 'SELENIUM_SERVER_HOST' => getenv('SELENIUM_SERVER_HOST') ?: 'selenium', 35 | 'BROWSER_NAME' => getenv('BROWSER_NAME') ?: 'chrome', 36 | 'PHP_BIN' => getenv('PHPBIN') ?: 'php', 37 | 'SCREEN_SHOT_URL' => getenv('CC_SCREEN_SHOTS_PATH') ?: '', 38 | 'THEME_ID' => getenv('THEME_ID') ?: 'apex', 39 | ]; 40 | 41 | function getTestDataDumpFilePath(): string 42 | { 43 | return Path::join(__DIR__, '/../', 'Data', 'generated', 'dump.sql'); 44 | } 45 | 46 | function getModuleTestDataDumpFilePath() 47 | { 48 | return Path::join(__DIR__, '/../', 'Data', 'testdata.sql'); 49 | } 50 | 51 | function getTestFixtureSqlFilePath(): string 52 | { 53 | $facts = new Facts(); 54 | 55 | $path = Path::join(__DIR__, '/../../', 'Fixtures', 'testdemodata_ce.sql'); 56 | 57 | if ($facts->getEdition() == 'EE') { 58 | $path = Path::join(__DIR__, '/../../', 'Fixtures', 'testdemodata_ee.sql'); 59 | } 60 | 61 | return $path; 62 | } 63 | 64 | function getMysqlConfigPath() 65 | { 66 | $facts = new Facts(); 67 | $configFilePath = Path::join($facts->getSourcePath(), 'config.inc.php'); 68 | $configFile = new ConfigFile($configFilePath); 69 | $generator = new DatabaseDefaultsFileGenerator($configFile); 70 | 71 | return $generator->generate(); 72 | } 73 | -------------------------------------------------------------------------------- /tests/Codeception/Data/testdata.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OXID-eSales/graphql-configuration-access/eeebcc1d69a4286f1d8efb75907eaf944c7a679d/tests/Codeception/Data/testdata.sql -------------------------------------------------------------------------------- /tests/Codeception/Support/AcceptanceTester.php: -------------------------------------------------------------------------------- 1 | create(); 25 | $this->assertEquals(new Theme(), $theme); 26 | 27 | $anotherTheme = $coreThemeFactory->create(); 28 | $this->assertNotSame($theme, $anotherTheme); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Integration/Infrastructure/ModuleListInfrastructureTest.php: -------------------------------------------------------------------------------- 1 | get(ShopConfigurationDaoBridgeInterface::class); 25 | $shopConfiguration = $shopConfigurationDaoBridge->get(); 26 | 27 | $moduleConfiguration1 = new ModuleConfiguration(); 28 | $moduleConfiguration1->setId('firstModule'); 29 | $moduleConfiguration1->setTitle(['en' => 'Module 1']); 30 | $moduleConfiguration1->setDescription(['en' => 'Module 1 description']); 31 | $moduleConfiguration1->setLang('en'); 32 | $moduleConfiguration1->setModuleSource('test'); 33 | 34 | $moduleConfiguration2 = new ModuleConfiguration(); 35 | $moduleConfiguration2->setId('secondModule'); 36 | $moduleConfiguration2->setTitle(['en' => 'Module 2']); 37 | $moduleConfiguration2->setDescription(['en' => 'Module 2 description']); 38 | $moduleConfiguration2->setLang('en'); 39 | $moduleConfiguration2->setModuleSource('test1'); 40 | 41 | $shopConfiguration 42 | ->addModuleConfiguration($moduleConfiguration1) 43 | ->addModuleConfiguration($moduleConfiguration2); 44 | $shopConfigurationDaoBridge->save($shopConfiguration); 45 | 46 | $sut = $this->getSut(); 47 | $modulesList = $sut->getModuleList(); 48 | $this->assertCount(2, $modulesList); 49 | $this->assertInstanceOf(ModuleDataTypeInterface::class, $modulesList[0]); 50 | $this->assertInstanceOf(ModuleDataTypeInterface::class, $modulesList[1]); 51 | $this->assertSame($modulesList[0]->getId(), $moduleConfiguration1->getId()); 52 | $this->assertSame($modulesList[1]->getId(), $moduleConfiguration2->getId()); 53 | } 54 | 55 | public function getSut(): ModuleListInfrastructure 56 | { 57 | return new ModuleListInfrastructure( 58 | $this->get(ShopConfigurationDaoBridgeInterface::class), 59 | $this->get(ModuleDataTypeFactoryInterface::class) 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Integration/Infrastructure/ThemeListInfrastructureTest.php: -------------------------------------------------------------------------------- 1 | getSut(); 26 | 27 | $themesArray = $sut->getThemes(); 28 | $this->assertNotEmpty($themesArray); 29 | $this->assertIsArray($themesArray); 30 | 31 | foreach ($themesArray as $theme) { 32 | $this->assertInstanceOf(ThemeDataTypeInterface::class, $theme); 33 | } 34 | } 35 | 36 | public function getSut(): ThemeListInfrastructure 37 | { 38 | return new ThemeListInfrastructure( 39 | $this->get(CoreThemeFactoryInterface::class), 40 | $this->get(ThemeDataTypeFactoryInterface::class) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Integration/Service/ModuleBlockListServiceTest.php: -------------------------------------------------------------------------------- 1 | get(ProjectYamlDaoInterface::class) 28 | ); 29 | 30 | $this->assertTrue($sut->isModuleBlocked('oe_graphql_base')); 31 | $this->assertTrue($sut->isModuleBlocked('oe_graphql_configuration_access')); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/PhpMd/exclude-ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | Exclude Static Access Rule 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/PhpMd/standard.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | Standard OXID Ruleset 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/PhpStan/phpstan-bootstrap.php: -------------------------------------------------------------------------------- 1 | createMock(ModuleActivationServiceInterface::class); 26 | $moduleActivationServiceMock 27 | ->method('activateModule') 28 | ->with($moduleId) 29 | ->willReturn(true); 30 | 31 | $sut = $this->getSut(moduleActivationService: $moduleActivationServiceMock); 32 | 33 | $result = $sut->activateModule($moduleId); 34 | $this->assertTrue($result); 35 | } 36 | 37 | public function testDeactivateModule(): void 38 | { 39 | $moduleId = 'testModuleId'; 40 | 41 | $moduleActivationServiceMock = $this->createMock(ModuleActivationServiceInterface::class); 42 | $moduleActivationServiceMock 43 | ->method('deactivateModule') 44 | ->with($moduleId) 45 | ->willReturn(true); 46 | 47 | $sut = $this->getSut(moduleActivationService: $moduleActivationServiceMock); 48 | 49 | $result = $sut->deactivateModule($moduleId); 50 | $this->assertTrue($result); 51 | } 52 | 53 | public function getSut( 54 | ModuleActivationServiceInterface $moduleActivationService = null 55 | ): ModuleActivationController { 56 | return new ModuleActivationController( 57 | moduleActivationService: $moduleActivationService 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Unit/Module/Controller/ModuleListControllerTest.php: -------------------------------------------------------------------------------- 1 | createStub(ComponentFilters::class); 26 | $moduleStub1 = $this->createStub(ModuleDataTypeInterface::class); 27 | $moduleStub2 = $this->createStub(ModuleDataTypeInterface::class); 28 | $filteredModules = [$moduleStub1, $moduleStub2]; 29 | 30 | $moduleListServiceMock = $this->createMock(ModuleListServiceInterface::class); 31 | $moduleListServiceMock->expects($this->once()) 32 | ->method('getModuleList') 33 | ->with($filtersStub) 34 | ->willReturn($filteredModules); 35 | 36 | $sut = new ModuleListController($moduleListServiceMock); 37 | $actualModules = $sut->modules($filtersStub); 38 | 39 | $this->assertSame($filteredModules, $actualModules); 40 | } 41 | 42 | public function testModulesListWithoutFilters(): void 43 | { 44 | $moduleStub = $this->createStub(ModuleDataTypeInterface::class); 45 | $componentFilters = new ComponentFilters(); 46 | 47 | $moduleListServiceSpy = $this->createMock(ModuleListServiceInterface::class); 48 | $moduleListServiceSpy->method('getModuleList') 49 | ->with($componentFilters) 50 | ->willReturn([$moduleStub]); 51 | 52 | $sut = new ModuleListController($moduleListServiceSpy); 53 | $resultModuleList = $sut->modules(null); 54 | 55 | $this->assertSame($resultModuleList, [$moduleStub]); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Unit/Module/DataType/ModuleDataTypeFactoryTest.php: -------------------------------------------------------------------------------- 1 | uniqid(), 26 | 'en' => uniqid(), 27 | ]; 28 | $descriptionData = [ 29 | 'de' => uniqid(), 30 | 'en' => uniqid(), 31 | ]; 32 | $expectedId = uniqid(); 33 | $expectedVersion = uniqid(); 34 | $expectedThumbnail = uniqid(); 35 | $expectedAuthor = uniqid(); 36 | $expectedUrl = uniqid(); 37 | $expectedEmail = uniqid(); 38 | $expectedLang = uniqid(); 39 | $expectedIsActivated = (bool)random_int(0, 1); 40 | 41 | $moduleConfigMock = $this->createConfiguredStub(ModuleConfiguration::class, [ 42 | 'getId' => $expectedId, 43 | 'getVersion' => $expectedVersion, 44 | 'getTitle' => $titlesData, 45 | 'getDescription' => $descriptionData, 46 | 'getThumbnail' => $expectedThumbnail, 47 | 'getAuthor' => $expectedAuthor, 48 | 'getUrl' => $expectedUrl, 49 | 'getEmail' => $expectedEmail, 50 | 'isActivated' => $expectedIsActivated, 51 | 'getLang' => $expectedLang 52 | ]); 53 | 54 | $languageServiceMock = $this->createMock(LanguageService::class); 55 | $languageServiceMock 56 | ->expects($this->exactly(2)) 57 | ->method('filterByLanguageAbbreviation') 58 | ->willReturnMap([ 59 | [$titlesData, $expectedLang, $titlesData['de']], 60 | [$descriptionData, $expectedLang, $descriptionData['de']] 61 | ]); 62 | 63 | $moduleDataTypeFactory = new ModuleDataTypeFactory($languageServiceMock); 64 | $moduleDataType = $moduleDataTypeFactory->createFromModuleConfiguration(moduleConfig: $moduleConfigMock); 65 | 66 | $this->assertSame($expectedId, $moduleDataType->getId()); 67 | $this->assertSame($expectedVersion, $moduleDataType->getVersion()); 68 | $this->assertSame($titlesData['de'], $moduleDataType->getTitle()); 69 | $this->assertSame($descriptionData['de'], $moduleDataType->getDescription()); 70 | $this->assertSame($expectedThumbnail, $moduleDataType->getThumbnail()); 71 | $this->assertSame($expectedAuthor, $moduleDataType->getAuthor()); 72 | $this->assertSame($expectedUrl, $moduleDataType->getUrl()); 73 | $this->assertSame($expectedEmail, $moduleDataType->getEmail()); 74 | $this->assertSame($expectedIsActivated, $moduleDataType->isActive()); 75 | $this->assertSame($expectedLang, $moduleDataType->getLang()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Unit/Module/DataType/ModuleDataTypeTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ComponentDataTypeInterface::class, $moduleDataType); 44 | $this->assertSame($thumbnail, $moduleDataType->getThumbnail()); 45 | $this->assertSame($author, $moduleDataType->getAuthor()); 46 | $this->assertSame($url, $moduleDataType->getUrl()); 47 | $this->assertSame($email, $moduleDataType->getEmail()); 48 | $this->assertSame($lang, $moduleDataType->getLang()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Module/Exception/ModuleActivationBlockedExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ModuleActivationBlockedException::class, $exception); 26 | $this->assertSame( 27 | sprintf('Module "%s" is in the blocklist and cannot be activated.', $moduleId), 28 | $exception->getMessage() 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Module/Exception/ModuleActivationExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ModuleActivationException::class, $exception); 25 | $this->assertSame('An error occurred while activating the module.', $exception->getMessage()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Unit/Module/Exception/ModuleDeactivationBlockedExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ModuleDeactivationBlockedException::class, $exception); 26 | $this->assertSame( 27 | sprintf('Module "%s" is in the blocklist and cannot be deactivated.', $moduleId), 28 | $exception->getMessage() 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Module/Exception/ModuleDeactivationExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ModuleDeactivationException::class, $exception); 25 | $this->assertSame('An error occurred while deactivating the module.', $exception->getMessage()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Unit/Module/Exception/ModulesNotFoundExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ModulesNotFoundException::class, $exception); 25 | $this->assertSame('Modules were not found.', $exception->getMessage()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Unit/Module/Service/ModuleBlocklistServiceTest.php: -------------------------------------------------------------------------------- 1 | getFileName())['dirname']; 31 | $filePath = 'testFilePath.yaml'; 32 | $expectedData = ['modules' => ['module1', 'module2']]; 33 | 34 | $configWrapperMock = $this->createMock(DIConfigWrapper::class); 35 | $configWrapperMock 36 | ->method('getConfigAsArray') 37 | ->willReturn($expectedData); 38 | 39 | $projectYamlDaoMock = $this->createMock(ProjectYamlDaoInterface::class); 40 | $projectYamlDaoMock 41 | ->method('loadDIConfigFile') 42 | ->with($classFilePath . '/../' . $filePath) 43 | ->willReturn($configWrapperMock); 44 | 45 | $sut = $this->getSut(moduleBlockListPath: $filePath, projectYamlDao: $projectYamlDaoMock); 46 | $actualResult = $sut->isModuleBlocked(moduleId: $moduleId); 47 | 48 | $this->assertSame($expectedResult, $actualResult); 49 | } 50 | 51 | public static function blockListDataProvider(): \Generator 52 | { 53 | yield 'isModuleBlocked returns true if Module is in the BlockList' => [ 54 | 'moduleId' => 'module1', 55 | 'expectedResult' => true 56 | ]; 57 | 58 | yield 'isModuleBlocked returns false if Module is not in the BlockList' => [ 59 | 'moduleId' => 'unknownModuleId', 60 | 'expectedResult' => false 61 | ]; 62 | } 63 | 64 | private function getSut( 65 | string $moduleBlockListPath, 66 | ProjectYamlDaoInterface $projectYamlDao 67 | ): ModuleBlocklistService { 68 | return new ModuleBlocklistService( 69 | moduleBlocklistPath: $moduleBlockListPath, 70 | projectYamlDao: $projectYamlDao 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/Unit/Module/Service/ModuleListServiceTest.php: -------------------------------------------------------------------------------- 1 | createStub(ComponentFiltersInterface::class); 29 | $moduleStub1 = $this->createStub(ModuleDataTypeInterface::class); 30 | $moduleStub2 = $this->createStub(ModuleDataTypeInterface::class); 31 | $filteredModules = [$moduleStub1]; 32 | 33 | $modulesConfigurations = [$moduleStub1, $moduleStub2]; 34 | 35 | $moduleListInfrastructureMock = $this->createMock(ModuleListInfrastructureInterface::class); 36 | $moduleListInfrastructureMock->method('getModuleList') 37 | ->willReturn($modulesConfigurations); 38 | 39 | $componentFilterServiceMock = $this->createMock(ComponentFilterServiceInterface::class); 40 | $componentFilterServiceMock->method('filterComponents') 41 | ->with($modulesConfigurations, $filtersStub) 42 | ->willReturn($filteredModules); 43 | 44 | $sut = new ModuleListService( 45 | moduleListInfrastructure: $moduleListInfrastructureMock, 46 | componentFilterService: $componentFilterServiceMock, 47 | ); 48 | 49 | $actualModules = $sut->getModuleList($filtersStub); 50 | $this->assertSame($filteredModules, $actualModules); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Unit/Shared/DataType/AbstractComponentDataTypeTest.php: -------------------------------------------------------------------------------- 1 | assertSame($id, $component->getId()); 32 | $this->assertSame($title, $component->getTitle()); 33 | $this->assertSame($version, $component->getVersion()); 34 | $this->assertSame($description, $component->getDescription()); 35 | $this->assertSame($isActive, $component->isActive()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Unit/Shared/DataType/BooleanSettingTest.php: -------------------------------------------------------------------------------- 1 | assertSame($name, $sut->getName()); 26 | $this->assertSame($value, $sut->getValue()); 27 | } 28 | 29 | public static function booleanSettingDataProvider(): \Generator 30 | { 31 | yield [uniqid(), true]; 32 | yield [uniqid(), false]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Unit/Shared/DataType/FloatSettingTest.php: -------------------------------------------------------------------------------- 1 | assertSame($name, $sut->getName()); 26 | $this->assertSame($value, $sut->getValue()); 27 | } 28 | 29 | public static function floatSettingDataProvider(): \Generator 30 | { 31 | yield "random float" => [uniqid(), rand(1, 100) / 10]; 32 | yield "random integer" => [uniqid(), rand(1, 100)]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Unit/Shared/DataType/IntegerSettingTest.php: -------------------------------------------------------------------------------- 1 | assertSame($name, $sut->getName()); 26 | $this->assertSame($value, $sut->getValue()); 27 | } 28 | 29 | public static function integerSettingDataProvider(): \Generator 30 | { 31 | yield "random integer" => [uniqid(), rand(1, 100)]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Unit/Shared/DataType/SettingTypeTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($settingIdentifier, $sut->getName()); 32 | $this->assertEquals($settingType, $sut->getType()); 33 | } 34 | 35 | /** @dataProvider isSupportedDataProvider */ 36 | public function testIsSupported(string $settingType, bool $expectation): void 37 | { 38 | $sut = new SettingType( 39 | 'someSettingName', 40 | $settingType 41 | ); 42 | 43 | $this->assertSame($expectation, $sut->isSupported()); 44 | } 45 | 46 | public static function isSupportedDataProvider(): \Generator 47 | { 48 | yield 'not supported case' => ['settingType' => 'someRandomSettingId', 'expectation' => false]; 49 | yield 'supported case' => ['settingType' => FieldType::ARRAY, 'expectation' => true]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Unit/Shared/DataType/StringSettingTest.php: -------------------------------------------------------------------------------- 1 | assertSame($name, $sut->getName()); 26 | $this->assertSame($value, $sut->getValue()); 27 | } 28 | 29 | public static function stringSettingDataProvider(): \Generator 30 | { 31 | yield "random strings" => [uniqid(), uniqid()]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Enum/FieldTypeTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(FieldType::validateFieldType($type)); 24 | } 25 | 26 | public function testInvalidType(): void 27 | { 28 | $this->assertFalse(FieldType::validateFieldType('INVALID_FIELDTYPE')); 29 | } 30 | 31 | /** @dataProvider fieldTypesDataProvider */ 32 | public function testGetEnums(string $type): void 33 | { 34 | $enums = FieldType::getEnums(); 35 | 36 | $this->assertContains($type, $enums); 37 | } 38 | 39 | public static function fieldTypesDataProvider(): \Generator 40 | { 41 | yield [FieldType::ASSOCIATIVE_ARRAY]; 42 | yield [FieldType::NUMBER]; 43 | yield [FieldType::ARRAY]; 44 | yield [FieldType::STRING]; 45 | yield [FieldType::BOOLEAN]; 46 | yield [FieldType::SELECT]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Exception/CollectionEncodingExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertSame('Error encountered while encoding collection data', $sut->getMessage()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Exception/InvalidCollectionExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertSame(sprintf('%s is not a valid collection string.', $value), $sut->getMessage()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Exception/WrongSettingValueExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertSame('Wrong setting value', $sut->getMessage()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Infrastructure/LanguageWrapperTest.php: -------------------------------------------------------------------------------- 1 | createConfiguredStub(Language::class, [ 25 | 'getLanguageAbbr' => $langAbbr, 26 | ]); 27 | 28 | $languageWrapper = new LanguageWrapper($languageMock); 29 | $actualLangAbbr = $languageWrapper->getCurrentLanguageAbbr(); 30 | 31 | $this->assertSame($langAbbr, $actualLangAbbr); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Service/ComponentFilterServiceTest.php: -------------------------------------------------------------------------------- 1 | createMock(ComponentFiltersInterface::class); 29 | $componentFiltersMock->method('filterComponent') 30 | ->willReturnCallback(function (ComponentDataTypeInterface $component) use (&$filterResults) { 31 | return array_shift($filterResults); 32 | }); 33 | 34 | $themeFilterService = new ComponentFilterService(); 35 | $componentListResult = $themeFilterService->filterComponents($componentList, $componentFiltersMock); 36 | $this->assertSame($expectedComponentListResult, array_values($componentListResult)); 37 | } 38 | 39 | public static function componentFilterResultProvider(): \Generator 40 | { 41 | $theme1 = self::createStub(ComponentDataTypeInterface::class); 42 | $theme2 = self::createStub(ComponentDataTypeInterface::class); 43 | 44 | yield "filter with filter results are both false" => [ 45 | 'componentList' => [$theme1, $theme2], 46 | 'filterResults' => [false, false], 47 | 'expectedComponentListResult' => [] 48 | ]; 49 | 50 | yield "filter with only first filter result is true" => [ 51 | 'componentList' => [$theme1, $theme2], 52 | 'filterResults' => [true, false], 53 | 'expectedComponentListResult' => [$theme1] 54 | ]; 55 | 56 | yield "filter with only second filter result is true" => [ 57 | 'componentList' => [$theme1, $theme2], 58 | 'filterResults' => [false, true], 59 | 'expectedComponentListResult' => [$theme2] 60 | ]; 61 | 62 | yield "filter with both filter results are true" => [ 63 | 'componentList' => [$theme1, $theme2], 64 | 'filterResults' => [true, true], 65 | 'expectedComponentListResult' => [$theme1, $theme2] 66 | ]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Service/JsonCollectionEncodingServiceTest.php: -------------------------------------------------------------------------------- 1 | 'value']; 26 | 27 | $this->assertSame('{"some":"value"}', $sut->encodeArrayToString($value)); 28 | } 29 | 30 | public function testJsonEncodeArrayException(): void 31 | { 32 | $sut = new JsonCollectionEncodingService(); 33 | $value = [&$value]; 34 | 35 | $this->expectException(CollectionEncodingException::class); 36 | $sut->encodeArrayToString($value); 37 | } 38 | 39 | /** @dataProvider jsonDecodeCollectionDataProvider */ 40 | public function testJsonDecodeCollection(string $value, array $expectedResult): void 41 | { 42 | $sut = new JsonCollectionEncodingService(); 43 | 44 | $this->assertEquals($expectedResult, $sut->decodeStringCollectionToArray($value)); 45 | } 46 | 47 | public static function jsonDecodeCollectionDataProvider(): \Generator 48 | { 49 | yield [ 50 | 'value' => '', 51 | 'expectedResult' => [] 52 | ]; 53 | 54 | yield [ 55 | 'value' => '["apple","banana"]', 56 | 'expectedResult' => ["apple", "banana"] 57 | ]; 58 | 59 | yield [ 60 | 'value' => '{"name":"John","age":30}', 61 | 'expectedResult' => ["name" => "John", "age" => 30] 62 | ]; 63 | } 64 | 65 | public function testJsonDecodeCollectionException(): void 66 | { 67 | $sut = new JsonCollectionEncodingService(); 68 | $value = '[2, "values"'; 69 | 70 | $this->expectException(InvalidCollectionException::class); 71 | $sut->decodeStringCollectionToArray($value); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Service/LanguageServiceTest.php: -------------------------------------------------------------------------------- 1 | 'Title in de language', 25 | 'en' => 'Title in en language', 26 | ]; 27 | $expectedExistingLanguageAbbreviation = 'de'; 28 | 29 | $languageWrapperMock = $this->createMock(LanguageWrapperInterface::class); 30 | $languageWrapperMock->method('getCurrentLanguageAbbr') 31 | ->willReturn($expectedExistingLanguageAbbreviation); 32 | 33 | $languageService = new LanguageService($languageWrapperMock); 34 | $actualResult = $languageService->filterByLanguageAbbreviation($titlesData, 'en'); 35 | 36 | $this->assertSame($titlesData[$expectedExistingLanguageAbbreviation], $actualResult); 37 | } 38 | 39 | public function testFilterByLanguageAbbreviationDefaultedToEnglish() 40 | { 41 | $titlesData = [ 42 | 'de' => 'Title in de language', 43 | 'en' => 'Title in en language', 44 | ]; 45 | $defaultLangAbbr = 'en'; 46 | 47 | $languageWrapperMock = $this->createMock(LanguageWrapperInterface::class); 48 | $languageWrapperMock->method('getCurrentLanguageAbbr')->willReturn('fr'); 49 | 50 | $languageService = new LanguageService($languageWrapperMock); 51 | $actualResult = $languageService->filterByLanguageAbbreviation($titlesData, $defaultLangAbbr); 52 | 53 | $this->assertSame($titlesData[$defaultLangAbbr], $actualResult); 54 | } 55 | 56 | public function testFilterByLanguageAbbreviationDefaultedToFirstInArray() 57 | { 58 | $titlesData = [ 59 | 'de' => 'Title in de language', 60 | 'en' => 'Title in en language', 61 | ]; 62 | 63 | $languageWrapperMock = $this->createMock(LanguageWrapperInterface::class); 64 | $languageWrapperMock->expects($this->once()) 65 | ->method('getCurrentLanguageAbbr')->willReturn('pl'); 66 | 67 | $languageService = new LanguageService($languageWrapperMock); 68 | $actualResult = $languageService->filterByLanguageAbbreviation($titlesData, 'fr'); 69 | 70 | $this->assertSame($titlesData['de'], $actualResult); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Service/NamespaceMapperTest.php: -------------------------------------------------------------------------------- 1 | 30 | self::$srcPath . '/Shared/Service/../../Module/Controller/', 31 | self::NAMESPACE_PREFIX . '\\Shop\\Controller' => 32 | self::$srcPath . '/Shared/Service/../../Shop/Controller/', 33 | self::NAMESPACE_PREFIX . '\\Theme\\Controller' => 34 | self::$srcPath . '/Shared/Service/../../Theme/Controller/', 35 | ]; 36 | 37 | $sut = $this->getSut(); 38 | $actualMapping = $sut->getControllerNamespaceMapping(); 39 | 40 | $this->assertSame($expectedMapping, $actualMapping); 41 | } 42 | 43 | public function testGetTypeNamespaceMapping(): void 44 | { 45 | $expectedMapping = [ 46 | self::NAMESPACE_PREFIX . '\\Shared\\DataType' => 47 | self::$srcPath . '/Shared/Service/../../Shared/DataType/', 48 | self::NAMESPACE_PREFIX . '\\Theme\\DataType' => 49 | self::$srcPath . '/Shared/Service/../../Theme/DataType/', 50 | self::NAMESPACE_PREFIX . '\\Module\\DataType' => 51 | self::$srcPath . '/Shared/Service/../../Module/DataType/', 52 | ]; 53 | 54 | $sut = $this->getSut(); 55 | $actualMapping = $sut->getTypeNamespaceMapping(); 56 | 57 | $this->assertSame($expectedMapping, $actualMapping); 58 | } 59 | 60 | private static function getSrcDirectoryPath(): string 61 | { 62 | $testsDir = 'tests'; 63 | $currentPath = __DIR__; 64 | $testsPos = strpos($currentPath, $testsDir); 65 | 66 | if ($testsPos === false) { 67 | return $currentPath; 68 | } 69 | 70 | return substr($currentPath, 0, $testsPos) . 'src'; 71 | } 72 | 73 | private function getSut(): NamespaceMapper 74 | { 75 | return new NamespaceMapper(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Unit/Shared/Service/PermissionProviderTest.php: -------------------------------------------------------------------------------- 1 | [ 21 | 'CHANGE_CONFIGURATION', 22 | 'LIST_THEMES', 23 | 'LIST_MODULES', 24 | 'ACTIVATE_MODULE' 25 | ], 26 | ]; 27 | 28 | $permissionProvider = new PermissionProvider(); 29 | $actualPermissions = $permissionProvider->getPermissions(); 30 | 31 | $this->assertSame($expectedPermissions, $actualPermissions); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Unit/Shop/Exception/NoSettingsFoundForShopExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertMatchesRegularExpression("/for shop: 5$/", $sut->getMessage()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Unit/Shop/Exception/WrongSettingTypeExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertSame('Wrong setting type', $sut->getMessage()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Unit/Shop/Infrastructure/AbstractShopSettingRepositoryTestCase.php: -------------------------------------------------------------------------------- 1 | createStub(ContextInterface::class), 28 | queryBuilderFactory: $queryBuilderFactory ?? $this->createStub(QueryBuilderFactoryInterface::class), 29 | configurationSettingDao: $shopSettingDao ?? $this->createStub(ShopConfigurationSettingDaoInterface::class), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Controller/ThemeListControllerTest.php: -------------------------------------------------------------------------------- 1 | createStub(ThemeDataTypeInterface::class); 26 | $themeFilters = $this->createStub(ComponentFilters::class); 27 | 28 | $themeListServiceMock = $this->createMock(ThemeListServiceInterface::class); 29 | $themeListServiceMock->expects($this->once()) 30 | ->method('getThemeList') 31 | ->with($themeFilters) 32 | ->willReturn([$theme]); 33 | 34 | $themeListController = new ThemeListController($themeListServiceMock); 35 | $resultedThemeList = $themeListController->themesList($themeFilters); 36 | 37 | $this->assertSame([$theme], $resultedThemeList); 38 | } 39 | 40 | public function testThemesListWithoutFilter(): void 41 | { 42 | $theme = $this->createStub(ThemeDataTypeInterface::class); 43 | $themeFilters = new ComponentFilters(); 44 | 45 | $themeListServiceMock = $this->createMock(ThemeListServiceInterface::class); 46 | $themeListServiceMock->expects($this->once()) 47 | ->method('getThemeList') 48 | ->with($themeFilters) 49 | ->willReturn([$theme]); 50 | 51 | $themeListController = new ThemeListController($themeListServiceMock); 52 | $resultedThemeList = $themeListController->themesList(null); 53 | 54 | $this->assertSame($resultedThemeList, [$theme]); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Controller/ThemeSwitchControllerTest.php: -------------------------------------------------------------------------------- 1 | createMock(ThemeSwitchServiceInterface::class); 27 | $themeSwitchServiceMock 28 | ->method('switchTheme') 29 | ->with($themeId) 30 | ->willReturn($expectedResult); 31 | 32 | $themeSwitchController = new ThemeSwitchController($themeSwitchServiceMock); 33 | $response = $themeSwitchController->switchTheme($themeId); 34 | 35 | $this->assertSame($expectedResult, $response); 36 | } 37 | 38 | public static function switchThemeProvider(): \Generator 39 | { 40 | yield 'test switch theme successful case' => [ 41 | 'themeId' => 'validThemeId', 42 | 'expectedResult' => true 43 | ]; 44 | 45 | yield 'test switch theme failure case' => [ 46 | 'themeId' => 'invalidThemeId', 47 | 'expectedResult' => false 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Theme/DataType/ThemeDataTypeFactoryTest.php: -------------------------------------------------------------------------------- 1 | createMock(Theme::class); 25 | 26 | $expectedTitle = uniqid(); 27 | $expectedId = uniqid(); 28 | $expectedVersion = uniqid(); 29 | $expectedDescription = uniqid(); 30 | $expectedActive = true; 31 | $expectedParentTheme = uniqid(); 32 | $expectedParentVersions = [uniqid(), uniqid()]; 33 | 34 | $themeMock->method('getInfo') 35 | ->willReturnMap([ 36 | ['title', $expectedTitle], 37 | ['id', $expectedId], 38 | ['version', $expectedVersion], 39 | ['description', $expectedDescription], 40 | ['active', $expectedActive], 41 | ['parentTheme', $expectedParentTheme], 42 | ['parentVersions', $expectedParentVersions], 43 | ]); 44 | 45 | $factory = new ThemeDataTypeFactory(); 46 | $themeDataType = $factory->createFromCoreTheme($themeMock); 47 | 48 | $this->assertInstanceOf(ThemeDataType::class, $themeDataType); 49 | $this->assertEquals($expectedTitle, $themeDataType->getTitle()); 50 | $this->assertEquals($expectedId, $themeDataType->getId()); 51 | $this->assertEquals($expectedVersion, $themeDataType->getVersion()); 52 | $this->assertEquals($expectedDescription, $themeDataType->getDescription()); 53 | $this->assertTrue($themeDataType->isActive()); 54 | $this->assertEquals($expectedParentTheme, $themeDataType->getParentTheme()); 55 | $this->assertEquals($expectedParentVersions, $themeDataType->getParentVersions()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Unit/Theme/DataType/ThemeDataTypeTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ComponentDataTypeInterface::class, $sut); 35 | $this->assertSame($name, $sut->getTitle()); 36 | $this->assertSame($id, $sut->getId()); 37 | $this->assertSame($version, $sut->getVersion()); 38 | $this->assertSame($description, $sut->getDescription()); 39 | $this->assertSame($active, $sut->isActive()); 40 | $this->assertSame($parentTheme, $sut->getParentTheme()); 41 | $this->assertSame($parentVersions, $sut->getParentVersions()); 42 | } 43 | 44 | public function testThemeDataTypeWithNullableValues(): void 45 | { 46 | $name = uniqid(); 47 | $id = uniqid(); 48 | $version = uniqid(); 49 | $description = uniqid(); 50 | $active = (bool)random_int(0, 1); 51 | $parentTheme = null; 52 | $parentVersions = null; 53 | 54 | $sut = new ThemeDataType($id, $name, $version, $description, $active, $parentTheme, $parentVersions); 55 | 56 | $this->assertNull($sut->getParentTheme()); 57 | $this->assertNull($sut->getParentVersions()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Exception/NoSettingsFoundForThemeExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertMatchesRegularExpression("/for theme: someTheme$/", $sut->getMessage()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Exception/ThemeActivationExceptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ThemeActivationException::class, $exception); 28 | $this->assertSame(self::THEME_NOT_ACTIVATED_MESSAGE, $exception->getMessage()); 29 | } 30 | 31 | public function testThemeActivationExceptionWithMessage() 32 | { 33 | $message = uniqid(); 34 | $exception = new ThemeActivationException($message); 35 | 36 | $this->assertInstanceOf(ThemeActivationException::class, $exception); 37 | $this->assertSame($message, $exception->getMessage()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Exception/ThemesNotFoundTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ThemesNotFound::class, $exception); 25 | $this->assertSame('No themes were found.', $exception->getMessage()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Infrastructure/AbstractThemeSettingRepositoryTestCase.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(ThemeSettingRepository::class) 30 | ->setConstructorArgs([ 31 | $context ?? $this->createMock(ContextInterface::class), 32 | $eventDispatcher ?? $this->createMock(EventDispatcherInterface::class), 33 | $queryBuilderFactory ?? $this->createMock(QueryBuilderFactoryInterface::class), 34 | $settingEncoder ?? $this->createMock(ShopSettingEncoderInterface::class) 35 | ]) 36 | ->onlyMethods($methods ?? []) 37 | ->getMock(); 38 | return $repository; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Infrastructure/ThemeListInfrastructureTest.php: -------------------------------------------------------------------------------- 1 | createStub(Theme::class); 28 | $theme2 = $this->createStub(Theme::class); 29 | $themeDataType1 = $this->createStub(ThemeDataType::class); 30 | $themeDataType2 = $this->createStub(ThemeDataType::class); 31 | 32 | $coreThemeMock = $this->createMock(Theme::class); 33 | $coreThemeMock->method('getList')->willReturn([$theme1, $theme2]); 34 | $coreThemeFactoryMock = $this->getCoreThemeFactoryMock(returnValue: $coreThemeMock); 35 | 36 | $themeDataTypeFactory = $this->createMock(ThemeDataTypeFactoryInterface::class); 37 | $themeDataTypeFactory->method('createFromCoreTheme')->willReturnMap([ 38 | [$theme1, $themeDataType1], 39 | [$theme2, $themeDataType2] 40 | ]); 41 | 42 | $sut = $this->getSut(coreThemeFactory: $coreThemeFactoryMock, themeDataTypeFactory: $themeDataTypeFactory); 43 | $actualThemesArray = $sut->getThemes(); 44 | $this->assertSame([$themeDataType1, $themeDataType2], $actualThemesArray); 45 | } 46 | 47 | public function testGetThemesThrowsException(): void 48 | { 49 | $coreThemeMock = $this->createMock(Theme::class); 50 | $coreThemeMock->method('getList')->willReturn([]); 51 | $coreThemeFactoryMock = $this->getCoreThemeFactoryMock(returnValue: $coreThemeMock); 52 | 53 | $sut = $this->getSut(coreThemeFactory: $coreThemeFactoryMock); 54 | 55 | $this->expectException(ThemesNotFound::class); 56 | $sut->getThemes(); 57 | } 58 | 59 | private function getSut( 60 | CoreThemeFactoryInterface $coreThemeFactory = null, 61 | ThemeDataTypeFactoryInterface $themeDataTypeFactory = null 62 | ): ThemeListInfrastructure { 63 | return new ThemeListInfrastructure( 64 | coreThemeFactory: $coreThemeFactory ?? $this->createStub(CoreThemeFactoryInterface::class), 65 | themeDataTypeFactory: $themeDataTypeFactory ?? $this->createStub(ThemeDataTypeFactoryInterface::class) 66 | ); 67 | } 68 | 69 | private function getCoreThemeFactoryMock(Theme $returnValue): CoreThemeFactoryInterface 70 | { 71 | $coreThemeFactoryMock = $this->createMock(CoreThemeFactoryInterface::class); 72 | $coreThemeFactoryMock->method('create') 73 | ->willReturn($returnValue); 74 | 75 | return $coreThemeFactoryMock; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Infrastructure/ThemeSettingRepositorySettersTest.php: -------------------------------------------------------------------------------- 1 | getSut(methods: ['getSettingValue', 'saveSettingValue']); 31 | $sut->method('getSettingValue') 32 | ->with($name, $this->logicalOr(...FieldType::getEnums()), $themeId) 33 | ->willThrowException(new NoSettingsFoundForThemeException($themeId)); 34 | $sut->expects($this->never()) 35 | ->method('saveSettingValue'); 36 | 37 | $this->expectException(NoSettingsFoundForThemeException::class); 38 | 39 | $sut->$repositoryMethod($name, $value, $themeId); 40 | } 41 | 42 | public static function notExistingSettingCheckTriggerDataProvider(): \Generator 43 | { 44 | yield "saveIntegerSetting" => [ 45 | 'repositoryMethod' => 'saveIntegerSetting', 46 | 'value' => 1234 47 | ]; 48 | yield "saveFloatSetting" => [ 49 | 'repositoryMethod' => 'saveFloatSetting', 50 | 'value' => 1.23 51 | ]; 52 | yield "saveBooleanSetting" => [ 53 | 'repositoryMethod' => 'saveBooleanSetting', 54 | 'value' => true 55 | ]; 56 | yield "saveStringSetting" => [ 57 | 'repositoryMethod' => 'saveStringSetting', 58 | 'value' => 'some string' 59 | ]; 60 | yield "saveSelectSetting" => [ 61 | 'repositoryMethod' => 'saveSelectSetting', 62 | 'value' => 'some select' 63 | ]; 64 | yield "saveCollectionSetting" => [ 65 | 'repositoryMethod' => 'saveCollectionSetting', 66 | 'value' => ['collection'] 67 | ]; 68 | yield "saveAssocCollectionSetting" => [ 69 | 'repositoryMethod' => 'saveAssocCollectionSetting', 70 | 'value' => ['collection'] 71 | ]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Service/ThemeListServiceTest.php: -------------------------------------------------------------------------------- 1 | createStub(ThemeDataTypeInterface::class); 29 | $themeDataType2 = $this->createStub(ThemeDataTypeInterface::class); 30 | $themes = [$themeDataType1, $themeDataType2]; 31 | $filteredThemeList = [$themeDataType1]; 32 | 33 | $themeListInfrastructureMock = $this->createMock(ThemeListInfrastructureInterface::class); 34 | $themeListInfrastructureMock->method('getThemes')->willReturn($themes); 35 | 36 | $componentFiltersStub = $this->createStub(ComponentFiltersInterface::class); 37 | 38 | $componentFilterServiceMock = $this->createMock(ComponentFilterServiceInterface::class); 39 | $componentFilterServiceMock->method('filterComponents') 40 | ->with($themes, $componentFiltersStub) 41 | ->willReturn($filteredThemeList); 42 | 43 | $themeListService = new ThemeListService( 44 | themeListInfrastructure: $themeListInfrastructureMock, 45 | componentFilterService: $componentFilterServiceMock, 46 | ); 47 | $actualThemes = $themeListService->getThemeList($componentFiltersStub); 48 | $this->assertSame($filteredThemeList, $actualThemes); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Theme/Service/ThemeSwitchServiceTest.php: -------------------------------------------------------------------------------- 1 | createMock(ThemeSwitchInfrastructureInterface::class); 27 | $themeSwitchInfrastructureMock 28 | ->method('switchTheme') 29 | ->with($themeId) 30 | ->willReturn($expectedResult); 31 | 32 | $themeSwitchInfrastructure = new ThemeSwitchService($themeSwitchInfrastructureMock); 33 | $response = $themeSwitchInfrastructure->switchTheme($themeId); 34 | 35 | $this->assertSame($expectedResult, $response); 36 | } 37 | 38 | public static function switchThemeProvider(): \Generator 39 | { 40 | yield 'test switch theme successful case' => [ 41 | 'expectedResult' => true 42 | ]; 43 | 44 | yield 'test switch theme failure case' => [ 45 | 'expectedResult' => false 46 | ]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Unit/UnitTestCase.php: -------------------------------------------------------------------------------- 1 | createMock(ContextInterface::class); 34 | $context->method('getCurrentShopId')->willReturn($shopId); 35 | 36 | return $context; 37 | } 38 | 39 | protected function getJsonEncodeServiceMock( 40 | array $repositoryResult, 41 | string $collectionEncodingResult 42 | ): CollectionEncodingServiceInterface { 43 | $jsonService = $this->createMock(CollectionEncodingServiceInterface::class); 44 | $jsonService->method('encodeArrayToString') 45 | ->with($repositoryResult) 46 | ->willReturn($collectionEncodingResult); 47 | return $jsonService; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/codeception.yml: -------------------------------------------------------------------------------- 1 | namespace: OxidEsales\GraphQL\ConfigurationAccess\Tests\Codeception 2 | params: 3 | - Codeception/Config/params.php 4 | paths: 5 | tests: Codeception 6 | output: Codeception/_output 7 | data: Codeception/Data 8 | support: Codeception/Support 9 | envs: Codeception/_envs 10 | actor_suffix: Tester 11 | 12 | settings: 13 | colors: true 14 | log: true 15 | memory_limit: 4096M 16 | 17 | extensions: 18 | enabled: 19 | - Codeception\Extension\RunFailed 20 | 21 | coverage: 22 | enabled: true 23 | remote: false 24 | local: true 25 | c3_url: '%SHOP_URL%' 26 | remote_config: 'vendor/oxid-esales/graphql-configuration-access/tests/codeception.yml' 27 | include: 28 | - ../src/* 29 | -------------------------------------------------------------------------------- /tests/phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Oxid Coding Standard 4 | 5 | 6 | ../src/ 7 | ./ 8 | ./tests/Codeception/Config 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ./migration 21 | 22 | 23 | 24 | 25 | ./tests 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | Unit/ 22 | 23 | 24 | Integration 25 | 26 | 27 | 28 | 29 | ../src 30 | 31 | 32 | ../tests 33 | 34 | 35 | 36 | --------------------------------------------------------------------------------