Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
Littlesqx committed Sep 24, 2018
0 parents commit ac50015
Showing 18 changed files with 855 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false

[*.{vue,js,scss}]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* text=auto

/tests export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.scrutinizer.yml export-ignore
.travis.yml export-ignore
phpunit.php export-ignore
phpunit.xml.dist export-ignore
phpunit.xml export-ignore
.php_cs export-ignore
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.DS_Store
*.idea
/vendor
/coverage
sftp-config.json
composer.lock
.subsplit
.php_cs.cache
27 changes: 27 additions & 0 deletions .php_cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
$header = <<<EOF
This file is part of the littlesqx/douban-book.
(c) littlesqx <littlesqx@gmail.com>
This source file is subject to the MIT license that is bundled.
EOF;

return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules(array(
'@Symfony' => true,
'header_comment' => array('header' => $header),
'array_syntax' => array('syntax' => 'short'),
'ordered_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'php_unit_construct' => true,
'php_unit_strict' => true,
))
->setFinder(
PhpCsFixer\Finder::create()
->exclude('vendor')
->in(__DIR__)
)
;
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<h1 align="center"> douban-book </h1>

<p align="center"> A book SDK.</p>


## Installing

```shell
$ composer require littlesqx/douban-book -vvv
```

## Usage

TODO

## Contributing

You can contribute in one of three ways:

1. File bug reports using the [issue tracker](https://github.com/littlesqx/douban-book/issues).
2. Answer questions or fix bugs on the [issue tracker](https://github.com/littlesqx/douban-book/issues).
3. Contribute new features or update the wiki.

_The code contribution process is not very formal. You just need to make sure that you follow the PSR-0, PSR-1, and PSR-2 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable._

## Thanks

- [豆瓣 API](https://developers.douban.com)

- [overtrue](https://github.com/overtrue)

## License

MIT
27 changes: 27 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "littlesqx/douban-book",
"description": "A book SDK.",
"license": "MIT",
"authors": [
{
"name": "littlesqx",
"email": "littlesqx@gmail.com"
}
],
"require": {
"php": ">=7.1.0",
"guzzlehttp/guzzle": "^6.3"
},
"require-dev": {
"phpunit/phpunit": "^7.3",
"mockery/mockery": "^1.1"
},
"autoload": {
"psr-4": {
"Littlesqx\\Book\\": "src"
},
"files": [
"src/helper.php"
]
}
}
21 changes: 21 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
</phpunit>
Empty file added src/.gitkeep
Empty file.
84 changes: 84 additions & 0 deletions src/Application.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book;

use GuzzleHttp\Client;
use Littlesqx\Book\Entity\Entity;
use Littlesqx\Book\Exception\HttpException;
use Littlesqx\Book\Exception\InvalidArgumentException;

class Application
{
protected $httpOptions = [];

protected $requestUrl = 'https://api.douban.com/v2/book/';

/**
* set http client options.
*
* @param array $httpOptions
* @return $this
*/
public function setHttpOptions(array $httpOptions)
{
$this->httpOptions = $httpOptions;
return $this;
}

/**
* get http client options.
*
* @return array
*/
public function getHttpOptions(): array
{
return $this->httpOptions;
}


/**
* get a http client.
*
* @return Client
*/
public function getHttpClient(): Client
{
return new Client($this->httpOptions);
}

/**
* get a book by isbn code.
*
* @param string $isbn
* @return Entity
* @throws HttpException
* @throws InvalidArgumentException
*/
public function getBook(string $isbn):? Entity
{
if (strlen($isbn) !== 13 && strlen($isbn) !== 10) {
throw new InvalidArgumentException('Invalid isbn code(isbn10 or isbn13): ' . $isbn);
}
$queryParams = ['isbn' => $isbn];
try {
$response = $this->getHttpClient()->get(
$this->requestUrl . array_to_path($queryParams)
);
if (200 === $response->getStatusCode()) {
return BookFactory::make($response->getBody()->getContents());
}
} catch (\Exception $e) {
throw new HttpException($e->getMessage(), $e->getCode(), $e);
}
}

}
47 changes: 47 additions & 0 deletions src/BookFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book;


use Littlesqx\Book\Entity\Book;
use Littlesqx\Book\Entity\Entity as EntityInterface;

class BookFactory
{
/**
* make a book entity.
*
* @param $params
* @return EntityInterface|null
*/
public static function make($params):? EntityInterface
{
$params = \json_decode($params, true);
if (!isset($params['title'])) {
return null;
}
$book = new Book();
$book->setIsbn($params['isbn13'])
->setTitle($params['title'])
->setSubtitle($params['subtitle'])
->setAuthor($params['author'])
->setAuthorIntro($params['author_intro'])
->setPrice($params['price'])
->setCatalog($params['catalog'])
->setPublicationDate($params['pubdate'])
->setPublisher($params['publisher'])
->setSummary($params['summary'])
->setCover($params['images']['large'])
->setTags(array_column($params['tags'], 'name'));
return $book;
}
}
324 changes: 324 additions & 0 deletions src/Entity/Book.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book\Entity;

class Book implements Entity
{
/**
* @var string $isbn book's isbn13 code.
*/
private $isbn;

/**
* @var string $title book's title.
*/
private $title;

/**
* @var string $subtitle book's subtitle
*/
private $subtitle;

/**
* @var array $author book's authors
*/
private $author;

/**
* @var string $authorIntro authors' introduction.
*/
private $authorIntro;

/**
* @var string $publicationDate book's publication date.
*/
private $publicationDate;

/**
* @var string $publisher book's publisher.
*/
private $publisher;

/**
* @var array $tags book's tags.
*/
private $tags;

/**
* @var string $catalog book's catalog.
*/
private $catalog;

/**
* @var string $summary book's summary.
*/
private $summary;

/**
* @var string $price book's price.
*/
private $price;

/**
* @var string $cover book's cover (image url)
*/
private $cover;

/**
* @return string
*/
public function getCover(): string
{
return $this->cover;
}

/**
* @param string $cover
* @return $this
*/
public function setCover(string $cover)
{
$this->cover = $cover;
return $this;
}

/**
* @return string
*/
public function getIsbn(): string
{
return $this->isbn;
}

/**
* @param string $isbn
* @return $this
*/
public function setIsbn(string $isbn)
{
$this->isbn = $isbn;
return $this;
}

/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}

/**
* @param string $title
* @return $this
*/
public function setTitle(string $title)
{
$this->title = $title;
return $this;
}

/**
* @return string
*/
public function getSubtitle(): string
{
return $this->subtitle;
}

/**
* @param string $subtitle
* @return $this
*/
public function setSubtitle(string $subtitle)
{
$this->subtitle = $subtitle;
return $this;
}

/**
* @return array
*/
public function getAuthor(): array
{
return $this->author;
}

/**
* @param array $author
* @return $this
*/
public function setAuthor(array $author)
{
$this->author = $author;
return $this;
}

/**
* @return string
*/
public function getAuthorIntro(): string
{
return $this->authorIntro;
}

/**
* @param string $authorIntro
* @return $this
*/
public function setAuthorIntro(string $authorIntro)
{
$this->authorIntro = $authorIntro;
return $this;
}

/**
* @return string
*/
public function getPublicationDate(): string
{
return $this->publicationDate;
}

/**
* @param string $publicationDate
* @return $this
*/
public function setPublicationDate(string $publicationDate)
{
$this->publicationDate = $publicationDate;
return $this;
}

/**
* @return string
*/
public function getPublisher(): string
{
return $this->publisher;
}

/**
* @param string $publisher
* @return $this
*/
public function setPublisher(string $publisher)
{
$this->publisher = $publisher;
return $this;
}

/**
* @return array
*/
public function getTags(): array
{
return $this->tags;
}

/**
* @param array $tags
* @return $this
*/
public function setTags(array $tags)
{
$this->tags = $tags;
return $this;
}

/**
* @return string
*/
public function getCatalog(): string
{
return $this->catalog;
}

/**
* @param string $catalog
* @return $this
*/
public function setCatalog(string $catalog)
{
$this->catalog = $catalog;
return $this;
}

/**
* @return string
*/
public function getSummary(): string
{
return $this->summary;
}

/**
* @param string $summary
* @return $this
*/
public function setSummary(string $summary)
{
$this->summary = $summary;
return $this;
}

/**
* @return string
*/
public function getPrice(): string
{
return $this->price;
}

/**
* @param string $price
* @return $this
*/
public function setPrice(string $price)
{
$this->price = $price;
return $this;
}

/**
* get attributes with array format.
*
* @return array
*/
public function toArray(): array
{
return [
'isbn' => $this->isbn,
'title' => $this->title,
'subtitle' => $this->subtitle,
'price' => $this->price,
'author' => $this->author,
'author_intro' => $this->authorIntro,
'publication_date' => $this->publicationDate,
'publisher' => $this->publisher,
'tags' => $this->tags,
'catalog' => $this->catalog,
'cover' => $this->cover,
'summary' => $this->summary
];
}

/**
* get attributes with json format.
*
* @return string
*/
public function toJSON(): string
{
return \json_encode($this->toArray(), JSON_UNESCAPED_UNICODE);
}
}
21 changes: 21 additions & 0 deletions src/Entity/Entity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book\Entity;

/**
* Interface EntityInterface
*/
interface Entity
{
public function toArray();
public function toJSON();
}
21 changes: 21 additions & 0 deletions src/Exception/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book\Exception;

/**
* Class Exception
* @package Littlesqx\Exception
*/
class Exception extends \Exception
{

}
21 changes: 21 additions & 0 deletions src/Exception/HttpException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book\Exception;

/**
* Class HttpException
* @package Littlesqx\Book\Exception
*/
class HttpException extends Exception
{

}
21 changes: 21 additions & 0 deletions src/Exception/InvalidArgumentException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book\Exception;

/**
* Class InvalidArgumentException
* @package Littlesqx\Book\Exception
*/
class InvalidArgumentException extends Exception
{

}
27 changes: 27 additions & 0 deletions src/helper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

if (!function_exists('array_to_path')) {

/**
* input: ['foo' => 'bar'], output: foo/bar
*
* @param $array
* @return string
*/
function array_to_path($array): string {
$path = '';
foreach ($array as $key => $value) {
$path .= "{$key}/{$value}";
}
return $path;
}
}
Empty file added tests/.gitkeep
Empty file.
141 changes: 141 additions & 0 deletions tests/ApplicationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

/*
* This file is part of the littlesqx/douban-book.
*
* (c) littlesqx <littlesqx@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Littlesqx\Book\Tests;

use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Psr7\Response;
use Littlesqx\Book\Application;
use Littlesqx\Book\Entity\Entity;
use Littlesqx\Book\Exception\HttpException;
use Littlesqx\Book\Exception\InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Mockery\Matcher\AnyArgs;

class ApplicationTest extends TestCase
{
/**
* @param string $isbn
*
* @dataProvider getBookExceptionProvider
*/
public function testGetBookException(string $isbn)
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid isbn code(isbn10 or isbn13): ' . $isbn);
(new Application())->getBook($isbn);
$this->fail('Failed to assert getBook throw exception with invalid argument.');
}

/**
* testGetBookException's data provider.
*
* @return array
*/
public function getBookExceptionProvider()
{
return [
['000123456789'],
['123456789']
];
}

/**
* test for get a book entity.
*
* @param string $isbn
*
* @dataProvider getBookProvider
*/
public function testGetBook(string $isbn)
{
$response = new Response(200, [], '{}');
$client = \Mockery::mock(Client::class);
$client->allows()
->get('https://api.douban.com/v2/book/isbn/' . $isbn)
->andReturn($response);

$app = \Mockery::mock(Application::class)->makePartial();
$app->allows()->getHttpClient()->andReturn($client);

$this->assertEmpty($app->getBook($isbn));

$response = new Response(
200, [],
'{"title":"","price":"","author":[],"publisher":""'
.',"author_intro":"","subtitle":"","isbn13":"","images":{"large":""},'
.'"catalog":"","pubdate":"","tags":[],"summary":""}'
);
$client = \Mockery::mock(Client::class);
$client->allows()
->get('https://api.douban.com/v2/book/isbn/' . $isbn)
->andReturn($response);

$app = \Mockery::mock(Application::class)->makePartial();
$app->allows()->getHttpClient()->andReturn($client);

$this->assertInstanceOf(Entity::class, $app->getBook($isbn));
}

/**
* testGetBook's data provider.
*
* @return array
*/
public function getBookProvider()
{
return [
['0123456789']
];
}

/**
* test for getHttpClient
*/
public function testGetHttpClient()
{
$app = new Application();
$this->assertInstanceOf(ClientInterface::class, $app->getHttpClient());
}

/**
* test for set http options.
*/
public function testSetHttpOptions()
{
$app = new Application();
// default config
$this->assertSame([], $app->getHttpOptions());
// set a new config
$app->setHttpOptions(['timeout' => 5000]);
$this->assertSame(['timeout' => 5000], $app->getHttpOptions());
}

/**
* test for http request exception.
*/
public function testGetBookWithGuzzleRuntimeException()
{
$client = \Mockery::mock(Client::class);
$client->allows()
->get(new AnyArgs())
->andThrow(new \Exception('request timeout'));

$app = \Mockery::mock(Application::class)->makePartial();
$app->allows()->getHttpClient()->andReturn($client);

$this->expectException(HttpException::class);
$this->expectExceptionMessage('request timeout');

$app->getBook('0123456789');
}
}

0 comments on commit ac50015

Please sign in to comment.