Comments (41)
@macta @samWson, Could you review and test this...
To facilitate PHARO SUBMIT TESTING by Exercism staff and other curious punters unfamiliar with Pharo,
I've these instructions in detailed Quick Start form... no pre-requisites.
- On the web site, join a fresh track (e.g. Python) in INDEPENDENT mode.
Observe "hello world" status icon is "unlocked". - Download "Pharo standalone" for your platform from https://pharo.org/download (scroll down page).
- Unzip and start
Pharo.exe
orpharo
. - Left-click on background and choose ``Playground``` from the World menu.
- Copy the following code into the Playground...
"CONFIGURE - LIVE EXERCISM WEB SITE"
apiToken := 'get it from https://exercism.io/my/settings'.
apiUrl := 'https://api.exercism.io/v1/solutions' asUrl.
"CONFIGURE - LOCAL DEV COPY EXERCISM WEBSITE"
apiToken := 'get it from http://lvh.me:3000/my/settings'.
apiUrl := 'http://lvh.me:3000/api/v1/solutions' asUrl.
"DOWNLOAD EXERCISE DATA"
client := ZnClient new.
client
url: apiUrl / 'latest' ;
headerAt: 'Authorization' put: 'Bearer ' , apiToken ;
queryAt: 'track_id' put: 'python' ;
queryAt: 'exercise_id' put: 'hello-world'.
response := client get.
solutionData := (STON fromString: response) at: 'solution'.
solutionData inspect. "also check website exercise icon has changed"
"SUBMIT EXERCISE"
iteration := 1 + (iteration ifNil: [ 0 ])."force change next line, otherwise Response(400 Bad Request)"
solutionEntity1 :=ZnByteArrayEntity bytes: 'MY SOLUTION #', iteration printString.
solutionEntity2 :=ZnByteArrayEntity bytes: 'SECOND PART OF MY SOLUTION.'.
solutionPart1 := ZnMimePart fieldName: 'files[]' fileName: 'mysolution.py' entity: solutionEntity1.
solutionPart2 := ZnMimePart fieldName: 'files[]' fileName: 'support.py' entity: solutionEntity2.
multiPartFormDataEntity := ZnMultiPartFormDataEntity new.
multiPartFormDataEntity
addPart: solutionPart1 ;
addPart: solutionPart2.
client := ZnClient new.
client
url: apiUrl / (solutionData at: 'id') ;
headerAt: 'Authorization' put: 'Bearer ' , apiToken ;
entity: multiPartFormDataEntity.
client patch.
client response inspect. "also check website is updated"
"REVIEW CODE"
badMethod := (ZnMimePart class >> #fieldName:fileName:entity:).
badMethod browse.
"INSTALL FIX"
badCode := badMethod sourceCode.
goodCode := badCode copyReplaceAll: 'form-data;name=' with: 'form-data; name='.
ZnMimePart class compile: goodCode
"REVIEW FIX"
(DiffMorph from: badCode to: goodCode) openInWindow
Now "Do It" a bit at a time...
- Select the two lines of the appropriate CONFIGURE, right-click and choose DoIt to evaluate them.
- Evaluate "DOWNLOAD EXERCISE" section.
A Dictionary of the JSON response should appear.
Observe website status icon changed to "half/half in progress" - Evaluate "SUBMIT EXERCISE" section.
A ZnRespone(500 Internal Server Error) should appear. - Evaluate "REVIEW CODE" section (and watch this window during next step).
- Evaluate "INSTALL FIX" section.
- Evaluate "SUBMIT EXERCISE" section again.
A ZnResponse(201 Created application) should appear.
Observe the website status icon changed to "thumbs up". Yay!" - Review the web site solution being updated every time the "DOWNLOAD EXERCISE" section is evaluated. Double-Yay!"
- Evaluate "REVIEW FIX" section.
Note, while the "badCode" variable is used, its not clear where the fault lies per standards. We can fix it easily in our own application and maybe get it changed in Pharo distribution, but should the server be more permissive in its input?
from pharo-smalltalk.
I'm going to have a go at building this solution from the ExercismSubmit
class stub. It's pushing my skills so I will probably make a few work in progress pull requests when I need help. Conveniently I've been building a lot of command pattern classes at work recently so hopefully I can make something that works.
from pharo-smalltalk.
from pharo-smalltalk.
Sorry guys I’ve been off the grid (turns out the outback has limited 3G ;) That method rings a bell - TonelWriter is quite convoluted and I recall we had to do do some nasty overriding to coerce it to even do stuff. I will check as I filed a PR with Tonel.
from pharo-smalltalk.
from pharo-smalltalk.
First the download, for comparison.
C:> exercism --verbose download --track=python --exercise=hello-world
========================= BEGIN DumpRequest =========================
GET /v1/solutions/latest?exercise_id=hello-world&track_id=python HTTP/1.1
Host: api.exercism.io
Authorization: Bearer d4a3xxxx-secret-xxxxxxx59c56
Content-Type: application/json
User-Agent: github.com/exercism/cli v3.0.8 (windows/amd64)
========================= END DumpRequest =========================
========================= BEGIN DumpResponse =========================
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Type: application/json; charset=utf-8
Date: Thu, 06 Sep 2018 05:42:13 GMT
Etag: W/"e438ca3065bc3f2ff0a29cd1bf73f33e"
Referrer-Policy: strict-origin-when-cross-origin
Server: nginx/1.10.3 (Ubuntu)
Set-Cookie: site_context=normal; domain=.exercism.io; path=/
Set-Cookie: _exercism_session=5m2%2F6wlYMBWhcXhKNtlek7%2BPUo85TXxxlVzhaKYbIVJkOJ9GoKJAkuPTmPjs4MeJlu0kton8KdebLQDFUxwdvB4CR3yW%2BRlPgjiD6X41CpNudC%2B709enVtVovsb05l32cbmxpHo%2FQ%2Fu4YSn4nXMtY41yo31Hxh5mXOrbN5g2bK5MY23wm2iUzPV7Sqhf04S4OVEfgmh4eyx0s9K2efCkjW2D6H2f%2BG0ZMdZD6%2Fd9oWDYCBafEFNKz66%2B0LI2gMI1X1ofg1ndbg%3D%3D--b3qbJIbGa2LYal3x--j1zHyWqD9wgYRIrT5RqNZw%3D%3D; domain=.exercism.io; path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 6d216682-8737-4b61-8a4c-cbd47dee59bf
X-Runtime: 0.102770
X-Xss-Protection: 1; mode=block
========================= END DumpResponse =========================
========================= BEGIN DumpRequest =========================
GET /v1/solutions/66f3b82a26c248d3934fcb11e708b25f/files/README.md HTTP/1.1
Host: api.exercism.io
Authorization: Bearer d4a3xxxx-secret-xxxxxxx59c56
Content-Type: application/json
User-Agent: github.com/exercism/cli v3.0.8 (windows/amd64)
========================= END DumpRequest =========================
========================= BEGIN DumpResponse =========================
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Date: Thu, 06 Sep 2018 05:42:14 GMT
Etag: W/"5aa9487af0280cceb18751f931b593be"
Referrer-Policy: strict-origin-when-cross-origin
Server: nginx/1.10.3 (Ubuntu)
Set-Cookie: site_context=normal; domain=.exercism.io; path=/
Set-Cookie: _exercism_session=%2F3RGG7GnSdRZVD%2FI7hhRYhTsF1FqwMHmlxmnuOd5M%2BDiGVoFZo%2B2vxZ2nSw4QIrNIFDsm7Xm3Z7K1lAkhP%2Bsx2tk%2BYaflHQPU5FxqXV8SNbnjLGbOBV62TfgxV9kHZhg48ZC9DFxjHHXXuH9fYCaHraw98LJ5T1wvXDQsdA6c72sx1G81Wm0XE31PrkibrPZQpz8EZKd16JvHvSPnf63RqFVk01msyAdoLLSFnqxMqfbTv4eUg8k0ews6FVD7EGdUCZ9WA%3D%3D--D1TA5%2BB%2F5dO7Fmfe--zjtS0ayiRM6ZryVvVjbjcA%3D%3D; domain=.exercism.io; path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 17117035-cce2-4316-b331-be5194f8ff9e
X-Runtime: 0.069309
X-Xss-Protection: 1; mode=block
========================= END DumpResponse =========================
========================= BEGIN DumpRequest =========================
GET /v1/solutions/66f3b82a26c248d3934fcb11e708b25f/files/hello_world.py HTTP/1.1
Host: api.exercism.io
Authorization: Bearer d4a3xxxx-secret-xxxxxxx59c56
Content-Type: application/json
User-Agent: github.com/exercism/cli v3.0.8 (windows/amd64)
========================= END DumpRequest =========================
========================= BEGIN DumpResponse =========================
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Date: Thu, 06 Sep 2018 05:42:15 GMT
Etag: W/"e3163528c26697e825dd5eec25279a86"
Referrer-Policy: strict-origin-when-cross-origin
Server: nginx/1.10.3 (Ubuntu)
Set-Cookie: site_context=normal; domain=.exercism.io; path=/
Set-Cookie: _exercism_session=ynyH%2BeWpTBiSa%2FR1WOhvD9FI3CoAzg8sQq6ommUOcq27XbXhoPh8KrMwHp2R5HQ5Qewkry0BvKHZT3kOiXEAa%2BAD24lk%2FzqQCBv%2BggbahAqxEvZNmC5FLQUeyTUE12k2SxK8XZLyD%2FHJ%2F7OF0wfvz7FOINkktpglX9wOIvzniab07i7JBvzvdFkfXlIt2BZZqChjUH34BdFrUt6sKJkjLDI3T%2F2YPXXFm1w8IRW3i5x%2F96rWPEUEvFfgbWghBRAwBKcVe2K0XQsx--X4PCsgG8bqHOgpM3--ZirJWNTuYF0h8OXXy0KNKA%3D%3D; domain=.exercism.io; path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: f5d1c8a1-2c16-490e-9922-7f9c55d7d920
X-Runtime: 0.074143
X-Xss-Protection: 1; mode=block
========================= END DumpResponse =========================
========================= BEGIN DumpRequest =========================
GET /v1/solutions/66f3b82a26c248d3934fcb11e708b25f/files/hello_world_test.py HTTP/1.1
Host: api.exercism.io
Authorization: Bearer d4a3xxxx-secret-xxxxxxx59c56
Content-Type: application/json
User-Agent: github.com/exercism/cli v3.0.8 (windows/amd64)
========================= END DumpRequest =========================
========================= BEGIN DumpResponse =========================
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Date: Thu, 06 Sep 2018 05:42:15 GMT
Etag: W/"da0c75d4be9181852a9813271c71a17f"
Referrer-Policy: strict-origin-when-cross-origin
Server: nginx/1.10.3 (Ubuntu)
Set-Cookie: site_context=normal; domain=.exercism.io; path=/
Set-Cookie: _exercism_session=1J7CtsAjgMv%2F1BtuLUWbDtt3LO56Pz5XbAVRTSIJG4Xqlww%2F%2BweLyrEUY6VdHKT%2FWqzU%2BUaVUvGF5alGaC5raOdoVY8xW9%2BWlk4rI9pj%2FlEZRC%2FfIbx4649uJ9HtY3aqxmc3RLsCpKHZ75PZ4OK4NWBOqcZgRueldWovcBHQf8F4jwSKzDIjzB7DPPM2wStCjjHnMXkXQ0%2BxzN50MAFU4C19%2Bzcg%2BTciYNKfyfv4eGAcND4%2BSIl5xe0HIGq3yUsEbvid4CAAzJiguYRKcyo%3D--9%2FXGSokZc8sfAlDl--SsXvDulOcD3DRPVlS1dTSw%3D%3D; domain=.exercism.io; path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 90199569-573d-4609-adad-6e392f634bee
X-Runtime: 0.068671
X-Xss-Protection: 1; mode=block
========================= END DumpResponse =========================
Downloaded to
C:\Users\Ben\Exercism\python\hello-world
This created hello-world folder holding four files.
The top three were downloaded and the fourth is metadata.
- hellow_world.py
- hello_world_test.py
- README.md
- .solution.json
{ "track":"python",
"exercise":"hello-world",
"id":"66f3b82a26c248d3934fcb11e708b25f",
"url":"https://exercism.io/my/solutions/66f3b82a26c248d3934fcb11e708b25f",
"handle":"bencoman",
"is_requester":true,
"auto_approve":true
}
Now Submitting...
After making edits, uploading two files since one is not informative enough.
C:>exercism --verbose submit hello_world.py hello_world_test.py
========================= BEGIN DumpRequest =========================
PATCH /v1/solutions/66f3b82a26c248d3934fcb11e708b25f HTTP/1.1
Host: api.exercism.io
Authorization: Bearer d4a3-secret-9c56
Content-Type: multipart/form-data; boundary=c0b6fc45e5447d6cef7e2a87f4fda74d59cadd4470013168da100f1c31e5
User-Agent: github.com/exercism/cli v3.0.8 (windows/amd64)
--c0b6fc45e5447d6cef7e2a87f4fda74d59cadd4470013168da100f1c31e5
Content-Disposition: form-data; name="files[]"; filename="hello_world.py"
Content-Type: application/octet-stream
def hello():
return 'Hello, World!'
--c0b6fc45e5447d6cef7e2a87f4fda74d59cadd4470013168da100f1c31e5
Content-Disposition: form-data; name="files[]"; filename="hello_world_test.py"
Content-Type: application/octet-stream
import unittest
import hello_world
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
class HelloWorldTest(unittest.TestCase):
def test_hello(self):
self.assertEqual(hello_world.hello(), 'Hello, World!')
if __name__ == '__main__':
unittest.main()
# Edited just to dirty file
--c0b6fc45e5447d6cef7e2a87f4fda74d59cadd4470013168da100f1c31e5--
========================= END DumpRequest =========================
========================= BEGIN DumpResponse =========================
HTTP/1.1 201 Created
Transfer-Encoding: chunked
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Type: application/json; charset=utf-8
Date: Thu, 06 Sep 2018 05:50:09 GMT
Etag: W/"44136fa355b3678a1146ad16f7e8649e"
Referrer-Policy: strict-origin-when-cross-origin
Server: nginx/1.10.3 (Ubuntu)
Set-Cookie: _exercism_session=12QdD0ohkwDsY%2FR%2BfWvuPPS8QeKIKwIJEBAfFVh28RzSnvx%2Fmra0TobHaNGzmCbn%2By0swKRh2t1sOAeOiQkNVftOSaXtr5NYr7dcrRsd6ciga%2Fz2%2FfqoIHMqMNPOklijQkW72LFuYlLHI6Nk9I%2Fghdm1Ng%3D%3D--td6UIK5czblg4N4w--6O%2F%2FoOZpbRTa%2B8rCYzCGLQ%3D%3D; domain=.exercism.io; path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 70efb155-773f-4eb0-8a8c-66223eef0d5d
X-Runtime: 0.184868
X-Xss-Protection: 1; mode=block
========================= END DumpResponse =========================
Your solution has been submitted successfully.
You can complete the exercise and unlock the next core exercise at:
https://exercism.io/my/solutions/66f3b82a26c248d3934fcb11e708b25f
from pharo-smalltalk.
Haven't bumped into the HTTP PATCH verb before. Some info...
WIkipedia - Patch verb
Please. Don't Patch Like An Idiot.
RFC 5789 - PATCH Method for HTTP
"It is important to realize that the request entity to PATCH is of a different content-type than the entity that it is modifying." [ref]
from pharo-smalltalk.
Just noting, running the identical command without any edits returns a bad status. The full diff shown here, and just the important part below...
========================= BEGIN DumpResponse =========================
HTTP/1.1 400 Bad Request
Making an edit to just one of the files submitted then gives good status...
========================= BEGIN DumpResponse =========================
HTTP/1.1 201 Created
from pharo-smalltalk.
I've been hacking away at a solution in the playground to get a rough idea of what works. I'm close but not quite there yet. This is all new territory for me. Here is my hacked together submitting client:
fileName := 'path to a Tonel file'.
client := ZnClient new
http;
host: 'api.exercism.io';
method: 'PATCH';
headerAt: 'Authorization' put: 'Bearer ', apiToken;
contentType: ZnMimeType applicationOctetStream;
contentType: ZnMimeType multiPartFormData;
headerAt: 'Content-Disposition' put: (ZnMimeType main: 'form-data' sub: '' parameters: { 'name' -> 'files[]'. 'filename' -> fileName } asDictionary).
Not quite working yet as I get the error: There is no request entity yet, cannot set its content-type
. I know roughly what I'm missing, I just haven't been able to find good documentation on how do to it among all the Zinc docs. I've been mainly referencing Enterprise Pharo.
from pharo-smalltalk.
Update on the above. I've got one step further and the client is now looking like this:
client := ZnClient new
entity: (ZnMultiPartFormDataEntity new addPart: 'boundary=f7b01eaa5fcf167c2094054c167fd0685b596a361e868587b0095593730d');
http;
host: 'api.exercism.io';
method: 'PATCH';
headerAt: 'Authorization' put: 'Bearer ', apiToken;
contentType: ZnMimeType applicationOctetStream;
contentType: ZnMimeType multiPartFormData;
headerAt: 'Content-Disposition' put: (ZnMimeType main: 'form-data' sub: '' parameters: { 'name' -> 'files[]'. 'filename' -> fileName } asDictionary);
patch.
Not working yet. I'm only just discovering what multipart form data entities are.
from pharo-smalltalk.
Please excuse the rambling, but I've had an exciting crash course on multipart form data entities and I'm finally figuring out parts on the Zinc library. Our client looks like this:
apiToken := (STON fromString: 'path to user.json file' asFileReference contents) at: #token.
fileName := 'path to a exercise file of Tonel format'.
multiPartFormDataEntity := ZnMultiPartFormDataEntity new
addPart: (ZnMimePart fieldName: 'files[]' fileNamed: fileName).
client := ZnClient new
entity: multiPartFormDataEntity;
http;
host: 'api.exercism.io';
method: 'PATCH';
headerAt: 'Authorization' put: 'Bearer ', apiToken.
client patch;
response.
Helpfully the Zinc library handles all the finer details of boundaries, content types and dispositions etc. automatically. The response on this one is 404 so it's not quite right yet. I'll keep looking into it an hopefully have something more tomorrow.
from pharo-smalltalk.
@samWson, looks like good progress. From my experience reported exercism/cli#732 I believe that service doesn't work with HTTP. Try HTTPS,
from pharo-smalltalk.
I've changed the scheme to HTTPS, but it is still a 404 response so something else is bad. Here is the transcript output:
2018-09-09 18:02:42 005 Connection Established api.exercism.io:443 46.51.205.192 831ms
2018-09-09 18:02:42 006 Request Written a ZnRequest(PATCH /) 0ms
2018-09-09 18:02:42 007 Response Read a ZnResponse(404 Not Found text/html;charset=UTF-8 1320B) 281ms
2018-09-09 18:02:42 008 PATCH / 404 1320B 281ms
I've tweaked around with the Authorization, using the wrong token, and hard coding the correct token, just to be sure that wasn't the problem. I've made sure that the URI is using HTTPS. I'm not sure what else could be wrong here.
from pharo-smalltalk.
I've had a pretty good go at this and am also stuck. I've now got the Exercism website up and running locally per https://github.com/exercism/website to facilitate observing the CLI's wire transfer in operation. I've successfully signed up to it with my github account, but it seems there is no track data loaded in the database. http://lvh.me:3000/my/tracks tells me I have 0 tracks and searching for python returns empty response. I've inquired how to load track data into into the database.
I presume I should configure the CLI like this...
C:>exercism configure --token 958b-new-secret-from-local-server-95df --api http://lvh.me:3000/v1
but I'm getting...
Error: The base API URL 'http://lvh.me:3000/v1' cannot be reached.
API returned 404 Not Found
Aside: I actually found it easier to not use docker which I struggled with, since I'm not familiar with it. Ended up running it under Windows 10 Subsystem for Linux Ubuntu 16.04 and accessing it from Chrome on Windows side.
from pharo-smalltalk.
kytrinyx> We are doing a multipart fileupload. Here's a test request:
POST / HTTP/1.1
Host: 127.0.0.1:49821
Accept-Encoding: gzip
Content-Length: 459
Content-Type: multipart/form-data; boundary=2982d7b5665d520d5dfd2f7521bfbf7ef6ebf698acd9fef92130f34c5161
User-Agent: Go-http-client/1.1
--2982d7b5665d520d5dfd2f7521bfbf7ef6ebf698acd9fef92130f34c5161
Content-Disposition: form-data; name="files[]"; filename="/file-1.txt"
Content-Type: application/octet-stream
This is file 1.
--2982d7b5665d520d5dfd2f7521bfbf7ef6ebf698acd9fef92130f34c5161
Content-Disposition: form-data; name="files[]"; filename="/subdir/file-2.txt"
Content-Type: application/octet-stream
This is file 2.
--2982d7b5665d520d5dfd2f7521bfbf7ef6ebf698acd9fef92130f34c5161--
@kytrinyx, thx for the sample request above (I copied here from the other closed thread). Can you confirm the use of POST and also the root "/" with no path. Several posts above (just below the "Now Submitting..." heading ) you can see that the CLI exercism --verbose
reported it was using PATCH rather than POST.
from pharo-smalltalk.
@samWson, I picked up that you're also a Ruby guy. This looks related to loading data into the local web site, just don't know how to invoke it -- any clue...
https://github.com/exercism/website/blob/master/app/services/git/syncs_tracks.rb
from pharo-smalltalk.
I was having trouble redirecting the CLI to my local Exercism website, then found the correct form was...
C:> exercism configure --token 958b-secret-from-local-site-95df --api http://lvh.me:3000/api/v1
from pharo-smalltalk.
One more hurdle I had to get over, WireShark doesn't work on loopback, so I had to get my local server and the client running on different machines.
Now Yay! I managed to capture HTTP off the wire for a submit (regardless of empty database)
PATCH /api/v1/solutions/7818c4b34026492dba638633790f2113 HTTP/1.1
Host: lvh.me:3000
User-Agent: github.com/exercism/cli v3.0.9 (linux/amd64)
Content-Length: 279
Authorization: Bearer 958ba94b-27e2-4f2d-9f0e-14f5188395df
Content-Type: multipart/form-data; boundary=d56d661390c0485c9094bb77dfca630597ec4e947172b8ac6a7b3d1971f1
Accept-Encoding: gzip
--d56d661390c0485c9094bb77dfca630597ec4e947172b8ac6a7b3d1971f1
Content-Disposition: form-data; name="files[]"; filename="hello_world.py"
Content-Type: application/octet-stream
def hello():
pass
EDIT3
--d56d661390c0485c9094bb77dfca630597ec4e947172b8ac6a7b3d1971f1--
Note the extra 'api' string before the '/v1' shown in the wire capture
compared to the previous exercism --verbose
captures logged above.
Now I discover that...
exercism.exe configure --token d4a3-mysecret-9c56 --api https://exercism.io/v1
doesn't work, while this does...
exercism.exe configure --token d4a3-mysecret-9c56 --api https://exercism.io/api/v1
But within Pharo, if I do...
ApiPath := 'api/v1/solutions/latest'
then ExercismDownload exercise: 'hello-world'
I get the following response string...
We launched a brand new version of Exercism.\n\n
Upgrade your client with:\n\n
exercism upgrade\n\n
Or delete the old CLI and follow the instructions\non the new site to get the new one.\n\n"}
I then reconfirmed that the following worked...
ApiPath := 'v1/solutions/latest'
then ExercismDownload exercise: 'hello-world'
@kytrinyx, @iHiD, I'm confused. Maybe https://github.com/exercism/website which I installed locally is the old site?
from pharo-smalltalk.
Here is where I'm up to. The request capture from the wire for following...
downloaded := ExercismDownload track: 'python' exercise: 'hello-world'.
downloaded solution inspect. "btw, Icon changes for exercise on Exercism site"
apiToken := 'd4a37d75-mysecret-4a4770459c56'.
myEntity1 :=ZnByteArrayEntity bytes: 'This is file 1.'.
myEntity2 :=ZnByteArrayEntity bytes: 'This is file 2.'.
myPart1 := ZnMimePart fieldName: 'files[]' fileName:'/file-1.txt' entity: myEntity1.
myPart2 := ZnMimePart fieldName: 'files[]' fileName:'/file-2.txt' entity: myEntity2.
multiPartFormDataEntity := ZnMultiPartFormDataEntity new addPart: myPart1; addPart: myPart2.
client := ZnClient new.
client
https;
host: 'api.exercism.io';
headerAt: 'Authorization' put: 'Bearer ' , apiToken;
setAcceptEncodingGzip;
entity: multiPartFormDataEntity;
url: 'http://api.exercism.io/v1/solutions/', (downloaded solution at: 'id').
client request headers removeKey: 'Accept'.
client patch.
client response inspect
shown below is very close to @kytrinyx sample a few posts above,
PATCH /v1/solutions/af454692256b4ce1aef52aba29a27b30 HTTP/1.1
Host: api.exercism.io
Content-Type: multipart/form-data;boundary=Boundary-Zn-QZXSRSCY
Content-Length: 372
Accept-Encoding: gzip
User-Agent: Zinc HTTP Components 1.0 (Pharo/6.0)
Authorization: Bearer d4a37d75-2da2-4300-9ec1-4a4770459c56
--Boundary-Zn-QZXSRSCY
Content-Type: application/octet-stream
Content-Length: 15
Content-Disposition: form-data;name="files[]";filename="/file-1.txt"
This is file 1.
--Boundary-Zn-QZXSRSCY
Content-Type: application/octet-stream
Content-Length: 15
Content-Disposition: form-data;name="files[]";filename="/file-2.txt"
This is file 2.
--Boundary-Zn-QZXSRSCY--
but gets a response...
500 Internal Server Error
<!DOCTYPE html>
<html>
<head>
<title>The page you ...out that.</p>
<p>Continue by heading back to <a href=""/"">the homepage</a>.</p>
</body>
</html>
Differences to CLI client appear to be:
- format of boundary string. But ours still seems to conform
- no space after semi-colons - ?
- different header order - but I can't believe that has an impact
- Content-Length for each part - ?
Difference to @kytrinyx sample a few posts above:
- PATCH instead of POST - Doing the same request as a POST returns a 200 Success, BUT no icon state change is visible on the web site.
- Authorization Bearer - but without it returns Unauthorized Request errors
from pharo-smalltalk.
Can you confirm the use of POST and also the root "/" with no path.
Shoot, no, that was from when I was working out the original request and had made a stand-alone app that only had one endpoint that accepted multi-part file uploads. So sorry about that rabbit hole!
from pharo-smalltalk.
I presume I should configure the CLI like this...
C:>exercism configure --token 958b-new-secret-from-local-server-95df --api http://lvh.me:3000/v1
but I'm getting...
Error: The base API URL 'http://lvh.me:3000/v1' cannot be reached.
API returned 404 Not Found
The --api value needs to be a URL you can hit in your browser. Are you able to go to http://lvh.me:3000?
from pharo-smalltalk.
You're right that this uses patch
not post
(the post goes back to my original stand-alone test).
from pharo-smalltalk.
Note the extra 'api' string before the '/v1' shown in the wire capture compared to the previous exercism --verbose captures logged above.
Ah, yes. We need to add documentation about this.
The production API is delivered from api.exercism.io/v1
, but locally we don't mount the API to a different place, so it needs to be accessed from /api/v1
from pharo-smalltalk.
I'm confused. Maybe https://github.com/exercism/website which I installed locally is the old site?
No, this is the new site. The error message is the one that you get if you hit an endpoint that is not valid for the API. So for example: http://api.exercism.io/api/v1/solutions/latest (notice that it has both api.exercism.io and the /api/
path segment). If you hit a valid URL: https://api.exercism.io/v1/solutions/latest then it will return a valid JSON response (in this case an empty JSON object, which is likely because we're hitting the endpoint without being authenticated. Presumably we'd actually want an error here).
from pharo-smalltalk.
Authorization Bearer - but without it returns Unauthorized Request errors
When I was originally developing the client I tested with both Authorization Bearer and Authorization Token and both worked.
from pharo-smalltalk.
I'm really not sure what is causing the 500 error when you hit production as described in #96 (comment). I can't find that error in our logs.
from pharo-smalltalk.
@samWson, I picked up that you're also a Ruby guy. This looks related to loading data into the local web site, just don't know how to invoke it -- any clue...
https://github.com/exercism/website/blob/master/app/services/git/syncs_tracks.rb
@bencoman at a guess it looks like it is for keeping the language tracks synchronized on the website (you wouldn't need to auto approve hello world on the users computer right?). I get lost where the code reaches out of the class with Git::ProblemSpecifications.head.fetch!
on line 26 and Git::SyncsTrack.sync!(track)
on line 40.
For invoking it do you mean at the Rails console using the class itself? The class method self.sync
is what your after. Almost everything else is private. You could use it at the console like so: Git::Syncstracks.sync(<collection of tracks>)
. There is an example of this here on line 28. This looks like it is the only place it is used in the code (other than the tests).
from pharo-smalltalk.
You probably want:
bundle exec rake git:fetch
bundle exec rake git:sync
from pharo-smalltalk.
Further discussion on getting a local copy of the Exercism website running moved to #103.
With that, I isolated our "500 INTERNAL SERVER ERROR" to a missing "space" in the "Content-Disposition:" of the Mime Part (as observed in raw HTTP Request captured by Wireshark.)
i.e. broken...
Content-Disposition: form-data;name="files[]";filename="myfile.txt"
working...
Content-Disposition: form-data; name="files[]"; filename="myfile.txt"
A diff of the full requests plus library code change is here... https://www.diffchecker.com/ETIfCUzx
from pharo-smalltalk.
PHARO SUBMIT TESTING RESULTS (log success directly in this post rather than spread across posts)
2018.09.14.BenComan Windows 10 works
2018.09.14.BenComan Linux Mint 7.3 64-bit works
2018.09.14.TimM OSX 10.13.5 (High Sierra) works
from pharo-smalltalk.
Nice one @bencoman - but the question is, is this a bug in that Pharo method for ZnMimePart - or is this a parsing bug in the exercism server? The mozilla docs for content-disposition don't mention a space is required, just that all parameters are separated by a ';'. (although all their examples show a space after each parameter)
We can of course get Pharo fixed (or add our own extension method to workaround this) - but it seems strange that space is required. I guess we should ask sven.
from pharo-smalltalk.
@bencoman cool. I've gone through your steps above and now I finally understand. I see the issue that we don't know where the bug is, Pharo or Exercism. I suggest in the short term we do a one time fix to our own code to get around this. That way we can keep moving forward and it's not hard to reverse if we need to.
from pharo-smalltalk.
I think we can add our own extension method (with an ex prefix) as I think we call it vs the library right?
I have a long flight coming up in a few days, and can look at replacing the shell out code.
Looks pretty easy given Ben’s example.
Or is one of you guys itching to do it?
from pharo-smalltalk.
Agree on short term action - we should adopt that method by changing its method-protocol to "*ExercismTools".
Now with the ancillary difficulties failing to get the Dev Local Website going under WSL, and doing it again on an old Linux box and then experimenting/analysing the Exercism CLI HTTP traffic, I burnt four FT days on it when I should have been working on something else. So if someone could do the work to implement my POC that would be great.
The final piece of the puzzle of how/where to store the solution metadata between the Download and the Submit has one option implemented in PR #104.
from pharo-smalltalk.
Cool, thanks Sam.
push up some variables from the the other DownloadCommand that Ben refactored this too.
To provide that info, I've merged PR #104 into master
. As I commented in that PR, there may be a better way, but this is sufficient for now to provide the solutionId
that Sam needs.
After pulling down the latest master
the Playground script can be rewritten...
"DOWNLOAD EXERCISE"
ExercismDownload exercise: 'hello-world'.
"SUBMIT EXERCISE"
solutionId := (ExercismSubmit solutionDataForExercise: 'hello-world') at: 'id'.
iteration := 1 + (iteration ifNil: [ 0 ]). "no changed entities ==> Response(400 Bad Request)"
solutionEntity1 :=ZnByteArrayEntity bytes: 'MY SOLUTION #', iteration printString.
solutionEntity2 :=ZnByteArrayEntity bytes: 'SECOND PART OF MY SOLUTION.'.
solutionPart1 := ZnMimePart exercismFieldName: 'files[]' fileName: 'mysolution.py' entity: solutionEntity1.
solutionPart2 := ZnMimePart exercismFieldName: 'files[]' fileName: 'support.py' entity: solutionEntity2.
multiPartFormDataEntity := ZnMultiPartFormDataEntity new.
multiPartFormDataEntity
addPart: solutionPart1 ;
addPart: solutionPart2.
client := ZnClient new.
client
headerAt: 'Authorization' put: 'Bearer ' , apiToken ;
url: apiUrl / solutionId ;
entity: multiPartFormDataEntity.
client patch.
client response inspect. "also check website is updated"
Now there are a few cases that may be useful:
a. Since our classes==files, specify the classes to Submit. e.g. ExercismSubmit classes: {HelloWorld}.
b. Since ultimately may invoke this by right-clicking on the exercise-package-tag in System Browser, specify the package-tag to submit. ExercismSubmit package: 'Exercism-HelloWorld'
c. Submit whole exercise via Exercism exercise-name e.g. ExercismSubmit exercise: 'hello-world'
from pharo-smalltalk.
After that merge, you can now do...
solutionId := (ExercismSubmit solutionDataForClass: HelloWorld) at: 'id'.
from pharo-smalltalk.
@bencoman @macta related to using the UI to submit exercises, the message ExTonelWriter>>#sourceDir:
gets sent in ExercismManager>>#submitToExercism:
, but this method is not implemented anywhere in ExTonelWriter
hierarchy. I tried removing it to see what would happen and it still seems like it is required.
(writer := ExTonelWriter new)
packageDir: (submissionDirectoryRef relativeTo: trackDirectoryRef) exPathString;
sourceDir: trackDirectoryRef;
writeSnapshot: packageOrTag snapshot.
I'm wondering at this point if it is a method we made ourselves and have accidentally deleted it, or my image is missing some code from a branch or just somehow is not up to date.
The same code is also used in ExercismGenerator>>#generateSourceFilesFor:to:
.
from pharo-smalltalk.
I've been looking around over the last week on this and I had a thought. We had to make some changes to TonelWriter
to make it work with an in memory file system for PR #110. I didn't include the changes to TonelWriter
in my last commit for that PR. They are summarised here.
@bencoman since it looks like your going to be busy for the time being, if you don't mind I'll make a PR for the changes to TonelWriter
based on your comment linked above.
After that I'll keep looking around to try and make some more progress.
from pharo-smalltalk.
It turns out the fix for TonelWriter
was simple (I hope this doesn't come back to bite me).
I've pushed PR #112 which should fix submitting exercises from the GUI. If there is any OSProcess
still in the repository it should be no longer in use and save to remove.
I think we finally may have a working product for all three operating systems now.
I'm going to take a break from coding for the next few days. We have a lot of issues open and I'd like to go back and review them so we can keep this project pointed in the right direction.
from pharo-smalltalk.
#112 is merged. I think this issue is finally fixed. We should have a working submit solution for all operating systems now.
from pharo-smalltalk.
btw, tracked the issue back to Rack, a Ruby library used by Sinatra, that is used by Exercism.
Opened rack/rack#1386
from pharo-smalltalk.
Related Issues (20)
- Cannot start hello-world exercise HOT 19
- Fix dependency conflict during loading dev baseline HOT 1
- Update link to CLI token in Pharo UI dialog
- Update conributing guidelines to be up-to-date
- Update reference material - Pharo By Example
- Fix typos in Key features description
- Error in getting solution path string in Pharo8
- Add support for P11 and remove support for versions 7, 8
- Test runner passes unexpected on empty class definition with no method defined
- Test runner should exit with zero code when result.json is produced
- Refactor Exercise test generator
- Add Binary Search Tree exercise
- Add Simple Cipher exercise
- Fix test runner error status, when all tests ran
- Out-of-sync unit tests for resistor color duo
- Building a training set of tags for pharo-smalltalk HOT 29
- Configure tokei for pharo HOT 11
- Building a training set of tags for pharo-smalltalk - fixed version (community solutions) HOT 19
- All pharo tests pass even though i didn't solve the exercise HOT 13
- Fix TonelParser to return correct error when parsing empty file
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pharo-smalltalk.