From 2a065d21053d3413a43f0b24aa0048952491690c Mon Sep 17 00:00:00 2001 From: Kaia Peacock Date: Fri, 6 Jun 2025 14:45:48 -0700 Subject: [PATCH 1/2] fixes async* apis that are not compatible with later releases of moc --- README.md | 4 -- canister_ids.json | 5 ++- dfx.json | 2 +- examples/http_greet/dfx.json | 16 -------- examples/http_greet/mops.toml | 3 +- examples/http_greet/src/http_greet/main.mo | 40 +++++++++--------- examples/http_greet/src/http_greet/upload.js | 4 +- examples/test/test.mo | 18 ++++---- src/lib.mo | 43 ++++++++++---------- 9 files changed, 60 insertions(+), 75 deletions(-) delete mode 100644 examples/http_greet/dfx.json diff --git a/README.md b/README.md index f1d9c40..eb19b47 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,3 @@ These functions are used internally by the library, but are also exported for us - Encodes a request as a blob - `public func yieldResponse(b : HttpResponse) : Blob` - Encodes a response as a blob - -## Credits - -This project currently copies the `http-parser` library into its source tree. This is because the `http-parser` library is not currently installable as a package on `mops`. Source code is available at https://github.com/NatLabs/http-parser.mo diff --git a/canister_ids.json b/canister_ids.json index 712d411..df1ac92 100644 --- a/canister_ids.json +++ b/canister_ids.json @@ -7,5 +7,8 @@ }, "test": { "ic": "qpyq6-kiaaa-aaaab-qaidq-cai" + }, + "http_greet": { + "ic": "qg33c-4aaaa-aaaab-qaica-cai" } -} \ No newline at end of file +} diff --git a/dfx.json b/dfx.json index bcd9d06..ecc23c1 100644 --- a/dfx.json +++ b/dfx.json @@ -19,7 +19,7 @@ }, "defaults": { "build": { - "packtool": "mops sources" + "packtool": "npx mops sources" } } } diff --git a/examples/http_greet/dfx.json b/examples/http_greet/dfx.json deleted file mode 100644 index bafbea2..0000000 --- a/examples/http_greet/dfx.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "canisters": { - "http_greet": { - "main": "src/http_greet/main.mo", - "type": "motoko" - } - }, - "defaults": { - "build": { - "args": "", - "packtool": "npx mops sources" - } - }, - "output_env_file": ".env", - "version": 1 -} diff --git a/examples/http_greet/mops.toml b/examples/http_greet/mops.toml index fad932f..777f22a 100644 --- a/examples/http_greet/mops.toml +++ b/examples/http_greet/mops.toml @@ -1,9 +1,10 @@ [package] name = "http_hello" -version = "0.1.0" +version = "1.0.0" description = "" repository = "" [dependencies] certified-cache = "0.2.0" assets = "0.2.0" +http-parser = "0.3.1" diff --git a/examples/http_greet/src/http_greet/main.mo b/examples/http_greet/src/http_greet/main.mo index c76dd69..7245803 100644 --- a/examples/http_greet/src/http_greet/main.mo +++ b/examples/http_greet/src/http_greet/main.mo @@ -20,7 +20,7 @@ shared ({ caller = creator }) actor class () { server.post( "/greet", - func(req : Request, res : ResponseClass) : async* Response { + func(req : Request, res : ResponseClass) : async Response { let parsedName = req.url.queryObj.get("name"); var name = ""; switch parsedName { @@ -42,7 +42,7 @@ shared ({ caller = creator }) actor class () { server.get( "/foo", - func(req : Request, res : ResponseClass) : async* Response { + func(req : Request, res : ResponseClass) : async Response { res.send({ status_code = 200; headers = [("Content-Type", "text/html")]; @@ -53,14 +53,14 @@ shared ({ caller = creator }) actor class () { }, ); - public shared ({ caller }) func authorize(other : Principal) : async* () { + public shared ({ caller }) func authorize(other : Principal) : async () { server.authorize({ caller; other; }); }; - public query func retrieve(path : Assets.Path) : async* Assets.Contents { + public query func retrieve(path : Assets.Path) : async Assets.Contents { assets.retrieve(path); }; @@ -72,14 +72,14 @@ shared ({ caller = creator }) actor class () { content : Blob; sha256 : ?Blob; } - ) : async* () { + ) : async () { server.store({ caller; arg; }); }; - public query func list(arg : {}) : async* [T.AssetDetails] { + public query func list(arg : {}) : async [T.AssetDetails] { assets.list(arg); }; @@ -88,7 +88,7 @@ shared ({ caller = creator }) actor class () { key : T.Key; accept_encodings : [Text]; } - ) : async* ({ + ) : async ({ content : Blob; content_type : Text; content_encoding : Text; @@ -98,7 +98,7 @@ shared ({ caller = creator }) actor class () { assets.get(arg); }; - public shared ({ caller }) func create_batch(arg : {}) : async* ({ + public shared ({ caller }) func create_batch(arg : {}) : async ({ batch_id : T.BatchId; }) { assets.create_batch({ @@ -112,7 +112,7 @@ shared ({ caller = creator }) actor class () { batch_id : T.BatchId; content : Blob; } - ) : async* ({ + ) : async ({ chunk_id : T.ChunkId; }) { assets.create_chunk({ @@ -121,42 +121,42 @@ shared ({ caller = creator }) actor class () { }); }; - public shared ({ caller }) func commit_batch(args : T.CommitBatchArguments) : async* () { + public shared ({ caller }) func commit_batch(args : T.CommitBatchArguments) : async () { assets.commit_batch({ caller; args; }); }; - public shared ({ caller }) func create_asset(arg : T.CreateAssetArguments) : async* () { + public shared ({ caller }) func create_asset(arg : T.CreateAssetArguments) : async () { assets.create_asset({ caller; arg; }); }; - public shared ({ caller }) func set_asset_content(arg : T.SetAssetContentArguments) : async* () { + public shared ({ caller }) func set_asset_content(arg : T.SetAssetContentArguments) : async () { assets.set_asset_content({ caller; arg; }); }; - public shared ({ caller }) func unset_asset_content(args : T.UnsetAssetContentArguments) : async* () { + public shared ({ caller }) func unset_asset_content(args : T.UnsetAssetContentArguments) : async () { assets.unset_asset_content({ caller; args; }); }; - public shared ({ caller }) func delete_asset(args : T.DeleteAssetArguments) : async* () { + public shared ({ caller }) func delete_asset(args : T.DeleteAssetArguments) : async () { assets.delete_asset({ caller; args; }); }; - public shared ({ caller }) func clear(args : T.ClearArguments) : async* () { + public shared ({ caller }) func clear(args : T.ClearArguments) : async () { assets.clear({ caller; args; @@ -165,7 +165,7 @@ shared ({ caller = creator }) actor class () { public type StreamingStrategy = { #Callback : { - callback : shared query StreamingCallbackToken -> async* StreamingCallbackHttpResponse; + callback : shared query StreamingCallbackToken -> async StreamingCallbackHttpResponse; token : StreamingCallbackToken; }; }; @@ -182,14 +182,14 @@ shared ({ caller = creator }) actor class () { token : ?StreamingCallbackToken; }; - public query func http_request_streaming_callback(token : T.StreamingCallbackToken) : async* StreamingCallbackHttpResponse { + public query func http_request_streaming_callback(token : T.StreamingCallbackToken) : async StreamingCallbackHttpResponse { assets.http_request_streaming_callback(token); }; - public query func http_request(req : HttpRequest) : async* HttpResponse { + public query func http_request(req : HttpRequest) : async HttpResponse { server.http_request(req); }; - public func http_request_update(req : HttpRequest) : async* HttpResponse { - await* server.http_request_update(req); + public func http_request_update(req : HttpRequest) : async HttpResponse { + await server.http_request_update(req); }; /** diff --git a/examples/http_greet/src/http_greet/upload.js b/examples/http_greet/src/http_greet/upload.js index 2616e66..06d228b 100644 --- a/examples/http_greet/src/http_greet/upload.js +++ b/examples/http_greet/src/http_greet/upload.js @@ -16,7 +16,7 @@ const Ids = require("../../.dfx/local/canister_ids.json"); const HOST = `http://127.0.0.1:4943`; // const HOST = "https://icp-api.io"; // const canisterId = Ids["http_greet"]["local"]; -const canisterId = "br5f7-7uaaa-aaaaa-qaaca-cai"; +const canisterId = "u6s2n-gx777-77774-qaaba-cai"; // const canisterId = "qg33c-4aaaa-aaaab-qaica-cai"; console.log(`canisterId: ${canisterId}`); @@ -30,6 +30,8 @@ seed.fill(0); const testIdentity = Ed25519KeyIdentity.generate(seed); +console.log(`testIdentity: ${testIdentity.getPrincipal().toText()}`); + // spawn shell command const { spawn } = require("child_process"); const child = spawn("dfx", [ diff --git a/examples/test/test.mo b/examples/test/test.mo index e77a60e..dc14469 100644 --- a/examples/test/test.mo +++ b/examples/test/test.mo @@ -20,7 +20,7 @@ shared ({ caller = creator }) actor class () { server.get( "/", - func(_ : Request, res : ResponseClass) : async* Response { + func(_ : Request, res : ResponseClass) : async Response { res.send({ status_code = 200; headers = [("Content-Type", "text/html")]; @@ -36,7 +36,7 @@ shared ({ caller = creator }) actor class () { // Cached endpoint server.get( "/hi", - func(_ : Request, res : ResponseClass) : async* Response { + func(_ : Request, res : ResponseClass) : async Response { res.send({ headers = [("Content-Type", "text/plain")]; status_code = 200; @@ -49,7 +49,7 @@ shared ({ caller = creator }) actor class () { server.get( "/json", - func(_ : Request, res : ResponseClass) : async* Response { + func(_ : Request, res : ResponseClass) : async Response { res.json({ status_code = 200; body = "{\"hello\":\"world\"}"; @@ -60,7 +60,7 @@ shared ({ caller = creator }) actor class () { server.get( "/404", - func(_ : Request, res : ResponseClass) : async* Response { + func(_ : Request, res : ResponseClass) : async Response { res.send({ status_code = 404; headers = [("Content-Type", "text/plain")]; @@ -80,7 +80,7 @@ shared ({ caller = creator }) actor class () { // Dynamic endpoint server.get( "/queryParams", - func(req : Request, res : ResponseClass) : async* Response { + func(req : Request, res : ResponseClass) : async Response { let obj = req.url.queryObj; let keys = Iter.fromArray(obj.keys); @@ -129,7 +129,7 @@ shared ({ caller = creator }) actor class () { server.get( "/cats", - func(_ : Request, res : ResponseClass) : async* Response { + func(_ : Request, res : ResponseClass) : async Response { var catJson = "["; for (cat in Iter.fromArray(cats)) { catJson := catJson # displayCat(cat) # ","; @@ -147,7 +147,7 @@ shared ({ caller = creator }) actor class () { server.get( "/cats/:name", - func(req : Request, res : ResponseClass) : async* Response { + func(req : Request, res : ResponseClass) : async Response { switch (req.params) { case null { res.send({ @@ -224,7 +224,7 @@ shared ({ caller = creator }) actor class () { server.post( "/cats", - func(req : Request, res : ResponseClass) : async* Response { + func(req : Request, res : ResponseClass) : async Response { let body = req.body; switch body { case null { @@ -271,7 +271,7 @@ shared ({ caller = creator }) actor class () { server.http_request(req); }; public func http_request_update(req : HttpRequest) : async HttpResponse { - await* server.http_request_update(req); + await server.http_request_update(req); }; public func invalidate_cache() : async () { diff --git a/src/lib.mo b/src/lib.mo index a8c5a6f..6563950 100644 --- a/src/lib.mo +++ b/src/lib.mo @@ -60,7 +60,7 @@ module { public func yieldResponse(b : HttpResponse) : Blob { b.body }; // Private Types - type HttpFunction = (Request) -> async* Response; + type HttpFunction = (Request) -> async Response; type RequestMap = HashMap.StableHashMap; type CacheStrategy = { @@ -162,14 +162,14 @@ module { return null; }; - private func handleFunction(map : HashMap.StableHashMap, req : Request, fallback : ?((Request) -> Response)) : async* Response { + private func handleFunction(map : HashMap.StableHashMap, req : Request, fallback : ?((Request) -> Response)) : async Response { let (simplifiedBaseRoute, simplifiedFullRoute) = Utils.simplifyRoute(req.url); ignore do ? { // Check for an exact match, including query parameters switch (map.get(simplifiedFullRoute)) { case (?f) { - return await* f(req); + return await f(req); }; case null {}; }; @@ -177,7 +177,7 @@ module { // Check for a match with the base route switch (map.get(simplifiedBaseRoute)) { case (?f) { - return await* f(req); + return await f(req); }; case null {}; }; @@ -193,7 +193,7 @@ module { body = req.body; params = ?params; }; - return await* f(request); + return await f(request); }; case null {}; }; @@ -209,20 +209,20 @@ module { missingResponse; }; - private func process_request(req : Request) : async* Response { + private func process_request(req : Request) : async Response { switch (req.method) { case "GET" { let fallback = staticFallback; - await* handleFunction(getRequests, req, ?fallback); + await handleFunction(getRequests, req, ?fallback); }; case "POST" { - await* handleFunction(postRequests, req, null); + await handleFunction(postRequests, req, null); }; case "PUT" { - await* handleFunction(putRequests, req, null); + await handleFunction(putRequests, req, null); }; case "DELETE" { - await* handleFunction(deleteRequests, req, null); + await handleFunction(deleteRequests, req, null); }; case _ { missingResponse; @@ -265,7 +265,6 @@ module { headers = response.headers; body = response.body; streaming_strategy = null; - upgrade = null; // expire after 10 seconds cache_strategy = #expireAfter { nanoseconds = Int.abs(Time.now()) + 10 * one_second_in_nanos; @@ -309,12 +308,12 @@ module { // Register a request handler that will be cached // GET requests are cached by default // POST, PUT, DELETE requests are not cached - private func registerRequestWithHandler(method : Text, path : Text, handler : (request : Request, response : ResponseClass) -> async* Response) { + private func registerRequestWithHandler(method : Text, path : Text, handler : (request : Request, response : ResponseClass) -> async Response) { if (method == "GET") { registerRequest( method, path, - func(request : Request) : async* Response { + func(request : Request) : async Response { var response = handler( request, ResponseClass( @@ -329,14 +328,14 @@ module { } ), ); - return await* response; + return await response; }, ); } else { registerRequest( method, path, - func(request : Request) : async* Response { + func(request : Request) : async Response { var response = handler( request, ResponseClass( @@ -351,25 +350,25 @@ module { } ), ); - return await* response; + return await response; }, ); }; }; - public func get(path : Text, handler : (request : Request, response : ResponseClass) -> async* Response) { + public func get(path : Text, handler : (request : Request, response : ResponseClass) -> async Response) { registerRequestWithHandler("GET", path, handler); }; - public func post(path : Text, handler : (request : Request, response : ResponseClass) -> async* Response) { + public func post(path : Text, handler : (request : Request, response : ResponseClass) -> async Response) { registerRequestWithHandler("POST", path, handler); }; - public func put(path : Text, handler : (request : Request, response : ResponseClass) -> async* Response) { + public func put(path : Text, handler : (request : Request, response : ResponseClass) -> async Response) { registerRequestWithHandler("PUT", path, handler); }; - public func delete(path : Text, handler : (request : Request, response : ResponseClass) -> async* Response) { + public func delete(path : Text, handler : (request : Request, response : ResponseClass) -> async Response) { registerRequestWithHandler("DELETE", path, handler); }; @@ -448,7 +447,7 @@ module { }; }; - public func http_request_update(request : HttpRequest) : async* HttpResponse { + public func http_request_update(request : HttpRequest) : async HttpResponse { // prune the cache ignore cache.remove(request); @@ -464,7 +463,7 @@ module { params = null; }; - let response = await* process_request(parsedrequest); + let response = await process_request(parsedrequest); let formattedResponse = { status_code = response.status_code; From 08c515f0ff3e6dc0d9c8a03c1a2d9d16d5ddc561 Mon Sep 17 00:00:00 2001 From: Kaia Peacock Date: Fri, 6 Jun 2025 14:50:32 -0700 Subject: [PATCH 2/2] fixing e2e job --- .github/workflows/e2e-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 9d8889c..0c99d39 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -50,7 +50,7 @@ jobs: run: npm run test:e2e - name: Clean Up - run: killall dfx replica + run: dfx killall aggregate: name: e2e:required