Add APIs for closet

This commit is contained in:
Pig Fang 2019-04-28 11:38:38 +08:00
parent b365b1c4bf
commit d8bec1d1fb
10 changed files with 233 additions and 109 deletions

View File

@ -84,6 +84,10 @@ class ClosetController extends Controller
return json(trans('user.closet.add.not-found'), 1);
}
if (! $texture->public && $texture->uploader != $user->uid) {
return json(trans('skinlib.show.private'), 1);
}
if ($user->closet()->where('tid', $request->tid)->count() > 0) {
return json(trans('user.closet.add.repeated'), 1);
}
@ -100,31 +104,22 @@ class ClosetController extends Controller
return json(trans('user.closet.add.success', ['name' => $request->input('name')]), 0);
}
public function rename(Request $request)
public function rename(Request $request, $tid)
{
$this->validate($request, [
'tid' => 'required|integer',
'new_name' => 'required|no_special_chars',
]);
$this->validate($request, ['name' => 'required|no_special_chars']);
$user = auth()->user();
if ($user->closet()->where('tid', $request->tid)->count() == 0) {
return json(trans('user.closet.remove.non-existent'), 1);
}
$user->closet()->updateExistingPivot($request->tid, ['item_name' => $request->new_name]);
$user->closet()->updateExistingPivot($request->tid, ['item_name' => $request->name]);
return json(trans('user.closet.rename.success', ['name' => $request->new_name]), 0);
return json(trans('user.closet.rename.success', ['name' => $request->name]), 0);
}
public function remove(Request $request)
public function remove($tid)
{
$this->validate($request, [
'tid' => 'required|integer',
]);
$tid = $request->tid;
$user = auth()->user();
if ($user->closet()->where('tid', $tid)->count() == 0) {

View File

@ -87,8 +87,8 @@ export default {
}
const { code, message } = await this.$http.post(
'/user/closet/rename',
{ tid: this.tid, new_name: newTextureName }
`/user/closet/rename/${this.tid}`,
{ new_name: newTextureName }
)
if (code === 0) {
this.textureName = newTextureName

View File

@ -15,10 +15,7 @@ export default Vue.extend<{
return
}
const { code, message } = await this.$http.post(
'/user/closet/remove',
{ tid: this.tid }
)
const { code, message } = await this.$http.post(`/user/closet/remove/${this.tid}`)
if (code === 0) {
this.$emit('item-removed')
this.$message.success(message!)

View File

@ -61,8 +61,8 @@ test('rename texture', async () => {
await flushPromises()
expect(wrapper.find('.texture-name > span').text()).toBe('new-name (steve)')
expect(Vue.prototype.$http.post).toBeCalledWith(
'/user/closet/rename',
{ tid: 1, new_name: 'new-name' }
'/user/closet/rename/1',
{ new_name: 'new-name' }
)
})
@ -88,7 +88,7 @@ test('remove texture', async () => {
button.trigger('click')
await flushPromises()
expect(wrapper.emitted()['item-removed']).toBeTruthy()
expect(Vue.prototype.$http.post).toBeCalledWith('/user/closet/remove', { tid: 1 })
expect(Vue.prototype.$http.post).toBeCalledWith('/user/closet/remove/1')
})
test('set as avatar', async () => {

View File

@ -19,3 +19,10 @@ Route::prefix('players')->middleware('auth:jwt,oauth')->group(function () {
Route::put('{pid}/textures', 'PlayerController@setTexture');
Route::delete('{pid}/textures', 'PlayerController@clearTexture');
});
Route::prefix('closet')->middleware('auth:jwt,oauth')->group(function () {
Route::get('', 'ClosetController@getClosetData');
Route::post('', 'ClosetController@add');
Route::put('{tid}', 'ClosetController@rename');
Route::delete('{tid}', 'ClosetController@remove');
});

View File

@ -80,8 +80,8 @@ Route::group([
Route::get('/closet', 'ClosetController@index');
Route::get('/closet-data', 'ClosetController@getClosetData');
Route::post('/closet/add', 'ClosetController@add');
Route::post('/closet/remove', 'ClosetController@remove');
Route::post('/closet/rename', 'ClosetController@rename');
Route::post('/closet/remove/{tid}', 'ClosetController@remove');
Route::post('/closet/rename/{tid}', 'ClosetController@rename');
// OAuth2 Management
Route::view('/oauth/manage', 'user.oauth');

162
tests/Api/tests/closet.rs Normal file
View File

@ -0,0 +1,162 @@
use crate::auth::login;
use crate::types::JsonBody;
use rusqlite::{params, Connection};
use serde::Deserialize;
use serde_json::json;
use std::env;
#[derive(Deserialize)]
struct Closet {
pub category: String,
pub total_pages: usize,
pub items: Vec<ClosetItem>,
}
#[derive(Deserialize)]
struct ClosetItem {
pub tid: u32,
pub name: String,
pub r#type: String,
pub size: u32,
pub hash: String,
pub uploader: u32,
pub public: bool,
}
#[test]
fn fetch_closet_info() {
let token = login();
let client = reqwest::Client::new();
let body = client
.get("http://127.0.0.1:32123/api/closet")
.header("Authorization", token.clone())
.send()
.unwrap()
.json::<JsonBody<Closet>>()
.unwrap();
assert!(body.is_success());
let closet = body.data().unwrap();
assert_eq!(closet.category, "skin");
assert_eq!(closet.total_pages, 0);
assert_eq!(closet.items.len(), 0);
let body = client
.get("http://127.0.0.1:32123/api/closet")
.header("Authorization", token)
.json(&json!({"category": "cape"}))
.send()
.unwrap()
.json::<JsonBody<Closet>>()
.unwrap();
assert!(body.is_success());
let closet = body.data().unwrap();
assert_eq!(closet.category, "cape");
assert_eq!(closet.total_pages, 0);
assert_eq!(closet.items.len(), 0);
}
#[test]
fn insert_to_closet() {
let conn = Connection::open(env::var("DB_DATABASE").unwrap()).unwrap();
conn.execute(
"INSERT INTO textures (name, type, hash, size, uploader, public, upload_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
params!["steve", "steve", "abc", 1, 1, 1, "2019-01-01 00:00:00"],
)
.unwrap();
conn.execute(
"INSERT INTO textures (name, type, hash, size, uploader, public, upload_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
params!["cape", "cape", "def", 1, 1, 1, "2019-01-01 00:00:00"],
)
.unwrap();
let token = login();
let client = reqwest::Client::new();
let body = client
.post("http://127.0.0.1:32123/api/closet")
.header("Authorization", token.clone())
.json(&json!({"tid": 1, "name": "my-first-texture"}))
.send()
.unwrap()
.json::<JsonBody<()>>()
.unwrap();
assert!(body.is_success());
let body = client
.get("http://127.0.0.1:32123/api/closet")
.header("Authorization", token)
.send()
.unwrap()
.json::<JsonBody<Closet>>()
.unwrap();
assert!(body.is_success());
let closet = body.data().unwrap();
assert_eq!(closet.total_pages, 1);
assert_eq!(closet.items.len(), 1);
let item = closet.items.get(0).unwrap();
assert_eq!(item.tid, 1);
assert_eq!(item.name, "my-first-texture");
assert_eq!(item.r#type, "steve");
assert_eq!(item.size, 1);
assert_eq!(item.hash, "abc");
assert_eq!(item.uploader, 1);
assert!(item.public);
}
#[test]
fn modify_name() {
let token = login();
let client = reqwest::Client::new();
let body = client
.put("http://127.0.0.1:32123/api/closet/1")
.header("Authorization", token.clone())
.json(&json!({"name": "renamed"}))
.send()
.unwrap()
.json::<JsonBody<()>>()
.unwrap();
assert!(body.is_success());
let body = client
.get("http://127.0.0.1:32123/api/closet")
.header("Authorization", token)
.send()
.unwrap()
.json::<JsonBody<Closet>>()
.unwrap();
assert!(body.is_success());
let closet = body.data().unwrap();
let item = closet.items.get(0).unwrap();
assert_eq!(item.tid, 1);
assert_eq!(item.name, "renamed");
}
#[test]
fn remove_texture() {
let token = login();
let client = reqwest::Client::new();
let body = client
.delete("http://127.0.0.1:32123/api/closet/1")
.header("Authorization", token.clone())
.send()
.unwrap()
.json::<JsonBody<()>>()
.unwrap();
assert!(body.is_success());
let body = client
.get("http://127.0.0.1:32123/api/closet")
.header("Authorization", token)
.send()
.unwrap()
.json::<JsonBody<Closet>>()
.unwrap();
assert!(body.is_success());
let closet = body.data().unwrap();
assert_eq!(closet.items.len(), 0);
}

View File

@ -1,11 +1,14 @@
#[cfg(test)]
mod types;
#[cfg(test)]
mod auth;
#[cfg(test)]
mod user;
mod closet;
#[cfg(test)]
mod players;
#[cfg(test)]
mod types;
#[cfg(test)]
mod user;

View File

@ -1,9 +1,7 @@
use crate::auth::login;
use crate::types::JsonBody;
use rusqlite::{params, Connection};
use serde::Deserialize;
use serde_json::json;
use std::env;
#[derive(Deserialize)]
struct Player {
@ -67,20 +65,6 @@ fn modify_player_name() {
#[test]
fn modify_textures() {
let conn = Connection::open(env::var("DB_DATABASE").unwrap()).unwrap();
conn.execute(
"INSERT INTO textures (name, type, hash, size, uploader, public, upload_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
params!["steve", "steve", "abc", 1, 1, 1, "2019-01-01 00:00:00"],
)
.unwrap();
conn.execute(
"INSERT INTO textures (name, type, hash, size, uploader, public, upload_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
params!["cape", "cape", "def", 1, 1, 1, "2019-01-01 00:00:00"],
)
.unwrap();
let client = reqwest::Client::new();
let body = client
.put("http://127.0.0.1:32123/api/players/1/textures")

View File

@ -129,8 +129,21 @@ class ClosetControllerTest extends TestCase
'message' => trans('user.closet.add.not-found'),
]);
// Add a texture successfully
// Texture is private
option(['score_award_per_like' => 5]);
$privateTexture = factory(Texture::class)->create([
'public' => false,
'uploader' => $uploader->uid + 1,
]);
$this->postJson(
'/user/closet/add',
['tid' => $privateTexture->tid, 'name' => $name]
)->assertJson([
'code' => 1,
'message' => trans('skinlib.show.private'),
]);
// Add a texture successfully
$this->postJson(
'/user/closet/add',
['tid' => $texture->tid, 'name' => $name]
@ -160,46 +173,28 @@ class ClosetControllerTest extends TestCase
$texture = factory(Texture::class)->create();
$name = 'new';
// Missing `tid` field
$this->postJson('/user/closet/rename')->assertJsonValidationErrors('tid');
// `tid` is not a integer
$this->postJson(
'/user/closet/rename',
['tid' => 'string']
)->assertJsonValidationErrors('tid');
// Missing `new_name` field
$this->postJson(
'/user/closet/rename',
['tid' => 0]
)->assertJsonValidationErrors('new_name');
// Missing `name` field
$this->postJson('/user/closet/rename/0')->assertJsonValidationErrors('name');
// `new_name` field has special characters
$this->postJson(
'/user/closet/rename',
['tid' => 0, 'new_name' => '\\']
)->assertJsonValidationErrors('new_name');
$this->postJson('/user/closet/rename/0', ['name' => '\\'])
->assertJsonValidationErrors('name');
// Rename a not-existed texture
$this->postJson(
'/user/closet/rename',
['tid' => -1, 'new_name' => $name]
)->assertJson([
$this->postJson('/user/closet/rename/-1', ['name' => $name])
->assertJson([
'code' => 1,
'message' => trans('user.closet.remove.non-existent'),
]);
// Rename a closet item successfully
$this->user->closet()->attach($texture->tid, ['item_name' => 'name']);
$this->postJson(
'/user/closet/rename',
['tid' => $texture->tid, 'new_name' => $name]
)->assertJson([
$this->postJson('/user/closet/rename/'.$texture->tid, ['name' => $name])
->assertJson([
'code' => 0,
'message' => trans('user.closet.rename.success', ['name' => 'new']),
'message' => trans('user.closet.rename.success', ['name' => $name]),
]);
$this->assertEquals(1, $this->user->closet()->where('item_name', 'new')->count());
$this->assertEquals(1, $this->user->closet()->where('item_name', $name)->count());
}
public function testRemove()
@ -208,20 +203,9 @@ class ClosetControllerTest extends TestCase
$texture = factory(Texture::class)->create(['uploader' => $uploader->uid]);
$likes = $texture->likes;
// Missing `tid` field
$this->postJson('/user/closet/remove')->assertJsonValidationErrors('tid');
// `tid` is not a integer
$this->postJson(
'/user/closet/remove',
['tid' => 'string']
)->assertJsonValidationErrors('tid');
// Rename a not-existed texture
$this->postJson(
'/user/closet/remove',
['tid' => -1]
)->assertJson([
$this->postJson('/user/closet/remove/-1')
->assertJson([
'code' => 1,
'message' => trans('user.closet.remove.non-existent'),
]);
@ -230,10 +214,8 @@ class ClosetControllerTest extends TestCase
option(['score_award_per_like' => 5]);
$this->user->closet()->attach($texture->tid, ['item_name' => 'name']);
$score = $this->user->score;
$this->postJson(
'/user/closet/remove',
['tid' => $texture->tid]
)->assertJson([
$this->postJson('/user/closet/remove/'.$texture->tid)
->assertJson([
'code' => 0,
'message' => trans('user.closet.remove.success'),
]);
@ -249,13 +231,7 @@ class ClosetControllerTest extends TestCase
option(['return_score' => false]);
$this->user->closet()->attach($texture->tid, ['item_name' => 'name']);
$score = $this->user->score;
$this->postJson(
'/user/closet/remove',
['tid' => $texture->tid]
)->assertJson([
'code' => 0,
'message' => trans('user.closet.remove.success'),
]);
$this->postJson('/user/closet/remove/'.$texture->tid)->assertJson(['code' => 0]);
$this->assertEquals($likes, Texture::find($texture->tid)->likes);
$this->assertEquals($score, $this->user->score);
$this->assertEquals(0, $this->user->closet()->count());