From c795b89ead7f434b4f148a84cedc3f15c9d4ecef Mon Sep 17 00:00:00 2001 From: Isaac Sai Date: Sat, 2 May 2020 16:54:58 +0000 Subject: [PATCH] Initial Release Ready for testing (#1) * Updated Functions * Updated Readme * Updated changelog --- .travis.yml | 19 +++++++++++++ changelog.md | 21 ++++++++++++--- config/ussd.php | 23 ++++++++++++---- readme.md | 51 +++++++++++++++++++++++++++++------ src/Commands/state.stub | 4 +-- src/Decision.php | 53 +++++++++++++++++++++++++++++++++++++ src/HasManipulators.php | 21 +++++++-------- src/Machine.php | 25 ++++++++++++----- src/Menu.php | 39 +++++++++++++++++++++++++++ src/Record.php | 17 +++++++++--- src/State.php | 48 ++++++++++++++++++--------------- src/UssdServiceProvider.php | 2 +- 12 files changed, 261 insertions(+), 62 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..22d840e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - '7.2' + - '7.3' + - '7.4' + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +before_script: + - travis_retry composer self-update + - travis_retry composer update --no-interaction --prefer-dist + +script: + - vendor/bin/phpunit \ No newline at end of file diff --git a/changelog.md b/changelog.md index 188a690..d02970e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,8 +1,23 @@ # Changelog -All notable changes to `Ussd` will be documented in this file. +All notable changes to `laravel Ussd` will be documented in this file. -## Version 1.0 +## [Unreleased] +## [v0.1.0] - 2020-05-02 ### Added -- Everything +- Ussd Package Project with README, contributing, changelog, license, etc. +- State class to define what should occur at various stages when user navigates +- Artisan command to create State classes +- Machine class to run all linked States +- HasManipulators traits to help Machine Class with common functions +- Menu class to be used create user menus in the various states +- Decision class to decide on how to link the various states after accepting user's input +- Record class to save data +- Ussd Class to provide access other classes +- Ussd facade to proxy to the Ussd class +- Ussd config to allow developers customize behaviour +- Ussd service Provider class to allow laravel know how to integrate the package + +[Unreleased]: ../../compare/v0.1.0...HEAD +[v0.1.0]: ../../releases/tag/v0.1.0 diff --git a/config/ussd.php b/config/ussd.php index e1bd7ba..b431732 100644 --- a/config/ussd.php +++ b/config/ussd.php @@ -14,6 +14,19 @@ 'class_namespace' => 'App\\Http\\Ussd', + /* + |-------------------------------------------------------------------------- + | Store + |-------------------------------------------------------------------------- + | + | This value sets the default store to use for the ussd record. + | The store can be found in your cache stores config + | + */ + + 'store' => null, + + /* |-------------------------------------------------------------------------- | Time to live @@ -24,17 +37,17 @@ | */ - 'cache_ttl' => 0, + 'cache_ttl' => null, /* |-------------------------------------------------------------------------- - | Store + | Default value |-------------------------------------------------------------------------- | - | This value sets the default store to use for the ussd record. - | The store can be found in your cache stores config + | This value return the default store value when a given cache key + | is not found | */ - 'store' => null, + 'cache_default' => null, ]; \ No newline at end of file diff --git a/readme.md b/readme.md index 89a3ea1..ac08c34 100644 --- a/readme.md +++ b/readme.md @@ -4,18 +4,53 @@ [![Total Downloads][ico-downloads]][link-downloads] [![Build Status][ico-travis]][link-travis] -This is where your description should go. Take a look at [contributing.md](contributing.md) to see a to do list. +Create ussd with ease. Take a look at [contributing.md](contributing.md) to see a to do list. ## Installation Via Composer ``` bash -$ composer require sparors/ussd +$ composer require sparors/laravel-ussd +``` + +Ussd is meant to provide zero configuration out of the box. But optional you can publish the configuration to customize it to suit you. + +``` bash +$ php artisan vendor:publish --provider="Sparors\Ussd\UssdServiceProvider" --tag=config ``` ## Usage +Create your ussd states by running the command + +``` bash +php artisan ussd:state Welcome +```` + +After creating your states, you can link them to one another and just create a machine to run it. + +``` php +setInput('1') + ->setNetwork('MTN') + ->setSessionId('12350') + ->setPhoneNumber('0545112466') + ->setInitialState(Welcome::class); + + return response()->json($ussd->run()); +}); +``` + +That all the magic you need to make it run + ## Change log Please see the [changelog](changelog.md) for more information on what has changed recently. @@ -43,12 +78,12 @@ If you discover any security related issues, please email isaacsai030@gmail.com MIT. Please see the [license file](license.md) for more information. -[ico-version]: https://img.shields.io/packagist/v/sparors/ussd.svg?style=flat-square -[ico-downloads]: https://img.shields.io/packagist/dt/sparors/ussd.svg?style=flat-square -[ico-travis]: https://img.shields.io/travis/sparors/ussd/master.svg?style=flat-square +[ico-version]: https://img.shields.io/packagist/v/sparors/laravel-ussd.svg?style=flat-square +[ico-downloads]: https://img.shields.io/packagist/dt/sparors/laravel-ussd.svg?style=flat-square +[ico-travis]: https://img.shields.io/travis/sparors/laravel-ussd/master.svg?style=flat-square -[link-packagist]: https://packagist.org/packages/sparors/ussd -[link-downloads]: https://packagist.org/packages/sparors/ussd -[link-travis]: https://travis-ci.org/sparors/ussd +[link-packagist]: https://packagist.org/packages/sparors/laravel-ussd +[link-downloads]: https://packagist.org/packages/sparors/laravel-ussd +[link-travis]: https://travis-ci.org/sparors/laravel-ussd [link-author]: https://github.com/sparors [link-contributors]: ../../contributors diff --git a/src/Commands/state.stub b/src/Commands/state.stub index dde6054..890dea5 100644 --- a/src/Commands/state.stub +++ b/src/Commands/state.stub @@ -6,12 +6,12 @@ use Sparors\Ussd\State; class [class] extends State { - protected function prepareMenu(): void + protected function beforeRendering(): void { // } - protected function prepareDecision(string $argument): void + protected function afterRendering(string $argument): void { // } diff --git a/src/Decision.php b/src/Decision.php index 4bda7a8..1187b3d 100644 --- a/src/Decision.php +++ b/src/Decision.php @@ -4,10 +4,12 @@ class Decision { + /** @var boolean */ protected $decided; protected $argument; + /** @var string */ protected $output; public function __construct($argument = null) @@ -38,11 +40,20 @@ private function setOutputForCondition($condition, $output) return $this; } + /** + * @return string + */ public function outcome() { return $this->output; } + /** + * @param mixed $argument + * @param string $output + * @param boolean $strict + * @return Decision + */ public function equal($argument, $output, $strict = false) { return $this->setOutputForCondition(function () use ($argument, $strict) { @@ -53,6 +64,10 @@ public function equal($argument, $output, $strict = false) }, $output); } + /** + * @param string $output + * @return Decision + */ public function numeric($output) { return $this->setOutputForCondition(function () { @@ -60,6 +75,10 @@ public function numeric($output) }, $output); } + /** + * @param string $output + * @return Decision + */ public function integer($output) { return $this->setOutputForCondition(function () { @@ -67,6 +86,10 @@ public function integer($output) }, $output); } + /** + * @param string $output + * @return Decision + */ public function amount($output) { return $this->setOutputForCondition(function () { @@ -74,6 +97,11 @@ public function amount($output) }, $output); } + /** + * @param mixed $argument + * @param string $output + * @return Decision + */ public function length($argument, $output) { return $this->setOutputForCondition(function () use ($argument) { @@ -81,6 +109,10 @@ public function length($argument, $output) }, $output); } + /** + * @param string $output + * @return Decision + */ public function phoneNumber($output) { return $this->setOutputForCondition(function () { @@ -88,6 +120,12 @@ public function phoneNumber($output) }, $output); } + /** + * @param int $start + * @param int $end + * @param string $output + * @return Decision + */ public function between($start, $end, $output) { return $this->setOutputForCondition(function () use ($start, $end) { @@ -95,6 +133,12 @@ public function between($start, $end, $output) }, $output); } + /** + * @param array $array + * @param string $output + * @param bool $strict + * @return Decision + */ public function in($array, $output, $strict = false) { return $this->setOutputForCondition(function () use ($array, $strict) { @@ -102,12 +146,21 @@ public function in($array, $output, $strict = false) }, $output); } + /** + * @param callable $function + * @param string $output + * @return Decision + */ public function custom($function, $output) { $func = function () use ($function) { return $function($this->argument); }; return $this->setOutputForCondition($func, $output); } + /** + * @param string $output + * @return Decision + */ public function any($output) { return $this->setOutputForCondition(function () { diff --git a/src/HasManipulators.php b/src/HasManipulators.php index 30ab6d5..4a8233e 100644 --- a/src/HasManipulators.php +++ b/src/HasManipulators.php @@ -10,30 +10,35 @@ trait HasManipulators public function setSessionId(string $sessionId) { $this->sessionId = $sessionId; + return $this; } public function setSessionIdFromRequest(string $key) { $this->sessionId = request($key); + return $this; } public function setPhoneNumber(string $phoneNumber) { $this->phoneNumber = $phoneNumber; + return $this; } public function setPhoneNumberFromRequest(string $key) { $this->phoneNumber = request($key); + return $this; } public function setNetwork(string $network) { $this->network = $network; + return $this; } @@ -46,30 +51,21 @@ public function setNetworkFromRequest(string $key) public function setInput(string $input) { $this->input = $input; + return $this; } public function setInputFromRequest(string $key) { $this->input = request($key); - return $this; - } - public function setType(string $type) - { - $this->type = $type; - return $this; - } - - public function setTypeFromRequest(string $key) - { - $this->type = request($key); return $this; } public function setStore(string $store) { $this->store = $store; + return $this; } @@ -81,6 +77,7 @@ public function set(array $parameters) $this->$property = $value; } } + return $this; } @@ -94,6 +91,7 @@ public function setFromRequest(array $parameters) $this->{Str::camel($key)} = request($key); } } + return $this; } @@ -106,6 +104,7 @@ public function setInitialState($state) } else { $this->initialState = null; } + return $this; } diff --git a/src/Machine.php b/src/Machine.php index 80d3ffa..ca1eccb 100644 --- a/src/Machine.php +++ b/src/Machine.php @@ -29,15 +29,15 @@ public function __construct() $this->input = null; $this->response = function (string $message, int $code) { return [ - 'default_message' => $message, - 'default_code' => $code, + 'message' => $message, + 'code' => $code, ]; }; } public function run() { - throw_if(is_null($this->sessionId), Exception::class, 'SessionId needs to be set be ussd machine can run.'); + throw_if(is_null($this->sessionId), Exception::class, 'SessionId needs to be set before ussd machine can run.'); $this->record = new Record(Cache::store($this->store), $this->sessionId); @@ -46,16 +46,27 @@ public function run() if ($this->record->has('__init')) { $active = $this->record->get('__active'); + throw_if(! class_exists($active), Exception::class, 'Active State Class needs to be set before ussd machine can run. It may be that your session has ended.'); + $activeClass = new $active; $state = $activeClass->next($this->input); + + throw_if(! class_exists($state), Exception::class, 'Continuing State Class needs to be set before ussd machine can run. It may be that your session has ended.'); + $stateClass = new $state; + + $stateClass->setRecord($this->record); $this->record->set('__active', $state); } else { + throw_if(! class_exists($this->initialState), Exception::class, 'Initial State Class needs to be set before ussd machine can run.'); + $this->record->set('__active', $this->initialState); $this->record->set('__init', true); + $stateClass = new $this->initialState; + $stateClass->setRecord($this->record); } return ($this->response)($stateClass->render(), $stateClass->getType()); } @@ -69,9 +80,9 @@ private function saveParameter(string $key, $value) private function saveParameters() { - $this->saveParameter('__sessionId', $this->sessionId); - $this->saveParameter('__phoneNumber', $this->phoneNumber); - $this->saveParameter('__network', $this->network); - $this->saveParameter('__input', $this->input); + $this->saveParameter('sessionId', $this->sessionId); + $this->saveParameter('phoneNumber', $this->phoneNumber); + $this->saveParameter('network', $this->network); + $this->saveParameter('input', $this->input); } } \ No newline at end of file diff --git a/src/Menu.php b/src/Menu.php index 35fcff6..570d35d 100644 --- a/src/Menu.php +++ b/src/Menu.php @@ -23,6 +23,9 @@ class Menu const NUMBERING_SEPARATOR_BRACKET_PLUS_SPACE = ") "; const NUMBERING_SEPARATOR_BRACKET_PLUS_DOUBLE_SPACE = ") "; + /** + * @var string + */ protected $menu; public function __construct($menu = '') @@ -72,33 +75,65 @@ private function listParser($items, $page, $numberPerPage, $numberingSeparator, } } + /** + * @param int $number + * @return Menu + */ public function lineBreak($number = 1) { $this->menu .= str_repeat("\n", $number); + return $this; } + /** + * @param string $text + * @return Menu + */ public function line($text) { $this->menu .= "$text\n"; + return $this; } + /** + * @param string $text + * @return Menu + */ public function text($text) { $this->menu .= $text; + return $this; } + /** + * @param array $items + * @param string $numberingSeparator + * @param string $itemsSeparator + * @param string $numbering + * @return Menu + */ public function listing($items, $numberingSeparator = self::NUMBERING_SEPARATOR_DOT, $itemsSeparator = self::ITEMS_SEPARATOR_LINE_BREAK, $numbering = self::NUMBERING_NUMERIC) { $this->listParser($items, 1, count($items), $numberingSeparator, $itemsSeparator, $numbering); + return $this; } + /** + * @param array $items + * @param int $page + * @param int $numberPerPage + * @param string $numberingSeparator + * @param string $itemsSeparator + * @param string $numbering + * @return Menu + */ public function paginateListing($items, $page = 1, $numberPerPage = 5, $numberingSeparator = self::NUMBERING_SEPARATOR_DOT, $itemsSeparator = self::ITEMS_SEPARATOR_LINE_BREAK, @@ -106,9 +141,13 @@ public function paginateListing($items, $page = 1, $numberPerPage = 5, { $this->listParser($items, $page, $numberPerPage, $numberingSeparator, $itemsSeparator, $numbering); + return $this; } + /** + * @return string + */ public function toString() { return $this->menu; diff --git a/src/Record.php b/src/Record.php index a930381..2215266 100644 --- a/src/Record.php +++ b/src/Record.php @@ -27,6 +27,15 @@ private function getKey($key) return "$this->id.$key"; } + /** + * @param int $ttl + * @return int|null + */ + private function getTtl($ttl) + { + return $ttl ?? config('ussd.cache_ttl'); + } + /** * @param array $keys * @return array @@ -145,12 +154,12 @@ public function flush() public function __set($name, $value) { - $this->set($name, $value); + $this->set($name, $value, config('ussd.cache_ttl')); } public function __get($name) { - return $this->get($name); + return $this->get($name, config('ussd.cache_default')); } public function __isset($name) @@ -166,9 +175,9 @@ public function __unset($name) public function __invoke($argument) { if (is_string($argument)) { - return $this->get($argument); + return $this->get($argument, config('ussd.cache_default')); } else if (is_array($argument)) { - $this->setMultiple($argument); + $this->setMultiple($argument, config('ussd.cache_ttl')); } } } diff --git a/src/State.php b/src/State.php index bf7591c..b4e2e7e 100644 --- a/src/State.php +++ b/src/State.php @@ -4,65 +4,71 @@ abstract class State { - // Just two needed - const START = 1; - const CONTINUE = 2; - const END = 3; + /** @var int */ + const CONTINUE = 1; + + /** @var int */ + const END = 2; /** @var int */ protected $type = self::CONTINUE; - /** @var \Sparors\Ussd\Menu */ + /** @var Menu */ protected $menu; - /** @var \Sparors\Ussd\Decision */ + /** @var Decision */ protected $decision; - public final function __construct() - { - // All State constructors should has no parameter - } + /** @var Record */ + protected $record; /** - * The menu to be displayed to users + * The function to run before the rendering */ - protected abstract function prepareMenu(): void; + protected abstract function beforeRendering(): void; /** * The view to be displayed to users + * + * @return string */ - public final function render(): string + public function render(): string { $this->menu = new Menu(); $this->beforeRendering(); - $this->prepareMenu(); return $this->menu->toString(); } /** - * The decision for the next state + * The function to run after the rendering * * @param string $argument */ - protected abstract function prepareDecision(string $argument): void; + protected abstract function afterRendering(string $argument): void; /** * The new State full path */ - public final function next(string $input): ?string + public function next(string $input): ?string { $this->decision = new Decision($input); - $this->prepareDecision($input); + $this->afterRendering($input); return $this->decision->outcome(); } /** - * The function to run before the render method + * @return int */ - private function beforeRendering(): void {} - public function getType() { return $this->type; } + + /** + * @param Record $record + */ + public function setRecord(Record $record) + { + $this->record = $record; + } } diff --git a/src/UssdServiceProvider.php b/src/UssdServiceProvider.php index 3de9589..4a66bd7 100644 --- a/src/UssdServiceProvider.php +++ b/src/UssdServiceProvider.php @@ -60,7 +60,7 @@ protected function bootForConsole() // Publishing the configuration file. $this->publishes([ __DIR__.'/../config/ussd.php' => config_path('ussd.php'), - ], 'ussd.config'); + ], 'config'); // Publishing the views. /*$this->publishes([