MSON data structures from external file - apiblueprint

I want to use the MSON data structure defined in an external file and refer to it in my API blueprint file. Is it possible? As of now I define both in the same file.
FORMAT: 1A
HOST: https://polls.apiblueprint.org/
# Suppliers
CRUD API to Add,Query,Update and Delete Suppliers.
## Suppliers Collection [/api/v1/suppliers]
### Add a Supplier [POST /supplier]
+ Parameters
-id : '1' (number) - The Supplier Id
-name: 'Supplier Name' (string) - The Supplier Name
+ Response 201 (application/json)
### Add multiple Suppliers [POST /suppliers]
+ Request (application/json)
- suppliers (array[Supplier])
+ Response 201 (application/json)
### Get All Suppliers [GET /suppliers]
+ Response 200 (application/json)
+ Attributes
- suppliers (array[Supplier])
+ Response 206 (application/json)
+ Attributes
- suppliers (array[Supplier])
### Get Individual Suppliers [GET /suppliers/{id}]
+ Response 200 (application/json)
+ Attributes
- Include Supplier
### Update Supplier [PUT /supplier]
+ Request (application/json)
- supplier (Supplier)
+ Response 200 (application/json)
### Update Suppliers [PUT /suppliers]
+ Request (application/json)
- suppliers (array[Supplier])
+ Response 200 (application/json)
### Delete a Supplier [DELETE /supplier]
+ Request (application/json)
- supplier (Supplier)
+ Response 204 (application/json)
# Data Structures
## Supplier (object)
- id : '1' (number) - The Supplier Id
- name : 'Supplier Name' (string) - The Supplier Name

Related

Linq with join - Get single result

I'm stucked with a linq query and I need a little bit of help.
I have the following tables
Table: Client
ClientId Name Age
(integer)
-------- ---------- -----
12635 John 23
87263 Derek 43
65237 Sarah 36
84735 Alice 28
Table: Action
Id Action ObjecId
(varchar)
-------- --------- ---------
202 Firefox 87263
203 Chrome 65237
204 Android 87263
205 Explorer 84735
206 Firefox 12635
My goal is to join both tables using ClientId and ObjectId fields. And for the Action table for each user I need to get only one record. If user has two records I need to get only the one with value='Android'
So the result with the above data should be:
Derek Android
Sarah Chrome
Alicie Explorer
John Firefox
I'm using the following approach.
var query = Set as IQueryable<Client>;
var actionQuery = Set as IQueryable<Action>
query= query.Join(actionQuery,
Client=> Client.ClientId.ToString(),
Action => Action.ObjectId.ToString(),
(Client, Action) => Client);
With the above I have a duplicate record for Derek, I need to get only one record for him, but I don't know how to do it. If a user has 2 records I need to get only the one with value='Android'.
Please, can you help me?
Thanks
If you need only a record where the ActionId is the highest, I'd do the following:
query = query.Join(actionQuery,
Client=> Client.ClientId.ToString(),
Action => Action.ObjectId.ToString(),
(Client, Action) => new { Client = Client, Action = Action })
.GroupBy(g => g.Client.Id)
.Select(g => g.OrderByDescending(gr => gr.Action.Id).First());
EDIT:
After the OP specified that if there are more actions for a particular client, one of those is always "Android" and this one must be chosen, I'm providing the following query:
query = query.Join(actions,
Client => Client.Id.ToString(),
Action => Action.ObjectId.ToString(),
(Client, Action) => new { Client = Client, Action = Action })
.GroupBy(g => g.Client.Id)
.Select(g => (g.Count() > 1) ? g.Where(gr => "Android".Equals(gr.Action.ActionDesc)).First() : g.First());

Multipart request rejected by Web API

I'm trying to send a Multipart request to a Web API using HttpWebRequest. The request I'm sending has the following format:
----------636194206488346738
Content-Disposition: form-data; name="file"; filename="A.png"
Content-Type:application/octet-stream
Content-Transfer-Encoding: binary
{Binary data in here}
----------636194206488346738--
{new line at the end}
And the request configuration is as follows:
Content-Type:"multipart/form-data; boundary=----------636194206488346738
Method: POST
Keep-Alive: True
When sending the request to the web API I get the Invalid end of stream error. However, I tried to convert the stream to text to see the actual data and it matches the example I added above.
However, when I'm using the WebClient and call the UploadFile method for the same purpose, I can successfully upload files to the API without any problem suggesting that something is wrong with my approach which is as follows.
My Constants:
Boundary = DateTime.Now.Ticks.ToString();
ContentType = "multipart/form-data; boundary=" + BoundaryDelimiter + Boundary;
BeginContent = System.Text.Encoding.UTF8.GetBytes("\r\n" + BoundaryDelimiter + Boundary + "\r\n");
EndContent = System.Text.Encoding.UTF8.GetBytes("\r\n" + BoundaryDelimiter + Boundary + "--\r\n");
Method for formatting form data:
private Byte[] FormDataFormat(String name, String fileName, String contentType)
=> System.Text.Encoding.UTF8.GetBytes(String.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type:{2}\r\nContent-Transfer-Encoding: binary\r\n\r\n", name, fileName, contentType));
Attaching file to a stream:
Stream = new MemoryStream();
foreach (var i in files) {
var tempEncode = FormDataFormat("file", i, "application/octet-stream");
var file = System.IO.File.ReadAllBytes(i); // Files are supposed to be small.
Stream.Write(BeginContent, 0, BeginContent.Length);
Stream.Write(tempEncode, 0, tempEncode.Length);
Stream.Write(file, 0, file.Length);
ContentLenght += BeginContent.Length + tempEncode.Length + file.Length;
}
Stream.Write(EndContent, 0, EndContent.Length);
ContentLenght += EndContent.Length;
Creating the request:
public HttpWebRequest Request(String method) {
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.ContentType = ContentType;
request.KeepAlive = true;
request.Method = method;
request.ContentLength = ContentLenght;
Stream.Seek(0, SeekOrigin.Begin);
Stream.CopyTo(request.GetRequestStream());
Stream.Dispose();
return request;
}
You haven't shown in your question the BoundaryDelimiter variable declaration but for the purpose of this answer I will assume that it is defined like this:
BoundaryDelimiter = "---------------------"
Now the problem with your code is that you are missing a couple of dashes (--) when calculating your boundary delimiters in order to conform to RFC2388.
So instead of:
BeginContent = System.Text.Encoding.UTF8.GetBytes("\r\n" + BoundaryDelimiter + Boundary + "\r\n");
EndContent = System.Text.Encoding.UTF8.GetBytes("\r\n" + BoundaryDelimiter + Boundary + "--\r\n");
You need this:
BeginContent = System.Text.Encoding.UTF8.GetBytes("\r\n" + BoundaryDelimiter + "--" + Boundary + "\r\n");
EndContent = System.Text.Encoding.UTF8.GetBytes("\r\n" + BoundaryDelimiter + "--" + Boundary + "--\r\n");
Notice the additional -- that I have added to your begin and end content boundary delimiters.
Check the example provided here:
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="submit-name"
Larry
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
Notice how the boundary is equal to AaB03x but the delimiter is actually --AaB03x and of course the end delimiter is --AaB03x--.
As a side note you could get rid of the ContentLenght variable (which you have misspelled by the way :-)) and remove this line:
request.ContentLength = ContentLenght
This will simplify your code as the HttpWebRequest class will automatically populate the Content-Length header depending on the number of bytes you have written to the request stream.
This being said, I would recommend you using the HttpClient class instead of HttpWebRequest because it already has built-in capabilities for properly encoding this kind of stuff. It will definitely make your code more readable as you will not have to worry about boundaries, delimiters and content lengths.

Mix Request/Response Body and Data Structures

I would like use aglio/api blueprint to create a nice documentation for our new API.
The JSON might be quite big (with lots of optional values), so I'd like to give a proper use case in the body, but also use data structures for the json schema.
However, whenever the schema fits exactly the body, the resulting HTML throws "Hello, world!"s at me, since I didn't fill in the example data - but since I've got a complete and valid example in the body, I wouldn't have expected aglio to create Hello World output.
For reference, that's what I would expect in the resulting htmls body to appear:
{
"a": "i want this to appear",
"b": "in my aglio",
"c": "html file"
}
This is what actually does appear:
{
"a": "Hello, world!",
"b": "Hello, world!",
"c": "Hello, world!",
"d": "Hello, world!"
}
And that's the raw api blueprint:
FORMAT: 1A
# JSON Schema
# Test [/post/something]
## A Test [POST]
+ Request (application/json)
+ Attributes (SomeObject)
+ Body
{
"a": "i want this to appear",
"b": "in my aglio",
"c": "html file"
}
+ Response 200
# Data Structures
## SomeObject (object)
+ a (string) - A
+ b (string) - B
+ c (string) - C
+ d (string, optional) - I'm optional, yet don't want to appear in the html, only the schema
So, first: is that a valid way to do things? Would you recommend a different approach? Is this a bug in aglio, because in apiary it works as I intend it to?
Thanks!
Found the corresponding github issue, looks like nothing's wrong with my description.
https://github.com/danielgtaylor/aglio/issues/221
You can do the following
FORMAT: 1A
# JSON Schema
# Test [/post/something]
JSON Schema Title
## A Test [POST]
+ Request (application/json)
+ Attributes (object)
+ a a-value (string, required) - description about a
+ b b-value (number, required) - description about b
+ c c-value (string, required) - description about c
+ d [a1, a2, a3] (array, optional) - I'm optional
+ Response 200 (application/json)
{
"message": "this works"
}
a a-value (string, required) - description
In above line "a" --> attribute name, "a-value" --> appears in body, "(string, required)" --> for schema generation, "description"--> schema attribute description

Bad Request error when calling HTTP_POST?

I have a question about the SAP Function Module "http_post".
I just want to post a short message (msg) out of the SAP to a Push Notification Server (pushd-Github-Projekt) I installed before. Now I'm not sure how to pass the message.
I tested the FM with the test-symbol:
CALL FUNCTION 'HTTP_POST'
exporting
ABSOLUTE_URI = uri " Uniform Resource Identifier (RFC 1945)
* REQUEST_ENTITY_BODY_LENGTH = 14 "request_entity_body_length
* RFC_DESTINATION = " RFC Destination
* PROXY = " HTTP Proxy Rechner
* PROXY_USER = " Benutzername auf dem Proxy Rechner
* PROXY_PASSWORD = " Passwort auf dem Proxy Rechner
* USER = " Benutzername auf dem HTTP Server
* PASSWORD = " Passwort auf dem HTTP Server
* BLANKSTOCRLF = " Blanks in CRLF konvertieren im Entity Body
* importing
* STATUS_CODE = " Statuscode ( 2xx = OK )
* STATUS_TEXT = " Text zum Statuscode
* RESPONSE_ENTITY_BODY_LENGTH = " Länge vom Response-Entity-Body
tables
REQUEST_ENTITY_BODY = '{"msg":"test"}' "request_entity_body
RESPONSE_ENTITY_BODY = '' " Response-Entity-Body Daten
RESPONSE_HEADERS = '' " Header Zeilen vom Response
REQUEST_HEADERS = 'Content-Type: application/json' "request_headers
* exceptions
* CONNECT_FAILED = 1
* TIMEOUT = 2
* INTERNAL_ERROR = 3
* TCPIP_ERROR = 4
* SYSTEM_FAILURE = 5
* COMMUNICATION_FAILURE = 6
* OTHERS = 7
.
I know my values are no tables, but I tested it with the test-symbol, where you can write the values directly in a table.
When I start the FM, I get an Bad Request error in the SAP
and this error at the push notification server:
SyntaxError: Unexpected token
at Object.parse (native)
at IncomingMessage.<anonymous> ...Path from the pushd..
express\node_modules\connect\lib\middleware\json.js:76:27
at incomingMessage.EventEmitter.emit events.js:92:17
at _stream:readable.js:919:16
at process._tickCallback <node.js:419:13>
Can anyone help me how to pass the request to the FM HTTP-Post? It has to be sth. with msg, because otherwise the Push-Notification-Server can't handle it.
In SAP_BASIS release 731 or higher, I would strongly recommend to use the class CL_HTTP_CLIENTfor doing HTTP requests. See here an example report on how to do it. Replace the dummy string http:1.2.3.4:80/testjon/by your URL in question.
report z_test_http_post.
start-of-selection.
perform start.
* ---
form start.
data: lv_status type i,
lv_error_occurred type flag,
lv_error_msg type string,
lv_response_body type string.
perform send_json using
'http://1.2.3.4:80/testjson/' " Use your URL here
'{"hello":"world"}' " Use your JSON here
changing lv_status lv_response_body
lv_error_occurred
lv_error_msg.
* Show result
format color col_heading.
write: / 'Response status:', lv_status.
if lv_error_occurred = 'X'.
format color col_negative.
write: / 'Error occurred:', lv_error_msg.
endif.
format color col_normal.
write: / 'Response:', lv_response_body.
endform. "start
form send_json using iv_url type string
iv_json_data type string
changing cv_status type i
cv_response_body type string
cv_error_occurred type flag
cv_error_msg type string.
data: lo_client type ref to if_http_client.
clear: cv_error_msg,
cv_status,
cv_error_occurred,
cv_error_msg.
if iv_url is initial.
* No URL passed
message e349(sbds) into cv_error_msg.
cv_error_occurred = 'X'.
return.
endif.
call method cl_http_client=>create_by_url
exporting
url = iv_url
importing
client = lo_client
exceptions
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
others = 4.
if sy-subrc ne 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
into cv_error_msg.
cv_error_occurred = 'X'.
return.
endif.
lo_client->request->set_cdata( iv_json_data ).
lo_client->request->set_content_type( 'application/json' ).
lo_client->request->set_method( 'POST' ).
call method lo_client->send
exceptions
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3
others = 4.
if sy-subrc ne 0.
lo_client->get_last_error( importing message = cv_error_msg ).
cv_error_occurred = 'X'.
return.
endif.
lo_client->receive( exceptions others = 1 ).
if sy-subrc ne 0.
lo_client->get_last_error( importing message = cv_error_msg ).
cv_error_occurred = 'X'.
return.
endif.
cv_response_body = lo_client->response->get_cdata( ).
lo_client->response->get_status( importing code = cv_status ).
endform.

A more elgant way to roll up multiple rows?

I'm new to codeigniter (although I suppose this isn't an exclusively CI question) and have a method in a model which selects data from two tables joined by id.
table 1 (tblclients) looks like this:
+----+------------+
+ id + c_name +
+----+------------+
+ 1 + Joe Bloggs +
+ 2 + Jim Bloggs +
+ 3 + Tim Bloggs +
+----+------------+
table 2 (tblstars) looks like this:
+----+------------+
+ id + s_date +
+----+------------+
+ 1 + 27/01/12 +
+ 1 + 15/02/12 +
+ 1 + 18/02/12 +
+ 2 + 03/01/12 +
+ 2 + 11/02/12 +
+ 2 + 15/02/12 +
+ 3 + 01/01/12 +
+ 3 + 19/02/12 +
+----+------------+
I want to 'roll up' the joined data into one line for each row in tblclients so I can output, for example:
+----+------------+--------------------------------+
+ id + Name + Dates +
+----+------------+--------------------------------+
+ 1 + Joe Bloggs + 27/01/12 15/02/12 18/02/12 +
+ 2 + Jim Bloggs + 03/01/12 11/02/12 15/02/12 +
+ 3 + Tim Bloggs + 01/01/12 19/02/12 +
+----+------------+--------------------------------+
Now I've 'solved' the problem by using the following in my model:
function get_clients_concat()
{
$query = $this->db
->select('tblclients.id, tblclients.c_name, GROUP_CONCAT(tblstars.s_date SEPARATOR "#") AS star_dates', NULL, FALSE)
->join('tblstars', 'tblstars.id = tblclients.id', 'left')
->order_by('tblclients.id')
->group_by('tblclients.id')
->get('tblclients');
return $query->result();
}
and then exploding the array (created by the GROUP_CONCAT) in my view and doing some processing with it there ... but it seems really CLUNKY.
Is there a better solution?
If you want to get all that data in a single query (with each row's id unique), then yes--that's the way to go.
If you need to sort or filter the results, you'll run into performance bottlenecks when the tables fill up.
It seems odd though--why wouldn't you select from tblstars (joining tblclients) instead, then use application logic to index the dates by c_name/id?
<?php
// Select data from tables
$data = $this->db
->select('tblclients.id, tblclients.c_name, tblstars.s_date')
->join('tblclients', 'tblclients.id = tblstars.id')
->order_by('tblstars.id')
->get('tblstars');
// Index data by client id
// (keeping record of client name and dates array for each)
$clients = array();
foreach ($data->result() as $result)
{
if (empty($clients[$result->id]))
{
$clients[$result->id] = array(
'name' => $result->c_name,
'dates' => array($result->s_date)
);
}
else
{
$clients[$result->id]['dates'][] = $result->s_date;
}
}
return $clients;

Resources