Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds simple POST endpoint at /runSquiggle, and adds open-api.json schema #2523

Merged
merged 11 commits into from
Jan 1, 2024
53 changes: 53 additions & 0 deletions packages/hub/src/app/api/runSquiggle/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from "next/server";

import { runSquiggle } from "@/graphql/queries/runSquiggle";

export async function POST(req: NextRequest) {
// Assuming 'code' is sent in the request body and is a string
try {
const body = await req.json();
if (body.code) {
let response = await runSquiggle(body.code);
if (response.isOk) {
return new NextResponse(
JSON.stringify({
result: response.resultJSON,
bindings: response.bindingsJSON,
}),
{
status: 200,
statusText: "OK",
headers: { "Content-Type": "application/json" },
}
);
} else {
return new NextResponse(
JSON.stringify({ error: response.errorString }),
{
status: 400,
statusText: "ERROR",
headers: { "Content-Type": "application/json" },
}
);
}
} else {
return new NextResponse(JSON.stringify({ error: "No code provided" }), {
status: 400,
statusText: "ERROR",
headers: { "Content-Type": "application/json" },
});
}
} catch (error) {
// Catch any errors, including JSON parsing errors
console.error("Error in POST request:", error);
return new NextResponse(
//We could give more information here, but we don't want to leak any information
JSON.stringify({ error: "An internal error occurred" }),
{
status: 500,
statusText: "Internal Server Error",
headers: { "Content-Type": "application/json" },
}
);
}
}
30 changes: 10 additions & 20 deletions packages/hub/src/graphql/queries/runSquiggle.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { Prisma } from "@prisma/client";
import crypto from "crypto";

import {
SqAbstractDistribution,
SqProject,
SqValue,
} from "@quri/squiggle-lang";
import { SqProject, SqValue } from "@quri/squiggle-lang";

import { builder } from "@/graphql/builder";
import { prisma } from "@/prisma";
Expand All @@ -14,19 +10,8 @@ function getKey(code: string): string {
return crypto.createHash("md5").update(code).digest("base64");
}

export const squiggleValueToJSON = (value: SqValue) => {
// this is a lazy shortcut to traverse the value tree; should be reimplemented without parse/stringify
return JSON.parse(
JSON.stringify(value.asJS(), (key, value) => {
if (value instanceof Map) {
return Object.fromEntries(value.entries());
}
if (value instanceof SqAbstractDistribution) {
return value.toString();
}
return value;
})
);
export const squiggleValueToJSON = (value: SqValue): any => {
return value.asJS();
};

type SquiggleOutput = {
Expand Down Expand Up @@ -91,10 +76,15 @@ builder.objectType(
}
);

async function runSquiggle(code: string): Promise<SquiggleOutput> {
export async function runSquiggle(code: string): Promise<SquiggleOutput> {
const MAIN = "main";

const project = SqProject.create();
const env = {
sampleCount: 1000, // int
xyPointLength: 1000, // int
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could see making this configurable later

};

const project = SqProject.create({ environment: env });

project.setSource(MAIN, code);
await project.run(MAIN);
Expand Down
91 changes: 91 additions & 0 deletions packages/hub/src/public/openapi-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"openapi": "3.0.0",
"info": {
"title": "Squiggle Code Processing API",
"version": "0.0.1"
},
"servers": [
{
"url": "https://squigglehub.org/api"
}
],
"paths": {
"/runSquiggle": {
"post": {
"summary": "Run the given Squiggle code string.",
"operationId": "runSquiggle",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The code to be processed."
}
},
"required": ["code"]
}
}
}
},
"responses": {
"200": {
"description": "Code processed successfully.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"result": {
"type": "string",
"description": "The result of the code processing."
},
"bindings": {
"type": "string",
"description": "Additional bindings from the code processing."
}
}
}
}
}
},
"400": {
"description": "Error in processing or no code provided.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string",
"description": "Error message explaining the failure."
}
}
}
}
}
},
"500": {
"description": "Internal Server Error.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string",
"description": "A generic error message indicating a server problem."
}
}
}
}
}
}
}
}
}
}
}
10 changes: 3 additions & 7 deletions packages/squiggle-lang/__tests__/public/SqValue_test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { OrderedMap } from "immutable";

import { run, sq } from "../../src/index.js";
import { testRun } from "../helpers/helpers.js";

Expand All @@ -9,23 +7,21 @@ describe("SqValue.asJS", () => {
await testRun('{ x: 5, y: [3, "foo", { dist: normal(5,2) } ] }')
).asJS();

expect(value).toBeInstanceOf(OrderedMap);
expect(value).toBeInstanceOf(Object);
});

test("Dict fields", async () => {
const value = (await testRun("{ x: 5 }")).asJS();

expect((value as any).get("value").get("x")).toBe(5);
expect((value as any).value.x).toBe(5);
});

test("Deeply nested dist", async () => {
const value = (
await testRun('{ x: 5, y: [3, "foo", { dist: normal(5,2) } ] }')
).asJS();

expect(
(value as any).get("value").get("y")[2].get("value").get("dist")
).toBeInstanceOf(Array);
expect((value as any).value.y[2].value.dist).toBeInstanceOf(Array);
});
});

Expand Down
Loading