CI là gì? CD là gì?
CI – Continuous Integration – Tích hợp liên tục
Tích hợp liên tục (CI) là phương pháp phát triển phần mềm đòi hỏi các thành viên trong nhóm tích hợp công việc thường xuyên. Mỗi ngày, các thành viên đều phải theo dõi và phát triển công việc của họ ít nhất một lần. Việc này sẽ được một nhóm khác kiểm tra tự động, nhóm này sẽ tiến hành kiểm thử truy hồi để phát hiện lỗi nhanh nhất có thể. Cả nhóm thấy rằng phương pháp tiếp cận này giúp giảm bớt vấn đề về tích hợp hơn và cho phép phát triển phần mềm gắn kết nhanh hơn.
CD – Continuous Delivery – Chuyển giao Liên tục
Chuyển giao liên tục là chuyển giao liên tục, là 1 tập hợp các kỹ thuật để triển khai tích hợp souce code trên môi trường staging ( một môi trường rất giống với môi trường production).
Config CI (phpcs, phpunit) cho 1 project Laravel đơn giản
1. Tạo API Laravel
1. Tạo project laravel
Chúng ta chỉ cần vào terminal và chạy lệnh sau
1 2 | composer create<span class="token operator">-</span>project <span class="token operator">--</span>prefer<span class="token operator">-</span>dist laravel<span class="token operator">/</span>laravel demo<span class="token operator">-</span>ci |
Tiếp theo init project vừa tạo vào lên git
1 2 3 4 5 6 7 8 | cd demo<span class="token operator">-</span>ci<span class="token operator">-</span>laravel git init git remote add origin <a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a><span class="token punctuation">.</span>com<span class="token punctuation">:</span>lethu96<span class="token operator">/</span>demo<span class="token operator">-</span>ci<span class="token punctuation">.</span>git git add <span class="token punctuation">.</span> git commit <span class="token operator">-</span>m <span class="token double-quoted-string string">"Init commit"</span> git push <span class="token operator">-</span>u origin master |
2. Config database
Trong bài naỳ SQLite sẽ được sử dụng cho testing và cơ sở dữ liệu chính của chúng ta. Thông thường, cơ sở dữ liệu chính là một DBMS phức tạp hơn như MySQL hoặc MSSQL nhưng để cho đơn giản với mục đích minh họa. Chúng ta sẽ giữ cấu hình cho cơ sở dữ liệu chính trong file .env
, và sau đó tạo một file .env.testing
để giữ cấu hình cơ sở dữ liệu cho việc test.
Trong file nv.testing chúng ta sẽ config như sau :
1 2 3 4 5 6 7 | <span class="token constant">DB_CONNECTION</span><span class="token operator">=</span>sqlite <span class="token constant">DB_HOST</span><span class="token operator">=</span><span class="token constant">null</span> <span class="token constant">DB_PORT</span><span class="token operator">=</span><span class="token constant">null</span> <span class="token constant">DB_DATABASE</span><span class="token operator">=</span>database<span class="token operator">/</span>database<span class="token punctuation">.</span>sqlite <span class="token constant">DB_USERNAME</span><span class="token operator">=</span><span class="token constant">null</span> <span class="token constant">DB_PASSWORD</span><span class="token operator">=</span><span class="token constant">null</span> |
Để có thể trỏ đến file cơ sở dữ liệu SQLite của chúng ta như chúng ta đã config trong file.env.testing
thì bên trong config/database.php
cũng phải thay thế cấu hình SQLite trong connections array bằng config bên dưới:
1 2 3 4 5 6 | <span class="token single-quoted-string string">'sqlite'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'driver'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'sqlite'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'database'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">database_path</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'database.sqlite'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'prefix'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">''</span><span class="token punctuation">,</span> <span class="token punctuation">]</span> |
Sau đó chạy lệnh migrate để kiểm tra config của chúng ta vừa rồi đã đúng chưa
1 2 | php artisan migrate |
Cài đặt token-based authentication với Passport
Để xác thực authen mình sẽ sử dụng package laravel/passport của Laravel
1 2 | composer <span class="token keyword">require</span> laravel<span class="token operator">/</span>passport v7<span class="token punctuation">.</span><span class="token number">5.1</span> |
Sau khi cài đặt xong, chạy lại lệnh migrate đểmigrations các bảng liên quan đến laravel/passport:
1 2 | php artisan migrate |
Passport yêu cầu các keys mã hóa để tạo tokens , các keys này cần được tạo và lưu trong cơ sở dữ liệu. Để tạo các keys này, chúng ta chạy lệnh
1 2 | php artisan passport<span class="token punctuation">:</span>install |
Sau khi chạy command thành công, bạn sẽ nhìn thấy các Client secret của mình
Bước tiếp theo là thêm LaravelPassportHasApiToken
trait vào model AppUser
. Chúng ta sẽ sử dụng được các method helper từ package laravel/passport
vào app để giúp kiểm tra user’s token và scopes. Mở file app/User.php
và thay thế nội dung của nó bằng code bên dưới:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">App</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Notifications<span class="token punctuation"></span>Notifiable</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Foundation<span class="token punctuation"></span>Auth<span class="token punctuation"></span>User</span> <span class="token keyword">as</span> Authenticatable<span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Laravel<span class="token punctuation"></span>Passport<span class="token punctuation"></span>HasApiTokens</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token keyword">extends</span> <span class="token class-name">Authenticatable</span> <span class="token punctuation">{</span> <span class="token keyword">use</span> <span class="token package">Notifiable</span><span class="token punctuation">,</span> HasApiTokens<span class="token punctuation">;</span> <span class="token keyword">protected</span> <span class="token variable">$fillable</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'name'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'email'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">protected</span> <span class="token variable">$hidden</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'password'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'remember_token'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </span> |
Tiếp theo ccần gọi method Passport::routes
trong hàm booth của file app/Providers/AuthServiceProvider.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation"></span>Providers</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Laravel<span class="token punctuation"></span>Passport<span class="token punctuation"></span>Passport</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Support<span class="token punctuation"></span>Facades<span class="token punctuation"></span>Gate</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Foundation<span class="token punctuation"></span>Support<span class="token punctuation"></span>Providers<span class="token punctuation"></span>AuthServiceProvider</span> <span class="token keyword">as</span> ServiceProvider<span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">AuthServiceProvider</span> <span class="token keyword">extends</span> <span class="token class-name">ServiceProvider</span> <span class="token punctuation">{</span> <span class="token keyword">protected</span> <span class="token variable">$policies</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'AppModel'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'AppPoliciesModelPolicy'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">boot</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">registerPolicies</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Passport<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">routes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Cấu hình cuối cùng mà Passport requires setting nó làm driver
option trong bộ bảo vệ xác thực API bên trong config/auth.php.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token single-quoted-string string">'guards'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'web'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'driver'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'session'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'provider'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'users'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'api'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'driver'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'passport'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'provider'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'users'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> |
4. Tạo API endpoints
Chúng ta sẽ build API đơn giản quản lí suser. User sẽ có những chức năng sau :
Đăng ký một tài khoản mới
Đăng nhập vào tài khoản của họ bằng thông tin đăng nhập của user
Tìm nạp thông tin của user
Đăng xuất khỏi ứng dụng
- Trước tiên chúng ta cần khai báo routes trong file
routes/api.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Http<span class="token punctuation"></span>Request</span><span class="token punctuation">;</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">group</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'prefix'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'auth'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'login'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'signup'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">group</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'middleware'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'auth:api'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'logout'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'user'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span> |
- Tiếp đến tạo Controller
1 2 | php artisan make<span class="token punctuation">:</span>controller AuthController |
Trong AuthController chúng ta code các function signup
, login
, logout
, user
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation"></span>Http<span class="token punctuation"></span>Controllers</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Http<span class="token punctuation"></span>Request</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Support<span class="token punctuation"></span>Facades<span class="token punctuation"></span>Auth</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Carbon<span class="token punctuation"></span>Carbon</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">App<span class="token punctuation"></span>User</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">AuthController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span> <span class="token punctuation">{</span> <span class="token comment">/** * Create user * * @param [string] name * @param [string] email * @param [string] password * @param [string] password_confirmation * @return [string] message */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">signup</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">validate</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'required|string'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'email'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'required|string|email|unique:users'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'required|string|confirmed'</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">name</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'email'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">email</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">bcrypt</span><span class="token punctuation">(</span><span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">password</span><span class="token punctuation">)</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">response</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'message'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'Successfully created user!'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">201</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * Login user and create token * * @param [string] email * @param [string] password * @param [boolean] remember_me * @return [string] access_token * @return [string] token_type * @return [string] expires_at */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">login</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">validate</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'email'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'required|string|email'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'required|string'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'remember_me'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'boolean'</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$credentials</span> <span class="token operator">=</span> <span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'email'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>Auth<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">attempt</span><span class="token punctuation">(</span><span class="token variable">$credentials</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">response</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'message'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'Unauthorized'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">401</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">user</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$tokenResult</span> <span class="token operator">=</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">createToken</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Personal Access Token'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$token</span> <span class="token operator">=</span> <span class="token variable">$tokenResult</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">token</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">remember_me</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token variable">$token</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">expires_at</span> <span class="token operator">=</span> Carbon<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">addWeeks</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token variable">$token</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">response</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'access_token'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$tokenResult</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">accessToken</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'token_type'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'Bearer'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'expires_at'</span> <span class="token operator">=</span><span class="token operator">></span> Carbon<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">parse</span><span class="token punctuation">(</span> <span class="token variable">$tokenResult</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">token</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">expires_at</span> <span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">toDateTimeString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * Logout user (Revoke the token) * * @return [string] message */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">logout</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">user</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">token</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">revoke</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">response</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'message'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'Successfully logged out'</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * Get the authenticated User * * @return [json] user object */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">user</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">response</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">json</span><span class="token punctuation">(</span><span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">user</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
- Tiếp đến tạo 1 Unit test để test API chúng ta vừa tạo
1 2 | php artisan make<span class="token punctuation">:</span>test UserTest |
Trong file UserTest
của chúng ta sẽ như sau :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">Tests<span class="token punctuation"></span>Feature</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Tests<span class="token punctuation"></span>TestCase</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Foundation<span class="token punctuation"></span>Testing<span class="token punctuation"></span>WithFaker</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation"></span>Foundation<span class="token punctuation"></span>Testing<span class="token punctuation"></span>RefreshDatabase</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">App<span class="token punctuation"></span>User</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">UserTest</span> <span class="token keyword">extends</span> <span class="token class-name">TestCase</span> <span class="token punctuation">{</span> <span class="token keyword">use</span> <span class="token package">WithFaker</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token variable">$password</span> <span class="token operator">=</span> <span class="token double-quoted-string string">"mypassword"</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">testUserCreation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$name</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">faker</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">name</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$email</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">faker</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">email</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$response</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">postJson</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/api/auth/signup'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$name</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'email'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$email</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">password</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password_confirmation'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">password</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$response</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertStatus</span><span class="token punctuation">(</span><span class="token number">201</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertExactJson</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'message'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token double-quoted-string string">"Successfully created user!"</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token comment">//testUserCreation</span> <span class="token punctuation">}</span> </span> |
Sau đó chúng ta tiến hành chạy test
1 2 3 | <span class="token punctuation">.</span><span class="token operator">/</span>vendor<span class="token operator">/</span>bin<span class="token operator">/</span>phpunit |
2. Tự đông hóa test với CircleCI
Đã đến lúc giới thiệu sức mạnh của CI / CD vào API Laravel của chúng ta. Mình sẽ tạo một pipeline để đảm bảo rằng khi chúng ta đẩy code mới, các test của chúng ta sẽ tự động chạy và chúng ta nhận được pipeline status CI / CD successful hoặc failed. (go)
1.CircleCI
Giới thiệu về CircleCI một chút :
CircleCI là 1 tool để giúp ta hiện thực hóa CI. Có nhiều tool CI khác cũng nổi tiếng nữa (Travis CI, Jenkins…), lý do mình chọn Circle CI chỉ vì nó khá đơn giản và đầy đủ các chức năng mà mình cần.
CircleCI sử dụng docker, trong cấu hình Circle CI ta sẽ chỉ định các docker image
sẽ sử dụng và các job
, trong các job
lại có các step
, trong các step
là cụ thể các command
. Ngoài ra còn có cấu hình filter
giúp ta linh hoạt điều chỉnh sao cho chỉ run các job khi có merge/push vào 1 số branch nhất định vân vân.
Mô tả quá trình run 1 job trên Circle CI:
Developer chỉ cần push hoặc merge vào 1 branch, Circle CI tự động biết event đó và khởi động lên job đã được cài đặt tương ứng.
Ban đầu Circle CI pull docker image về và run lên trên môi trường cloud của nó.
Tiếp theo nó chạy các step đã được cài đặt trong docker container, thông thường step đầu tiên luôn là checkout tức là git checkout lấy source về (mặc định lưu trong thư mục ~/project)
Các step tiếp theo được chạy tùy vào độ sáng tạo của bạn, ví dụ job để build thì thường là npm install rồi npm run abcxyz hay job để deploy thì có thể là aws s3 sync hay serverless deploy…
Sau khi tất cả các step đã chạy xong, job kết thúc. Nếu exit code của job là error thì mặc định ta sẽ nhận được mail thông báo failed nữa.
Nói tóm lại, sau khi cài đặt và cấu hình ta chỉ việc dev còn các công việc như build, chạy test, deploy vân vân được tự động hóa hoàn toàn và chạy tức thì trên môi trường cloud mạnh mẽ miễn phí của Circle CI.
1. Đăng kí plan
CircleCI cung cấp cho người dùng 4 gói, ở đây mình sẽ đăng kí gói free (vì mình hổng có xiên)
các bạn có thể thấy ở gói free chúng ta có :
1500 phút build
Không giới hạn repository và user
4x project build cùng lúc với public repository và 1x với private repository
Sau khi install xong thì sẽ chuyển đến dashboard như sau :
2. Tích hợp vào project
Mình sẽ lựa chọn project đã tạo phía trên demo-ci sau đó click setup project
Circleci hỗ trợ chúng ta rất nhiều ngôn ngữ như PHP, Java, .NET, Go, IOS, Android, Python…. ở đây mình sẽ sử dụng PHP
1. TÍch hợp vào project laravel của mình
Tạo thư mục .circleci
trong project
Trong thư mục .circleci
Tạo file có tên làconfig.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <span class="token shell-comment comment"># PHP CircleCI 2.0 configuration file</span> <span class="token shell-comment comment">#</span> <span class="token shell-comment comment"># Check https:</span><span class="token comment">//circleci.com/docs/2.0/language-php/ for more details</span> <span class="token shell-comment comment">#</span> version<span class="token punctuation">:</span> <span class="token number">2</span> jobs<span class="token punctuation">:</span> build<span class="token punctuation">:</span> docker<span class="token punctuation">:</span> <span class="token shell-comment comment"># Specify the version you desire here</span> <span class="token operator">-</span> image<span class="token punctuation">:</span> circleci<span class="token operator">/</span>php<span class="token punctuation">:</span><span class="token number">7.1</span><span class="token operator">-</span>node<span class="token operator">-</span>browsers <span class="token shell-comment comment"># Specify service dependencies here if necessary</span> <span class="token shell-comment comment"># CircleCI maintains a library of pre-built images</span> <span class="token shell-comment comment"># documented at https:</span><span class="token comment">//circleci.com/docs/2.0/circleci-images/</span> <span class="token shell-comment comment"># Using the RAM variation mitigates I/O contention</span> <span class="token shell-comment comment"># for database intensive operations.</span> <span class="token shell-comment comment"># - image: circleci/mysql:5.7-ram</span> <span class="token shell-comment comment">#</span> <span class="token shell-comment comment"># - image: redis:2.8.19</span> steps<span class="token punctuation">:</span> <span class="token operator">-</span> checkout <span class="token operator">-</span> run<span class="token punctuation">:</span> sudo apt update <span class="token shell-comment comment"># PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> sudo docker<span class="token operator">-</span>php<span class="token operator">-</span>ext<span class="token operator">-</span>install zip <span class="token shell-comment comment"># Download and cache dependencies</span> <span class="token operator">-</span> restore_cache<span class="token punctuation">:</span> keys<span class="token punctuation">:</span> <span class="token shell-comment comment"># "composer.lock" can be used if it is committed to the repo</span> <span class="token operator">-</span> v1<span class="token operator">-</span>dependencies<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"composer.json"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token shell-comment comment"># fallback to using the latest cache if no exact match is found</span> <span class="token operator">-</span> v1<span class="token operator">-</span>dependencies<span class="token operator">-</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> <span class="token double-quoted-string string">"Install Dependencies"</span> command<span class="token punctuation">:</span> composer install <span class="token operator">-</span>n <span class="token operator">--</span>prefer<span class="token operator">-</span>dist <span class="token operator">-</span> save_cache<span class="token punctuation">:</span> key<span class="token punctuation">:</span> v1<span class="token operator">-</span>dependencies<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"composer.json"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> paths<span class="token punctuation">:</span> <span class="token operator">-</span> <span class="token punctuation">.</span><span class="token operator">/</span>vendor <span class="token operator">-</span> restore_cache<span class="token punctuation">:</span> keys<span class="token punctuation">:</span> <span class="token operator">-</span> node<span class="token operator">-</span>v1<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"package.json"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">-</span> node<span class="token operator">-</span>v1<span class="token operator">-</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> yarn install <span class="token operator">-</span> save_cache<span class="token punctuation">:</span> key<span class="token punctuation">:</span> node<span class="token operator">-</span>v1<span class="token operator">-</span><span class="token punctuation">{</span><span class="token punctuation">{</span> checksum <span class="token double-quoted-string string">"package.json"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span> paths<span class="token punctuation">:</span> <span class="token operator">-</span> node_modules <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> <span class="token double-quoted-string string">"Create Environment file and generate app key"</span> command<span class="token punctuation">:</span> <span class="token operator">|</span> mv <span class="token punctuation">.</span>env<span class="token punctuation">.</span>testing <span class="token punctuation">.</span>env php artisan key<span class="token punctuation">:</span>generate <span class="token shell-comment comment"># prepare the database</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> touch storage<span class="token operator">/</span>testing<span class="token punctuation">.</span>sqlite <span class="token operator">-</span> run<span class="token punctuation">:</span> php artisan migrate <span class="token operator">--</span>env<span class="token operator">=</span>testing <span class="token operator">--</span>database<span class="token operator">=</span>sqlite_testing <span class="token operator">--</span>force <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> <span class="token double-quoted-string string">"Generate Passport encryption keys"</span> command<span class="token punctuation">:</span> php artisan passport<span class="token punctuation">:</span>install <span class="token shell-comment comment"># run tests with phpunit</span> <span class="token operator">-</span> run<span class="token punctuation">:</span> name<span class="token punctuation">:</span> <span class="token double-quoted-string string">"Run Tests"</span> command<span class="token punctuation">:</span> <span class="token punctuation">.</span><span class="token operator">/</span>vendor<span class="token operator">/</span>bin<span class="token operator">/</span>phpunit |
Sau đo chúng ta lên github click Start building để bắt đầu build project . CircleCI sẽ bắt đầu chạy cấu hình pipeline . Nếu bạn đã làm theo đúng hướng dẫn, bạn sẽ có một bản build thành công được chỉ ra bởi màn hình bên dưới.
Perfect . Chúng ta đã có thể cấp nguồn thành công API Laravel của mình bằng một pipeline CI /CD tự động chạy test sử dụng CircleCI.
Chúng ta sẽ thêm 1 method test fucntion login vào file UserTest.php
và push code để quan sát cách CircleCI tự động chạy thử nghiệm mới được thêm của chúng ta.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">testUserLogin</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$name</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">faker</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">name</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$email</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">faker</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">email</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$name</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'email'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$email</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">bcrypt</span><span class="token punctuation">(</span><span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">password</span><span class="token punctuation">)</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$response</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">postJson</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/api/auth/login'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'email'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$email</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'password'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">password</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$response</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">assertStatus</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">assertAuthenticated</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Bây giờ save file , commit và push lên GitHub. CircleCI sẽ một lần nữa chạy pipeline với các test, bao gồm cả testcase mà chúng ta vừa thêm vào.
Kết luận
Nguồn tham khảo
https://circleci.com/blog/build-a-ci-powered-restful-api-with-laravel/