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

Public spec generation in separate endpoint #264

Merged
merged 12 commits into from
Jul 29, 2021
Merged
5 changes: 2 additions & 3 deletions services/cms/src/blocks/ExerciseTask/IFrameEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,11 @@ const IFrameEditor: React.FC<IFrameEditorProps> = ({ url, props }) => {
if (uncheckedMessage === "current-state") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const uncheckedData = (messageContainer as any).data
if (!uncheckedData || !uncheckedData.private_spec || !uncheckedData.public_spec) {
console.error("Invalid message")
if (!uncheckedData || !uncheckedData.private_spec) {
console.error("Invalid message: missing message.data or message.data.private_spec")
return
}
props.setAttributes({
jolampi marked this conversation as resolved.
Show resolved Hide resolved
public_spec: JSON.stringify(uncheckedData.public_spec),
private_spec: JSON.stringify(uncheckedData.private_spec),
})
}
Expand Down
7 changes: 5 additions & 2 deletions services/example-exercise/src/pages/api/grade.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { NextApiRequest, NextApiResponse } from "next"

import { Alternative, Answer } from "../../util/stateInterfaces"
import { Alternative, Answer, ClientErrorResponse } from "../../util/stateInterfaces"

export default (req: NextApiRequest, res: NextApiResponse): unknown => {
export default (
req: NextApiRequest,
res: NextApiResponse<GradingResult | ClientErrorResponse>,
): void => {
if (req.method !== "POST") {
return res.status(404).json({ message: "Not found" })
}
Expand Down
29 changes: 29 additions & 0 deletions services/example-exercise/src/pages/api/public-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { NextApiRequest, NextApiResponse } from "next"

import { Alternative, ClientErrorResponse, PublicAlternative } from "../../util/stateInterfaces"

export default (req: NextApiRequest, res: NextApiResponse): void => {
if (req.method !== "POST") {
return res.status(404).json({ message: "Not found" })
}

return handleGet(req, res)
}

function handleGet(
req: NextApiRequest,
res: NextApiResponse<PublicAlternative[] | ClientErrorResponse>,
) {
const uncheckedAlternatives: unknown = req.body
if (!Array.isArray(uncheckedAlternatives)) {
return res
.status(400)
.json({ message: "Malformed data:" + JSON.stringify(uncheckedAlternatives) })
}

const publicAlternatives = uncheckedAlternatives.map<PublicAlternative>((x: Alternative) => ({
id: x.id,
name: x.name,
}))
return res.status(200).json(publicAlternatives)
}
18 changes: 8 additions & 10 deletions services/example-exercise/src/pages/api/service-info.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
import type { NextApiRequest, NextApiResponse } from "next"

import { ExerciseServiceInfoApi } from "../../shared-module/bindings"
import basePath from "../../shared-module/utils/base-path"
import { ClientErrorResponse } from "../../util/stateInterfaces"

export default (req: NextApiRequest, res: NextApiResponse): unknown => {
export default (
req: NextApiRequest,
res: NextApiResponse<ExerciseServiceInfoApi | ClientErrorResponse>,
): void => {
if (req.method !== "GET") {
return res.status(404).json({ message: "Not found" })
}

return handleGet(req, res)
}

interface ServiceInfo {
service_name: string
editor_iframe_path: string
exercise_iframe_path: string
submission_iframe_path: string
grade_endpoint_path: string
}

const handleGet = (_req: NextApiRequest, res: NextApiResponse<ServiceInfo>) => {
const handleGet = (_req: NextApiRequest, res: NextApiResponse<ExerciseServiceInfoApi>) => {
const prefix = basePath()
res.json({
service_name: "Example exercise",
editor_iframe_path: `${prefix}/editor`,
exercise_iframe_path: `${prefix}/exercise`,
submission_iframe_path: `${prefix}/submission`,
grade_endpoint_path: `${prefix}/api/grade`,
public_spec_endpoint_path: `${prefix}/api/public-spec`,
})
}
9 changes: 5 additions & 4 deletions services/example-exercise/src/pages/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useEffect, useState } from "react"

import Editor from "../components/Editor"
import useStateWithOnChange from "../hooks/useStateWithOnChange"
import convertStateToSpecs from "../util/convertStateToSpecs"
import { Alternative } from "../util/stateInterfaces"

const EditorPage: React.FC = () => {
Expand All @@ -11,10 +10,12 @@ const EditorPage: React.FC = () => {
if (!port) {
return
}
port.postMessage({
const message = {
message: "current-state",
data: convertStateToSpecs(newValue),
})
data: { private_spec: newValue },
}
console.info("Sending current data", JSON.stringify(message))
port.postMessage(message)
})

// const router = useRouter()
Expand Down
25 changes: 0 additions & 25 deletions services/example-exercise/src/util/convertStateToSpecs.ts

This file was deleted.

4 changes: 4 additions & 0 deletions services/example-exercise/src/util/stateInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ export interface Alternative {
export interface Answer {
selectedOptionId?: string
}

export interface ClientErrorResponse {
message: string
}
2 changes: 1 addition & 1 deletion services/headless-lms/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ blake3 = "0.3.7"
# Recursively walk a directory.
walkdir = "2.3.2"
# higher level HTTP client library
reqwest = { version = "0.11.3", features = ["brotli", "gzip"] }
reqwest = { version = "0.11.3", features = ["brotli", "gzip", "json"] }
# Sessions for Actix web
actix-session = "0.5.0-beta.1"
# An extensible, strongly-typed implementation of OAuth2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Add down migration script here
COMMENT ON COLUMN exercise_service_info.public_spec_endpoint_path IS NULL;
COMMENT ON COLUMN exercise_service_info.submission_iframe_path IS NULL;
ALTER TABLE exercise_service_info DROP COLUMN public_spec_endpoint_path;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Add up migration script here
ALTER TABLE exercise_service_info
ADD COLUMN public_spec_endpoint_path VARCHAR(255);
UPDATE exercise_service_info
SET public_spec_endpoint_path = ''
WHERE public_spec_endpoint_path IS NULL;
ALTER TABLE exercise_service_info
ALTER COLUMN public_spec_endpoint_path
SET NOT NULL;
COMMENT ON COLUMN exercise_service_info.public_spec_endpoint_path IS 'URL to an endpoint that will generate the public spec for an exercise service from respective private spec.';
COMMENT ON COLUMN exercise_service_info.submission_iframe_path IS 'URL to the iframe where an exercise submission can be viewed.';
Loading