wordpress_client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.hound.yml +2 -0
  4. data/.rubocop.yml +1065 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +11 -0
  7. data/Guardfile +29 -0
  8. data/LICENSE +21 -0
  9. data/README.md +63 -0
  10. data/Rakefile +1 -0
  11. data/circle.yml +3 -0
  12. data/lib/wordpress_client.rb +25 -0
  13. data/lib/wordpress_client/category.rb +4 -0
  14. data/lib/wordpress_client/client.rb +142 -0
  15. data/lib/wordpress_client/connection.rb +186 -0
  16. data/lib/wordpress_client/errors.rb +10 -0
  17. data/lib/wordpress_client/media.rb +37 -0
  18. data/lib/wordpress_client/media_parser.rb +48 -0
  19. data/lib/wordpress_client/paginated_collection.rb +53 -0
  20. data/lib/wordpress_client/post.rb +62 -0
  21. data/lib/wordpress_client/post_parser.rb +113 -0
  22. data/lib/wordpress_client/replace_metadata.rb +81 -0
  23. data/lib/wordpress_client/replace_terms.rb +62 -0
  24. data/lib/wordpress_client/rest_parser.rb +17 -0
  25. data/lib/wordpress_client/tag.rb +4 -0
  26. data/lib/wordpress_client/term.rb +34 -0
  27. data/lib/wordpress_client/version.rb +3 -0
  28. data/spec/category_spec.rb +8 -0
  29. data/spec/client_spec.rb +411 -0
  30. data/spec/connection_spec.rb +270 -0
  31. data/spec/docker/Dockerfile +40 -0
  32. data/spec/docker/README.md +37 -0
  33. data/spec/docker/dbdump.sql.gz +0 -0
  34. data/spec/docker/htaccess +10 -0
  35. data/spec/docker/restore-dbdump.sh +13 -0
  36. data/spec/fixtures/category.json +1 -0
  37. data/spec/fixtures/image-media.json +1 -0
  38. data/spec/fixtures/invalid-post-id.json +1 -0
  39. data/spec/fixtures/post-with-forbidden-metadata.json +1 -0
  40. data/spec/fixtures/post-with-metadata.json +1 -0
  41. data/spec/fixtures/simple-post.json +1 -0
  42. data/spec/fixtures/tag.json +1 -0
  43. data/spec/fixtures/thoughtful.jpg +0 -0
  44. data/spec/fixtures/validation-error.json +1 -0
  45. data/spec/integration/attachments_crud_spec.rb +51 -0
  46. data/spec/integration/categories_spec.rb +60 -0
  47. data/spec/integration/category_assignment_spec.rb +29 -0
  48. data/spec/integration/posts_crud_spec.rb +118 -0
  49. data/spec/integration/posts_finding_spec.rb +86 -0
  50. data/spec/integration/posts_metadata_spec.rb +27 -0
  51. data/spec/integration/posts_with_attachments_spec.rb +21 -0
  52. data/spec/integration/tag_assignment_spec.rb +29 -0
  53. data/spec/integration/tags_spec.rb +36 -0
  54. data/spec/media_spec.rb +63 -0
  55. data/spec/paginated_collection_spec.rb +64 -0
  56. data/spec/post_spec.rb +114 -0
  57. data/spec/replace_metadata_spec.rb +56 -0
  58. data/spec/replace_terms_spec.rb +51 -0
  59. data/spec/shared_examples/term_examples.rb +37 -0
  60. data/spec/spec_helper.rb +28 -0
  61. data/spec/support/docker_runner.rb +49 -0
  62. data/spec/support/fixtures.rb +19 -0
  63. data/spec/support/integration_macros.rb +10 -0
  64. data/spec/support/wordpress_server.rb +103 -0
  65. data/spec/tag_spec.rb +8 -0
  66. data/wordpress_client.gemspec +27 -0
  67. metadata +219 -0
@@ -0,0 +1,270 @@
1
+ require "spec_helper"
2
+
3
+ module WordpressClient
4
+ describe Connection do
5
+ subject(:connection) {
6
+ Connection.new(url: "http://example.com/", username: "jane", password: "doe")
7
+ }
8
+
9
+ let(:base_url) { "http://jane:doe@example.com/wp/v2" }
10
+ let(:model) { class_double(Post, parse: model_instance) }
11
+ let(:model_instance) { instance_double(Post) }
12
+
13
+ it "is constructed with a url, username and a password" do
14
+ connection = Connection.new(url: "http://example.com/wp-json", username: "x", password: "x")
15
+ expect(connection.url).to eq('http://example.com/wp-json')
16
+ end
17
+
18
+ it "does not show password when inspected" do
19
+ connection = Connection.new(url: "/", username: "x", password: "secret")
20
+ expect(connection.to_s).to_not include "secret"
21
+ expect(connection.inspect).to_not include "secret"
22
+ end
23
+
24
+ describe "get" do
25
+ it "can get a single model" do
26
+ stub_get("#{base_url}/foo?some_param=12", returns: {title: "Foo"})
27
+ expect(model).to receive(:parse).with("title" => "Foo").and_return(model_instance)
28
+ expect(connection.get(model, "foo", some_param: 12)).to eq model_instance
29
+ end
30
+
31
+ it "can get several models" do
32
+ stub_get("#{base_url}/foos?some_param=12&page=1&per_page=1", returns: ["result"], total: 1)
33
+
34
+ expect(model).to receive(:parse).with("result").and_return(model_instance)
35
+
36
+ expect(
37
+ connection.get_multiple(model, "foos", some_param: 12, page: 1, per_page: 1).to_a
38
+ ).to eq [model_instance]
39
+ end
40
+
41
+ it "paginates the result when fetching multiple models" do
42
+ stub_get("#{base_url}/foos?page=4&per_page=15", returns: ["result"], total: 62)
43
+ collection = connection.get_multiple(model, "foos", page: 4, per_page: 15)
44
+
45
+ expect(collection).to be_instance_of(PaginatedCollection)
46
+ expect(collection.total).to eq 62
47
+ expect(collection.current_page).to eq 4
48
+ expect(collection.per_page).to eq 15
49
+ end
50
+
51
+ it "raises WordpressClient::TimeoutError when requests time out" do
52
+ stub_request(:get, /./).to_timeout
53
+ expect { connection.get(model, "foo") }.to raise_error(TimeoutError)
54
+ expect { connection.get_multiple(model, "foo") }.to raise_error(TimeoutError)
55
+ end
56
+
57
+ it "raises ServerError if response is invalid JSON" do
58
+ stub_get(/./, body: "[")
59
+ expect { connection.get(model, "foo") }.to raise_error(ServerError, /parse/)
60
+ expect { connection.get_multiple(model, "foo") }.to raise_error(ServerError, /parse/)
61
+ end
62
+
63
+ it "raises ServerError if response is not application/json" do
64
+ stub_get(/./, content_type: "text/plain")
65
+ expect { connection.get(model, "foo") }.to raise_error(ServerError, /plain/)
66
+ expect { connection.get_multiple(model, "foo") }.to raise_error(ServerError, /plain/)
67
+ end
68
+
69
+ it "raises NotFoundError if response is 404" do
70
+ stub_get(/./, status: 404)
71
+ expect { connection.get(model, "foo") }.to raise_error(NotFoundError)
72
+ expect { connection.get_multiple(model, "foo") }.to raise_error(NotFoundError)
73
+ end
74
+ end
75
+
76
+ describe "creating" do
77
+ it "can create models" do
78
+ stub_successful_post_with_redirect(
79
+ "#{base_url}/foos", {title: "Bar"}, redirects_to: "#{base_url}/foos/1"
80
+ )
81
+ stub_get("#{base_url}/foos/1", returns: {id: 1, title: "Bar"})
82
+
83
+ expect(model).to receive(:parse).with(
84
+ "id" => 1, "title" => "Bar"
85
+ ).and_return(model_instance)
86
+
87
+ response = connection.create(model, "foos", title: "Bar")
88
+ expect(response).to eq model_instance
89
+ end
90
+
91
+ it "can ignore the response" do
92
+ stub_successful_post_with_redirect(
93
+ "#{base_url}/foos", {title: "Bar"}, redirects_to: "#{base_url}/foos/1"
94
+ )
95
+
96
+ response = connection.create_without_response("foos", title: "Bar")
97
+ expect(response).to be true
98
+ end
99
+
100
+ it "can pass extra parameters when following redirect" do
101
+ stub_successful_post_with_redirect(
102
+ "#{base_url}/foos", {title: "Bar"}, redirects_to: "#{base_url}/foos/1"
103
+ )
104
+ redirect = stub_get("#{base_url}/foos/1?extra=param", returns: {})
105
+
106
+ connection.create(model, "foos", {title: "Bar"}, redirect_params: {extra: "param"})
107
+ expect(redirect).to have_been_made
108
+ end
109
+
110
+ it "raises NotFoundError if response is 400 with rest_post_invalid_id as error code" do
111
+ stub_failing_post(/./, returns: json_fixture("invalid-post-id.json"), status: 400)
112
+
113
+ expect { connection.create(model, "foo", {}) }.to raise_error(NotFoundError, /post id/i)
114
+ expect { connection.create_without_response("foo", {}) }.to raise_error(NotFoundError)
115
+ end
116
+
117
+ it "raises ValidationError on any other 400 responses" do
118
+ stub_failing_post(/./, returns: json_fixture("validation-error.json"), status: 400)
119
+
120
+ expect {
121
+ connection.create(model, "foo", {})
122
+ }.to raise_error(ValidationError, /status is not one of/)
123
+
124
+ expect {
125
+ connection.create_without_response("foo", {})
126
+ }.to raise_error(ValidationError, /status is not one of/)
127
+ end
128
+ end
129
+
130
+ describe "patching" do
131
+ it "can patch models" do
132
+ stub_patch("#{base_url}/foos/1", {title: "Bar"}, returns: {id: 1, title: "Bar"})
133
+
134
+ expect(model).to receive(:parse).with(
135
+ "id" => 1, "title" => "Bar"
136
+ ).and_return(model_instance)
137
+
138
+ response = connection.patch(model, "foos/1", title: "Bar")
139
+ expect(response).to eq model_instance
140
+ end
141
+
142
+ it "can ignore responses" do
143
+ stub_patch("#{base_url}/foos/1", {title: "Bar"}, returns: {id: 1, title: "Bar"})
144
+ response = connection.patch_without_response("foos/1", title: "Bar")
145
+ expect(response).to be true
146
+ end
147
+
148
+ it "raises NotFoundError if response is 400 with rest_post_invalid_id as error code" do
149
+ stub_patch(/./, {}, returns: json_fixture("invalid-post-id.json"), status: 400)
150
+ expect { connection.patch(model, "foo", {}) }.to raise_error(NotFoundError, /post id/i)
151
+ expect { connection.patch_without_response("foo", {}) }.to raise_error(NotFoundError)
152
+ end
153
+
154
+ it "raises ValidationError on any other 400 responses" do
155
+ stub_patch(/./, {}, returns: json_fixture("validation-error.json"), status: 400)
156
+
157
+ expect {
158
+ connection.patch(model, "foo", {})
159
+ }.to raise_error(ValidationError, /status is not one of/)
160
+
161
+ expect {
162
+ connection.patch_without_response("foo", {})
163
+ }.to raise_error(ValidationError, /status is not one of/)
164
+ end
165
+ end
166
+
167
+ describe "deleting" do
168
+ it "can delete paths" do
169
+ encoding = "".encoding
170
+
171
+ stub_request(:delete, "#{base_url}/foos/1").with(
172
+ headers: {"content-type" => "application/json; charset=#{encoding}"},
173
+ body: {"force" => true}.to_json,
174
+ ).to_return(
175
+ headers: {"content-type" => "application/json; charset=utf-8"},
176
+ body: {status: "Heyhoo! Nice one!"}.to_json,
177
+ )
178
+
179
+ response = connection.delete("foos/1", force: true)
180
+ expect(response).to be true
181
+ end
182
+
183
+ it "raises ValidationError on 400 responses" do
184
+ stub_request(:delete, /./).to_return(
185
+ status: 400,
186
+ body: "{}",
187
+ headers: {"content-type" => "application/json; charset=utf-8"},
188
+ )
189
+
190
+ expect {
191
+ connection.delete("foo", {})
192
+ }.to raise_error(ValidationError)
193
+ end
194
+ end
195
+
196
+ describe "uploading" do
197
+ it "posts the given IO and returns the resulting model" do
198
+ stub_request(:post, "#{base_url}/files").with(
199
+ headers: {
200
+ "content-length" => "11",
201
+ "content-type" => "text/plain",
202
+ # WP API does not parse normal Content-Disposition and instead ops
203
+ # to using their own format:
204
+ # https://github.com/WP-API/WP-API/issues/1744
205
+ "content-disposition" => 'filename=foo.txt',
206
+ },
207
+ body: "hello world",
208
+ ).to_return(
209
+ status: 201, # Created
210
+ headers: {"Location" => "#{base_url}/files/67"}
211
+ )
212
+ stub_get("#{base_url}/files/67", returns: {id: 67})
213
+
214
+ model_instance = double("some model instance")
215
+ model = double("some model")
216
+
217
+ expect(model).to receive(:parse).with("id" => 67).and_return model_instance
218
+
219
+ result = connection.upload(
220
+ model, "files", StringIO.new("hello world"), mime_type: "text/plain", filename: "foo.txt"
221
+ )
222
+
223
+ expect(result).to eq model_instance
224
+ end
225
+ end
226
+
227
+ def stub_get(
228
+ path,
229
+ returns: {},
230
+ status: 200,
231
+ body: returns.to_json,
232
+ total: nil,
233
+ content_type: "application/json"
234
+ )
235
+ headers = {"content-type" => "#{content_type}; charset=utf-8"}
236
+ headers["X-WP-Total"] = total.to_s if total
237
+
238
+ stub_request(:get, path).to_return(status: status, body: body, headers: headers)
239
+ end
240
+
241
+ def stub_successful_post_with_redirect(path, data, redirects_to:)
242
+ stub_request(:post, path).with(
243
+ headers: {"content-type" => "application/json; charset=#{"".encoding}"},
244
+ body: data.to_json,
245
+ ).to_return(
246
+ status: 201, # Created
247
+ headers: {"Location" => redirects_to}
248
+ )
249
+ end
250
+
251
+ def stub_failing_post(path, returns:, status:)
252
+ stub_request(:post, path).to_return(
253
+ status: status,
254
+ body: returns.to_json,
255
+ headers: {"content-type" => "application/json; charset=utf-8"},
256
+ )
257
+ end
258
+
259
+ def stub_patch(path, data, returns:, status: 200)
260
+ stub_request(:patch, path).with(
261
+ headers: {"content-type" => "application/json; charset=#{"".encoding}"},
262
+ body: data.to_json,
263
+ ).to_return(
264
+ status: status,
265
+ headers: {"content-type" => "application/json; charset=utf-8"},
266
+ body: returns.to_json,
267
+ )
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,40 @@
1
+ FROM appcontainers/wordpress
2
+
3
+ # base image only contains American timezones
4
+ RUN yum install -y tzdata
5
+
6
+ ENV APP_NAME testapp.com
7
+ ENV APACHE_SVRALIAS www.testapp.com localhost
8
+ ENV MYSQL_SERVER localhost
9
+ ENV MYSQL_CLIENT localhost
10
+ ENV MYSQL_PASS P@ssw0rd
11
+ ENV MYSQL_DB testapp
12
+ ENV APP_USER admin
13
+ ENV APP_PASS P@ssw0rd
14
+ ENV WP_KEY ILoveFlappyjacks
15
+
16
+ # Restore a copy of a set up database on first boot
17
+ COPY dbdump.sql.gz /tmp/dbdump.sql.gz
18
+ COPY restore-dbdump.sh /tmp/.restore-dbdump.sh
19
+ RUN chmod +x /tmp/.restore-dbdump.sh && \
20
+ echo "if [ -f /tmp/.restore-dbdump.sh ]; then /tmp/.restore-dbdump.sh; rm -fr /tmp/.restore-dbdump.sh; fi" >> /root/.bashrc
21
+
22
+ ### Install API and API Basic Auth plugins
23
+
24
+ # We need tar to unpack the WP plugins
25
+ RUN yum install -y tar
26
+
27
+ # WP API
28
+ # Get download link from https://wordpress.org/plugins/rest-api/
29
+ RUN curl -SL -o /tmp/rest-api.zip https://downloads.wordpress.org/plugin/rest-api.2.0-beta8.zip \
30
+ && unzip /tmp/rest-api.zip -d /var/www/html/wordpress/wp-content/plugins/ \
31
+ && rm /tmp/rest-api.zip
32
+
33
+ # Basic Auth
34
+ # Get download link from GitHub or something.
35
+ RUN curl -SL https://github.com/WP-API/Basic-Auth/archive/master.tar.gz \
36
+ | tar -xzC /var/www/html/wordpress/wp-content/plugins/ \
37
+ && mv /var/www/html/wordpress/wp-content/plugins/Basic-Auth* /var/www/html/wordpress/wp-content/plugins/basic-auth
38
+
39
+ # Make pretty permalinks work so API is accessible
40
+ COPY htaccess /var/www/html/wordpress/.htaccess
@@ -0,0 +1,37 @@
1
+ # How this was built
2
+
3
+ ## dbdump
4
+
5
+ If you need to regenerate the dbdump, remove the part where the file is copied and restored from the `Dockerfile`, then build and start the image.
6
+
7
+ ```bash
8
+ docker build -t wpclient-test .
9
+ docker run -it -p 8181:80 wpclient-test
10
+ ```
11
+
12
+ You'll get a prompt inside the container. Open your browser and go to the newly booted application (`localhost:8181` if you have native Docker; otherwise go to your `$DOCKER_HOST_IP:8181` URL – see `echo $DOCKER_HOST` in your local shell).
13
+
14
+ You'll be greeted by the Wordpress installer. Fill in everything and complete the installation.
15
+
16
+ Then, **in your container's terminal**, run the following command:
17
+
18
+ ```bash
19
+ mysqldump -u "$MYSQL_USER" --password="$MYSQL_PASS" --host="$MYSQL_HOST" "$MYSQL_DB" | gzip -9 > /tmp/dbdump.sql.gz
20
+ ```
21
+
22
+ You can then copy the file to your host using the `docker cp` command:
23
+
24
+ ```bash
25
+ docker ps | grep wpclient-test
26
+ # See the container ID or name of your running container
27
+ docker cp THE-CONTAINER-ID:/tmp/dbdump.sql.gz .
28
+ ```
29
+
30
+ You can then shut down your container by logging out of the container terminal.
31
+
32
+ Restore the DB loading code from the `Dockerfile`, commit this file to the repo and rebuild everything and verify that Wordpress is still set up correctly when the dump is restored.
33
+
34
+ `spec/docker/restore-dbdump.sh` hard codes an expectation that the IP in your
35
+ datadump is a specfic IP. So you will need to change that hard coded
36
+ expectation to match the new IP used in your data dump. You can find out what
37
+ that IP is by examining the dumo but it is probably the value set in `$DOCKER_HOST`.
Binary file
@@ -0,0 +1,10 @@
1
+ # BEGIN WordPress
2
+ <IfModule mod_rewrite.c>
3
+ RewriteEngine On
4
+ RewriteBase /
5
+ RewriteRule ^index\.php$ - [L]
6
+ RewriteCond %{REQUEST_FILENAME} !-f
7
+ RewriteCond %{REQUEST_FILENAME} !-d
8
+ RewriteRule . /index.php [L]
9
+ </IfModule>
10
+ # END WordPress
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ service mysqld start
3
+
4
+ gunzip -c /tmp/dbdump.sql.gz | \
5
+ sed "s/192\.168\.99\.100:8181/${WORDPRESS_HOST:-localhost:8181}/g" | \
6
+ mysql \
7
+ --user="$MYSQL_USER" \
8
+ --password="$MYSQL_PASS" \
9
+ --host="$MYSQL_SERVER" \
10
+ --batch \
11
+ "$MYSQL_DB"
12
+
13
+ rm /tmp/dbdump.sql.gz
@@ -0,0 +1 @@
1
+ {"id":1,"count":1,"description":"","link":"http:\/\/192.168.99.100:8080\/category\/uncategorized\/","name":"Uncategorized","slug":"uncategorized","taxonomy":"category","parent":0,"_links":{"self":[{"href":"http:\/\/192.168.99.100:8080\/wp-json\/wp\/v2\/terms\/category\/1"}],"collection":[{"href":"http:\/\/192.168.99.100:8080\/wp-json\/wp\/v2\/terms\/category"}]}}
@@ -0,0 +1 @@
1
+ {"id":5,"date":"2015-11-17T14:51:26","guid":{"rendered":"http:\/\/example.com\/wp-content\/uploads\/2015\/11\/thoughtful.jpg"},"modified":"2015-11-17T14:51:26","modified_gmt":"2015-11-17T14:51:26","slug":"thoughtful","type":"attachment","link":"http:\/\/example.com\/?attachment_id=5","title":{"rendered":"thoughtful"},"author":1,"comment_status":"open","ping_status":"closed","alt_text":"","caption":"","description":"","media_type":"image","media_details":{"width":600,"height":800,"file":"2015\/11\/thoughtful.jpg","image_meta":{"aperture":0,"credit":"","camera":"","caption":"","created_timestamp":0,"copyright":"\u00a9 JoeDaEskimo - http:\/\/www.redbubble.com\/people\/JoeDaEskim","focal_length":0,"iso":0,"shutter_speed":0,"title":"","orientation":0},"sizes":{}},"post":null,"source_url":"http:\/\/example.com\/wp-content\/uploads\/2015\/11\/thoughtful.jpg","_links":{"self":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/media\/5"}],"collection":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/media"}],"author":[{"embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/comments?post_id=5"}]}}
@@ -0,0 +1 @@
1
+ {"code":"rest_post_invalid_id","message":"Invalid post ID.","data":{"status":404}}
@@ -0,0 +1 @@
1
+ {"id":4,"date":null,"guid":{"rendered":"http:\/\/192.168.99.100:8181\/?p=4"},"modified":null,"modified_gmt":null,"slug":"","type":"post","link":"http:\/\/192.168.99.100:8181\/?p=4","title":{"rendered":"Metadata creation"},"content":{"rendered":""},"excerpt":{"rendered":""},"author":1,"featured_image":0,"comment_status":"open","ping_status":"open","sticky":false,"format":"standard","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts"}],"author":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/comments?post_id=4"}],"version-history":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/revisions"}],"https:\/\/api.w.org\/attachment":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/media?post_parent=4"}],"https:\/\/api.w.org\/term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/terms\/category"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/terms\/tag"},{"taxonomy":"post_format","embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/terms\/post_format"}],"https:\/\/api.w.org\/meta":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/meta"}]},"_embedded":{"author":[{"avatar_urls":{"24":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=24&d=mm&r=g","48":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=48&d=mm&r=g","96":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=96&d=mm&r=g"},"description":"","id":1,"link":"http:\/\/192.168.99.100:8181\/author\/test\/","name":"test","url":"","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/users\/1"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/users"}]}}],"replies":[[{"id":1,"parent":0,"author":0,"author_name":"Mr WordPress","author_url":"https:\/\/wordpress.org\/","author_avatar_urls":{"24":"http:\/\/1.gravatar.com\/avatar\/?s=24&d=mm&r=g","48":"http:\/\/1.gravatar.com\/avatar\/?s=48&d=mm&r=g","96":"http:\/\/2.gravatar.com\/avatar\/?s=96&d=mm&r=g"},"date":"2015-11-03T07:47:41","content":{"rendered":"<p>Hi, this is a comment.<br \/>\nTo delete a comment, just log in and view the post&#039;s comments. There you will have the option to edit or delete them.<\/p>\n"},"link":"http:\/\/192.168.99.100:8181\/2015\/11\/03\/hello-world\/#comment-1","type":"comment","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/comments\/1"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/comments"}],"up":[{"embeddable":true,"post_type":"post","href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/1"}]}}]],"https:\/\/api.w.org\/term":[[{"id":1,"link":"http:\/\/192.168.99.100:8181\/category\/uncategorized\/","name":"Uncategorized","slug":"uncategorized","taxonomy":"category","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/terms\/category\/1"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/terms\/category"}]}}],[],[{"code":"rest_no_route","message":"No route was found matching the URL and request method","data":{"status":404}}]],"https:\/\/api.w.org\/meta":[[{"code":"rest_forbidden","message":"Sorry, you cannot view the meta for this post.","data":{"status":403}}]]}}
@@ -0,0 +1 @@
1
+ {"id":4,"date":null,"guid":{"rendered":"http:\/\/192.168.99.100:8181\/?p=4"},"modified":null,"modified_gmt":null,"slug":"","type":"post","link":"http:\/\/192.168.99.100:8181\/?p=4","title":{"rendered":"Metadata creation"},"content":{"rendered":""},"excerpt":{"rendered":""},"author":1,"featured_image":0,"comment_status":"open","ping_status":"open","sticky":false,"format":"standard","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts"}],"author":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/comments?post_id=4"}],"version-history":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/revisions"}],"https:\/\/api.w.org\/attachment":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/media?post_parent=4"}],"https:\/\/api.w.org\/term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/terms\/category"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/terms\/tag"},{"taxonomy":"post_format","embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/terms\/post_format"}],"https:\/\/api.w.org\/meta":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/4\/meta"}]},"_embedded":{"author":[{"avatar_urls":{"24":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=24&d=mm&r=g","48":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=48&d=mm&r=g","96":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=96&d=mm&r=g"},"description":"","id":1,"link":"http:\/\/192.168.99.100:8181\/author\/test\/","name":"test","url":"","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/users\/1"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/users"}]}}],"replies":[[{"id":1,"parent":0,"author":0,"author_name":"Mr WordPress","author_url":"https:\/\/wordpress.org\/","author_avatar_urls":{"24":"http:\/\/1.gravatar.com\/avatar\/?s=24&d=mm&r=g","48":"http:\/\/1.gravatar.com\/avatar\/?s=48&d=mm&r=g","96":"http:\/\/2.gravatar.com\/avatar\/?s=96&d=mm&r=g"},"date":"2015-11-03T07:47:41","content":{"rendered":"<p>Hi, this is a comment.<br \/>\nTo delete a comment, just log in and view the post&#039;s comments. There you will have the option to edit or delete them.<\/p>\n"},"link":"http:\/\/192.168.99.100:8181\/2015\/11\/03\/hello-world\/#comment-1","type":"comment","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/comments\/1"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/comments"}],"up":[{"embeddable":true,"post_type":"post","href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/posts\/1"}]}}]],"https:\/\/api.w.org\/term":[[{"id":1,"link":"http:\/\/192.168.99.100:8181\/category\/uncategorized\/","name":"Uncategorized","slug":"uncategorized","taxonomy":"category","_links":{"self":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/terms\/category\/1"}],"collection":[{"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/v2\/terms\/category"}]}}],[],[{"code":"rest_no_route","message":"No route was found matching the URL and request method","data":{"status":404}}]],"https:\/\/api.w.org\/meta":[[{"id":2,"key":"foo","value":"bar","_links":{"about":[{"embeddable":true,"href":"http:\/\/192.168.99.100:8181\/wp-json\/wp\/posts\/4"}]}}]]}}
@@ -0,0 +1 @@
1
+ {"id":1,"date":"2015-11-03T07:47:41","guid":{"rendered":"http:\/\/example.com\/?p=1"},"modified":"2015-11-17T09:18:22","modified_gmt":"2015-11-17T09:18:22","slug":"hello-world","type":"post","link":"http:\/\/example.com\/2015\/11\/03\/hello-world\/","title":{"rendered":"Hello world!"},"content":{"rendered":"<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n"},"excerpt":{"rendered":"<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n"},"author":1,"featured_image":0,"comment_status":"open","ping_status":"open","sticky":false,"format":"standard","_links":{"self":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/posts\/1"}],"collection":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/posts"}],"author":[{"embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/comments?post_id=1"}],"version-history":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/posts\/1\/revisions"}],"https:\/\/api.w.org\/attachment":[{"embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/media?post_parent=1"}],"https:\/\/api.w.org\/term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/example.com\/wp-json\/wp\/v2\/posts\/1\/terms\/category"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/posts\/1\/terms\/tag"},{"taxonomy":"post_format","embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/posts\/1\/terms\/post_format"}],"https:\/\/api.w.org\/meta":[{"embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/v2\/posts\/1\/meta"}]},"_embedded":{"author":[{"avatar_urls":{"24":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=24&d=mm&r=g","48":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=48&d=mm&r=g","96":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=96&d=mm&r=g"},"description":"","id":1,"link":"http:\/\/example.com\/author\/test\/","name":"test","url":"","_links":{"self":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/users\/1"}],"collection":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/users"}]}}],"replies":[[{"id":1,"parent":0,"author":0,"author_name":"Mr WordPress","author_url":"http:\/\/wordpress.org\/","author_avatar_urls":{"24":"http:\/\/1.gravatar.com\/avatar\/?s=24&d=mm&r=g","48":"http:\/\/2.gravatar.com\/avatar\/?s=48&d=mm&r=g","96":"http:\/\/2.gravatar.com\/avatar\/?s=96&d=mm&r=g"},"date":"2015-11-03T07:47:41","content":{"rendered":"<p>Hi, this is a comment.<br \/>\nTo delete a comment, just log in and view the post&#039;s comments. There you will have the option to edit or delete them.<\/p>\n"},"link":"http:\/\/example.com\/2015\/11\/03\/hello-world\/#comment-1","type":"comment","_links":{"self":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/comments\/1"}],"collection":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/comments"}],"up":[{"embeddable":true,"post_type":"post","href":"http:\/\/example.com\/wp-json\/wp\/v2\/posts\/1"}]}}]],"https:\/\/api.w.org\/term":[[{"id":1,"link":"http:\/\/example.com\/category\/uncategorized\/","name":"Uncategorized","slug":"uncategorized","taxonomy":"category","_links":{"self":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/terms\/category\/1"}],"collection":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/terms\/category"}]}}],[{"id":2,"link":"http:\/\/example.com\/tag\/foo\/","name":"Foo","slug":"foo","taxonomy":"post_tag","_links":{"self":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/terms\/tag\/2"}],"collection":[{"href":"http:\/\/example.com\/wp-json\/wp\/v2\/terms\/tag"}]}}],[{"code":"rest_no_route","message":"No route was found matching the URL and request method","data":{"status":404}}]],"https:\/\/api.w.org\/meta":[[{"id":3,"key":"Some test","value":"Definitely","_links":{"about":[{"embeddable":true,"href":"http:\/\/example.com\/wp-json\/wp\/posts\/1"}]}}]]}}
@@ -0,0 +1 @@
1
+ {"id":2,"count":1,"description":"","link":"http:\/\/192.168.99.100:8080\/tag\/foo\/","name":"Foo","slug":"foo","taxonomy":"post_tag","_links":{"self":[{"href":"http:\/\/192.168.99.100:8080\/wp-json\/wp\/v2\/terms\/tag\/2"}],"collection":[{"href":"http:\/\/192.168.99.100:8080\/wp-json\/wp\/v2\/terms\/tag"}]}}
Binary file
@@ -0,0 +1 @@
1
+ {"code":"rest_invalid_param","message":"Invalid parameter(s): status (status is not one of publish, future, draft, pending, private)","data":{"status":400,"params":["status (status is not one of publish, future, draft, pending, private)"]}}
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+
3
+ describe "Attachments" do
4
+ setup_integration_client
5
+
6
+ it "can be created" do
7
+ media = open_fixture("thoughtful.jpg") do |io|
8
+ client.upload(io, mime_type: "image/jpeg", filename: "thoughtful.jpg")
9
+ end
10
+
11
+ expect(media).to be_instance_of(WordpressClient::Media)
12
+
13
+ expect(client.find_media(media.id)).to be_instance_of(WordpressClient::Media)
14
+ end
15
+
16
+ it "can be created from a file on disk" do
17
+ path = fixture_path("thoughtful.jpg")
18
+ media = client.upload_file(path, mime_type: "image/jpeg")
19
+
20
+ expect(media).to be_instance_of(WordpressClient::Media)
21
+
22
+ expect(client.find_media(media.id)).to be_instance_of(WordpressClient::Media)
23
+ end
24
+
25
+ it "can be updated" do
26
+ media = find_or_create_attachment
27
+ updated = client.update_media(media.id, title: "Totally updated media")
28
+ expect(updated.title_html).to eq "Totally updated media"
29
+ end
30
+
31
+ it "can be listed" do
32
+ find_or_create_attachment
33
+
34
+ media = client.media(per_page: 1)
35
+
36
+ expect(media.size).to be > 0
37
+ expect(media.first).to be_instance_of(WordpressClient::Media)
38
+ end
39
+
40
+ it "uses HTML for the title" do
41
+ media = find_or_create_attachment
42
+ updated = client.update_media(media.id, title: "Images & paint")
43
+ expect(updated.title_html).to eq "Images &amp; paint"
44
+ end
45
+
46
+ def find_or_create_attachment
47
+ client.media(per_page: 1).first || open_fixture("thoughtful.jpg") do |io|
48
+ client.upload(io, mime_type: "image/jpeg", filename: "thoughtful.jpg")
49
+ end
50
+ end
51
+ end