wordpress_client 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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