feat(backend): Verwerking van leerobjecten in ZIP-formaat.
This commit is contained in:
		
							parent
							
								
									509dd6bfab
								
							
						
					
					
						commit
						86ba4ea11e
					
				
					 4 changed files with 192 additions and 1 deletions
				
			
		|  | @ -2,7 +2,13 @@ import dwengoApiLearningObjectProvider from './dwengo-api-learning-object-provid | |||
| import { LearningObjectProvider } from './learning-object-provider.js'; | ||||
| import { envVars, getEnvVar } from '../../util/envVars.js'; | ||||
| import databaseLearningObjectProvider from './database-learning-object-provider.js'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { | ||||
|     FilteredLearningObject, | ||||
|     LearningObjectIdentifierDTO, | ||||
|     LearningPathIdentifier | ||||
| } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import {getLearningObjectRepository} from "../../data/repositories"; | ||||
| import {processLearningObjectZip} from "./learning-object-zip-processing-service"; | ||||
| 
 | ||||
| function getProvider(id: LearningObjectIdentifierDTO): LearningObjectProvider { | ||||
|     if (id.hruid.startsWith(getEnvVar(envVars.UserContentPrefix))) { | ||||
|  | @ -42,6 +48,21 @@ const learningObjectService = { | |||
|     async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> { | ||||
|         return getProvider(id).getLearningObjectHTML(id); | ||||
|     }, | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Store the learning object in the given zip file in the database. | ||||
|      */ | ||||
|     async storeLearningObject(learningObjectPath: string): Promise<void> { | ||||
|         const learningObjectRepository = getLearningObjectRepository(); | ||||
|         const learningObject = await processLearningObjectZip(learningObjectPath); | ||||
| 
 | ||||
|         if (!learningObject.hruid.startsWith(getEnvVar(envVars.UserContentPrefix))) { | ||||
|             throw Error("Learning object name must start with the user content prefix!"); | ||||
|         } | ||||
| 
 | ||||
|         await learningObjectRepository.save(learningObject, {preventOverwrite: true}); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| export default learningObjectService; | ||||
|  |  | |||
|  | @ -0,0 +1,63 @@ | |||
| import unzipper from 'unzipper'; | ||||
| import mime from 'mime-types'; | ||||
| import {LearningObjectMetadata} from "@dwengo-1/common/dist/interfaces/learning-content"; | ||||
| import {LearningObject} from "../../entities/content/learning-object.entity"; | ||||
| import {getAttachmentRepository, getLearningObjectRepository} from "../../data/repositories"; | ||||
| 
 | ||||
| /** | ||||
|  * Process an uploaded zip file and construct a LearningObject from its contents. | ||||
|  * @param filePath Path of the zip file to process. | ||||
|  */ | ||||
| export async function processLearningObjectZip(filePath: string): Promise<LearningObject> { | ||||
|     const learningObjectRepo = getLearningObjectRepository(); | ||||
|     const attachmentRepo = getAttachmentRepository(); | ||||
| 
 | ||||
|     const zip = await unzipper.Open.file(filePath); | ||||
| 
 | ||||
|     let metadata: LearningObjectMetadata | null = null; | ||||
|     const attachments: {name: string, content: Buffer}[] = []; | ||||
|     let content: Buffer | null = null; | ||||
| 
 | ||||
|     for (const file of zip.files) { | ||||
|         if (file.type === "Directory") { | ||||
|             throw Error("The learning object zip file should not contain directories."); | ||||
|         } else if (file.path === "metadata.json") { | ||||
|             metadata = await processMetadataJson(file); | ||||
|         } else if (file.path.startsWith("index.")) { | ||||
|             content = await processFile(file); | ||||
|         } else { | ||||
|             attachments.push({ | ||||
|                 name: file.path, | ||||
|                 content: await processFile(file) | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (!metadata) { | ||||
|         throw Error("Missing metadata.json file"); | ||||
|     } | ||||
|     if (!content) { | ||||
|         throw Error("Missing index file"); | ||||
|     } | ||||
| 
 | ||||
|     const learningObject = learningObjectRepo.create(metadata); | ||||
|     const attachmentEntities = attachments.map(it => attachmentRepo.create({ | ||||
|         name: it.name, | ||||
|         content: it.content, | ||||
|         mimeType: mime.lookup(it.name) || "text/plain", | ||||
|         learningObject | ||||
|     })) | ||||
|     learningObject.attachments.push(...attachmentEntities); | ||||
| 
 | ||||
|     return learningObject; | ||||
| } | ||||
| 
 | ||||
| async function processMetadataJson(file: unzipper.File): LearningObjectMetadata { | ||||
|     const buf = await file.buffer(); | ||||
|     const content = buf.toString(); | ||||
|     return JSON.parse(content); | ||||
| } | ||||
| 
 | ||||
| async function processFile(file: unzipper.File): Promise<Buffer> { | ||||
|     return await file.buffer(); | ||||
| } | ||||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger