{"id":17960430,"url":"https://github.com/flyrell/avast-configuration-manager","last_synced_at":"2025-04-03T18:23:31.663Z","repository":{"id":48063717,"uuid":"393303725","full_name":"Flyrell/avast-configuration-manager","owner":"Flyrell","description":"Project created as part of an interview for Avast","archived":false,"fork":false,"pushed_at":"2021-08-09T10:46:23.000Z","size":182,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-22T01:04:17.591Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Flyrell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-06T08:04:06.000Z","updated_at":"2023-07-28T17:42:36.000Z","dependencies_parsed_at":"2022-07-28T23:09:23.268Z","dependency_job_id":null,"html_url":"https://github.com/Flyrell/avast-configuration-manager","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flyrell%2Favast-configuration-manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flyrell%2Favast-configuration-manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flyrell%2Favast-configuration-manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flyrell%2Favast-configuration-manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Flyrell","download_url":"https://codeload.github.com/Flyrell/avast-configuration-manager/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247053604,"owners_count":20875868,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-29T11:06:23.656Z","updated_at":"2025-04-03T18:23:31.625Z","avatar_url":"https://github.com/Flyrell.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Avast Interview Task\n\n## Task\n- From attached XML file (resources/config.xml), please export data to Redis,\n- key \"subdomains\" will contain JSON with all subdomains (e.g. [\"http://secureline.tools.avast.com\", \"http://gf.tools.avast.com\"]),\n- keys \"cookie:%NAME%:%HOST%\" will contain values of cookie elements (e.g. key \"cookie:dlp-avast:amazon\" will contain string \"mmm_amz_dlp_777_ppc_m\"),\n- use docker-compose for setting up cloud environment (PHP and Redis needs to have their own containers),\n- please use PHPUnit for tests.\n- to run the app please use this command: export.sh /path/to/xml\n- if \"-v\" argument is present in command it should print all keys saved to Redis (export.sh -v /path/to/xml)\n\n## Installation\n```bash\ngit clone https://github.com/Flyrell/avast-configuration-manager.git\ncd avast-configuration-manager\ndocker-compose up -d\ndocker-compose run php composer install\n```\n\n## Solution\n\n### Command\nAs you may have noticed - this was pretty robust one. \nNot that it had to be robust, but I wanted to show you various design patterns, code expandability predictions, etc.\nI will explain how the program works in the following lines...\n\n#### Entry point\nThe entry point for our application is (based on the task) the `export.sh` file.\nThis file is added to our `php` service image and mounted to `/use/local/bin` by docker-compose,\nwhich allows us to execute it without prepending `./` (looks like it was a desired behaviour in task specification).\n\nThe `export.sh` file acts like the bridge between the CLI and Symfony's command ecosystem as it\nexecutes only the following line `cd /var/www/symfony \u0026\u0026 php bin/console config:load \u003cargs\u003e`.\n\nTo run export.sh use:\n```bash\n# without verbosity\ndocker-compose run php export.sh resources/config.xml\n\n# with verbosity\ndocker-compose run php export.sh -v resources/config.xml\n```\n\n#### Configuration load command\nThe `config:load` command accepts one argument, which is a path to the `.xml` file with\nconfiguration. When the path is not provided, error is presented to the user.\n\nCommand also accepts verbosity level options like `-q`, `-v`, `-vv` and `-vvv`.\nAll options are presented by default via Symfony's command line (I haven't created them myself)\nand for the purpose of this program, the following applies:\n\n- no verbosity option or `-q`: results is no output\n- `-v`, `-vv` and `-vvv`: outputs all the keys saved to cache\n\nThe purpose of this command is to extract the passed argument (filepath)\nand pass it to `ConfigService::loadFromFile` method.\n\n#### ConfigService\nCurrently, contains only the `loadFromFile` method, but as the application\nmight grow we can add other methods like `loadFromString`, etc.\n\nMethod `loadFromFile` returns `iterable` with saved keys.\n\nIn our case, `ConfigService` runs the `ConfigParserInterface` to parse the\npassed configuration and then saves the parsed configuration to the cache via\n`CacheInterface`.\n\n### Parsing\n\n#### ConfigParserInterface\nNot that we're creating a reusable library, but using interfaces is always a good practice.\nThis interface exposes two methods: `parseFile` and `parseString`.\n\n#### ConfigParser (impl. ConfigParserInterface)\nThe `parseFile` method calls the `FileService::read` to get the contents of the file.\nWhen file contents are obtained, the `parseString` method is called, which iterates over\navailable format parsers and uses the first supported one. This enables us to easily add new format parsers\nin the future (e.g. `JsonParser`). Format parsers are added to the array in `services.yaml`.\n\n#### FileService\nSimple service used for obtaining the file contents from existing files.\n\n#### FormatParserInterface\nInterface used for format parsers. Contains only two methods: `supports` and `parse`.\n\n#### XMLFormatParser (impl. FormatParserInterface)\nMethod `supports` tries to parse the contents of the XML to determine if the XML is correct and also\nvalidates the config via `ConfigValidatorInterface`.\n\nMethod `parse` traverses the parsed DOM from the entry point (`config` element).\nWhenever an element is found, similar design pattern as with format parsers is repeated.\nFunction iterates over available node parsers until the supported one is found and then parse method is called.\nEach node parser returns and array of parsed results, which are then added to the `ConfigDto`.\n\n#### XMLNodeParserInterface and specific XMLNodeParsers\nNo need to explain this, as the previous point went through the core of it.\n\n#### ConfigValidatorInterface\nExposes method `validate` which validates the provided content.\n\n#### AbstractConfigValidator (impl. ConfigValidatorInterface)\nLoads configuration schema (or mapping, as some would say) and contains helper methods to validate it.\nConfiguration schema is present in `config/validation/config_schema.yaml`. It enables us to quickly add supported\nproperties and, hopefully, still have the correct validation for them.\n\n#### XMLConfigValidator (ext. AbstractConfigValidator)\nTraverses the configuration schema and runs the callback for each schema element to check against the DOM.\n\n### Cache\nAs mention in the task, Redis is used as the main (and only) caching mechanism.\nFor best results when invalidating the cache, tags are enabled.\n\n#### CacheableCollection\n\nFor easier cache manipulation and no \"hard-coding\" of what should be stored, the `CacheableCollection` was created.\nThe cacheable collection groups multiple `CacheableCollectionItem` object and sets properties\nas expiration and tags globally for each item inside.\n\nObject `CacheableCollectionItem` contains value and the key under it's going to be stored.\n\nOf course, both collection and item operates with interfaces, which can be used for your own implementation\n(as in the Dtos below).\n\n#### Dtos\n`ConfigDto` contains parsed configuration items and implements `CacheableCollectionIterface`. This enables us\nto save the whole config at once. Both normal and `CacheableCollectionItemIterface` items can be added. Whenever\nthe normal key-value is added to the configuration it gets converted to `CacheableCollectionItem` automatically.\n\nWhile parsing the configuration, `SubdomainsDto` and `CookieDto` instances are created\nrespectively. Both Dtos implement `CacheableCollectionItemIterface`.\nThere should only be one `CacheableCollectionItemIterface` item per \"key in cache\".\n\n#### App\\Cache\\CacheInterface\n\nIn order to work with `CacheableCollection` we can use `App\\Cache\\CacheInterface`, which is automatically\nconfigured to auto-wire custom `RedisCacheAdapter`. The interface currently exposes only `saveCollection` method.\n\n### Other\n\n#### Enum\nNative enums are not yet in the PHP v8.0 and will be introduced in v8.1. Fortunately,\nthere's an alternative way to store enum types, which is setting up the public constants\nin a public classes.\n\n#### Exception\nCustom Exception objects to correctly handle errors in the application.\n\n\n## Testing\nFor now, only unit tests were created for the application.\nUnit test are grouped in their own test suite and can be executed via:\n```bash\n docker-compose run php vendor/bin/phpunit --testsuite Unit\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflyrell%2Favast-configuration-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflyrell%2Favast-configuration-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflyrell%2Favast-configuration-manager/lists"}