diff --git a/src/app/Console/Command.php b/src/app/Console/Command.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Command.php @@ -0,0 +1,62 @@ +getObject(\App\Domain::class, $domain, 'namespace'); + } + + /** + * Find an object. + * + * @param string $objectClass The name of the class + * @param string $objectIdOrTitle The name of a database field to match. + * @param string|null $objectTitle An additional database field to match. + * + * @return mixed + */ + public function getObject($objectClass, $objectIdOrTitle, $objectTitle) + { + $object = $objectClass::find($objectIdOrTitle); + + if (!$object && !empty($objectTitle)) { + $object = $objectClass::where($objectTitle, $objectIdOrTitle)->first(); + } + + return $object; + } + + /** + * Find the user. + * + * @param string $user User ID or email + * + * @return \App\User|null + */ + public function getUser($user) + { + return $this->getObject(\App\User::class, $user, 'email'); + } + + /** + * Find the wallet. + * + * @param string $wallet Wallet ID + * + * @return \App\Wallet|null + */ + public function getWallet($wallet) + { + return $this->getObject(\App\Wallet::class, $wallet, null); + } +} diff --git a/src/app/Console/Commands/DBPing.php b/src/app/Console/Commands/DB/PingCommand.php rename from src/app/Console/Commands/DBPing.php rename to src/app/Console/Commands/DB/PingCommand.php --- a/src/app/Console/Commands/DBPing.php +++ b/src/app/Console/Commands/DB/PingCommand.php @@ -1,11 +1,11 @@ currency @@ -16,14 +17,14 @@ * * @var string */ - protected $signature = 'data:countries'; + protected $signature = 'data:import:countries'; /** * The console command description. * * @var string */ - protected $description = 'Fetches countries map from wikipedia'; + protected $description = 'Fetches countries map from country.io'; /** * Execute the console command. @@ -32,43 +33,63 @@ */ public function handle() { + $today = Carbon::now()->toDateString(); + $countries = []; $currencies = []; - $currencies_url = 'http://country.io/currency.json'; - $countries_url = 'http://country.io/names.json'; - - $this->info("Fetching currencies from $currencies_url..."); + $currencySource = 'http://country.io/currency.json'; + $countrySource = 'http://country.io/names.json'; - // fetch currency table and create an index by country page url - $currencies_json = file_get_contents($currencies_url); + // + // countries + // + $file = storage_path("countries-{$today}.json"); - if (!$currencies_json) { - $this->error("Failed to fetch currencies"); - return; - } + $this->info("Retrieving {$countrySource}..."); - $this->info("Fetching countries from $countries_url..."); + \App\Utils::downloadFile($countrySource, $file); - $countries_json = file_get_contents($countries_url); + $countryJson = file_get_contents($file); - if (!$countries_json) { + if (!$countryJson) { $this->error("Failed to fetch countries"); - return; + return 1; } - $currencies = json_decode($currencies_json, true); - $countries = json_decode($countries_json, true); + $countries = json_decode($countryJson, true); if (!is_array($countries) || empty($countries)) { $this->error("Invalid countries data"); + return 1; + } + + // + // currencies + // + $file = storage_path("currencies-{$today}.json"); + + $this->info("Retrieving {$currencySource}..."); + + \App\Utils::downloadFile($currencySource, $file); + + // fetch currency table and create an index by country page url + $currencyJson = file_get_contents($file); + + if (!$currencyJson) { + $this->error("Failed to fetch currencies"); return; } + $currencies = json_decode($currencyJson, true); + if (!is_array($currencies) || empty($currencies)) { $this->error("Invalid currencies data"); - return; + return 1; } + // + // export + // $file = resource_path('countries.php'); $this->info("Generating resource file $file..."); @@ -76,6 +97,7 @@ asort($countries); $out = " $name) { $currency = $currencies[$code] ?? null; @@ -84,12 +106,13 @@ } if (!$currency) { - $this->error("Unknown currency for {$name} ({$code}). Skipped."); + $this->warn("Unknown currency for {$name} ({$code}). Skipped."); continue; } $out .= sprintf(" '%s' => ['%s','%s'],\n", $code, $currency, addslashes($name)); } + $out .= "];\n"; file_put_contents($file, $out); diff --git a/src/app/Console/Commands/Data/ImportCommand.php b/src/app/Console/Commands/Data/ImportCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Data/ImportCommand.php @@ -0,0 +1,40 @@ +output = $this->output; + $execution->handle(); + } + } +} diff --git a/src/app/Console/Commands/Discount/CreateCommand.php b/src/app/Console/Commands/Discount/CreateCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Discount/CreateCommand.php @@ -0,0 +1,12 @@ +orderBy('discount')->get()->each( - function ($discount) { - $name = $discount->description; - - if ($discount->code) { - $name .= " [{$discount->code}]"; - } - - $this->info( - sprintf( - "%s %3d%% %s", - $discount->id, - $discount->discount, - $name - ) - ); - } - ); - } -} diff --git a/src/app/Console/Commands/DiscountsCommand.php b/src/app/Console/Commands/DiscountsCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/DiscountsCommand.php @@ -0,0 +1,12 @@ +argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { + $this->error("Domain not found."); return 1; } diff --git a/src/app/Console/Commands/DomainList.php b/src/app/Console/Commands/Domain/ListDomains.php rename from src/app/Console/Commands/DomainList.php rename to src/app/Console/Commands/Domain/ListDomains.php --- a/src/app/Console/Commands/DomainList.php +++ b/src/app/Console/Commands/Domain/ListDomains.php @@ -1,11 +1,11 @@ argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { + $this->error("Domain not found."); return 1; } diff --git a/src/app/Console/Commands/DomainSetWallet.php b/src/app/Console/Commands/Domain/SetWallet.php rename from src/app/Console/Commands/DomainSetWallet.php rename to src/app/Console/Commands/Domain/SetWallet.php --- a/src/app/Console/Commands/DomainSetWallet.php +++ b/src/app/Console/Commands/Domain/SetWallet.php @@ -1,15 +1,15 @@ argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { $this->error("Domain not found."); diff --git a/src/app/Console/Commands/DomainStatus.php b/src/app/Console/Commands/Domain/Status.php rename from src/app/Console/Commands/DomainStatus.php rename to src/app/Console/Commands/Domain/Status.php --- a/src/app/Console/Commands/DomainStatus.php +++ b/src/app/Console/Commands/Domain/Status.php @@ -1,11 +1,11 @@ argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { + $this->error("Domain not found."); return 1; } @@ -48,15 +39,15 @@ 'active' => Domain::STATUS_ACTIVE, 'suspended' => Domain::STATUS_SUSPENDED, 'deleted' => Domain::STATUS_DELETED, - 'ldapReady' => Domain::STATUS_LDAP_READY, - 'verified' => Domain::STATUS_VERIFIED, 'confirmed' => Domain::STATUS_CONFIRMED, + 'verified' => Domain::STATUS_VERIFIED, + 'ldapReady' => Domain::STATUS_LDAP_READY, ]; foreach ($statuses as $text => $bit) { $func = 'is' . \ucfirst($text); - $this->info(sprintf("%d %s: %s", $bit, $text, $domain->$func())); + $this->info(sprintf("%d %s: %s", $bit, $text, $domain->$func() ? "yes" : "no")); } $this->info("In total: {$domain->status}"); diff --git a/src/app/Console/Commands/DomainSuspend.php b/src/app/Console/Commands/Domain/Suspend.php rename from src/app/Console/Commands/DomainSuspend.php rename to src/app/Console/Commands/Domain/Suspend.php --- a/src/app/Console/Commands/DomainSuspend.php +++ b/src/app/Console/Commands/Domain/Suspend.php @@ -1,11 +1,11 @@ argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { + $this->error("Domain not found."); return 1; } - $this->info("Found domain: {$domain->id}"); - $domain->suspend(); } } diff --git a/src/app/Console/Commands/DomainUnsuspend.php b/src/app/Console/Commands/Domain/Unsuspend.php rename from src/app/Console/Commands/DomainUnsuspend.php rename to src/app/Console/Commands/Domain/Unsuspend.php --- a/src/app/Console/Commands/DomainUnsuspend.php +++ b/src/app/Console/Commands/Domain/Unsuspend.php @@ -1,11 +1,11 @@ argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { + $this->error("Domain not found."); return 1; } - $this->info("Found domain {$domain->id}"); - $domain->unsuspend(); } } diff --git a/src/app/Console/Commands/Domain/UsersCommand.php b/src/app/Console/Commands/Domain/UsersCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Domain/UsersCommand.php @@ -0,0 +1,13 @@ +argument('domain'))->first(); - - if (!$domain) { - return 1; - } - - if ($domain->isPublic()) { - $this->error("This domain is a public registration domain."); - return 1; - } - - // TODO: actually implement listing users - $wallet = $domain->wallet(); - - if (!$wallet) { - $this->error("This domain isn't billed to a wallet."); - return 1; - } - - $mailboxSKU = \App\Sku::where('title', 'mailbox')->first(); - - if (!$mailboxSKU) { - $this->error("No mailbox SKU available."); - } - - $entitlements = $wallet->entitlements() - ->where('entitleable_type', \App\User::class) - ->where('sku_id', $mailboxSKU->id)->get(); - - $users = []; - - foreach ($entitlements as $entitlement) { - $users[] = $entitlement->entitleable; - } - - usort($users, function ($a, $b) { - return $a->email > $b->email; - }); - - foreach ($users as $user) { - $this->info($user->email); - } - } -} diff --git a/src/app/Console/Commands/DomainsCommand.php b/src/app/Console/Commands/DomainsCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/DomainsCommand.php @@ -0,0 +1,12 @@ +argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { + $this->error("Domain not found"); return 1; } diff --git a/src/app/Console/Commands/Job/DomainUpdate.php b/src/app/Console/Commands/Job/DomainUpdate.php --- a/src/app/Console/Commands/Job/DomainUpdate.php +++ b/src/app/Console/Commands/Job/DomainUpdate.php @@ -2,8 +2,7 @@ namespace App\Console\Commands\Job; -use App\Domain; -use Illuminate\Console\Command; +use App\Console\Command; class DomainUpdate extends Command { @@ -28,9 +27,10 @@ */ public function handle() { - $domain = Domain::where('namespace', $this->argument('domain'))->first(); + $domain = $this->getDomain($this->argument('domain')); if (!$domain) { + $this->error("Domain not found"); return 1; } diff --git a/src/app/Console/Commands/Job/UserCreate.php b/src/app/Console/Commands/Job/UserCreate.php --- a/src/app/Console/Commands/Job/UserCreate.php +++ b/src/app/Console/Commands/Job/UserCreate.php @@ -2,8 +2,7 @@ namespace App\Console\Commands\Job; -use App\User; -use Illuminate\Console\Command; +use App\Console\Command; class UserCreate extends Command { @@ -28,9 +27,10 @@ */ public function handle() { - $user = User::where('email', $this->argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found"); return 1; } diff --git a/src/app/Console/Commands/Job/UserUpdate.php b/src/app/Console/Commands/Job/UserUpdate.php --- a/src/app/Console/Commands/Job/UserUpdate.php +++ b/src/app/Console/Commands/Job/UserUpdate.php @@ -2,8 +2,7 @@ namespace App\Console\Commands\Job; -use App\User; -use Illuminate\Console\Command; +use App\Console\Command; class UserUpdate extends Command { @@ -28,9 +27,10 @@ */ public function handle() { - $user = User::where('email', $this->argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found"); return 1; } diff --git a/src/app/Console/Commands/Job/WalletCheck.php b/src/app/Console/Commands/Job/WalletCheck.php --- a/src/app/Console/Commands/Job/WalletCheck.php +++ b/src/app/Console/Commands/Job/WalletCheck.php @@ -2,8 +2,8 @@ namespace App\Console\Commands\Job; +use App\Console\Command; use App\Wallet; -use Illuminate\Console\Command; class WalletCheck extends Command { @@ -31,6 +31,7 @@ $wallet = Wallet::find($this->argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/MollieInfo.php b/src/app/Console/Commands/MollieInfo.php --- a/src/app/Console/Commands/MollieInfo.php +++ b/src/app/Console/Commands/MollieInfo.php @@ -2,8 +2,7 @@ namespace App\Console\Commands; -use App\User; -use Illuminate\Console\Command; +use App\Console\Command; class MollieInfo extends Command { @@ -29,14 +28,13 @@ public function handle() { if ($this->argument('user')) { - $user = User::where('email', $this->argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("Wallet not found"); return 1; } - $this->info("Found user: {$user->id}"); - $wallet = $user->wallets->first(); $provider = new \App\Providers\Payment\Mollie(); diff --git a/src/app/Console/Commands/Package/ReadCommand.php b/src/app/Console/Commands/Package/ReadCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Package/ReadCommand.php @@ -0,0 +1,12 @@ +option('package')) { + $packages = Package::where('title', $this->option('package'))->get(); + } else { + $packages = Package::all(); + } + + foreach ($packages as $package) { + $this->info("{$package->title}"); + + foreach ($package->skus as $sku) { + $this->info(sprintf(" %s (%d)", $sku->title, $sku->pivot->qty)); + } + } + } +} diff --git a/src/app/Console/Commands/PackageSkus.php b/src/app/Console/Commands/PackageSkus.php deleted file mode 100644 --- a/src/app/Console/Commands/PackageSkus.php +++ /dev/null @@ -1,51 +0,0 @@ -info(sprintf("Package: %s", $package->title)); - - foreach ($package->skus as $sku) { - $this->info(sprintf(" SKU: %s (%d)", $sku->title, $sku->pivot->qty)); - } - } - } -} diff --git a/src/app/Console/Commands/PackagesCommand.php b/src/app/Console/Commands/PackagesCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/PackagesCommand.php @@ -0,0 +1,12 @@ +argument('user')) { - $user = User::where('email', $this->argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found"); return 1; } - $this->info("Found user: {$user->id}"); - $wallet = $user->wallets->first(); $provider = PaymentProvider::factory('stripe'); diff --git a/src/app/Console/Commands/Transaction/ReadCommand.php b/src/app/Console/Commands/Transaction/ReadCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Transaction/ReadCommand.php @@ -0,0 +1,12 @@ +argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } diff --git a/src/app/Console/Commands/User/CreateCommand.php b/src/app/Console/Commands/User/CreateCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/User/CreateCommand.php @@ -0,0 +1,12 @@ +argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } diff --git a/src/app/Console/Commands/UserDiscount.php b/src/app/Console/Commands/User/Discount.php rename from src/app/Console/Commands/UserDiscount.php rename to src/app/Console/Commands/User/Discount.php --- a/src/app/Console/Commands/UserDiscount.php +++ b/src/app/Console/Commands/User/Discount.php @@ -1,10 +1,10 @@ argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } - $this->info("Found user {$user->id}"); - if ($this->argument('discount') === '0') { $discount = null; } else { $discount = \App\Discount::find($this->argument('discount')); if (!$discount) { + $this->error("Discount not found."); return 1; } } diff --git a/src/app/Console/Commands/UserDomains.php b/src/app/Console/Commands/User/Domains.php rename from src/app/Console/Commands/UserDomains.php rename to src/app/Console/Commands/User/Domains.php --- a/src/app/Console/Commands/UserDomains.php +++ b/src/app/Console/Commands/User/Domains.php @@ -1,20 +1,20 @@ argument('userid'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } diff --git a/src/app/Console/Commands/UserEntitlements.php b/src/app/Console/Commands/User/Entitlements.php rename from src/app/Console/Commands/UserEntitlements.php rename to src/app/Console/Commands/User/Entitlements.php --- a/src/app/Console/Commands/UserEntitlements.php +++ b/src/app/Console/Commands/User/Entitlements.php @@ -1,12 +1,12 @@ argument('userid'))->first(); + $user = $this->getUser($this->argument('userid')); if (!$user) { + $this->error("User not found."); return 1; } - $this->info("Found user: {$user->id}"); - $skus_counted = []; foreach ($user->entitlements as $entitlement) { diff --git a/src/app/Console/Commands/UserForceDelete.php b/src/app/Console/Commands/User/ForceDelete.php rename from src/app/Console/Commands/UserForceDelete.php rename to src/app/Console/Commands/User/ForceDelete.php --- a/src/app/Console/Commands/UserForceDelete.php +++ b/src/app/Console/Commands/User/ForceDelete.php @@ -1,11 +1,11 @@ where('email', $this->argument('user'))->first(); + $id = $this->argument('user'); + + if (is_numeric($id)) { + $user = \App\User::find($id); + } else { + $user = \App\User::withTrashed()->where('email', $id)->first(); + } if (!$user) { + $this->error("User not found."); return 1; } if (!$user->trashed()) { - $this->error('The user is not yet deleted'); + $this->error("The user is not yet deleted"); return 1; } diff --git a/src/app/Console/Commands/User/ReadCommand.php b/src/app/Console/Commands/User/ReadCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/User/ReadCommand.php @@ -0,0 +1,12 @@ +argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } @@ -55,7 +46,7 @@ foreach ($statuses as $text => $bit) { $func = 'is' . \ucfirst($text); - $this->info(sprintf("%d %s: %s", $bit, $text, $user->$func())); + $this->info(sprintf("%d %s: %s", $bit, $text, $user->$func() ? "yes" : "no")); } $this->info("In total: {$user->status}"); diff --git a/src/app/Console/Commands/UserSuspend.php b/src/app/Console/Commands/User/Suspend.php rename from src/app/Console/Commands/UserSuspend.php rename to src/app/Console/Commands/User/Suspend.php --- a/src/app/Console/Commands/UserSuspend.php +++ b/src/app/Console/Commands/User/Suspend.php @@ -1,11 +1,11 @@ argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } - $this->info("Found user: {$user->id}"); - $user->suspend(); } } diff --git a/src/app/Console/Commands/UserUnsuspend.php b/src/app/Console/Commands/User/Unsuspend.php rename from src/app/Console/Commands/UserUnsuspend.php rename to src/app/Console/Commands/User/Unsuspend.php --- a/src/app/Console/Commands/UserUnsuspend.php +++ b/src/app/Console/Commands/User/Unsuspend.php @@ -1,11 +1,11 @@ argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } - $this->info("Found user {$user->id}"); - $user->unsuspend(); } } diff --git a/src/app/Console/Commands/UserVerify.php b/src/app/Console/Commands/User/Verify.php rename from src/app/Console/Commands/UserVerify.php rename to src/app/Console/Commands/User/Verify.php --- a/src/app/Console/Commands/UserVerify.php +++ b/src/app/Console/Commands/User/Verify.php @@ -1,10 +1,10 @@ argument('user'))->first(); + $user = $this->getUser($this->argument('user')); if (!$user) { + $this->error("User not found."); return 1; } - $this->info("Found user: {$user->id}"); - $job = new \App\Jobs\UserVerify($user); $job->handle(); } diff --git a/src/app/Console/Commands/User/WalletCommand.php b/src/app/Console/Commands/User/WalletCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/User/WalletCommand.php @@ -0,0 +1,13 @@ +argument('user'))->first(); - - if (!$user) { - return 1; - } - - foreach ($user->wallets as $wallet) { - $this->info("{$wallet->id} {$wallet->description}"); - } - } -} diff --git a/src/app/Console/Commands/UsersCommand.php b/src/app/Console/Commands/UsersCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/UsersCommand.php @@ -0,0 +1,12 @@ +argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/WalletBalances.php b/src/app/Console/Commands/Wallet/Balances.php rename from src/app/Console/Commands/WalletBalances.php rename to src/app/Console/Commands/Wallet/Balances.php --- a/src/app/Console/Commands/WalletBalances.php +++ b/src/app/Console/Commands/Wallet/Balances.php @@ -1,10 +1,10 @@ owner) { + $this->error("Wallet not found (or account deleted)."); return 1; } diff --git a/src/app/Console/Commands/Wallet/ControllersCommand.php b/src/app/Console/Commands/Wallet/ControllersCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Wallet/ControllersCommand.php @@ -0,0 +1,13 @@ +option('user')) { - $user = \App\User::where('email', $this->option('user')) - ->orWhere('id', $this->option('user'))->first(); + $user = $this->getUser($this->option('user')); if (!$user) { + $this->error("User not found"); return 1; } diff --git a/src/app/Console/Commands/WalletGetBalance.php b/src/app/Console/Commands/Wallet/GetBalance.php rename from src/app/Console/Commands/WalletGetBalance.php rename to src/app/Console/Commands/Wallet/GetBalance.php --- a/src/app/Console/Commands/WalletGetBalance.php +++ b/src/app/Console/Commands/Wallet/GetBalance.php @@ -1,10 +1,10 @@ argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/WalletGetDiscount.php b/src/app/Console/Commands/Wallet/GetDiscount.php rename from src/app/Console/Commands/WalletGetDiscount.php rename to src/app/Console/Commands/Wallet/GetDiscount.php --- a/src/app/Console/Commands/WalletGetDiscount.php +++ b/src/app/Console/Commands/Wallet/GetDiscount.php @@ -1,10 +1,10 @@ argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/WalletMandate.php b/src/app/Console/Commands/Wallet/Mandate.php rename from src/app/Console/Commands/WalletMandate.php rename to src/app/Console/Commands/Wallet/Mandate.php --- a/src/app/Console/Commands/WalletMandate.php +++ b/src/app/Console/Commands/Wallet/Mandate.php @@ -1,11 +1,11 @@ argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/Wallet/OwnerCommand.php b/src/app/Console/Commands/Wallet/OwnerCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Wallet/OwnerCommand.php @@ -0,0 +1,13 @@ +argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/WalletSetDiscount.php b/src/app/Console/Commands/Wallet/SetDiscount.php rename from src/app/Console/Commands/WalletSetDiscount.php rename to src/app/Console/Commands/Wallet/SetDiscount.php --- a/src/app/Console/Commands/WalletSetDiscount.php +++ b/src/app/Console/Commands/Wallet/SetDiscount.php @@ -1,10 +1,10 @@ argument('wallet'))->first(); + $wallet = \App\Wallet::find($this->argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } @@ -51,6 +42,7 @@ $discount = \App\Discount::find($this->argument('discount')); if (!$discount) { + $this->error("Discount not found"); return 1; } diff --git a/src/app/Console/Commands/WalletTransactions.php b/src/app/Console/Commands/Wallet/Transactions.php rename from src/app/Console/Commands/WalletTransactions.php rename to src/app/Console/Commands/Wallet/Transactions.php --- a/src/app/Console/Commands/WalletTransactions.php +++ b/src/app/Console/Commands/Wallet/Transactions.php @@ -1,10 +1,10 @@ argument('wallet'))->first(); + $wallet = \App\Wallet::find($this->argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/WalletUntil.php b/src/app/Console/Commands/Wallet/Until.php rename from src/app/Console/Commands/WalletUntil.php rename to src/app/Console/Commands/Wallet/Until.php --- a/src/app/Console/Commands/WalletUntil.php +++ b/src/app/Console/Commands/Wallet/Until.php @@ -1,10 +1,10 @@ argument('wallet')); if (!$wallet) { + $this->error("Wallet not found"); return 1; } diff --git a/src/app/Console/Commands/WalletDiscount.php b/src/app/Console/Commands/WalletDiscount.php deleted file mode 100644 --- a/src/app/Console/Commands/WalletDiscount.php +++ /dev/null @@ -1,62 +0,0 @@ -argument('wallet'))->first(); - - if (!$wallet) { - return 1; - } - - // FIXME: Using '0' for delete might be not that obvious - - if ($this->argument('discount') === '0') { - $wallet->discount()->dissociate(); - } else { - $discount = \App\Discount::find($this->argument('discount')); - - if (!$discount) { - return 1; - } - - $wallet->discount()->associate($discount); - } - - $wallet->save(); - } -} diff --git a/src/app/Console/Commands/WalletSetting/ReadCommand.php b/src/app/Console/Commands/WalletSetting/ReadCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/WalletSetting/ReadCommand.php @@ -0,0 +1,12 @@ +argument('domain'))->firstOrFail(); - - $this->info("Found domain: {$domain->id}"); - - $statuses = [ - 'active' => Domain::STATUS_ACTIVE, - 'suspended' => Domain::STATUS_SUSPENDED, - 'deleted' => Domain::STATUS_DELETED, - 'ldapReady' => Domain::STATUS_LDAP_READY, - 'verified' => Domain::STATUS_VERIFIED, - 'confirmed' => Domain::STATUS_CONFIRMED, - ]; - - // I'd prefer "-state" and "+state" syntax, but it's not possible - $delete = false; - if ($update = $this->option('del')) { - $delete = true; - } elseif ($update = $this->option('add')) { - // do nothing - } - - if (!empty($update)) { - $map = \array_change_key_case($statuses); - $update = \strtolower($update); - - if (isset($map[$update])) { - if ($delete && $domain->status & $map[$update]) { - $domain->status ^= $map[$update]; - $domain->save(); - } elseif (!$delete && !($domain->status & $map[$update])) { - $domain->status |= $map[$update]; - $domain->save(); - } - } - } - - $domain_state = []; - foreach (\array_keys($statuses) as $state) { - $func = 'is' . \ucfirst($state); - if ($domain->$func()) { - $domain_state[] = $state; - } - } - - $this->info("Status: " . \implode(',', $domain_state)); - } -} diff --git a/src/app/Console/Development/UserStatus.php b/src/app/Console/Development/UserStatus.php deleted file mode 100644 --- a/src/app/Console/Development/UserStatus.php +++ /dev/null @@ -1,86 +0,0 @@ -argument('userid'))->firstOrFail(); - - $this->info("Found user: {$user->id}"); - - $statuses = [ - 'active' => User::STATUS_ACTIVE, - 'suspended' => User::STATUS_SUSPENDED, - 'deleted' => User::STATUS_DELETED, - 'ldapReady' => User::STATUS_LDAP_READY, - 'imapReady' => User::STATUS_IMAP_READY, - ]; - - // I'd prefer "-state" and "+state" syntax, but it's not possible - $delete = false; - if ($update = $this->option('del')) { - $delete = true; - } elseif ($update = $this->option('add')) { - // do nothing - } - - if (!empty($update)) { - $map = \array_change_key_case($statuses); - $update = \strtolower($update); - - if (isset($map[$update])) { - if ($delete && $user->status & $map[$update]) { - $user->status ^= $map[$update]; - $user->save(); - } elseif (!$delete && !($user->status & $map[$update])) { - $user->status |= $map[$update]; - $user->save(); - } - } - } - - $user_state = []; - foreach (\array_keys($statuses) as $state) { - $func = 'is' . \ucfirst($state); - if ($user->$func()) { - $user_state[] = $state; - } - } - - $this->info("Status: " . \implode(',', $user_state)); - } -} diff --git a/src/app/Console/ObjectCreateCommand.php b/src/app/Console/ObjectCreateCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/ObjectCreateCommand.php @@ -0,0 +1,56 @@ +description = "Create a {$this->objectName}"; + $this->signature = "{$this->objectName}:create"; + + $class = new $this->objectClass(); + + foreach ($class->getFillable() as $fillable) { + $this->signature .= " {--{$fillable}=}"; + } + + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + $argument = $this->argument($this->objectName); + + $object = $this->getObject($this->objectClass, $argument, $this->objectTitle); + + if (!$object) { + return 1; + } + + $this->info(var_export($object->toArray(), true)); + } +} diff --git a/src/app/Console/ObjectListCommand.php b/src/app/Console/ObjectListCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/ObjectListCommand.php @@ -0,0 +1,77 @@ +description = "List {$this->objectName}s"; + + $classes = class_uses_recursive($this->objectClass); + + if (in_array(SoftDeletes::class, $classes)) { + $this->signature = sprintf( + "%ss {--with-deleted : Include deleted %ss}", + $this->objectName, + $this->objectName + ); + } else { + $this->signature = "{$this->objectName}s"; + } + + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + $classes = class_uses_recursive($this->objectClass); + + if (in_array(SoftDeletes::class, $classes) && $this->option('with-deleted')) { + $objects = $this->objectClass::withTrashed(); + } else { + $objects = new $this->objectClass(); + } + + $objects->each( + function ($object) { + if (!empty($this->objectTitle)) { + $title = $object->{$this->objectTitle}; + } else { + $title = $object->{$object->getKeyName()}; + } + + if ($object->deleted_at) { + $this->info("{$title} (deleted at {$object->deleted_at}"); + } else { + $this->info("{$title}"); + } + } + ); + } +} diff --git a/src/app/Console/ObjectReadCommand.php b/src/app/Console/ObjectReadCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/ObjectReadCommand.php @@ -0,0 +1,54 @@ +description = "Read a {$this->objectName}"; + $this->signature = sprintf( + "%s:read {%s}", + $this->objectName, + $this->objectName + ); + + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + $argument = $this->argument($this->objectName); + + $object = $this->getObject($this->objectClass, $argument, $this->objectTitle); + + if (!$object) { + return 1; + } + + $this->info(var_export($object->toArray(), true)); + } +} diff --git a/src/app/Console/ObjectRelationListCommand.php b/src/app/Console/ObjectRelationListCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/ObjectRelationListCommand.php @@ -0,0 +1,148 @@ +description = "List {$this->objectRelation} for a {$this->objectName}"; + $this->signature = sprintf( + "%s:%s {%s}", + $this->objectName, + $this->objectRelation, + $this->objectName + ); + + $this->signature .= " {--attr=* : Attributes other than the primary unique key to include}"; + + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + $argument = $this->argument($this->objectName); + + $object = $this->getObject( + $this->objectClass, + $argument, + $this->objectTitle + ); + + if (!$object) { + $this->error("No such {$this->objectName} {$argument}"); + return 1; + } + + if (method_exists($object, $this->objectRelation)) { + $result = call_user_func([$object, $this->objectRelation]); + } elseif (property_exists($object, $this->objectRelation)) { + $result = $object->{"{$this->objectRelation}"}; + } else { + $this->error("No such relation {$this->objectRelation}"); + return 1; + } + + if ($result instanceof \Illuminate\Database\Eloquent\Collection) { + $result->each( + function ($entry) { + $this->info($this->toString($entry)); + } + ); + } elseif ($result instanceof \Illuminate\Database\Eloquent\Relations\Relation) { + $result->each( + function ($entry) { + $this->info($this->toString($entry)); + } + ); + } elseif (is_array($result)) { + foreach ($result as $entry) { + $this->info($this->toString($entry)); + } + } else { + $this->info($this->toString($result)); + } + } + + private function toString($entry) + { + $str = [ + $entry->{$entry->getKeyName()} + ]; + + foreach ($this->option('attr') as $attr) { + if ($attr == $entry->getKeyName()) { + die("surely you must be joking.\n"); + } + + if (!array_key_exists($attr, $entry->toArray())) { + $this->error("Attribute {$attr} isn't available"); + continue; + } + + if (is_numeric($entry->{$attr})) { + $str[] = $entry->{$attr}; + } else { + $str[] = !empty($entry->{$attr}) ?: "null"; + } + } + + return implode(" ", $str); + } +} diff --git a/src/app/Domain.php b/src/app/Domain.php --- a/src/app/Domain.php +++ b/src/app/Domain.php @@ -96,8 +96,75 @@ return $this; } + /** + * Ownership verification by checking for a TXT (or CNAME) record + * in the domain's DNS (that matches the verification hash). + * + * @return bool True if verification was successful, false otherwise + * @throws \Exception Throws exception on DNS or DB errors + */ + public function confirm(): bool + { + if ($this->isConfirmed()) { + return true; + } + + $hash = $this->hash(self::HASH_TEXT); + $confirmed = false; + + // Get DNS records and find a matching TXT entry + $records = \dns_get_record($this->namespace, DNS_TXT); + + if ($records === false) { + throw new \Exception("Failed to get DNS record for {$this->namespace}"); + } + + foreach ($records as $record) { + if ($record['txt'] === $hash) { + $confirmed = true; + break; + } + } + + // Get DNS records and find a matching CNAME entry + // Note: some servers resolve every non-existing name + // so we need to define left and right side of the CNAME record + // i.e.: kolab-verify IN CNAME .domain.tld. + if (!$confirmed) { + $cname = $this->hash(self::HASH_CODE) . '.' . $this->namespace; + $records = \dns_get_record('kolab-verify.' . $this->namespace, DNS_CNAME); + + if ($records === false) { + throw new \Exception("Failed to get DNS record for {$this->namespace}"); + } + + foreach ($records as $records) { + if ($records['target'] === $cname) { + $confirmed = true; + break; + } + } + } + + if ($confirmed) { + $this->status |= Domain::STATUS_CONFIRMED; + $this->save(); + } + + return $confirmed; + } + + /** + * Return the entitlement for this domain. + * + * @return \App\Entitlement|null + */ public function entitlement() { + if ($this->isPublic()) { + return null; + } + return $this->morphOne('App\Entitlement', 'entitleable'); } @@ -111,6 +178,26 @@ return self::whereRaw($where)->get(['namespace'])->pluck('namespace')->toArray(); } + /** + * Generate a verification hash for this domain + * + * @param int $mod One of: HASH_CNAME, HASH_CODE (Default), HASH_TEXT + * + * @return string Verification hash + */ + public function hash($mod = null): string + { + $cname = 'kolab-verify'; + + if ($mod === self::HASH_CNAME) { + return $cname; + } + + $hash = \md5('hkccp-verify-' . $this->namespace); + + return $mod === self::HASH_TEXT ? "$cname=$hash" : $hash; + } + /** * Returns whether this domain is active. * @@ -253,84 +340,6 @@ $this->attributes['status'] = $new_status; } - /** - * Ownership verification by checking for a TXT (or CNAME) record - * in the domain's DNS (that matches the verification hash). - * - * @return bool True if verification was successful, false otherwise - * @throws \Exception Throws exception on DNS or DB errors - */ - public function confirm(): bool - { - if ($this->isConfirmed()) { - return true; - } - - $hash = $this->hash(self::HASH_TEXT); - $confirmed = false; - - // Get DNS records and find a matching TXT entry - $records = \dns_get_record($this->namespace, DNS_TXT); - - if ($records === false) { - throw new \Exception("Failed to get DNS record for {$this->namespace}"); - } - - foreach ($records as $record) { - if ($record['txt'] === $hash) { - $confirmed = true; - break; - } - } - - // Get DNS records and find a matching CNAME entry - // Note: some servers resolve every non-existing name - // so we need to define left and right side of the CNAME record - // i.e.: kolab-verify IN CNAME .domain.tld. - if (!$confirmed) { - $cname = $this->hash(self::HASH_CODE) . '.' . $this->namespace; - $records = \dns_get_record('kolab-verify.' . $this->namespace, DNS_CNAME); - - if ($records === false) { - throw new \Exception("Failed to get DNS record for {$this->namespace}"); - } - - foreach ($records as $records) { - if ($records['target'] === $cname) { - $confirmed = true; - break; - } - } - } - - if ($confirmed) { - $this->status |= Domain::STATUS_CONFIRMED; - $this->save(); - } - - return $confirmed; - } - - /** - * Generate a verification hash for this domain - * - * @param int $mod One of: HASH_CNAME, HASH_CODE (Default), HASH_TEXT - * - * @return string Verification hash - */ - public function hash($mod = null): string - { - $cname = 'kolab-verify'; - - if ($mod === self::HASH_CNAME) { - return $cname; - } - - $hash = \md5('hkccp-verify-' . $this->namespace); - - return $mod === self::HASH_TEXT ? "$cname=$hash" : $hash; - } - /** * Suspend this domain. * @@ -361,6 +370,49 @@ $this->save(); } + /** + * List user accounts for this domain. + * + * @return \App\User[] A list of users. + */ + public function users() + { + if ($this->isPublic()) { + $this->error("This domain is a public registration domain."); + return 1; + } + + // TODO: actually implement listing users + $wallet = $this->wallet(); + + if (!$wallet) { + $this->error("This domain isn't billed to a wallet."); + return 1; + } + + $mailboxSKU = \App\Sku::where('title', 'mailbox')->first(); + + if (!$mailboxSKU) { + $this->error("No mailbox SKU available."); + } + + $entitlements = $wallet->entitlements() + ->where('entitleable_type', \App\User::class) + ->where('sku_id', $mailboxSKU->id)->get(); + + $users = []; + + foreach ($entitlements as $entitlement) { + $users[] = $entitlement->entitleable; + } + + usort($users, function ($a, $b) { + return $a->email > $b->email; + }); + + return $users; + } + /** * Verify if a domain exists in DNS * diff --git a/src/app/User.php b/src/app/User.php --- a/src/app/User.php +++ b/src/app/User.php @@ -54,10 +54,8 @@ * @var array */ protected $fillable = [ - 'id', 'email', 'password', - 'password_ldap', 'status' ]; diff --git a/src/app/Utils.php b/src/app/Utils.php --- a/src/app/Utils.php +++ b/src/app/Utils.php @@ -24,6 +24,45 @@ return $start->diffInDays($end) + 1; } + /** + * Download a file from the interwebz and store it locally. + * + * @param string $source The source location + * @param string $target The target location + * @param bool $force Force the download (and overwrite target) + * + * @return void + */ + public static function downloadFile($source, $target, $force = false) + { + if (is_file($target) && !$force) { + return; + } + + \Log::info("Retrieving {$source}"); + + $fp = fopen($target, 'w'); + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $source); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_FILE, $fp); + curl_exec($curl); + + if (curl_errno($curl)) { + \Log::error("Request error on {$source}: " . curl_error($curl)); + + curl_close($curl); + fclose($fp); + + unlink($target); + return; + } + + curl_close($curl); + fclose($fp); + } + /** * Provide all unique combinations of elements in $input, with order and duplicates irrelevant. * diff --git a/src/database/seeds/local/UserSeeder.php b/src/database/seeds/local/UserSeeder.php --- a/src/database/seeds/local/UserSeeder.php +++ b/src/database/seeds/local/UserSeeder.php @@ -108,7 +108,7 @@ $ned->assignSku(\App\Sku::where('title', 'activesync')->first(), 1); - // Ned is a controller on Jack's wallet + // Ned is a controller on John's wallet $john->wallets()->first()->addController($ned); // Ned is also our 2FA test user diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Domain/AddTest.php rename from src/tests/Feature/Console/UserDiscountTest.php rename to src/tests/Feature/Console/Domain/AddTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Domain/AddTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Domain/DeleteTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/Domain/DeleteTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Domain/DeleteTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/Domain/ListDomainsTest.php b/src/tests/Feature/Console/Domain/ListDomainsTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Domain/ListDomainsTest.php @@ -0,0 +1,19 @@ +artisan('domain:list') + ->assertExitCode(0); + + $this->markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Domain/ListUsersTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/Domain/ListUsersTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Domain/ListUsersTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Domain/SetStatusTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/Domain/SetStatusTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Domain/SetStatusTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Domain/SetWalletTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/Domain/SetWalletTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Domain/SetWalletTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/Domain/StatusTest.php b/src/tests/Feature/Console/Domain/StatusTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Domain/StatusTest.php @@ -0,0 +1,19 @@ +artisan('domain:status kolab.org') + ->assertExitCode(0); + + $this->markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Domain/SuspendTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/Domain/SuspendTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Domain/SuspendTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Domain/UnsuspendTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/Domain/UnsuspendTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Domain/UnsuspendTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/User/AddAliasTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/User/AddAliasTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/User/AddAliasTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/WalletDiscountTest.php b/src/tests/Feature/Console/User/DeleteTest.php rename from src/tests/Feature/Console/WalletDiscountTest.php rename to src/tests/Feature/Console/User/DeleteTest.php --- a/src/tests/Feature/Console/WalletDiscountTest.php +++ b/src/tests/Feature/Console/User/DeleteTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/User/DiscountTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/User/DiscountTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/User/DiscountTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDomainsTest.php b/src/tests/Feature/Console/User/DomainsTest.php rename from src/tests/Feature/Console/UserDomainsTest.php rename to src/tests/Feature/Console/User/DomainsTest.php --- a/src/tests/Feature/Console/UserDomainsTest.php +++ b/src/tests/Feature/Console/User/DomainsTest.php @@ -1,11 +1,14 @@ artisan('user:domains john@kolab.org') diff --git a/src/tests/Feature/Console/UserEntitlementsTest.php b/src/tests/Feature/Console/User/EntitlementsTest.php rename from src/tests/Feature/Console/UserEntitlementsTest.php rename to src/tests/Feature/Console/User/EntitlementsTest.php --- a/src/tests/Feature/Console/UserEntitlementsTest.php +++ b/src/tests/Feature/Console/User/EntitlementsTest.php @@ -1,11 +1,14 @@ artisan('user:entitlements john@kolab.org') diff --git a/src/tests/Feature/Console/UserForceDeleteTest.php b/src/tests/Feature/Console/User/ForceDeleteTest.php rename from src/tests/Feature/Console/UserForceDeleteTest.php rename to src/tests/Feature/Console/User/ForceDeleteTest.php --- a/src/tests/Feature/Console/UserForceDeleteTest.php +++ b/src/tests/Feature/Console/User/ForceDeleteTest.php @@ -1,11 +1,11 @@ artisan('user:status john@kolab.org') + ->assertExitCode(0); + + $this->markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/User/SuspendTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/User/SuspendTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/User/SuspendTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/User/UnsuspendTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/User/UnsuspendTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/User/UnsuspendTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/User/VerifyTest.php copy from src/tests/Feature/Console/UserDiscountTest.php copy to src/tests/Feature/Console/User/VerifyTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/User/VerifyTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/UserWalletsTest.php b/src/tests/Feature/Console/User/WalletsTest.php rename from src/tests/Feature/Console/UserWalletsTest.php rename to src/tests/Feature/Console/User/WalletsTest.php --- a/src/tests/Feature/Console/UserWalletsTest.php +++ b/src/tests/Feature/Console/User/WalletsTest.php @@ -1,11 +1,14 @@ artisan('user:wallets john@kolab.org') diff --git a/src/tests/Feature/Console/Wallet/AddTransactionTest.php b/src/tests/Feature/Console/Wallet/AddTransactionTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/AddTransactionTest.php @@ -0,0 +1,16 @@ +markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/Wallet/BalancesTest.php b/src/tests/Feature/Console/Wallet/BalancesTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/BalancesTest.php @@ -0,0 +1,19 @@ +artisan('wallet:balances') + ->assertExitCode(0); + + $this->markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/WalletChargeTest.php b/src/tests/Feature/Console/Wallet/ChargeTest.php rename from src/tests/Feature/Console/WalletChargeTest.php rename to src/tests/Feature/Console/Wallet/ChargeTest.php --- a/src/tests/Feature/Console/WalletChargeTest.php +++ b/src/tests/Feature/Console/Wallet/ChargeTest.php @@ -1,11 +1,11 @@ artisan('wallet:expected') + ->assertExitCode(0); + + $this->markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/Wallet/GetBalanceTest.php b/src/tests/Feature/Console/Wallet/GetBalanceTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/GetBalanceTest.php @@ -0,0 +1,16 @@ +markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/Wallet/GetDiscountTest.php b/src/tests/Feature/Console/Wallet/GetDiscountTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/GetDiscountTest.php @@ -0,0 +1,16 @@ +markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/UserDiscountTest.php b/src/tests/Feature/Console/Wallet/MandateTest.php rename from src/tests/Feature/Console/UserDiscountTest.php rename to src/tests/Feature/Console/Wallet/MandateTest.php --- a/src/tests/Feature/Console/UserDiscountTest.php +++ b/src/tests/Feature/Console/Wallet/MandateTest.php @@ -1,11 +1,14 @@ markTestIncomplete(); diff --git a/src/tests/Feature/Console/Wallet/SetBalanceTest.php b/src/tests/Feature/Console/Wallet/SetBalanceTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/SetBalanceTest.php @@ -0,0 +1,16 @@ +markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/Wallet/SetDiscountTest.php b/src/tests/Feature/Console/Wallet/SetDiscountTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/SetDiscountTest.php @@ -0,0 +1,16 @@ +markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/Wallet/TransactionsTest.php b/src/tests/Feature/Console/Wallet/TransactionsTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/TransactionsTest.php @@ -0,0 +1,16 @@ +markTestIncomplete(); + } +} diff --git a/src/tests/Feature/Console/Wallet/UntilTest.php b/src/tests/Feature/Console/Wallet/UntilTest.php new file mode 100644 --- /dev/null +++ b/src/tests/Feature/Console/Wallet/UntilTest.php @@ -0,0 +1,48 @@ +deleteTestUser('wallet-charge@kolabnow.com'); + } + + /** + * {@inheritDoc} + */ + public function tearDown(): void + { + $this->deleteTestUser('wallet-charge@kolabnow.com'); + + parent::tearDown(); + } + + /** + * Test command run for a specified wallet + */ + public function testHandleSingle(): void + { + $user = $this->getTestUser('wallet-charge@kolabnow.com'); + $wallet = $user->wallets()->first(); + + // Non-existing wallet + $this->artisan('wallet:until unknown') + ->expectsOutput("Wallet not found") + ->assertExitCode(1); + + // TODO + $this->artisan('wallet:until ' . $wallet->id) + ->assertExitCode(0); + + $this->markTestIncomplete(); + } +}