function doGet(request) {
// sheet id
var ss = SpreadsheetApp.openById("ใส่ sheet id ของคุณ");
var sheet = ss.getActiveSheet()
const values = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).getValues();
// Converts data rows in json format
const result = values.map(([a, b, c , d , e , f]) => {
return ({ id: a, topic: b, content: c, author : d , date : e , view : f})
})
return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON);
}
openById : ให้ระบุ sheet id ที่ได้จาก https://docs.google.com/spreadsheets/d/187hL4DXXXXXXXXXXXXXXXXXXXXXXX-p_JOGjYxU/edit#gid=0 ซึ่งของแต่ละ sheet จะไม่ซ้ำกัน
การพัฒนาแอพพลิเคชั่น ข้อแนะนำคือควรทำ Unit Tests มองการพัฒนาในระยะยาว หากต้องมีการแก้ไข issue เล็กน้อย แต่ไม่แน่ใจว่าจะเกิด defect กับระบบเดิมหรือไม่ การมี unit tests ช่วยประหยัดเวลาการ regression tests ได้เยอะมาก และคุณจะเห็นประโยชน์ของการทำ Unit tests อย่างแน่นอน
รู้จัก @RestController annotation สร้าง routes RESTful API
annotation @RestController implement มาจาก @Controller ของ spring web เพื่อใช้งานสำหรับการสร้าง RESTful API (ไม่ใช่ static view) เพิ่มความสะดวกในการพัฒนา RESTful API ให้ง่ายยิ่งขึ้น
ตัวอย่างการสร้าง Route (text/plain)
package com.poolsawat.starter.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
ทดสอบ http://localhost:8080/
Greetings from Spring Boot!
เพิ่ม Route (application/json)
...
@RequestMapping("/domain")
public String[] json() {
return new String[] {"www",".","poolsawat",".","com"};
}
...
restart server จากนั้น ทดสอบ http://localhost:8080/domain
[
"www",
".",
"poolsawat",
".",
"com"
]
เพิ่ม Test Dependencies ใน POM
ก่อนที่จะเริ่มเขียน Unit Tests จำเป็นต้องเพิ่ม “spring-boot-starter-test” เข้าไปที่ไฟล์ POM (ไฟล์ pom.xml) มาเพิ่มกันเลย
.andExpect(status().isOk()) expect htto status code OK (status code 200) หากไม่ใช่จะ fail ทันที
.andExpect(content().string(equalTo(“Greetings from Spring Boot!”))) expect content body ต้องเป็นคำว่า “Greetings from Spring Boot!” หากไม่ใช่จะ fail ทันที
จากนั้นสร้าง maven test build
apply -> run
ตรวจสอบที่ console panel
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.poolsawat.starter.controller.HelloControllerTest
...
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.606 s - in com.poolsawat.starter.controller.HelloControllerTest
2020-10-15 10:57:54.262 INFO 5864 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.231 s
[INFO] Finished at: 2020-10-15T10:57:54+07:00
[INFO] Final Memory: 18M/220M
[INFO] ------------------------------------------------------------------------
ในทุกครั้งที่มีการแก้ไข java code จำเป็นต้องมีการ restart web server spring boot มี hot reload (การ auto restart web server เมื่อมี code change)
การพัฒนาแอพพลิเคชั่นที่มี Test ถือว่าเป็นแอพพลิเคชั่นที่ดี ข้อดีของการทำ Test มีเยอะมากช่วยลดเวลาการ Regression Test ได้เยอะ ถ้าแอพพลิเคชั่นของคุณยังไม่มี test เริ่มทำได้แล้ว เพราะถ้าไม่ทำคุณจะคุยกับเขา (ทีมพัฒนา แอพพลิเคชั่นที่มี test) ไม่รู้เรื่อง บทความต่อไปจะเป็นเรื่องเกี่ยวกับอะไร คอยติดตามกันนะครับ
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePhotosTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('photos', function (Blueprint $table) {
$table->id();
$table->string('photo_name');
$table->integer('photo_size');
$table->string('photo_url');
$table->enum('photo_status', ['active', 'inactive']);
$table->dateTime('photo_date', 0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('photos');
}
}
ในการกำหนด datatype ของ column ตารางมีรูปแบบที่หลากหลาย มี function รองรับอยู่แล้ว สำหรับการกำหนดสร้าง สามารถดูเพิ่มเติมได้จากตารางนี้
Available Column Types
The schema builder contains a variety of column types that you may specify when building your tables:
ก่อนที่จะทดสอบติดตั้ง npm i -g json เพื่อ pritty json response เพื่อความสวยงาม
$ curl localhost:8000/api/photos | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 28 0 28 0 0 65 0 --:--:-- --:--:-- --:--:-- 65
{
"name": "index",
"photos": []
}
/* fetch photos all */
$ curl localhost:8000/api/photos/create | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 31 0 31 0 0 73 0 --:--:-- --:--:-- --:--:-- 73
{
"name": "create",
"status": true
}
/* create photo */
$ curl -X POST -H "Content-Type: application/json" -d "{\"photo_name\":\"laravel\",\"photo_size\":1024,\"photo_url\":\"https://laravel.com/img/logotype.min.svg\",\"photo_status\":\"active\",\"photo_date\":\"2020-10-06 10:24:01\"}" http://127.0.0.1:8000/api/photos | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 357 0 201 100 156 1116 866 --:--:-- --:--:-- --:--:-- 1994
{
"name": "store",
"payload": {
"photo_name": "laravel",
"photo_size": 1024,
"photo_url": "https://laravel.com/img/logotype.min.svg",
"photo_status": "active",
"photo_date": "2020-10-06 10:24:01"
},
"status": true
}
/* create photo with payload */
$ curl localhost:8000/api/photos/1 | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 286 0 286 0 0 722 0 --:--:-- --:--:-- --:--:-- 722
{
"name": "show",
"id": "1",
"photo": {
"id": 1,
"photo_name": "laravel",
"photo_size": 1024,
"photo_url": "https://laravel.com/img/logotype.min.svg",
"photo_status": "active",
"photo_date": "2020-10-06 10:32:24",
"created_at": "2020-10-06T10:32:24.000000Z",
"updated_at": "2020-10-06T10:32:24.000000Z"
}
}
/* fetch photo by id */
$ curl localhost:8000/api/photos/1/edit | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 286 0 286 0 0 709 0 --:--:-- --:--:-- --:--:-- 709
{
"name": "edit",
"id": "1",
"photo": {
"id": 1,
"photo_name": "laravel",
"photo_size": 1024,
"photo_url": "https://laravel.com/img/logotype.min.svg",
"photo_status": "active",
"photo_date": "2020-10-06 10:32:24",
"created_at": "2020-10-06T10:32:24.000000Z",
"updated_at": "2020-10-06T10:32:24.000000Z"
}
}
/* fetch photo by id for edit */
$ curl -X PATCH -H "Content-Type: application/json" -d "{\"id\":1,\"photo_name\":\"laravel patch\",\"photo_size\":2048,\"photo_url\":\"https://laravel.com/img/logotype.min.svg\",\"photo_status\":\"active\",\"photo_date\":\"2020-10-06 10:24:01\"}" http://127.0.0.1:8000/api/photos/1 | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 393 0 224 100 169 1108 836 --:--:-- --:--:-- --:--:-- 1945
{
"name": "update",
"status": true,
"payload": {
"id": 1,
"photo_name": "laravel patch",
"photo_size": 2048,
"photo_url": "https://laravel.com/img/logotype.min.svg",
"photo_status": "active",
"photo_date": "2020-10-06 10:24:01"
},
"id": "1"
}
/* patch photo with payload */
$ curl localhost:8000/api/photos/1 | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 292 0 292 0 0 707 0 --:--:-- --:--:-- --:--:-- 705
{
"name": "show",
"id": "1",
"photo": {
"id": 1,
"photo_name": "laravel patch",
"photo_size": 2048,
"photo_url": "https://laravel.com/img/logotype.min.svg",
"photo_status": "active",
"photo_date": "2020-10-06 10:36:22",
"created_at": "2020-10-06T10:32:24.000000Z",
"updated_at": "2020-10-06T10:36:22.000000Z"
}
}
curl -X DELETE http://127.0.0.1:8000/api/photos/1 | json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 41 0 41 0 0 221 0 --:--:-- --:--:-- --:--:-- 221
{
"name": "destroy",
"status": true,
"id": "1"
}
/* delete photo by id */
laravel สร้าง generate /app/Http/Controllers/PhotoController.php ภายในไฟล์จะมีการสร้าง function RESTful template มาให้
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PhotoController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
มาทำความรู้จักการทำงานแต่ละ function มามีการทำงานอย่างไร ตามการอธิบายจากตารางนี้
implement PhotoController ตาม function การทำงานเพื่อใช้ตรวจสอบการ call จาก REST client
ผมจะเพิ่ม function response()->json(); เพื่อทำการ return http response คืนค่ากลับไปในรูปแบบ application/json format
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PhotoController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return response()->json(['name' => 'index']);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return response()->json(['name' => 'create']);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
return response()->json(['name' => 'store', 'payload' => $request->all()]);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
return response()->json(['name' => 'show', 'id' => $id]);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
return response()->json(['name' => 'edit', 'id' => $id]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
return response()->json(['name' => 'update', 'payload' => $request->all(), 'id' => $id]);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
return response()->json(['name' => 'destroy', 'id' => $id]);
}
}
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
/**
* Create a new component instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\View\View|string
*/
public function render()
{
return view('components.alert');
}
}
Java Server Faces (JSF) เป็นเว็บแอ็พพลิเคชันที่ใช้ Java ซึ่งมีจุดมุ่งหมายเพื่อลดความซับซ้อนในการพัฒนาอินเทอร์เฟซสำหรับผู้ใช้บนเว็บ JavaServer Faces เป็นเทคโนโลยีการแสดงผลแบบมาตรฐานซึ่งได้รับการประกาศไว้ในข้อกำหนดผ่านกระบวนการ Java Community Process