-
Notifications
You must be signed in to change notification settings - Fork 318
/
main.mo
146 lines (123 loc) · 5.47 KB
/
main.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import Debug "mo:base/Debug";
import Blob "mo:base/Blob";
import Cycles "mo:base/ExperimentalCycles";
import Array "mo:base/Array";
import Nat8 "mo:base/Nat8";
import Text "mo:base/Text";
//import the custom types we have in Types.mo
import Types "Types";
actor {
//function to transform the response
public query func transform(raw : Types.TransformArgs) : async Types.CanisterHttpResponsePayload {
let transformed : Types.CanisterHttpResponsePayload = {
status = raw.response.status;
body = raw.response.body;
headers = [
{
name = "Content-Security-Policy";
value = "default-src 'self'";
},
{
name = "Referrer-Policy";
value = "strict-origin"
},
{
name = "Permissions-Policy";
value = "geolocation=(self)" },
{
name = "Strict-Transport-Security";
value = "max-age=63072000";
},
{
name = "X-Frame-Options";
value = "DENY"
},
{
name = "X-Content-Type-Options";
value = "nosniff"
},
];
};
transformed;
};
//PULIC METHOD
//This method sends a POST request to a URL with a free API we can test.
public func send_http_post_request() : async Text {
//1. DECLARE IC MANAGEMENT CANISTER
//We need this so we can use it to make the HTTP request
let ic : Types.IC = actor ("aaaaa-aa");
//2. SETUP ARGUMENTS FOR HTTP GET request
// 2.1 Setup the URL and its query parameters
//This URL is used because it allows us to inspect the HTTP request sent from the canister
let host : Text = "putsreq.com";
let url = "https://putsreq.com/aL1QS5IbaQd4NTqN3a81"; //HTTP that accepts IPV6
// 2.2 prepare headers for the system http_request call
//idempotency keys should be unique so we create a function that generates them.
let idempotency_key: Text = generateUUID();
let request_headers = [
{ name = "Host"; value = host # ":443" },
{ name = "User-Agent"; value = "http_post_sample" },
{ name= "Content-Type"; value = "application/json" },
{ name= "Idempotency-Key"; value = idempotency_key }
];
// The request body is an array of [Nat8] (see Types.mo) so we do the following:
// 1. Write a JSON string
// 2. Convert ?Text optional into a Blob, which is an intermediate reprepresentation before we cast it as an array of [Nat8]
// 3. Convert the Blob into an array [Nat8]
let request_body_json: Text = "{ \"name\" : \"Grogu\", \"force_sensitive\" : \"true\" }";
let request_body_as_Blob: Blob = Text.encodeUtf8(request_body_json);
let request_body_as_nat8: [Nat8] = Blob.toArray(request_body_as_Blob); // e.g [34, 34,12, 0]
// 2.2.1 Transform context
let transform_context : Types.TransformContext = {
function = transform;
context = Blob.fromArray([]);
};
// 2.3 The HTTP request
let http_request : Types.HttpRequestArgs = {
url = url;
max_response_bytes = null; //optional for request
headers = request_headers;
//note: type of `body` is ?[Nat8] so we pass it here as "?request_body_as_nat8" instead of "request_body_as_nat8"
body = ?request_body_as_nat8;
method = #post;
transform = ?transform_context;
};
//3. ADD CYCLES TO PAY FOR HTTP REQUEST
//IC management canister will make the HTTP request so it needs cycles
//See: https://internetcomputer.org/docs/current/motoko/main/cycles
//The way Cycles.add() works is that it adds those cycles to the next asynchronous call
//See: https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-http_request
Cycles.add(230_850_258_000);
//4. MAKE HTTPS REQUEST AND WAIT FOR RESPONSE
//Since the cycles were added above, we can just call the IC management canister with HTTPS outcalls below
let http_response : Types.HttpResponsePayload = await ic.http_request(http_request);
//5. DECODE THE RESPONSE
//As per the type declarations in `Types.mo`, the BODY in the HTTP response
//comes back as [Nat8s] (e.g. [2, 5, 12, 11, 23]). Type signature:
//public type HttpResponsePayload = {
// status : Nat;
// headers : [HttpHeader];
// body : [Nat8];
// };
//We need to decode that [Na8] array that is the body into readable text.
//To do this, we:
// 1. Convert the [Nat8] into a Blob
// 2. Use Blob.decodeUtf8() method to convert the Blob to a ?Text optional
// 3. We use Motoko syntax "Let... else" to unwrap what is returned from Text.decodeUtf8()
let response_body: Blob = Blob.fromArray(http_response.body);
let decoded_text: Text = switch (Text.decodeUtf8(response_body)) {
case (null) { "No value returned" };
case (?y) { y };
};
//6. RETURN RESPONSE OF THE BODY
let result: Text = decoded_text # ". See more info of the request sent at: " # url # "/inspect";
result
};
//PRIVATE HELPER FUNCTION
//Helper method that generates a Universally Unique Identifier
//this method is used for the Idempotency Key used in the request headers of the POST request.
//For the purposes of this exercise, it returns a constant, but in practice it should return unique identifiers
func generateUUID() : Text {
"UUID-123456789";
}
};