Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"files": null,
"lines": null
},
"generated_at": "2025-10-09T10:36:06Z",
"generated_at": "2025-10-09T13:24:14Z",
"plugins_used": [
{
"name": "AWSKeyDetector"
Expand Down Expand Up @@ -283,21 +283,21 @@
{
"hashed_secret": "e1166e6dd837019ab04f130ab34c425e04161645",
"is_verified": false,
"line_number": 382,
"line_number": 384,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "f0c5bc5473fd2f959bdac630e625aa33346fd12a",
"is_verified": false,
"line_number": 429,
"line_number": 431,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "29080f1c58f9859ddaa6aeda7d2c410c12e222dc",
"is_verified": false,
"line_number": 461,
"line_number": 463,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@ export class MockJwtCookieAuthGuard extends AuthGuard("cookie-strategy") {
const request: RequestWithUserSession = context.switchToHttp().getRequest();

request.user = {
userId: "dev-user",
role: UserRole.LEARNER,
groupId: "string",
assignmentId: 1888,
userId: "magdy.hafez@ibm.com1",
role: UserRole.AUTHOR,
groupId: "autogen-faculty-v1-course-v1-IND-AI0103EN-v1",
assignmentId: 1,
gradingCallbackRequired: false,
returnUrl: "https://skills.network",
launch_presentation_locale: "en",
};

return true;
}
}
77 changes: 46 additions & 31 deletions apps/api-gateway/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,51 @@ import { AppModule } from "./app.module";
import { winstonOptions } from "./logger/config";

async function bootstrap() {
const app = await NestFactory.create(AppModule, {
cors: false,
logger: WinstonModule.createLogger(winstonOptions),
});
app.use(json({ limit: "1000mb" }));
app.use(urlencoded({ limit: "1000mb", extended: true }));
app.setGlobalPrefix("api", {
exclude: ["health", "health/liveness", "health/readiness"],
});

app.enableVersioning({
type: VersioningType.URI,
});

app.use(helmet());

app.use(cookieParser());

const config = new DocumentBuilder()
.setTitle("API")
.setDescription("API Description")
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("api", app, document, {
customSiteTitle: "API Docs",
customCss: ".swagger-ui .topbar .topbar-wrapper { display: none; }",
});

app.enableShutdownHooks();

await app.listen(process.env.API_GATEWAY_PORT ?? 3000);
const logger = WinstonModule.createLogger(winstonOptions);

try {
const app = await NestFactory.create(AppModule, {
cors: false,
logger,
});
app.use(json({ limit: "1000mb" }));
app.use(urlencoded({ limit: "1000mb", extended: true }));
app.setGlobalPrefix("api", {
exclude: ["health", "health/liveness", "health/readiness"],
});

app.enableVersioning({
type: VersioningType.URI,
});

app.use(helmet());

app.use(cookieParser());

const config = new DocumentBuilder()
.setTitle("API")
.setDescription("API Description")
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("api", app, document, {
customSiteTitle: "API Docs",
customCss: ".swagger-ui .topbar .topbar-wrapper { display: none; }",
});

app.enableShutdownHooks();

const port = process.env.API_GATEWAY_PORT ?? 3000;
await app.listen(port, "0.0.0.0");

logger.log(`🚀 API Gateway is running on port ${port}`);
logger.log(
`📚 API Documentation available at http://localhost:${port}/api`,
);
} catch (error) {
logger.error("❌ Failed to start API Gateway:", error);
// eslint-disable-next-line unicorn/no-process-exit
process.exit(1);
}
}

void bootstrap();
3 changes: 2 additions & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"test:staged": "jest --bail --findRelatedTests --passWithNoTests --collectCoverage=false",
"test:watch": "jest --watch",
"create-initial-versions": "dotenv -e ./dev.env -- ts-node src/scripts/create-initial-versions.ts",
"translation-audit": "node dist/src/scripts/translation-audit.js"
"translation-audit": "node dist/src/scripts/translation-audit.js",
"verify-migration": "dotenv -e ./dev.env -- ts-node scripts/verify-migration.ts"
},
"prisma": {
"seed": "ts-node prisma/seed.ts"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- CreateEnum for CorrectAnswerVisibility
CREATE TYPE "CorrectAnswerVisibility" AS ENUM ('NEVER', 'ALWAYS', 'ON_PASS');

-- Add the new column with ALWAYS as default
ALTER TABLE "Assignment" ADD COLUMN "correctAnswerVisibility" "CorrectAnswerVisibility" DEFAULT 'ALWAYS';

-- Convert existing data: if showCorrectAnswer was true, set to ON_PASS, otherwise set to NEVER
UPDATE "Assignment"
SET "correctAnswerVisibility" = CASE
WHEN "showCorrectAnswer" = true THEN 'ON_PASS'::"CorrectAnswerVisibility"
ELSE 'NEVER'::"CorrectAnswerVisibility"
END;

-- Make the new column NOT NULL after data conversion
ALTER TABLE "Assignment" ALTER COLUMN "correctAnswerVisibility" SET NOT NULL;

-- Drop the old column
ALTER TABLE "Assignment" DROP COLUMN "showCorrectAnswer";

-- Add the new column to AssignmentVersion table
ALTER TABLE "AssignmentVersion" ADD COLUMN "correctAnswerVisibility" "CorrectAnswerVisibility" DEFAULT 'ALWAYS';

-- Update existing AssignmentVersion data: copy from parent Assignment
UPDATE "AssignmentVersion" av
SET "correctAnswerVisibility" = a."correctAnswerVisibility"
FROM "Assignment" a
WHERE av."assignmentId" = a.id;

-- Make the new column NOT NULL after data conversion
ALTER TABLE "AssignmentVersion" ALTER COLUMN "correctAnswerVisibility" SET NOT NULL;
20 changes: 12 additions & 8 deletions apps/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ generator client {
provider = "prisma-client-js"
}

// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
Expand All @@ -23,6 +22,13 @@ enum AssignmentQuestionDisplayOrder {
DEFINED /// The assignments are displayed in a predefined order
RANDOM /// The assignments are displayed in a random order
}

/// This enum defines when correct answers should be visible to learners
enum CorrectAnswerVisibility {
NEVER /// Correct answers are never shown to learners
ALWAYS /// Correct answers are always shown after submission
ON_PASS /// Correct answers are only shown when learners pass the assignment
}
model GradingAudit {
id Int @id @default(autoincrement())
questionId Int
Expand Down Expand Up @@ -82,7 +88,7 @@ model Job {
progress String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
result Json? // Add this field to store generated questions
result Json?
}
model GradingJob {
id Int @id @default(autoincrement())
Expand Down Expand Up @@ -114,7 +120,7 @@ model publishJob {
percentage Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
result Json? // Add this field to store generated questions
result Json?
}
/// This model tracks the AI usage for each assignment and usage type
model AIUsage {
Expand Down Expand Up @@ -188,7 +194,6 @@ enum ChatRole {
SYSTEM
}

/// The Group model represents a particular group of assignments (having the same groupID)
model Group {
id String @unique /// Unique ID of the course
assignments AssignmentGroup[] /// The list of assignments associated with this group
Expand All @@ -204,7 +209,6 @@ model AssignmentGroup {
@@id([assignmentId, groupId]) /// Unique identifier for the relationship
}

/// The Assignment model represents a particular assignment
model Assignment {
id Int @id @default(autoincrement()) /// Unique identifier for the assignment
name String /// Name of the assignment
Expand All @@ -231,8 +235,8 @@ model Assignment {
showAssignmentScore Boolean @default(true) /// Should the assignment score be shown to the learner after its submission
showQuestionScore Boolean @default(true) /// Should the question score be shown to the learner after its submission
showSubmissionFeedback Boolean @default(true) /// Should the AI provide feedback when the learner submits a question
showQuestions Boolean @default(true) /// Should the questions be shown to the learner
showCorrectAnswer Boolean @default(true) /// Should the correct answer be shown to the learner after its submission
showQuestions Boolean @default(true) /// Should the questions be shown to the learner
correctAnswerVisibility CorrectAnswerVisibility @default(ALWAYS) /// When should correct answers be shown to learners
updatedAt DateTime @default(now()) @updatedAt /// The DateTime at which the assignment was last updated
languageCode String? /// The language code for the assignment
currentVersionId Int? /// The ID of the current active version
Expand Down Expand Up @@ -282,6 +286,7 @@ model AssignmentVersion {
showQuestionScore Boolean @default(true) /// Show question score
showSubmissionFeedback Boolean @default(true) /// Show submission feedback
showQuestions Boolean @default(true) /// Show questions
correctAnswerVisibility CorrectAnswerVisibility @default(ALWAYS) /// When to show correct answers to learners
languageCode String? /// Language code
createdBy String /// User who created this version
createdAt DateTime @default(now()) /// When version was created
Expand Down Expand Up @@ -523,7 +528,6 @@ model Report {

similarityScore Float? // Similarity score (0-1) with the parent report
}
// Add to schema.prisma
model UserNotification {
id Int @id @default(autoincrement())
userId String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
UserSessionRequest,
} from "../../../auth/interfaces/user.session.interface";
import { Roles } from "../../../auth/role/roles.global.guard";
import { SuccessPageDataDto } from "../../attempt/dto/success-page-data.dto";

Check warning on line 30 in apps/api/src/api/assignment/attempt/attempt.controller.ts

View workflow job for this annotation

GitHub Actions / Lint & Test - api

'SuccessPageDataDto' is defined but never used
import {
GRADE_SUBMISSION_EXCEPTION,
IN_COOLDOWN_PERIOD,
Expand Down
Loading
Loading