Skip to content

Commit

Permalink
Add support for posting content via a REST API (#793)
Browse files Browse the repository at this point in the history
* WIP

* Adds posting to create article.

* Adds rate limiting of 6 requests per minute to API.

* WIP

* Adds tests for api token jobs.

* Tweaks

* Tweaks

* CS

* CS

* Dump schema

* Handle duplicate GitHub ID upon registration (#782)

* Added check inside the create method of the RegisterController to find duplicate github ID

* Added error message saying "We already found a user with the given GitHub account.......

* fixed failing test

* fixed stycli errors

* styleci fixes

* styleci fixes

* more styleci fixes

* wip

Co-authored-by: Dries Vints <[email protected]>

* Update sponsors

* Upgrade tailwind to v3 (#795)

* update dependencies

* update classes

* remove classes duplication

* resolve conflict

* Remove dependency

Co-authored-by: faissaloux <[email protected]>

* wip

Co-authored-by: Dries Vints <[email protected]>
Co-authored-by: Efe Omoregie <[email protected]>
Co-authored-by: Joe Dixon <[email protected]>
Co-authored-by: faissaloux <[email protected]>
  • Loading branch information
5 people authored Jan 8, 2022
1 parent dac19fb commit 3e13297
Show file tree
Hide file tree
Showing 25 changed files with 683 additions and 17 deletions.
22 changes: 15 additions & 7 deletions app/Http/Controllers/Articles/ArticlesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Http\Controllers\Controller;
use App\Http\Middleware\Authenticate;
use App\Http\Requests\ArticleRequest;
use App\Http\Resources\ArticleResource;
use App\Jobs\CreateArticle;
use App\Jobs\DeleteArticle;
use App\Jobs\UpdateArticle;
Expand All @@ -17,6 +18,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;

class ArticlesController extends Controller
{
Expand Down Expand Up @@ -101,11 +103,13 @@ public function create()

public function store(ArticleRequest $request)
{
$article = $this->dispatchNow(CreateArticle::fromRequest($request));
$article = $this->dispatchSync(CreateArticle::fromRequest($request));

$this->success($request->shouldBeSubmitted() ? 'articles.submitted' : 'articles.created');

return redirect()->route('articles.show', $article->slug());
return $request->wantsJson()
? ArticleResource::make($article)
: redirect()->route('articles.show', $article->slug());
}

public function edit(Article $article)
Expand All @@ -125,25 +129,29 @@ public function update(ArticleRequest $request, Article $article)

$wasNotPreviouslySubmitted = $article->isNotSubmitted();

$article = $this->dispatchNow(UpdateArticle::fromRequest($article, $request));
$article = $this->dispatchSync(UpdateArticle::fromRequest($article, $request));

if ($wasNotPreviouslySubmitted && $request->shouldBeSubmitted()) {
$this->success('articles.submitted');
} else {
$this->success('articles.updated');
}

return redirect()->route('articles.show', $article->slug());
return $request->wantsJson()
? ArticleResource::make($article)
: redirect()->route('articles.show', $article->slug());
}

public function delete(Article $article)
public function delete(Request $request, Article $article)
{
$this->authorize(ArticlePolicy::DELETE, $article);

$this->dispatchNow(new DeleteArticle($article));
$this->dispatchSync(new DeleteArticle($article));

$this->success('articles.deleted');

return redirect()->route('articles');
return $request->wantsJson()
? response()->json([], Response::HTTP_NO_CONTENT)
: redirect()->route('articles');
}
}
33 changes: 33 additions & 0 deletions app/Http/Controllers/Settings/ApiTokenController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Http\Requests\CreateApiTokenRequest;
use App\Http\Requests\DeleteApiTokenRequest;
use App\Jobs\CreateApiToken;
use App\Jobs\DeleteApiToken;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Support\Facades\Auth;

class ApiTokenController extends Controller
{
public function __construct()
{
$this->middleware(Authenticate::class);
}

public function store(CreateApiTokenRequest $request)
{
$this->dispatchSync(new CreateApiToken(Auth::user(), $request->name()));

return redirect()->route('settings.profile');
}

public function destroy(DeleteApiTokenRequest $request)
{
$this->dispatchSync(new DeleteApiToken(Auth::user(), $request->id()));

return redirect()->route('settings.profile');
}
}
20 changes: 20 additions & 0 deletions app/Http/Requests/CreateApiTokenRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateApiTokenRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
];
}

public function name(): string
{
return (string) $this->get('name');
}
}
20 changes: 20 additions & 0 deletions app/Http/Requests/DeleteApiTokenRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class DeleteApiTokenRequest extends FormRequest
{
public function rules(): array
{
return [
'id' => ['required', 'exists:personal_access_tokens,id'],
];
}

public function id(): string
{
return (string) $this->get('id');
}
}
28 changes: 28 additions & 0 deletions app/Http/Resources/ArticleResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \App\Models\Article
*/
class ArticleResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->getKey(),
'url' => route('articles.show', $this->slug()),
'title' => $this->title(),
'body' => $this->body(),
'original_url' => $this->originalUrl(),
'author' => AuthorResource::make($this->author()),
'tags' => TagResource::collection($this->tags()),
'is_submitted' => $this->isSubmitted(),
'submitted_at' => $this->submittedAt(),
'created_at' => $this->createdAt(),
'updated_at' => $this->updatedAt(),
];
}
}
24 changes: 24 additions & 0 deletions app/Http/Resources/AuthorResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \App\Models\User
*/
class AuthorResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->getKey(),
'email' => $this->emailAddress(),
'username' => $this->username(),
'name' => $this->name(),
'bio' => $this->bio(),
'twitter_handle' => $this->twitter(),
'github_username' => $this->githubUsername(),
];
}
}
20 changes: 20 additions & 0 deletions app/Http/Resources/TagResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \App\Models\Tag
*/
class TagResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->getKey(),
'name' => $this->name(),
'slug' => $this->slug(),
];
}
}
24 changes: 24 additions & 0 deletions app/Jobs/CreateApiToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Jobs;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class CreateApiToken implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public function __construct(private User $user, private string $name)
{
}

public function handle(): void
{
$this->user->createToken($this->name);
}
}
24 changes: 24 additions & 0 deletions app/Jobs/DeleteApiToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Jobs;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class DeleteApiToken implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public function __construct(private User $user, private int $tokenId)
{
}

public function handle(): void
{
$this->user->tokens()->where('id', $this->tokenId)->delete();
}
}
2 changes: 2 additions & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Auth;
use Laravel\Sanctum\HasApiTokens;

final class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens;
use HasFactory;
use HasTimestamps;
use Notifiable;
Expand Down
3 changes: 2 additions & 1 deletion app/Providers/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

Expand Down Expand Up @@ -49,7 +50,7 @@ public function boot()
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60);
return Limit::perMinute(6);
});
}
}
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"laravel-notification-channels/twitter": "^5.0",
"laravel/framework": "8.75.0",
"laravel/horizon": "^5.2",
"laravel/sanctum": "^2.13",
"laravel/slack-notification-channel": "^2.3",
"laravel/socialite": "^5.0",
"laravel/ui": "^3.0",
Expand Down
66 changes: 65 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 3e13297

Please sign in to comment.