README.md in fitting-3.0.2 vs README.md in fitting-4.0.0

- old
+ new

@@ -1,22 +1,93 @@ # Fitting <img align="right" width="192" height="192" -alt="Optimizt avatar: OK sign with Mona Lisa picture between the fingers" +alt="Fitting avatar: Documents with hangers" src="./images/logo.png"> -There are such ways of describing the API documentation as API Blueprint, Swagger and OpenAPI. And using the tests already writed for your code, you can reuse them to find out the documentation coverage. -This makes it easy to find out how much the documentation matches the implementation. +We set up test logs, validate them according to your API documentation and show the documentation coverage with logs. -* Cool if you already have a project with tests and documentation, you can check how good everything is and fix microbags. +Test logs setting supports RSpec test (and WebMock stubbing) for Ruby On Rails application and API documentation supports API Blueprint, +Swagger and OpenAPI. -* If are you going developing API and write documentation for it, this tool will help you document-driven development easily create high-quality write API documentation and API. +This reduces the costs of support, testers and analysts. -* Also, if you have an undocumented API, this is an easy way to describe it. +Log +```text +FITTING incoming request {"method":"POST","path":"/public/api/v1/inboxes/tEX5JiZyceiwuKMi1oN9Sf8S/contacts","body":{},"response":{"status":200,"content_type":"application/json","body":{"source_id":"00dbf18d-879e-47cb-ac45-e9aece266eb1","pubsub_token":"ktn6YwPus57JDf4e59eFPom5","id":3291,"name":"shy-surf-401","email":null,"phone_number":null}},"title":"./spec/controllers/public/api/v1/inbox/contacts_controller_spec.rb:9","group":"./spec/controllers/public/api/v1/inbox/contacts_controller_spec.rb","host":"www.example.com"} +FITTING outgoing request {"method":"POST","path":"/v1/organizations/org_id/meeting","body":{},"response":{"status":200,"content_type":"application/json","body":{"success":true,"data":{"meeting":{"id":"meeting_id","roomName":"room_name"}}}},"title":"./spec/controllers/api/v1/accounts/integrations/dyte_controller_spec.rb:50","group":"./spec/controllers/api/v1/accounts/integrations/dyte_controller_spec.rb","host":"api.cluster.dyte.in"} +``` -![exmaple](images/example.png) +validation +```console +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.FFF..FFFFFFFFFF....F.......F...FF.....F...F....F..............................FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF..FF.F..FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF..FF........FFF...FFFF......FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF........FFFFFFFFFFF..FFFFFF..FFFFFFFFFFFFFFFFF.......FFFFFF.............FFFFFFFFFFFF....F........FFF.F...FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF............FF........FFF......FFFFFFFFFFFFFFFFFFFFFF....FFFFFF......F............FFFF........FFFFFFFFFFFFFF.....FFFFFFFFFFFFFFFFFFFFFFF..FF.....FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.....FF..........FFFFFFFFFFFFFFFFFF...FFFF...............F.F....FF..FFFFFFFF + 1) Fitting::Doc::NotFound log error: + +host: www.example.com +method: POST +path: /public/api/v1/inboxes/{inbox_identifier}/contacts +code: 200 + +content-type: application/json + +json-schema: { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Id of the contact" + }, + "source_id": { + "type": "string", + "description": "The session identifier of the contact" + }, + "name": { + "type": "string", + "description": "Name of the contact" + }, + "email": { + "type": "string", + "description": "Email of the contact" + }, + "pubsub_token": { + "type": "string", + "description": "The token to be used to connect to chatwoot websocket" + } + } +} + +body: { + "source_id": "c9e8c31f-06df-49b4-8fb9-4466457ae65b", + "pubsub_token": "Zgc7DEvaj5TkgZ1a4C7AvJXo", + "id": 3293, + "name": "restless-snowflake-670", + "email": null, + "phone_number": null +} + +error [ + "The property '#/email' of type null did not match the following type: string in schema e56b7e65-d70c-5f7a-a96c-982df5f8f2f7" +] + +... + +804 examples, 565 failure, 0 pending + +Coverage: 65.51% +``` + +and cover +![exmaple](images/b1.png) + +![exmaple](images/b2.png) + +![exmaple](images/w1.png) + +![exmaple](images/w2.png) + ## Installation Add this line to your application's Gemfile: ```ruby gem 'fitting' ``` @@ -30,173 +101,265 @@ ```bash $ gem install fitting ``` ## Usage -And next to your `spec_helper.rb`: +### Log +Firstly, improve `test.log`. +To your `spec_helper.rb`: + ```ruby require 'fitting' -Fitting.save_test_data +Fitting.logger ``` +Delete all files `log/*.log` and run rspec + +You get more information about incoming and outgoing request in `log/fitting*.log`. + +```text +FITTING incoming request {"method":"POST","path":"/public/api/v1/inboxes/tEX5JiZyceiwuKMi1oN9Sf8S/contacts","body":{},"response":{"status":200,"content_type":"application/json","body":{"source_id":"00dbf18d-879e-47cb-ac45-e9aece266eb1","pubsub_token":"ktn6YwPus57JDf4e59eFPom5","id":3291,"name":"shy-surf-401","email":null,"phone_number":null}},"title":"./spec/controllers/public/api/v1/inbox/contacts_controller_spec.rb:9","group":"./spec/controllers/public/api/v1/inbox/contacts_controller_spec.rb","host":"www.example.com"} +FITTING outgoing request {"method":"POST","path":"/v1/organizations/org_id/meeting","body":{},"response":{"status":200,"content_type":"application/json","body":{"success":true,"data":{"meeting":{"id":"meeting_id","roomName":"room_name"}}}},"title":"./spec/controllers/api/v1/accounts/integrations/dyte_controller_spec.rb:50","group":"./spec/controllers/api/v1/accounts/integrations/dyte_controller_spec.rb","host":"api.cluster.dyte.in"} +``` + +### Validation +Secondly, validate the logs to the documentation. + Add this to your `.fitting.yml`: -### OpenAPI 2.0 -Also Swagger +```yaml +APIs: + - host: www.example.com + type: openapi2 + path: swagger/swagger.json +``` +Run +```bash +bundle e rake fitting:validate +``` + +Console output + +```console +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.FFF..FFFFFFFFFF....F.......F...FF.....F...F....F..............................FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF..FF.F..FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF..FF........FFF...FFFF......FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF........FFFFFFFFFFF..FFFFFF..FFFFFFFFFFFFFFFFF.......FFFFFF.............FFFFFFFFFFFF....F........FFF.F...FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF............FF........FFF......FFFFFFFFFFFFFFFFFFFFFF....FFFFFF......F............FFFF........FFFFFFFFFFFFFF.....FFFFFFFFFFFFFFFFFFFFFFF..FF.....FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.....FF..........FFFFFFFFFFFFFFFFFF...FFFF...............F.F....FF..FFFFFFFF + + 1) Fitting::Doc::NotFound log error: + +host: www.example.com +method: POST +path: /public/api/v1/inboxes/{inbox_identifier}/contacts +code: 200 + +content-type: application/json + +json-schema: { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Id of the contact" + }, + "source_id": { + "type": "string", + "description": "The session identifier of the contact" + }, + "name": { + "type": "string", + "description": "Name of the contact" + }, + "email": { + "type": "string", + "description": "Email of the contact" + }, + "pubsub_token": { + "type": "string", + "description": "The token to be used to connect to chatwoot websocket" + } + } +} + +body: { + "source_id": "c9e8c31f-06df-49b4-8fb9-4466457ae65b", + "pubsub_token": "Zgc7DEvaj5TkgZ1a4C7AvJXo", + "id": 3293, + "name": "restless-snowflake-670", + "email": null, + "phone_number": null +} + +error [ + "The property '#/email' of type null did not match the following type: string in schema e56b7e65-d70c-5f7a-a96c-982df5f8f2f7" +] + +... + +804 examples, 565 failure, 0 pending + +Coverage: 65.51% +``` + +### Coverage +And task will create HTML (`coverage/fitting.html`) reports. + +![exmaple](images/b1.png) + +![exmaple](images/b2.png) + +More information on action coverage + +![exmaple2](images/w1.png) + +![exmaple2](images/w2.png) + +## Settings + +### APIs + +#### type + +##### OpenAPI 2.0 +Swagger + ```yaml -prefixes: - - name: /api/v1 +APIs: + - host: www.example.com type: openapi2 - schema_paths: - - doc.json + path: doc/api.json ``` -### OpenAPI 3.0 +##### OpenAPI 3.0 Also OpenAPI ```yaml -prefixes: - - name: /api/v1 +APIs: + - host: www.example.com type: openapi3 - schema_paths: - - doc.yaml + path: doc/api.json ``` -### API Blueprint -First you need to install [drafter](https://github.com/apiaryio/drafter). -Works after conversion from API Blueprint to API Elements (in YAML file) with Drafter. +##### API Blueprint +First you need to install [drafter](https://github.com/apiaryio/drafter) or [crafter](https://github.com/funbox/crafter). +Works after conversion from API Blueprint to API Elements (in YAML file) with Drafter or Crafter. That is, I mean that you first need to do this ```bash drafter doc.apib -o doc.yaml ``` +or + +```bash +node_modules/.bin/crafter doc.apib > doc.yaml +``` + and then ```yaml -prefixes: - - name: /api/v1 +APIs: + - host: www.example.com type: drafter - schema_paths: - - doc.yaml + path: doc/api.yaml ``` -### Tomograph +or -To use additional features of the pre-converted [tomograph](https://github.com/funbox/tomograph) - ```yaml -prefixes: - - name: /api/v1 - type: tomogram - schema_paths: - - doc.json +APIs: + - host: www.example.com + type: crafter + path: doc/api.yaml ``` -## Run -Run tests first to get run artifacts +##### Tomograph + +To use additional features of the pre-converted [tomograph](https://github.com/funbox/tomograph) + +example + ```bash -bundle e rspec +bundle exec tomograph -d crafter --exclude-description doc/api.yml doc/api.json ``` and then -```bash -bundle e rake fitting:report -``` -Run tests by outgoing request first to get run artifacts -```bash -bundle e rake fitting_out:report +```yaml +APIs: + - host: www.example.com + type: tomogram + path: doc/api.json ``` -Console ouptut +#### prefix -```text -/api/v1 -POST /api/v1/accounts/{account_id}/inboxes 0% 200 0% 404 0% 403 -PATCH /api/v1/accounts/{account_id}/inboxes/{id} 0% 200 0% 404 0% 403 -POST /api/v1/accounts/{account_id}/inboxes/{id}/set_agent_bot 0% 204 100% 404 0% 403 -GET /api/v1/agent_bots 0% 200 0% 404 0% 403 -GET /api/v1/accounts/{account_id}/conversations 0% 200 0% 400 0% description -POST /api/v1/accounts/{account_id}/conversations 0% 200 0% 403 -GET /api/v1/accounts/{account_id}/conversations/{id} 59% 200 0% 404 0% 403 -POST /api/v1/accounts/{account_id}/conversations/{id}/toggle_status 80% 200 0% 404 0% 403 -GET /api/v1/accounts/{account_id}/conversations/{id}/messages 47% 200 0% 404 0% 403 -POST /api/v1/accounts/{account_id}/conversations/{id}/messages 0% 200 0% 404 0% 403 -GET /api/v1/accounts/{account_id}/conversations/{id}/labels 100% 200 0% 404 0% 403 -POST /api/v1/accounts/{account_id}/conversations/{id}/labels 100% 200 0% 404 0% 403 -POST /api/v1/accounts/{account_id}/conversations/{id}/assignments 77% 200 0% 404 0% 403 -GET /api/v1/accounts/{account_id}/contacts 0% 200 0% 400 -POST /api/v1/accounts/{account_id}/contacts 14% 200 0% 400 -GET /api/v1/accounts/{account_id}/contacts/{id} 14% 200 0% 404 0% 403 -PUT /api/v1/accounts/{account_id}/contacts/{id} 0% 204 0% 404 0% 403 -GET /api/v1/accounts/{account_id}/contacts/{id}/conversations 0% 200 0% 404 0% 403 -GET /api/v1/accounts/{account_id}/contacts/search 0% 200 0% 401 -POST /api/v1/accounts/{account_id}/contacts/{id}/contact_inboxes 0% 200 0% 401 100% 422 -GET /api/v1/profile 88% 200 100% 401 +Setting the prefix name is optional. For example, you can do this: -tests_without_prefixes: 42 -tests_without_actions: 144 -tests_without_responses: 43 +```yaml +APIs: + - host: www.example.com + prefix: /api/v3 + type: openapi2 + path: swagger/swagger.json ``` -And task will create HTML (`fitting/index.html`) reports. +### SkipValidation -![exmaple](images/example.png) +#### host -More information on action coverage +It is not necessary to immediately describe each host in detail, you can only specify its name and skip it until you are ready to documented it -![exmaple2](images/example2.png) +```yaml +SkipAPIs: + - host: api.cluster.dyte.in +``` -## prefix name +#### prefix -Setting the prefix name is optional. For example, you can do this: +If you want to skip a specific prefix in the host ```yaml -prefixes: - - type: openapi2 - schema_paths: - - doc.json +SkipAPIs: + - host: api.cluster.dyte.in + prefix: /admin/api ``` -## prefix skip +#### method and path -It is not necessary to immediately describe each prefix in detail, you can only specify its name and skip it until you are ready to documented it +If you want to skip a specific request in the host + ```yaml -prefixes: -- name: /api/v1 - type: openapi2 - schema_paths: - - doc.json -- name: /api/v3 - skip: true +SkipAPIs: + - host: api.cluster.dyte.in + method: GET + path: /api/v1/cars ``` -For work with WebMock outgoing request, you should set up outgoing prefixes +### NoCov + +It is not necessary to immediately test each doc in detail, you can only specify its name and skip it until you are ready to test it + ```yaml -outgoing_prefixes: -- name: /api/v1 - type: openapi2 - schema_paths: - - doc.json -- name: /api/v3 - skip: true +NoCov: + - host: sso.test + method: GET + path: /users/{userId} ``` -You can choose location that must be teste +### Debug +If you find bug, you can debug it or create task in this github project with new file `coverage/fitting.debug.yml` + ```yaml -prefixes: - - type: openapi2 - schema_paths: - - doc.json - only: - - POST /api/v1/users - - GET /api/v1/user/{id} +Debug: + - host: www.example.com + method: GET + path: /api/v3/users + code: 200 + content-type: application/json ``` ## Contributing Bug reports and pull requests are welcome on GitHub at [github.com/funbox/fitting](https://github.com/funbox/fitting).