Skip to content

Commit

Permalink
Switch back to V1 API, as V2 seems not complete.
Browse files Browse the repository at this point in the history
Also, add local app tests, which fixes positiondev#8.
  • Loading branch information
dbp committed Nov 11, 2015
1 parent 42987e2 commit 1602d0f
Show file tree
Hide file tree
Showing 78 changed files with 8,915 additions and 12,750 deletions.
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
## Note: We use version 1 of the api (1.2.3 specifically), until they actually support everything in version 2.

For testing, you should install the `wp-cli`, which allows us to have
a development version of a wordpress server running.

Expand Down Expand Up @@ -32,24 +34,38 @@ $ wp server --port=5555
Now log in to the UI at `http://localhost:5555/wp-admin/` with user
`offset` and password `111` and go and change the Permalink settings
to anything but default. Some permalink is needed to make any of the
requests work... To test that it is working, run the following command
(requires the `jq` utility, which you can install on macs with `brew
install jq`):
requests work... Next go to the plugins section and activate both JSON
Basic Authentication and WP REST API. To test that it is working,
run the following command (requires the `jq` utility, which you can
install on macs with `brew install jq`):

```
curl http://localhost:5555/wp-json/wp/v2/ | jq
```

Which should print out a bunch of json.

Now insert the needed test posts:

Now clear the default and insert the needed test posts:

```
wp post create --post_title='A first post' --post_status=publish --post_date='2014-10-01 07:00:00' --post_content="This is the content"
wp post delete 1
wp post create --post_title='A first post' --post_status=publish --post_date='2014-10-01 07:00:00' --post_content="This is the content" --post_author=1
wp post create --post_title='A second post' --post_status=publish --post_date='2014-10-02 07:00:00' --post_content="This is the second post content" --post_author=1
wp post create --post_title='A third post' --post_status=publish --post_date='2014-10-10 07:00:00' --post_content="This is the third post content" --post_author=1
wp post create --post_title='A fourth post' --post_status=publish --post_date='2014-10-15 07:00:00' --post_content="This is the fourth post content" --post_author=1
```

Now go into the admin, and go to users, and set the first and last
name of the admin `offset` to `Ira` and `Rubel`.
name of the admin `offset` to `Ira` and `Rubel`. Change "display name
publically" to "Ira Rubel".

Finally, set up the categories and tags. You unfortunately can't do
that through the `wp` program yet. So create one category with slug,
`cat1`, and add the first post to it. Then create two tags, `tag1` and
`tag2`, and tag the first three posts with `tag1`, and the second post
with `tag2`.


**NOTE(dbp 2015-11-08):**
Expand Down
101 changes: 65 additions & 36 deletions spec/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ import Snap.Snaplet.Wordpress
import Snap.Snaplet.Wordpress.Cache.Redis
import Snap.Snaplet.Wordpress.Types

(++) :: Monoid a => a -> a -> a
(++) = mappend

----------------------------------------------------------
-- Section 1: Example application used for testing. --
----------------------------------------------------------
Expand All @@ -56,7 +53,7 @@ instance HasHeist App where
enc a = TL.toStrict . TL.decodeUtf8 . encode $ a

article2 = object [ "ID" .= (2 :: Int)
, "title" .= object ["rendered" .= ("The post" :: Text)]
, "title" .= ("The post" :: Text)
, "excerpt" .= ("summary" :: Text)
]

Expand All @@ -69,20 +66,20 @@ jacobinFields = [N "featured_image" [N "attachment_meta" [N "sizes" [N "mag-feat

localApp :: [(Text, Text)] -> SnapletInit App App
localApp tmpls =
do makeSnaplet "app" "App." Nothing $ do
h <- nestSnaplet "" heist $ heistInit ""
addConfig h $ set scTemplateLocations (return templates) mempty
r <- nestSnaplet "" redis redisDBInitConf
w <- nestSnaplet "" wordpress $ initWordpress' config h r wordpress
return $ App h r w
where mkTmpl (name, html) = let (Right doc) = X.parseHTML "" (T.encodeUtf8 html)
in ([T.encodeUtf8 name], DocumentFile doc Nothing)
templates = return $ M.fromList (map mkTmpl tmpls)
config = (def { wpConfEndpoint = "http://localhost:5555/wp-json/wp/v2"
, wpConfCacheBehavior = NoCache
, wpConfExtraFields = jacobinFields
, wpConfLogger = Just print
})
makeSnaplet "app" "App." Nothing $ do
h <- nestSnaplet "" heist $ heistInit ""
addConfig h $ set scTemplateLocations (return templates) mempty
r <- nestSnaplet "" redis redisDBInitConf
w <- nestSnaplet "" wordpress $ initWordpress' config h r wordpress
return $ App h r w
where mkTmpl (name, html) = let (Right doc) = X.parseHTML "" (T.encodeUtf8 html)
in ([T.encodeUtf8 name], DocumentFile doc Nothing)
templates = return $ M.fromList (map mkTmpl tmpls)
config = (def { wpConfEndpoint = "http://localhost:5555/wp-json"
, wpConfCacheBehavior = NoCache
, wpConfExtraFields = jacobinFields
, wpConfLogger = Nothing
})

renderingApp :: [(Text, Text)] -> Text -> SnapletInit App App
renderingApp tmpls response = makeSnaplet "app" "App." Nothing $ do
Expand Down Expand Up @@ -112,24 +109,22 @@ queryingApp tmpls record = makeSnaplet "app" "An snaplet example application." N
config = (def { wpConfEndpoint = ""
, wpConfRequester = Just $ Requester recordingRequester
, wpConfCacheBehavior = NoCache})
recordingRequester "/terms/tag" [] =
recordingRequester "/taxonomies/post_tag/terms" [] =
return $ enc $ [object [ "ID" .= (177 :: Int)
, "slug" .= ("home-featured" :: Text)
, "meta" .= object ["links" .= object ["self" .= ("/177" :: Text)]]
]
,object [ "ID" .= (160 :: Int)
, "slug" .= ("featured-global" :: Text)
, "meta" .= object ["links" .= object ["self" .= ("/160" :: Text)]]
]
]
recordingRequester "/terms/category" [] =
recordingRequester "/taxonomies/category/terms" [] =
return $ enc $ [object [ "ID" .= (159 :: Int)
, "slug" .= ("bookmarx" :: Text)
, "meta" .= object ["links" .= object ["self" .= ("/159" :: Text)]]
]
]
recordingRequester url params = do
modifyMVar_ record $ (return . (++ [mkUrlUnescape url params]))
modifyMVar_ record $ (return . (<> [mkUrlUnescape url params]))
return ""
mkUrlUnescape url params = (url <> "?" <> (T.intercalate "&" $ map (\(k, v) -> k <> "=" <> v) params))

Expand All @@ -150,7 +145,7 @@ cachingApp = makeSnaplet "app" "An snaplet example application." Nothing $ do
shouldRenderTo :: (Text, Text) -> Text -> Spec
shouldRenderTo (tags, response) match =
snap (route []) (renderingApp [("test", tags)] response) $
it (T.unpack $ tags ++ " should render to match " ++ match) $
it (T.unpack $ tags <> " should render to match " <> match) $
do t <- eval (do st <- getHeistState
builder <- (fst . fromJust) $ renderTemplate st "test"
return $ T.decodeUtf8 $ toByteString builder)
Expand All @@ -164,7 +159,7 @@ clearRedisCache = runRedisDB redis $ rdelstar "wordpress:*"

article1 :: Value
article1 = object [ "ID" .= ("1" :: Text)
, "title" .= object ["rendered" .= ("Foo bar" :: Text)]
, "title" .= ("Foo bar" :: Text)
, "excerpt" .= ("summary" :: Text)
]

Expand Down Expand Up @@ -222,12 +217,12 @@ main = hspec $ do
>>= shouldEqual Nothing
it "should find post aggregates in cache" $
do let key = PostsKey (Set.fromList [NumFilter 20, OffsetFilter 0])
eval (with wordpress $ wpCacheSet' key ("[" ++ enc article1 ++ "]"))
eval (with wordpress $ wpCacheSet' key ("[" <> enc article1 <> "]"))
eval (with wordpress $ wpCacheGet' key)
>>= shouldEqual (Just $ "[" ++ enc article1 ++ "]")
>>= shouldEqual (Just $ "[" <> enc article1 <> "]")
it "should not find post aggregates after expire handler is called" $
do let key = PostsKey (Set.fromList [NumFilter 20, OffsetFilter 0])
eval (with wordpress $ wpCacheSet' key ("[" ++ enc article1 ++ "]"))
eval (with wordpress $ wpCacheSet' key ("[" <> enc article1 <> "]"))
eval (with wordpress $ wpExpirePost' (PostByPermalinkKey "2000" "1" "the-article"))
eval (with wordpress $ wpCacheGet' key)
>>= shouldEqual Nothing
Expand Down Expand Up @@ -303,19 +298,56 @@ main = hspec $ do
describe "live tests (which require running wordpress server)" $
snap (route
[("/2014/10/a-first-post", render "single")
,("/many", render "many")
,("/many2", render "many2")
,("/many3", render "many3")
,("/page1", render "page1")
,("/page2", render "page2")
,("/num1", render "num1")
,("/num2", render "num2")
,("/num3", render "num3")
,("/tag1", render "tag1")
,("/tag2", render "tag2")
,("/tag3", render "tag3")
,("/tag4", render "tag4")
,("/tag5", render "tag5")
,("/tag6", render "tag6")
,("/tag7", render "tag7")
,("/cat1", render "cat1")
,("/cat2", render "cat2")
,("/cat3", render "cat3")
,("/fields", render "fields")
,("/2014/10/a-second-post/", render "author-date")
])
(localApp [("single", "<wp><wpPostByPermalink><wpTitle/></wpPostByPermalink></wp>")
,("author-date", "<wp><wpPostByPermalink><wpAuthor><wpName/></wpAuthor><wpDate><wpYear/>/<wpMonth/></wpDate></wpPostByPermalink></wp>")
,("fields", "<wp><wpPosts limit=1 categories=\"-bookmarx\"><wpFeaturedImage><wpAttachmentMeta><wpSizes><wpThumbnail><wpUrl/></wpThumbnail></wpSizes></wpAttachmentMeta></wpFeaturedImage></wpPosts></wp>")
,("extra-fields", "<wp><wpPosts limit=1 categories=\"-bookmarx\"><wpFeaturedImage><wpAttachmentMeta><wpSizes><wpMagFeatured><wpUrl/></wpMagFeatured></wpSizes></wpAttachmentMeta></wpFeaturedImage></wpPosts></wp>")
]
,("many", "<wpPosts limit=2><wpTitle/></wpPosts>")
,("many1", "<wpPosts><wpTitle/></wpPosts>")
,("many2", "<wpPosts offset=1 limit=1><wpTitle/></wpPosts>")
,("many3", "<wpPosts offset=0 limit=1><wpTitle/></wpPosts>")
,("page1", "<wpPosts limit=10 page=1><wpTitle/></wpPosts>")
,("page2", "<wpPosts limit=10 page=2><wpTitle/></wpPosts>")
,("num1", "<wpPosts num=2><wpTitle/></wpPosts>")
,("num2", "<wpPosts num=2 page=2 limit=1><wpTitle/></wpPosts>")
,("num3", "<wpPosts num=1 page=3><wpTitle/></wpPosts>")
,("tag1", "<wpPosts tags=\"tag1\" limit=10><wpTitle/></wpPosts>")
,("tag2", "<wpPosts limit=10><wpTitle/></wpPosts>")
,("tag3", "<wpPosts tags=\"+tag1\" limit=10><wpTitle/></wpPosts>")
,("tag4", "<wpPosts tags=\"-tag1\" limit=1><wpTitle/></wpPosts>")
,("tag5", "<wpPosts tags=\"+tag1\" limit=1><wpTitle/></wpPosts>")
,("tag6", "<wpPosts tags=\"+tag1,-tag2\" limit=1><wpTitle/></wpPosts>")
,("tag7", "<wpPosts tags=\"+tag1,+tag2\" limit=1><wpTitle/></wpPosts>")
,("cat1", "<wpPosts categories=\"cat1\" limit=10><wpTitle/></wpPosts>")
,("cat2", "<wpPosts limit=10><wpTitle/></wpPosts>")
,("cat3", "<wpPosts categories=\"-cat1\" limit=10><wpTitle/></wpPosts>")
,("author-date", "<wp><wpPostByPermalink><wpAuthor><wpName/></wpAuthor><wpDate><wpYear/>/<wpMonth/></wpDate></wpPostByPermalink></wp>")
,("fields", "<wp><wpPosts limit=1 categories=\"-cat1\"><wpFeaturedImage><wpAttachmentMeta><wpSizes><wpThumbnail><wpUrl/></wpThumbnail></wpSizes></wpAttachmentMeta></wpFeaturedImage></wpPosts></wp>")
]
) $
do it "should have title on page" $
do r <- get "/2014/10/a-first-post"
liftIO $ print r
shouldHaveText "A first post" r
it "should not have most recent post's title" $
it "should be able to limit" $
do p1 <- get "/many"
get "/many1" >>= shouldNotEqual p1
it "should be able to offset" $
Expand Down Expand Up @@ -355,9 +387,6 @@ main = hspec $ do
do c1 <- get "/cat1"
c2 <- get "/cat3"
c1 `shouldNotEqual` c2
it "should be able to use extra fields set in application" $
do get "/fields" >>= shouldHaveText "https://"
get "/extra-fields" >>= shouldHaveText "https://"


shouldQueryTo :: Text -> [Text] -> Spec
Expand Down
2 changes: 1 addition & 1 deletion src/Snap/Snaplet/Wordpress/Field.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ instance (Functor m, Monad m) => Show (Field m) where

postFields :: (Functor m, Monad m) => [Field m]
postFields = [F "ID"
,C "title" ["title", "rendered"]
,F "title"
,F "status"
,F "type"
,N "author" [F "ID",F "name",F "first_name",F "last_name",F "description"]
Expand Down
2 changes: 1 addition & 1 deletion src/Snap/Snaplet/Wordpress/Init.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import Snap.Snaplet.Wordpress.Splices
import Snap.Snaplet.Wordpress.Types

instance Default (WordpressConfig m) where
def = WordpressConfig "http://127.0.0.1:8080/wp-json/wp/v2" Nothing (CacheSeconds 600) [] Nothing
def = WordpressConfig "http://127.0.0.1:8080/wp-json" Nothing (CacheSeconds 600) [] Nothing

initWordpress :: Snaplet (Heist b)
-> Snaplet RedisDB
Expand Down
2 changes: 1 addition & 1 deletion src/Snap/Snaplet/Wordpress/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Snap.Snaplet.Wordpress.Utils
wpRequestInt :: Requester -> Text -> WPKey -> IO Text
wpRequestInt runHTTP endpt key =
case key of
TaxDictKey resName -> req ("/terms/" <> resName) []
TaxDictKey resName -> req ("/taxonomies/" <> resName <> "/terms") []
PostByPermalinkKey year month slug ->
req "/posts" [("filter[year]", year)
,("filter[monthnum]", month)
Expand Down
4 changes: 2 additions & 2 deletions src/Snap/Snaplet/Wordpress/Splices.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ wpPostsSplice wp extraFields wpLens =
outputChildren <- manyWithSplices runChildren (postSplices extraFields)
(getPromise promise)
postsQuery <- parseQueryNode <$> getParamNode
tagDict <- lift $ lookupTaxDict (TaxDictKey "tag") wp
tagDict <- lift $ lookupTaxDict (TaxDictKey "post_tag") wp
catDict <- lift $ lookupTaxDict (TaxDictKey "category") wp
let wpKey = mkWPKey tagDict catDict postsQuery
return $ yieldRuntime $
Expand Down Expand Up @@ -156,7 +156,7 @@ wpPrefetch :: Wordpress b
wpPrefetch wp =
do n <- getParamNode
childrenRes <- runChildren
tagDict <- lift $ lookupTaxDict (TaxDictKey "tag") wp
tagDict <- lift $ lookupTaxDict (TaxDictKey "post_tag") wp
catDict <- lift $ lookupTaxDict (TaxDictKey "category") wp
let wpKeys = findPrefetchables tagDict catDict n
return $ yieldRuntime $
Expand Down
27 changes: 6 additions & 21 deletions src/Snap/Snaplet/Wordpress/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,13 @@ instance Show (TaxSpec a) where
show (TaxPlus t) = "+" ++ (T.unpack t)
show (TaxMinus t) = "-" ++ (T.unpack t)

newtype TaxRes a = TaxRes (Int, Text)
newtype TaxRes a = TaxRes (Int, Text) deriving (Show)

instance FromJSON (TaxRes a) where
parseJSON (Object o) = do meta <- o .: "meta"
links <- meta .: "links"
url <- links .: "self"
slug <- o .: "slug"
let id = last $ T.splitOn "/" url
case readSafe id of
Nothing -> fail "FromJSON TaxRes: Could not get ID from self link"
Just i -> return $ TaxRes (i, slug)
-- NOTE(dbp 2014-12-16):
-- See https://github.com/WP-API/WP-API/issues/726
-- The ID is currently wrong. So use the self-link
-- instead (which should always be correct. Correcting
-- the ID by 1, on the other hand, would break when upstream
-- is fixed).
-- But in theory, the following would be all that's needed.
-- TaxRes <$> ((,) <$> o .: "ID" <*> o .: "slug")
parseJSON (Object o) = TaxRes <$> ((,) <$> o .: "ID" <*> o .: "slug")
parseJSON _ = mzero

data TaxDict a = TaxDict {dict :: [TaxRes a], desc :: Text}
data TaxDict a = TaxDict {dict :: [TaxRes a], desc :: Text} deriving (Show)

type Year = Text
type Month = Text
Expand All @@ -114,7 +99,7 @@ data WPKey = PostKey Int
deriving (Eq, Show, Ord)

tagChars :: String
tagChars = ['a'..'z'] ++ "-"
tagChars = ['a'..'z'] ++ "-" ++ digitChars

digitChars :: String
digitChars = ['0'..'9']
Expand All @@ -137,7 +122,7 @@ instance Show (TaxSpecList a) where
show (TaxSpecList ts) = intercalate "," (map show ts)

instance Read (TaxSpecList a) where
readsPrec _ ts = let vs = map (readSafe) $ T.splitOn "," $ T.pack ts in
readsPrec _ ts = let vs = map readSafe $ T.splitOn "," $ T.pack ts in
if all isJust vs
then [(TaxSpecList $ catMaybes vs, "")]
else []
Expand All @@ -147,4 +132,4 @@ data WPQuery = WPPostsQuery{ qlimit :: Int
, qoffset :: Int
, qpage :: Int
, qtags :: TaxSpecList TagType
, qcats :: TaxSpecList CatType}
, qcats :: TaxSpecList CatType} deriving (Show)
34 changes: 34 additions & 0 deletions wp/wp-content/plugins/Basic-Auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Basic Authentication handler
This plugin adds Basic Authentication to a WordPress site.

Note that this plugin requires sending your username and password with every
request, and should only be used for development and testing. We strongly
recommend using the [OAuth 1.0a][oauth] authentication handler for production.

## Installing
1. Download the plugin into your plugins directory
2. Enable in the WordPress admin

## Using
This plugin adds support for Basic Authentication, as specified in [RFC2617][].
Most HTTP clients will allow you to use this authentication natively. Some
examples are listed below.

### cURL

```sh
curl --user admin:password http://example.com/wp-json/
```

### WP_Http

```php
$args = array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( $username . ':' . $password ),
),
);
```

[oauth]: https://github.com/WP-API/OAuth1
[RFC2617]: https://tools.ietf.org/html/rfc2617
Loading

0 comments on commit 1602d0f

Please sign in to comment.