Skip to content

Commit

Permalink
Merge branch 'develop' into Gradebook-Page-styling-Cards#78&82
Browse files Browse the repository at this point in the history
  • Loading branch information
jessehartloff authored Oct 16, 2024
2 parents 1d32dd1 + f9e5ade commit 4a22c58
Show file tree
Hide file tree
Showing 42 changed files with 1,481 additions and 475 deletions.
Binary file added .DS_Store
Binary file not shown.
59 changes: 59 additions & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Builds DevU images on develop
# tagged as beta

name: Build DevU beta
on:
push:
branches:
- develop

jobs:
build-docker:
runs-on: ubuntu-latest
permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
packages: write # to be able to publish docker image packages

steps:
- uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0

# todo run tests

# docker image build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

- name: Convert image name to lowercase
run: |
original_string=${{ github.repository }}
echo "repo_url=$(echo $original_string | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
# beta tags
- name: API beta image
run: |
docker build . -f api.Dockerfile -t ghcr.io/${{ env.repo_url }}-api:beta
docker push ghcr.io/${{ env.repo_url }}-api:beta
- name: client beta image
run: |
docker build . -f client.Dockerfile -t ghcr.io/${{ env.repo_url }}-client:beta
docker push ghcr.io/${{ env.repo_url }}-client:beta
- name: nginx beta image
run: |
docker build . -f nginx.Dockerfile -t ghcr.io/${{ env.repo_url }}-nginx:beta
docker push ghcr.io/${{ env.repo_url }}-nginx:beta
- name: build tango image
run: |
docker build ./tango -f ./tango/Dockerfile -t ghcr.io/${{ env.repo_url }}-tango:beta
docker push ghcr.io/${{ env.repo_url }}-tango:beta
97 changes: 97 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Builds DevU images for release
# tagged as latest and version number

name: Release
on:
push:
branches:
- release

jobs:
tag-release:
name: tag-release
runs-on: ubuntu-latest
permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
packages: write # to be able to publish docker image packages

steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "lts/*"
- name: install plugins
run: npm install --no-save @semantic-release/git @semantic-release/changelog -D

- name: tag version number release based on commits
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release

build-docker:
needs:
- tag-release
runs-on: ubuntu-latest
permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
packages: write # to be able to publish docker images

steps:
- uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0

- name: Get version tag from git history
id: tagName
uses: "WyriHaximus/github-action-get-previous-tag@v1"

# todo run tests
- name: Convert image name to lowercase
run: |
original_string=${{ github.repository }}
echo "repo_url=$(echo $original_string | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
# docker image build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

- name: API latest and version image
run: |
docker build . -f api.Dockerfile -t ghcr.io/${{ env.repo_url }}-api:${{ steps.tagName.outputs.tag }}
docker push ghcr.io/${{ env.repo_url }}-api:${{ steps.tagName.outputs.tag }}
docker build . -f api.Dockerfile -t ghcr.io/${{ env.repo_url }}-api:latest
docker push ghcr.io/${{ env.repo_url }}-api:latest
- name: client latest and version image
run: |
docker build . -f client.Dockerfile -t ghcr.io/${{ env.repo_url }}-client:${{ steps.tagName.outputs.tag }}
docker push ghcr.io/${{ env.repo_url }}-client:${{ steps.tagName.outputs.tag }}
docker build . -f client.Dockerfile -t ghcr.io/${{ env.repo_url }}-client:latest
docker push ghcr.io/${{ env.repo_url }}-client:latest
- name: nginx latest and version image
run: |
docker build . -f nginx.Dockerfile -t ghcr.io/${{ env.repo_url }}-nginx:${{ steps.tagName.outputs.tag }}
docker push ghcr.io/${{ env.repo_url }}-nginx:${{ steps.tagName.outputs.tag }}
docker build . -f nginx.Dockerfile -t ghcr.io/${{ env.repo_url }}-nginx:latest
docker push ghcr.io/${{ env.repo_url }}-nginx:latest
- name: tango latest and version image
run: |
docker build ./tango -f ./tango/Dockerfile -t ghcr.io/${{ env.repo_url }}-tango:latest
docker push ghcr.io/${{ env.repo_url }}-tango:latest
14 changes: 14 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"branches": [
{
"name": "release"
}
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/github",
"@semantic-release/git"
]
}
30 changes: 19 additions & 11 deletions api.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
FROM python:alpine AS config

WORKDIR /stage
COPY devU-api/config ./config
COPY devU-api/scripts/generateConfig.sh ./generateConfig.sh
RUN apk add --no-cache bash jq openssl \
&& pip install yq
RUN ./generateConfig.sh ./default.yml

FROM node:20 as module_builder
FROM node:20 AS module_builder

WORKDIR /tmp

Expand All @@ -17,6 +8,19 @@ RUN npm install && \
npm run clean-directory && \
npm run build-docker

FROM docker.io/python:alpine AS config-builder

WORKDIR /config

RUN apk add --no-cache bash jq openssl \
&& pip install yq

COPY devU-api/scripts/ .

COPY devU-api/config/ ./config

RUN ./generateConfig.sh default.yml

FROM node:20

WORKDIR /app
Expand All @@ -27,9 +31,13 @@ RUN npm install

COPY ./devU-api .

COPY --from=config /stage/default.yml ./config/default.yml
COPY --from=config-builder /config/default.yml ./config/default.yml

COPY --from=module_builder /tmp/devu-shared-modules ./devu-shared-modules

# Indicate that the api is running in docker; value here is irrelevant
ENV IS_DOCKER=0

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait /wait
RUN chmod +x /wait

Expand Down
8 changes: 4 additions & 4 deletions devU-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
"version": "1.0.0",
"description": "DevU API",
"scripts": {
"start": "ts-node-dev src/index.ts",
"update-shared": "npm update devu-shared-modules",
"start": "npm run migrate && ts-node-dev src/index.ts",
"migrate": "npm run typeorm -- migration:run -d src/database.ts",
"update-shared": "cd ../devU-shared && npm run build-local && npm i",
"typeorm": "typeorm-ts-node-commonjs",
"test": "jest --passWithNoTests",
"clean": "rimraf build/*",
"lint": "tsc",
"build": "npm-run-all clean lint",
"format": "prettier --write \"./**/*.{js,ts,json,md}\"",
"pre-commit": "lint-staged",
"generate-config": "docker run --pull always -v $(pwd)/config:/config --user $(id -u):$(id -g) --rm ubautograding/devtools /generateConfig.sh config/default.yml",
"populate-db": "ts-node-dev ./scripts/populate-db.ts",
"tango": "ts-node-dev ./src/tango/tests/tango.endpoint.test.ts",
"api-services": "docker compose -f ../docker-compose.yml --profile dev-api up -d",
"api-services": "docker compose -f ../docker-compose.yml --profile dev-api up",
"api-services-stop": "docker compose -f ../docker-compose.yml --profile dev-api stop"
},
"lint-staged": {
Expand Down
77 changes: 75 additions & 2 deletions devU-api/src/entities/assignment/assignment.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import AssignmentService from './assignment.service'
import { GenericResponse, NotFound, Updated } from '../../utils/apiResponse.utils'

import { serialize } from './assignment.serializer'
import { BucketNames, downloadFile, uploadFile } from '../../fileStorage'
import { generateFilename } from '../../utils/fileUpload.utils'

export async function detail(req: Request, res: Response, next: NextFunction) {
try {
Expand All @@ -22,6 +24,40 @@ export async function detail(req: Request, res: Response, next: NextFunction) {
}
}

export async function handleAttachmentLink(req: Request, res: Response, next: NextFunction) {
try {
const bucketName = BucketNames.ASSIGNMENTSATTACHMENTS
const courseId = parseInt(req.params.courseId)
const fileName = req.params.filename
const assignmentId = parseInt(req.params.assignmentId)

if (!courseId) return res.status(400).json('Bucket not found')
if (!fileName) return res.status(400).json('File name not found')
if (!assignmentId) return res.status(400).json('Assignment id not found')

const assignment = await AssignmentService.retrieve(assignmentId, courseId)
if (!assignment) return res.status(404).json('Assignment not found')

const ind = assignment.attachmentsHashes.findIndex(value => {
return value == fileName
})
if (ind == -1) return res.status(404).json('File not found')

const file = assignment.attachmentsHashes[ind]
const name = assignment.attachmentsFilenames[ind]

const buffer = await downloadFile(bucketName, file)

res.setHeader('Content-Disposition', `attachment; filename="${name}"`)
res.setHeader('Content-Type', 'application/octet-stream')
res.setHeader('Content-Length', buffer.length)
res.send(buffer)
} catch (error) {
console.error('Error retrieving file:', error)
res.status(500).send('Error retrieving file')
}
}

export async function getByCourse(req: Request, res: Response, next: NextFunction) {
try {
const courseId = parseInt(req.params.courseId)
Expand All @@ -34,6 +70,7 @@ export async function getByCourse(req: Request, res: Response, next: NextFunctio
next(err)
}
}

export async function getReleased(req: Request, res: Response, next: NextFunction) {
try {
const courseId = parseInt(req.params.courseId)
Expand All @@ -47,21 +84,57 @@ export async function getReleased(req: Request, res: Response, next: NextFunctio
}
}


async function processFiles(req: Request) {
let fileHashes: string[] = []
let fileNames: string[] = []

// save files
if (req.files) {
console.log()
if (Array.isArray(req.files)) {
for (let index = 0; index < req.files.length; index++) {
const item = req.files[index]
const filename = generateFilename(item.originalname, item.size)
await uploadFile(BucketNames.ASSIGNMENTSATTACHMENTS, item, filename)
fileHashes.push(filename)
fileNames.push(item.originalname)
}
} else {
console.warn(`Files where not in array format ${req.files}`)
}
} else {
console.warn(`No files where processed`)
}

return { fileHashes, fileNames }
}

export async function post(req: Request, res: Response, next: NextFunction) {
try {
const { fileNames, fileHashes } = await processFiles(req)

req.body['attachmentsFilenames'] = fileNames
req.body['attachmentsHashes'] = fileHashes

const assignment = await AssignmentService.create(req.body)
const response = serialize(assignment)

res.status(201).json(response)
} catch (err) {
if (err instanceof Error) {
res.status(400).json(new GenericResponse(err.message))
res.status(400).json(new GenericResponse(err.message))
}
}
}

export async function put(req: Request, res: Response, next: NextFunction) {
try {
const { fileNames, fileHashes } = await processFiles(req)

req.body['attachmentsFilenames'] = fileNames
req.body['attachmentsHashes'] = fileHashes

req.body.id = parseInt(req.params.assignmentId)
const results = await AssignmentService.update(req.body)

Expand All @@ -86,4 +159,4 @@ export async function _delete(req: Request, res: Response, next: NextFunction) {
}
}

export default { detail, post, put, _delete, getByCourse, getReleased }
export default { detail, post, put, _delete, getByCourse, getReleased, handleAttachmentLink }
14 changes: 14 additions & 0 deletions devU-api/src/entities/assignment/assignment.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ export default class AssignmentModel {
* type: string
* format: date-time
* description: Must be in ISO 8601 format
* fileHashes:
* type: string
* array: true
* description: filename hashes of stored attachments use this to retrieve and query attachments, matches the index of the fileNames, i.e. filename[i] is the name of hash[i]
* fileNames:
* type: string
* array: true
* description: filenames of stored attachments, matches the index of the fileHashes, i.e. filename[i] is the name of hash[i]
*/

@PrimaryGeneratedColumn()
Expand Down Expand Up @@ -95,4 +103,10 @@ export default class AssignmentModel {

@DeleteDateColumn({ name: 'deleted_at' })
deletedAt?: Date

@Column({ name: 'attachmentsHashes', array: true, default: [], type: 'text' })
attachmentsHashes: string[]

@Column({ name: 'attachmentsFilenames', array: true, default: [], type: 'text', nullable: false })
attachmentsFilenames: string[]
}
Loading

0 comments on commit 4a22c58

Please sign in to comment.