diff --git a/.gitignore b/.gitignore index 6798768..6b3fac2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ placid/composer.json placid/composer.lock -placid/composer.phar \ No newline at end of file +placid/composer.phar +/vendor/ diff --git a/Placid/PlacidAPI.php b/Placid/PlacidAPI.php deleted file mode 100755 index 807da43..0000000 --- a/Placid/PlacidAPI.php +++ /dev/null @@ -1,28 +0,0 @@ -request($method, $path); - } else { - return $client->request($method); - } - } - - public function send($request, $options = null) - { - if ($options) { - return $this->client->get($request->getUrl(), $options); - } - - return $this->client->send($request); - } -} diff --git a/Placid/PlacidTags.php b/Placid/PlacidTags.php deleted file mode 100755 index d1d3ba6..0000000 --- a/Placid/PlacidTags.php +++ /dev/null @@ -1,109 +0,0 @@ -getParam('handle', null); - $request = $this->getConfig($handle); - $method = $this->getConfig('method'); - $cacheDuration = $this->getParam('duration', isset($request['duration']) ? $request['duration'] : $this->getConfig('cacheDuration', 1440)); - - // Request options - $options['client']['base_uri'] = $this->getParam('url', isset($request['url']) ? $request['url'] : null); - $options['client']['headers'] = isset($request['headers']) ? $request['headers'] : null; - $options['access_token'] = $this->getParam('access_token', isset($request['access_token']) ? $request['access_token'] : null); - $options['cache'] = $this->getParamBool('cache', isset($request['cache']) ? $request['cache'] : true); - $options['path'] = $this->getParam('path', null); - $options['query'] = $this->getParam('query', isset($request['query']) ? $request['query'] : null); - // Set up the cached_id - $cacheId = base64_encode(urlencode($options['client']['base_uri'].(is_array($options['query']) ? implode(' ', $options['query']) : $options['query']))); - - /* - Has a query been set in the template. - */ - if ($options['query'] && !is_array($options['query'])) { - - // Get the query parameter as a string and explode it. - $queries = explode(',', $options['query']); - - // Make sure this is a clean array. - $options['client']['query'] = []; - - // Map each query from the exploded array into a variable - // Then add them to the query array - foreach ($queries as $query) { - list($key, $value) = explode(':', $query); - if ($method != 'GET') { - $options['client']['form_params'][$key] = $value; - } else { - $options['client']['query'][$key] = $value; - } - } - } - - // If an access token is set, lets set it to our client - if ($options['access_token'] && !$request) { - // Try and get the token from the config - try { - $token = $this->fetch('tokens')[$options['access_token']]; - } catch (Exception $e) { - // Log needs to go here - $token = null; - } - $options['access_token'] = $token; - } - - // Do the cache thing - // --------------------------------------------------------- - if ($options['cache']) { - // Try and get a cached response - $cached_response = $this->cache->get($cacheId); - if ($cached_response) { - if (count($cached_response) == count($cached_response, COUNT_RECURSIVE)) { - return $cached_response; - } - return ['response' => $cached_response]; - } - } - - // Do we have an access token we need to append? - if ($options['access_token']) { - $options['client']['query']['access_token'] = $options['access_token']; - } - - $options['client']['query'] = $options['query']; - - try { - $response = $this->api('Placid')->request($options['client'], $options['path'], $method); - $response = json_decode($response->getBody(), true); - } catch (\GuzzleHttp\Exception\ClientException $e) { - $response = null; - } - - // Do we need to cache the request? - if ($options['cache']) { - $this->cache->put($cacheId, $response, $cacheDuration); - } - // If there is no result, pass the `no_results` tag back - if (!$response) { - return ['no_results' => true]; - } - - if (count($response) == count($response, COUNT_RECURSIVE)) { - return $response; - } - - return ['response' => $response]; - } -} diff --git a/Placid/default.yaml b/Placid/default.yaml deleted file mode 100644 index b408c51..0000000 --- a/Placid/default.yaml +++ /dev/null @@ -1,2 +0,0 @@ -method: GET -cacheDuration: 1440 \ No newline at end of file diff --git a/Placid/meta.yaml b/Placid/meta.yaml deleted file mode 100755 index 9af5f16..0000000 --- a/Placid/meta.yaml +++ /dev/null @@ -1,6 +0,0 @@ -name: Placid -version: 2.0.3 -description: Consume REST services in your templates -url: https://github.com/alecritson/Placid-Statamic -developer: Alec Ritson -developer_url: http://itsalec.co.uk diff --git a/Readme.md b/Readme.md index 865f277..d22a2fe 100644 --- a/Readme.md +++ b/Readme.md @@ -1,17 +1,7 @@ -# Placid Beta (Statamic) +# Placid Statamic [![StyleCI](https://styleci.io/repos/25640354/shield?branch=v2)](https://styleci.io/repos/25640354) -**Please note this is for use on Statamic `v2.*`, please see the v1 branch for Statamic `v1.*`** - - -_Also._ - -> I haven't had a great deal of time to delve into plugin development on the new Statamic, however I have run tests -with this version of the plugin and things seem to work. If they don't for you then absolutely feel free to create -an issue on the repo, just start it with `[V2]` so I know what version you are on about. - - --- Placid allows you to consume RESTful APIs in your Statamic templates, using Guzzle to handle the requests. @@ -22,138 +12,89 @@ Placid allows you to consume RESTful APIs in your Statamic templates, using Guzz - Predefined requests - Headers - Access tokens - -#### Updates / Changes -- **v2.0.0** - Support for the new version of Statamic - ### Installation -Copy the `Placid` folder to your **site/addons** directory and you're good to go +Require the addon via composer + +``` +composer require ritson/placid-statamic +``` ### Parameters -- **URL**: The URL to request -- **duration** (number): The time in seconds until the cache refreshes (default is 7200 / 2 hours) -- **handle** (string) : The handle specified in the placid config -- **cache** (boolean) : Whether you want the request to be cached (default is 1) +- **host**: The API host +- **cache** (number): The time in seconds until the cache refreshes (default is 7200 / 2 hours) +- **handle** (string) : The handle of the resource to use - **method** (string) : You can set which method to use on the request, default is 'GET' - **query** (string) : Add your queries here, see [queries](#queries) for more info - **path** (string) : Add your own custom path, see [paths](#paths) for details +- **auth** (string) : Handle for the auth scheme to use ### Saved requests -You can set up requests for placid in **site/settings/addons/placid.yaml** like so: - - dribbble: - url: 'http://api.dribbble.com/shots/everyone' - cache: 1 - refresh: 60 - - weather_api: - url: 'http://api.openweathermap.org/data/2.5/weather' - query: - q: 'London,uk' - - github: - url: 'https://api.github.com/repos/alecritson/Placid-Statamic' - access_token: OAUTH-TOKEN - headers: - Authorization: token OAUTH-TOKEN +You can set up requests for placid in **resources/placid/requests** like so: + +``` yaml + // resources/placid/requests/placeholder.yaml + host: https://jsonplaceholder.typicode.com + method: GET + path: posts/:id + auth: placeholder // See Authentication section + segments: + id: 1 + headers: + accept: application/json + query: + foo: bar + formParams: + foo: bar +``` + +#### Authentication +You can define authorisation schemes to use and reuse on your requests. You define them in **resources/placid/auth** like so: -The query array works out as `q=London,uk` in the url +``` yaml +// resources/placid/auth/placeholder.yaml +headers: + Authorization: Bearer :token +token: services.api.token +``` -**If you use `access_token` it will be appended to the url, if you use the `headers` array then it will be sent through the request headers.** +The above will send the token through the headers, you define your tokens in your config and reference as you would any Laravel config item. -### Defaults -You can specify default config values for Placid to use +If you need to send your access token through the query string, define your auth scheme like so: - placid_defaults: - refresh: 1000 - -#### Access tokens -You can define access tokens in the config so you can use them in your templates without having to re enter them for every request, they look like this in the config: +``` yaml +// resources/placid/auth/placeholder.yaml +query: + access_token: :token +token: services.api.token +``` - placid_tokens: - github: SOME-ACCESS-TOKEN +The query string and headers will be merged with any that are already present on the request ## Usage To use this plugin in your templates, simply use these tags: -### Example Code Block with manual URL +### Basic example - {{ placid:request url="http://api.dribbble.com/shots/everyone" cache="0" refresh="1200" }} - {{ shots }} - {{ title }} - {{ /shots }} - {{ /placid:request }} - -### Example code block with handle - {{ placid:request handle="dribbble" }} - {{ shots }} - {{ title }} - {{ /shots }} - {{ /placid:request }} - -*If you are unsure as to what tags to use within the placid variable pair, just pop the api url into your browser and work it out from there* - -If your API returns an array then Placid will automatically cast this to a `response` variable which you can loop over like: - - {{ placid:request handle="tester" }} - {{ response }} - {{ some.value }} - {{ /response }} - {{ /placid:request }} - -### Queries -You can add queries to the request from the template using a `key:value` pattern separated by commas (`,`), something like this: - - {{ placid:request handle="feed" query="posts:5,limit:4" }} - {{ /placid:request }} - -which will work out something like: `http://someapi.co.uk/feed?posts=5&limit=4` - -### Paths -You can change the request path without having to keep overwritting the url. - - {{ placid:request handle="stripe" path="/v1/customers/{{ id }}" }} - {{ email }} - {{ /placid:request }} - -So if you have set the url to something like `https://api.stripe.com/v1/charges` in the config, it would be replaced as `https://api.stripe.com/v1/customers/123`, for example. - -### Tokens -To reuse access tokens that are stored in your config simply add the `access_token` parameter with the name of the token you want from `placid_tokens` in the placid config file - - {{ placid:request handle="githubRepo" access_token="github" }} - {{ /placid:request }} - -### Handling no results -You can catch when there are no results just like you would in an entries loop: - - {{ placid:request url="http://www.dustysquirrels.com/noapi" }} - {{ if no_results }} - No results - {{ else }} - Squirrels! - {{ endif }} - {{ /placid:request }} - -> This is a bit buggy at the moment, still looking into it. - -## API -You can utilize Placid in your own plugins via the Statamic plugin API. - -### Request - - $request = $this->addon->api('placid')->request($url) - -This will return a `GuzzleHttp\Message\Request Object` which you can interact with, read the [Guzzle docs](http://guzzle.readthedocs.org/en/latest/http-messages.html#requests) for more info. If you need a different method to `GET` then just pass it in the second parameter. - -### Send - - $response = $this->addon->api('placid')->send($request) - -This will send the request and return a `GuzzleHttp\Message\Response Object`, again you can interact with this but if you just want to get the response content you could just do `$response->json()`, check the [Guzzle docs](http://guzzle.readthedocs.org/en/latest/http-messages.html#responses) for more info. +``` +{{ placid handle="placeholder" }} + {{ response.data }} +

{{ title }}

+ {{ /response.data }} +{{ /placid }} +``` + +### Full example + +``` +{{ placid host="https://jsonplaceholder.typicode.com" path=":part/:id" cache="60" query="foo:bar|bar:baz" segments="part:posts|id:1" headers="foo:bar" }} + {{ response.data }} +

{{ title }}

+ {{ /response.data }} +{{ /placid }} +``` ## Support,issues,feedback If you want to leave feedback about this project, feel free to get in touch on [twitter](http://www.twitter.com/alecritson) if you experience any issues please just create a new issue here on the Repo diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..f8460d0 --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "ritson/placid-statamic", + "description": "Consume RESTful APIs in your Statamic templates", + "type": "statamic-addon", + "require": { + "guzzlehttp/guzzle": "~6.0", + "statamic/cms": "3.0.*@beta" + }, + "license": "MIT", + "authors": [ + { + "name": "Alec Ritson", + "email": "hello@itsalec.co.uk" + } + ], + "minimum-stability": "dev", + "autoload": { + "psr-4": { + "Ritson\\Placid\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Ritson\\Placid\\PlacidServiceProvider" + ] + } + } +} diff --git a/src/AbstractResource.php b/src/AbstractResource.php new file mode 100644 index 0000000..f60af6c --- /dev/null +++ b/src/AbstractResource.php @@ -0,0 +1,50 @@ +{$method}($value); + } elseif (property_exists($this, $key)) { + $this->{$key} = $value; + } + return $this; + } + + /** + * Create a resource from a record + */ + public function createFromRecord(array $record) + { + foreach ($record as $key => $value) { + $this->set($key, $value); + } + return $this; + } + + protected function getPartsFromString($string) + { + $parts = explode('|', $string); + + return collect(array_map(function ($val) { + return explode(':', $val); + }, $parts))->mapWithKeys(function ($value) { + return [$value[0] => $value[1]]; + })->toArray(); + } + + public function getHeaders() + { + return $this->headers; + } + + public function getQuery() + { + return $this->query; + } +} diff --git a/src/PlacidAuthResource.php b/src/PlacidAuthResource.php new file mode 100644 index 0000000..086c8f6 --- /dev/null +++ b/src/PlacidAuthResource.php @@ -0,0 +1,35 @@ +token = config($value, null); + } + + public function getHeaders() + { + // Replace any tokens + foreach ($this->headers as $key => $value) { + $this->headers[$key] = str_replace(':token', $this->token, $value); + } + return $this->headers; + } + + public function getQuery() + { + // Replace any tokens + foreach ($this->query as $key => $value) { + $this->query[$key] = str_replace(':token', $this->token, $value); + } + return $this->query; + } +} diff --git a/src/PlacidRepository.php b/src/PlacidRepository.php new file mode 100644 index 0000000..8d28bee --- /dev/null +++ b/src/PlacidRepository.php @@ -0,0 +1,33 @@ +createFromRecord($record); + + return $resource; + } +} diff --git a/src/PlacidResource.php b/src/PlacidResource.php new file mode 100644 index 0000000..3d59d23 --- /dev/null +++ b/src/PlacidResource.php @@ -0,0 +1,172 @@ +accessToken = config($value, null); + } + + /** + * Get the client + */ + public function client() + { + return new Client([ + 'base_uri' => $this->host, + 'query' => $this->query, + 'form_params' => $this->formParams, + 'headers' => $this->headers, + ]); + } + + /** + * Prepare the path with segments + */ + protected function preparePath() + { + if (is_string($this->segments)) { + $this->segments = $this->getPartsFromString($this->segments); + } + foreach ($this->segments as $key => $value) { + $this->path = str_replace(":{$key}", $value, $this->path); + } + } + + /** + * Set the value for the query string + * @param String|Array $value + */ + protected function setQuery($value) + { + if (is_string($value)) { + $queryParts = $this->getPartsFromString($value); + } else { + $queryParts = $value; + } + + $this->query = collect($this->query)->merge($queryParts)->toArray(); + } + + + /** + * Set the value for the query string + * @param String|Array $value + */ + protected function setHeaders($value) + { + if (is_string($value)) { + $headerParts = $this->getPartsFromString($value); + } else { + $headerParts = $value; + } + + $this->headers = collect($this->headers)->merge($headerParts)->toArray(); + } + + /** + * Prepare the resource for authentication + */ + protected function prepareAuth() + { + if (!$this->auth) { + return; + } + + $authResource = (new PlacidRepository)->getByHandle($this->auth, 'auth'); + + // Are we using headers? + $authHeaders = collect($authResource->getHeaders()); + $authQuery = collect($authResource->getQuery()); + + $this->headers = collect($this->headers)->merge($authHeaders)->toArray(); + $this->query = collect($this->query)->merge($authQuery)->toArray(); + } + + /** + * Execute the resource + */ + public function execute() + { + $this->preparePath(); + + if ($this->cache && !$this->auth) { + return Cache::remember($this->getCacheKey(), $this->cache, function () { + $response = $this->client()->request($this->method, $this->path); + return (new PlacidResponse)->resolve($response); + }); + } + + + $this->prepareAuth(); + + try { + $response = $this->client()->request($this->method, $this->path); + return (new PlacidResponse)->resolve($response); + } catch (RequestException $e) { + return (new PlacidResponse)->resolve($e->getResponse() ?? $e); + } + } + + protected function getCacheKey() + { + return base64_encode( + urlencode($this->host . $this->path . implode('&', $this->query)) + ); + } +} diff --git a/src/PlacidResponse.php b/src/PlacidResponse.php new file mode 100644 index 0000000..ed6137f --- /dev/null +++ b/src/PlacidResponse.php @@ -0,0 +1,44 @@ +status = $response->getCode(); + $this->contents = $response->getMessage(); + } else { + $this->contents = json_decode($response->getBody()->getContents(), true); + $this->status = $response->getStatusCode(); + } + return $this; + } + + public function getStatus() + { + return $this->status; + } + + public function getContents() + { + return $this->contents; + } + + public function toArray() + { + return [ + 'response' => [ + 'status' => $this->status, + 'data' => $this->contents, + ] + ]; + } +} \ No newline at end of file diff --git a/src/PlacidServiceProvider.php b/src/PlacidServiceProvider.php new file mode 100644 index 0000000..a6f8a53 --- /dev/null +++ b/src/PlacidServiceProvider.php @@ -0,0 +1,13 @@ +repo = $repo; + } + + public function index() + { + return $this->request(); + } + + // protected function get + + /** + * The {{ placid:example }} tag. + * + * @return string|array + */ + public function request() + { + $handle = $this->getParam('handle', null); + + $options = [ + 'host' => $this->getParam('host', null), + 'headers' => $this->getParam('headers', null), + 'path' => $this->getParam('path', null), + 'cache' => $this->getParam('cache', null), + 'method' => $this->getParam('method', null), + 'segments' => $this->getParam('segments', null), + 'url' => $this->getParam('url', null), + 'query' => $this->getParam('query', null), + 'auth' => $this->getParam('auth', null) + ]; + + if ($handle) { + $resource = $this->repo->getByHandle($handle); + } else { + $resource = new PlacidResource; + } + + foreach ($options as $key => $value) { + if ($value) { + $resource->set($key, $value); + } + } + + $response = $resource->execute(); + + return $response->toArray(); + } +}