Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Grant Winney committed Feb 19, 2018
2 parents 072a91f + ddd9f18 commit d3c2cd8
Showing 1 changed file with 41 additions and 13 deletions.
54 changes: 41 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@

This is a [wrapper](https://grantwinney.com/what-is-an-api-wrapper-and-how-do-i-write-one/) around the [Ghost API](https://api.ghost.org), a RESTful JSON API built into the core of the [Ghost blogging platform](https://ghost.org/).

I like Ghost, and it's been awhile since I really flexed my C# muscles, so I decided to do this as an exercise. I don't even have a use for it yet, so if you do please [let me know](https://twitter.com/GrantWinney)! Check out the [official docs](https://api.ghost.org/docs) and read about [my own experience](https://grantwinney.com/what-is-the-ghost-api/) too.

***(For now, this wrapper only implements the `GET` method on the various API endpoints.)***
I like Ghost, C# and [experimenting with APIs](https://grantwinney.com/tag/api/), so this seemed like the perfect intellectual pursuit right now. I don't even have a use for it yet, but if you find one please [let me know](https://twitter.com/GrantWinney)! Check out the [official Ghost API docs](https://api.ghost.org/docs) and read about [my own experience using them](https://grantwinney.com/what-is-the-ghost-api/).

## Usage

### Accessing Public API
### Accessing the Public API

If you only need to access the public API (assuming it's enabled on your site), all you need is the URL of your site and the client id and secret.

If you only need to access the public API (assuming it's enabled on the site), all you need is the URL of the site and the client id and secret.
If you don't have the client id or secret, view the source code of any post, and look for a block similar to this:

```html
<script type="text/javascript">
ghost.init({
clientId: "ghost-frontend",
clientSecret: "53f1f0e99723"
});
</script>
```

Once you have those pieces of information, you can access anything "public", which is the GET action on the various endpoints.

```csharp
var url = "https://your-site.com"
Expand All @@ -31,11 +42,13 @@ var posts = postResponse.Posts;
var post = auth.GetPostBySlug(PostSlug);
```

### Accessing Private API (need a new auth token)
### Accessing the Private API (need a new auth token)

If the public API is disabled in the site settings, or you need to create or delete data, you'll need an authorization (aka authentication) token.

If the public API is disabled in the site settings, or you need to create or delete data, you'll need an authorization token.
If you don't have an auth token, you'll need your username and password, as well as your client id and secret (see previous section on where to find those). There's no proper OAuth implementation yet that allows you to request an auth token by logging into Ghost.

You can get an authorization token by supplying a username and password in the constructor, and it'll be used in subsequent requests.
Once you have those pieces of information, you can supply them to the constructor, and it'll be used in all subsequent requests on that `GhostAPI` instance.

```csharp
var url = "https://your-site.com"
Expand All @@ -44,14 +57,17 @@ var clientSecret = "1234abcd6789";
var username = "[email protected]";
var password = "some-password";

// internally, this gets an auth token and saves it
var auth = new GhostAPI(url, clientId, clientSecret, username, password);

var post = auth.GetPostBySlug(PostSlug);
```

### Accessing Private API (use an existing token)
You can get the generated auth token with `auth.AuthorizationToken`.

### Accessing the Private API (use an existing token)

If you already have an auth token, you can just supply that:
If you already have an auth token, you can just supply that directly:

```csharp
var url = "https://your-site.com"
Expand All @@ -64,13 +80,17 @@ var post = auth.GetPostBySlug(PostSlug);

## Versioning

This wrapper is written around v1.14.0 of the API, currently the latest version. If they update the API in the future, I'll most likely create a tag for the previous release, before updating to the newest one.
This wrapper is written around v1.14.0 of the API, currently the latest version. When they update the API in the future (and it's likely they will), I'll probably create a tag for the current release, before updating for the newest one.

## Running the Tests

The tests are setup to run against an actual instance of the Ghost blog, with its public API enabled.

The "GhostSharpTests" class, which contains the tests, is a partial class. The other part of the class is in the "GhostSharpTestsSetup.cs" file. Find the empty `const` fields and fill in the missing data, and then the tests should run okay.
**I made every effort for the tests to not get in the way of real posts, tags or users, and to clean up after themselves.** I ran them about 50 times against my own blog, which is very much production for me. It creates draft posts and deletes them, tags no one will notice and deletes them, etc. There's not too much we can do with users, so I perform a few GET requests to make sure that part works. Still, caveat emptor.

The "GhostSharpTests" class, which contains the tests, is a partial class. The other part of the class is in the "TestBase.cs" file. Find the empty `const` fields and fill in the missing data, and then the tests should run okay.

_Note: Sometimes if you run the whole suite, one or two tests will fail. Run the failed ones individually and they'll pass. I think this has to do with my using the same id for tests, and something isn't completely removed from the system before the next test tries to create it again._

## Implementation Details

Expand All @@ -80,7 +100,7 @@ By default, when requesting a post with the Ghost API, you get the author id as

That caused a problem for me, since [RestSharp](http://restsharp.org/) needs a matching class to deserialize the response to, and you can't have a single class with an "Author" property that is both an `int` *and* an `Author` class.

To get around this shortcoming, I automatically include full author information. This could result in more data being sent back then necessary - especially if, for instance, you make a request for 300 posts at once and you're the only author on your blog. It's the best I could come up with for now though.
To get around this shortcoming, I created a couple classes under the covers, to temporarily store the data in. By the time the post is returned to you, however, it's wrangled into a consistent format. The `Author` property is `null` if you didn't get full author info; otherwise it's populated. The `AuthorId` field stores the author ID, in either case.

### Ghost Query Language (GQL)

Expand All @@ -102,6 +122,14 @@ GQL might be a little buggy, or else their API doesn't support it completely, as

Just be aware that if you set a value for any of the `xxxxQueryParams.Filter` properties, you may not get the results that you expect.

### Limitations on Creating (POSTing) Posts

For some reason, I have not yet been able to get much more than the post title to create correctly in Ghost, even though running the same query via Postman works okay. I think it has something to do with RestSharp, and the object I'm passing to it, which it then transforms and sends to Ghost. Haven't tracked it down yet.

### Other Issues / Nice-to-haves

I've opened up a few issues with nice-to-haves and whatnots, which anyone can tackle. I may get to them eventually - who knows... probably moreso if anyone says they'd find them helpful.

## Problems?

[Open an issue](https://github.com/grantwinney/GhostSharp/issues/new), and include errors, unexpected behavior, steps to reproduce, etc. The more details, the better!
Expand Down

0 comments on commit d3c2cd8

Please sign in to comment.