diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e0f0286 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git/ +.idea/ +.cache/ +Makefile +quiz.yaml +README.md diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..7990191 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,66 @@ +include: + - project: "devops/pena-continuous-integration" + file: "/templates/docker/build-template.gitlab-ci.yml" + - project: "devops/pena-continuous-integration" + file: "/templates/docker/deploy-template.gitlab-ci.yml" + +stages: + - build + - deploy + +build-app: + stage: build + extends: .build_template + rules: + - if: "$CI_COMMIT_BRANCH == $STAGING_BRANCH || $CI_COMMIT_BRANCH == $PRODUCTION_BRANCH" + script: + - docker build -t $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-core:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID --build-arg GITLAB_TOKEN=$GITLAB_TOKEN $CI_PROJECT_DIR + - docker push $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-core:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + +build-storer: + stage: build + extends: .build_template + rules: + - if: "$CI_COMMIT_BRANCH == $STAGING_BRANCH || $CI_COMMIT_BRANCH == $PRODUCTION_BRANCH" + script: + - docker build -t $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-storer:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID --build-arg GITLAB_TOKEN=$GITLAB_TOKEN $CI_PROJECT_DIR -f ./storer/Dockerfile + - docker push $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-storer:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + + +build-worker: + stage: build + extends: .build_template + rules: + - if: "$CI_COMMIT_BRANCH == $STAGING_BRANCH || $CI_COMMIT_BRANCH == $PRODUCTION_BRANCH" + script: + - docker build -t $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-worker:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID --build-arg GITLAB_TOKEN=$GITLAB_TOKEN $CI_PROJECT_DIR -f ./worker/Dockerfile + - docker push $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-worker:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + +build-answerer: + stage: build + extends: .build_template + rules: + - if: "$CI_COMMIT_BRANCH == $STAGING_BRANCH || $CI_COMMIT_BRANCH == $PRODUCTION_BRANCH" + script: + - docker build -t $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-answerer:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID --build-arg GITLAB_TOKEN=$GITLAB_TOKEN $CI_PROJECT_DIR -f ./answerer/Dockerfile + - docker push $CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH-answerer:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + +deploy-staging: + stage: deploy + tags: + - staging + extends: .deploy_template + rules: + - if: "$CI_COMMIT_BRANCH == $STAGING_BRANCH" + after_script: + - ls + +deploy-prod: + stage: deploy + tags: + - prod + extends: .deploy_template + rules: + - if: "$CI_COMMIT_BRANCH == $PRODUCTION_BRANCH" + after_script: + - ls diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9c97704 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM golang:alpine as build +WORKDIR /app +RUN apk add git +COPY . . +ARG GITLAB_TOKEN +ENV GOPRIVATE=penahub.gitlab.yandexcloud.net/backend/penahub_common +RUN git config --global url."https://buildToken:glpat-axA8ttckx3aPf_xd2Dym@penahub.gitlab.yandexcloud.net/".insteadOf "https://penahub.gitlab.yandexcloud.net/" +RUN go mod download +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o core + +FROM alpine as prod +COPY --from=build /app/core . +COPY --from=build /app/dal/schema /dal/schema +EXPOSE 1488 +ENV IS_PROD_LOG=false +ENV IS_PROD=false +ENV PORT=1488 +ENV PG_CRED="host=postgres port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable" +CMD ["/core"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..845ad9d --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +GOCMD=go +GOBUILD=$(GOCMD) build +GOCLEAN=$(GOCMD) clean +GOTEST=$(GOCMD) test +COMMIT?=$(shell git rev-parse --short HEAD) +BUILD_TIME?=$(shell date -u '+%Y-%m-%d_%H:%M:%S') +GOOS?=linux +GOARCH?=amd64 +DOCKER_REGISTRY=yourRegistryHost: +BINARY_NAME=$(shell basename `pwd`) +PORT?=1488 +SHELL = /bin/bash +LDFLAGS=-s -w -X github.com/skeris/appInit/version.Release=${shell git describe --tags --abbrev=0} \ + -X github.com/skeris/appInit/version.Commit=${COMMIT} -X github.com/skeris/appInit/version.BuildTime=${BUILD_TIME} + +all: compile run +clean: + rm -f $(BINARY_NAME) + rm -f ./worker/worker +compile: clean + CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} $(GOBUILD) -ldflags "${LDFLAGS}" -o ${BINARY_NAME} + CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} $(GOBUILD) -ldflags "${LDFLAGS}" -o ./worker/worker +run: compile + ./$(BINARY_NAME) +container: compile + docker build -t $(BINARY_NAME):${shell git describe --tags --abbrev=0} . + docker build -t $(BINARY_NAME)-worker:${shell git describe --tags --abbrev=0} ./worker +docker-push: container + docker tag $(BINARY_NAME) $(DOCKER_REGISTRY)/$(BINARY_NAME) + docker tag $(BINARY_NAME)-worker $(DOCKER_REGISTRY)/$(BINARY_NAME)-worker +pull: + docker pull $(DOCKER_REGISTRY)/$(BINARY_NAME) + docker pull $(DOCKER_REGISTRY)/$(BINARY_NAME)-worker + docker tag $(DOCKER_REGISTRY)/$(BINARY_NAME) $(BINARY_NAME) + docker tag $(DOCKER_REGISTRY)/$(BINARY_NAME)-worker $(BINARY_NAME)-worker +run-container: + docker run --rm --name squiz --network host -p 1488:1488 $(BINARY_NAME):latest +test: + $(GOTEST) -v -race ./... +commit-all: + git add -A + git commit -a + git push +push-new-release: commit-all + git tag ${shell git describe --tags --abbrev=0 | awk -F '.' '{print "v"$$1+1".0.0"}'} + git push --tags +push-new-feature: commit-all + git tag ${shell git describe --tags --abbrev=0 | awk -F '.' '{print $$1"."$$2+1".0"}'} + git push --tags +push-new-state: commit-all + git tag ${shell git describe --tags --abbrev=0 | awk -F '.' '{print $$1"."$$2"."$$3+1}'} + git push --tags +benchmark: + mv ./tests/new.txt ./tests/old.txt + go test -run=NONE -bench=. -benchmem ./tests -test.short > ./tests/new.txt + benchstat -html ./tests/old.txt ./tests/new.txt > benchmark.html + +# show full set of messages +test-in-docker-debug: + docker-compose -f deployments/test/docker-compose.yaml up --build --force-recreate + +# show only relevant messages +test-in-docker: + docker-compose -f deployments/test/docker-compose.yaml up --build --force-recreate --exit-code-from test-squiz 2>/dev/null | grep ^test-squiz diff --git a/TestsDockerfile b/TestsDockerfile new file mode 100644 index 0000000..25846f2 --- /dev/null +++ b/TestsDockerfile @@ -0,0 +1,7 @@ +FROM golang:alpine +WORKDIR /app +RUN apk add git +COPY . . +RUN git config --global url."https://forgomod:glpat-uzHiaztQGzR1BL8RiQTK@penahub.gitlab.yandexcloud.net/".insteadOf "https://penahub.gitlab.yandexcloud.net/" +RUN apk add --no-cache git && go mod download +CMD ["go", "test", "./tests"] diff --git a/openapi.yaml b/openapi.yaml index e69de29..ca3e4ff 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -0,0 +1,1210 @@ +openapi: 3.0.3 +info: + title: QUIZ service api + description: Title + version: 1.0.0 +components: + requestBodies: + GetQuizResultsReq: + content: + 'application/json': + schema: + type: object + properties: + to: + description: таймштамп времени, до которого выбирать статистику. если 0 или не передано - этого ограничения нет. верхняя граница времени + type: integer + from: + description: таймштамп времени, после которого выбирать статистику. если 0 или не передано - этого ограничения нет. нижняя граница времени + new: + description: флаг, по которому вернутся только новые результаты, ещё не просмотренные пользователем + type: boolean + page: + description: номер страницы для пагинации + type: integer + require: true + limit: + description: размер страницы для пагинации + type: integer + require: true + QuizCreateReq: + content: + 'application/json': + schema: + type: object + properties: + fingerprinting: + description: set true for save deviceId + type: boolean + repeatable: + description: set true for allow user to repeat quiz + type: boolean + note_prevented: + description: set true for save statistic of incomplete quiz passing + type: boolean + mail_notifications: + description: set true for mail notification for each quiz passing + type: boolean + unique_answers: + description: set true for save statistics only for unique quiz passing + type: boolean + name: + description: name of quiz. max 280 length + type: string + description: + description: description of quiz + type: string + config: + description: config of quiz. serialized json for rules of quiz flow + type: string + status: + description: status of quiz. allow only '', 'draft', 'template', 'stop', 'start' + type: string + limit: + description: limit is count of max quiz passing + type: integer + due_to: + description: last time when quiz is valid. timestamp in seconds + type: integer + time_of_passing: + description: seconds to pass quiz + type: integer + pausable: + description: true if it is allowed for pause quiz + type: boolean + question_cnt: + description: count of questions + type: integer + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + QuizGetListReq: + content: + 'application/json': + schema: + type: object + properties: + limit: + description: max items on page + type: integer + offset: + description: page number + type: integer + from: + description: start time of time period. timestamp in seconds + type: integer + to: + description: end time of time period. timestamp in seconds + type: integer + search: + description: string for fulltext search in titles of quizes + type: string + status: + description: allow only - draft, template, timeout, stop, start, offlimit + type: string + deleted: + description: get deleted quizes + type: boolean + archived: + description: get archived quizes + type: boolean + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + QuestionCreateReq: + content: + 'application/json': + schema: + type: object + properties: + quiz_id: + description: id of quiz for what question is creating + type: integer + title: + description: title of question. max length 512 + type: string + description: + description: description of question. html/text + type: string + type: + description: type of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating + type: string + required: + description: set true if user MUST answer this question + type: boolean + page: + description: page of question + type: integer + content: + description: json serialized of question content settings + type: string + QuestionGetListReq: + content: + 'application/json': + schema: + type: object + properties: + limit: + description: max items on page + type: integer + offset: + description: page number + type: integer + from: + description: start time of time period. timestamp in seconds + type: integer + to: + description: end time of time period. timestamp in seconds + type: integer + search: + description: string for fulltext search in titles of questions + type: string + type: + description: allow only - text, select, file, variant, images, varimg, emoji, date, number, page, rating or empty string + type: string + deleted: + description: get deleted quizes + type: boolean + required: + description: get only require questions + type: boolean + quiz_id: + description: relation to quiz + type: integer + GetQuizReq: + content: + 'application/json': + schema: + type: object + properties: + quiz_id: + type: string + description: secret string id for customer activity + limit: + type: integer + description: page size + page: + type: integer + description: page of questions + need_config: + type: boolean + description: make true if you need all quiz settings with questions page + UpdateQuestionReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of question for update + required: true + title: + type: string + description: new title of question + desc: + type: string + description: new fulltext plain/html description + type: + type: string + description: new type of question + content: + type: string + description: new content of question + required: + type: boolean + description: set true if this question must be answered + page: + type: integer + description: page of question + UpdateQuizReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of question for update + required: true + fp: + type: boolean + description: set true for storing fingerprints + required: true + rep: + type: boolean + description: set true for allow to repeat quiz after passing + required: true + note_prevented: + type: boolean + description: set true for store unfinished passing + required: true + mailing: + type: boolean + description: set true if we should send passing result on every passing + required: true + uniq: + type: boolean + description: set true if we allow only one user quiz passing + required: true + name: + type: string + description: new name of the quiz + desc: + type: string + description: new descriptions of the quiz + conf: + type: string + description: new config of the quiz + status: + type: string + description: new status. only draft,template,stop,start allowed + limit: + type: integer + description: max amount of quiz passing + required: true + due_to: + type: integer + description: max time of quiz passing + required: true + time_of_passing: + type: integer + description: max time to pass quiz + required: true + pausable: + type: boolean + description: allow to pause quiz to user + required: true + question_cnt: + description: count of questions + type: integer + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + CopyQuestionReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of copying question + required: true + quiz_id: + type: integer + description: quiz id for copy question to another quiz + CopyQuizReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of copied quiz + required: true + GetQuizHistoryReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of quiz for history + required: true + l: + type: integer + description: naumber of quiz due page + p: + type: integer + description: number of page + DeactivateReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of deactivating struct + required: true + responses: + GetQuizResultsResp: + description: список результатов, уместившийся на запрошенной странице + content: + 'application/json': + schema: + type: object + properties: + total_count: + description: общее количество элементов удволетворяющее фильтру. нужно для формирования логики пагинации + type: integer + results: + description: список результатов + type: array + items: + type: object + properties: + content: + description: содержимое ответа + type: string + id: + description: айдишник ответа + type: integer + new: + description: статус, был ли просмотрен ответ + type: boolean + created_at: + description: время создания этого результата + type: string + QuizModel: + description: object of created quiz + content: + 'application/json': + schema: + type: object + properties: + id: + description: Id of created quiz + type: integer + qid: + description: string id for customers + type: string + deleted: + description: true if quiz deleted + type: boolean + archived: + description: true if quiz archived + type: boolean + fingerprinting: + description: set true for save deviceId + type: boolean + repeatable: + description: set true for allow user to repeat quiz + type: boolean + note_prevented: + description: set true for save statistic of incomplete quiz passing + type: boolean + mail_notifications: + description: set true for mail notification for each quiz passing + type: boolean + unique_answers: + description: set true for save statistics only for unique quiz passing + type: boolean + name: + description: name of quiz. max 280 length + type: string + description: + description: description of quiz + type: string + config: + description: config of quiz. serialized json for rules of quiz flow + type: string + status: + description: status of quiz. allow only '', 'draft', 'template', 'stop', 'start' + type: string + limit: + description: limit is count of max quiz passing + type: integer + due_to: + description: last time when quiz is valid. timestamp in seconds + type: integer + time_of_passing: + description: seconds to pass quiz + type: integer + pausable: + description: true if it is allowed for pause quiz + type: boolean + version: + description: version of quiz + type: integer + version_comment: + description: version comment to version of quiz + type: string + parent_ids: + description: array of previous versions of quiz + type: array + items: + type: integer + created_at: + description: time of creating + type: string + updated_at: + description: time of last updating + type: string + question_cnt: + description: count of questions + type: integer + passed_count: + description: count passings + type: integer + average_time: + description: average time of passing + type: integer + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + QuizListGetResp: + description: list of quizes with all filtered quizes count + content: + 'application/json': + schema: + type: object + properties: + count: + description: count of all filtered items + type: integer + items: + description: count of all filtered items + type: array + items: + $ref: '#/components/responses/QuizModel' + QuestionModel: + description: object of created question + content: + 'application/json': + schema: + type: object + properties: + id: + description: Id of created question + type: integer + quiz_id: + description: relation to quiz + type: integer + title: + description: title of question. max 512 length + type: string + description: + description: description of question + type: string + type: + description: status of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating + type: string + required: + description: user must pass this question + type: boolean + deleted: + description: true if question is deleted + type: boolean + page: + description: page if question + type: integer + content: + description: serialized json of created question + type: string + version: + description: version of quiz + type: integer + parent_ids: + description: array of previous versions of quiz + type: array + items: + type: integer + created_at: + description: time of creating + type: string + updated_at: + description: time of last updating + type: string + QuestionListGetResp: + description: list of quizes with all filtered quizes count + content: + 'application/json': + schema: + type: object + properties: + count: + description: count of all filtered items + type: integer + items: + description: count of all filtered items + type: array + items: + $ref: '#/components/responses/QuizModel' + QuizGetResp: + description: short response for customer + content: + 'application/json': + schema: + type: object + properties: + cnt: + type: integer + description: count of all questions in quiz + settings: + type: object + properties: + fp: + type: boolean + description: store fingerprint if true + rep: + type: boolean + description: allow repeat after end of quiz passing + name: + type: string + description: title of quiz + cfg: + type: string + description: serialized json of quiz graph + lim: + type: integer + description: max amount of passing + due: + type: integer + description: last time of new ansvers are valid + delay: + type: integer + description: time that given to customer for pass quiz + pausable: + type: boolean + description: allow user to pause quiz passing + items: + type: array + description: array of xhort items of quiestions for quiz + items: + type: object + properties: + id: + type: integer + description: identificator of question + title: + type: string + description: title of question + desc: + type: string + description: description of question html plaintext + typ: + type: string + description: type of question such as text, select, file, variant, images, varimg, emoji, date, number, page, rating + req: + type: boolean + description: mark question as required + p: + type: integer + description: page of quiz + c: + type: string + description: config of question. such as variants for select + UpdateResp: + description: id of new version of question + content: + 'application/json': + schema: + type: object + properties: + updated: + type: integer + DeactivateResp: + description: id of deleted structure + content: + 'application/json': + schema: + type: object + properties: + deactivated: + type: integer + schemas: + Answer: + type: object + properties: + Id: + type: integer + description: id ответа + Content: + type: string + description: контент ответа + QuestionId: + type: integer + description: id вопроса к которому ответ + QuizId: + type: integer + description: id опроса к которому ответ + Fingerprint: + type: string + description: fingerprint + Session: + type: string + description: сессия + Result: + type: boolean + description: true or false? + CreatedAt: + type: string + format: date-time + description: таймшап когда ответ создан + New: + type: boolean + description: новый ответ? + Deleted: + type: boolean + description: удален? + + +paths: + /liveness: + get: + description: Check that service is running + responses: + '200': + description: healthcheck. if it non 200, that service is not running + /readiness: + get: + description: Check that service is ready for handle requests + responses: + '200': + description: healthcheck. if it non 200, that service is not running + /quiz/create: + post: + description: Create quiz + requestBody: + $ref: '#/components/requestBodies/QuizCreateReq' + responses: + '201': + $ref: '#/components/responses/QuizModel' + '422': + description: name field should have less then 280 chars + content: + 'application/json': + schema: + type: string + default: name field should have less then 280 chars + '406': + description: not acceptable input data + content: + 'application/json': + schema: + type: string + default: status on creating must be only draft,template,stop,start or due to time must be lesser then now + '409': + description: not logical entry + content: + 'application/json': + schema: + type: string + default: you can pause quiz only if it has deadline for passing + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/getList: + post: + description: method for obtain page of quiz list for paginated list + requestBody: + $ref: '#/components/requestBodies/QuizGetListReq' + responses: + '200': + $ref: '#/components/responses/QuizListGetResp' + '406': + description: inappropriate status, allowed only '', 'stop','start','draft', 'template','timeout','offlimit' + content: + 'application/json': + schema: + type: string + default: inappropriate status + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/create: + post: + description: method for creating question for quiz + requestBody: + $ref: '#/components/requestBodies/QuestionCreateReq' + responses: + '200': + $ref: '#/components/responses/QuestionModel' + '422': + description: not valid data + content: + 'application/json': + schema: + type: string + default: title field should have less then 512 chars + '406': + description: not acceptable data + content: + 'application/json': + schema: + type: string + default: type must be only test,button,file,checkbox,select, none + '424': + description: failed relation to quiz + content: + 'application/json': + schema: + type: string + default: failed relation to quiz + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/getList: + post: + description: method for obtain page of question list for paginated list + requestBody: + $ref: '#/components/requestBodies/QuestionGetListReq' + responses: + '200': + $ref: '#/components/responses/QuestionListGetResp' + '406': + description: inappropriate type, allowed only '','test','none','file', 'button','select','checkbox' + content: + 'application/json': + schema: + type: string + default: inappropriate status + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/get: + post: + description: method for obtaining settings of quiz and question page + requestBody: + $ref: '#/components/requestBodies/GetQuizReq' + responses: + '200': + $ref: '#/components/responses/QuizGetResp' + '206': + $ref: '#/components/responses/QuizGetResp' + '400': + description: you cat get quiz only if you know it's uuid + content: + 'application/json': + schema: + type: string + default: no quiz id provided + '411': + description: limit should not be 0 when no aettings is need + content: + 'application/json': + schema: + type: string + default: no data requested + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '423': + description: you can not get data for inactive quiz + content: + 'application/json': + schema: + type: string + default: quiz is inactive + '410': + description: you can not get timeouted quiz + content: + 'application/json': + schema: + type: string + default: quiz timeouted + /question/edit: + patch: + description: method for update question. delete old question and create new with increment version + requestBody: + $ref: '#/components/requestBodies/UpdateQuestionReq' + responses: + '200': + $ref: '#/components/responses/UpdateResp' + '424': + description: return when no question id was send + content: + 'application/json': + schema: + type: string + default: need id of question for update + '422': + description: too large new title + content: + 'application/json': + schema: + type: string + default: title field should have less then 512 chars + '406': + description: inappropriate status sent + content: + 'application/json': + schema: + type: string + default: type must be only test,button,file,checkbox,select, none or empty string + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/edit: + patch: + description: method for update quiz + requestBody: + $ref: '#/components/requestBodies/UpdateQuizReq' + responses: + '200': + $ref: '#/components/responses/UpdateResp' + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: need id of quiz for update + '422': + description: too large new title + content: + 'application/json': + schema: + type: string + default: name field should have less then 280 chars + '406': + description: inappropriate status sent + content: + 'application/json': + schema: + type: string + default: status on creating must be only draft,template,stop,start or due to time must be lesser then now + '409': + description: impossible state creating + content: + 'application/json': + schema: + type: string + default: you can pause quiz only if it has deadline for passing + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/copy: + post: + description: method for copy question to another quiz or to duplicate quiz + requestBody: + $ref: '#/components/requestBodies/CopyQuestionReq' + responses: + '201': + $ref: '#/components/responses/UpdateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no question id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /quiz/copy: + post: + description: method for copy question to another quiz or to duplicate quiz + requestBody: + $ref: '#/components/requestBodies/CopyQuizReq' + responses: + '201': + $ref: '#/components/responses/UpdateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /quiz/history: + post: + description: method for obtain page of quiz history for paginated list + requestBody: + $ref: '#/components/requestBodies/GetQuizHistoryReq' + responses: + '200': + description: page of history quiz changes + content: + 'application/json': + schema: + type: array + items: + $ref: '#/components/responses/QuizModel' + '424': + description: no id provided + content: + 'application/json': + schema: + type: string + default: no id provided + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/history: + post: + description: method for obtain page of question history for paginated list + requestBody: + $ref: '#/components/requestBodies/GetQuizHistoryReq' + responses: + '200': + description: page of history question changes + content: + 'application/json': + schema: + type: array + items: + $ref: '#/components/responses/QuestionModel' + '424': + description: no id provided + content: + 'application/json': + schema: + type: string + default: no id provided + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/delete: + delete: + description: method for mark quiz as deleted + requestBody: + $ref: '#/components/requestBodies/DeactivateReq' + responses: + '200': + $ref: '#/components/responses/DeactivateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /quiz/archive: + patch: + description: method for mark quiz as archived + requestBody: + $ref: '#/components/requestBodies/DeactivateReq' + responses: + '200': + $ref: '#/components/responses/DeactivateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /question/delete: + delete: + description: method for mark question as deleted + requestBody: + $ref: '#/components/requestBodies/DeactivateReq' + responses: + '200': + $ref: '#/components/responses/DeactivateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no question id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /results/getResults/{quizId}: + post: + description: получение списка результатов респондентов проходивших опрос + requestBody: + $ref: '#/components/requestBodies/GetQuizResultsReq' + responses: + '200': + $ref: '#/components/responses/GetQuizResultsResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /results/delete/{resultId}: + delete: + description: мягкое удаление результата. не удаляет, но помечает удаленным + responses: + '200': + description: если все хорошо,то и отправлять нечего + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /results/seen: + patch: + description: устаревает результаты, т.е. делает их new = false + requestBody: + content: + 'application/json': + schema: + type: object + properties: + answers: + description: список айдишников результатов, которые надо устареть + type: array + items: + type: integer + responses: + '200': + description: если все хорошо,то и отправлять нечего + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + + /results/{quizID}/export: + post: + summary: экспорт результатов quiz в csv + description: экспорт результатов quiz в csv + parameters: + - in: path + name: quizID + description: id quiz, из которого нужно экспортировать результаты + required: true + schema: + type: integer + format: uint64 + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + to: + type: string + format: date + description: Дата окончания диапазона времени для экспорта результатов + from: + type: string + format: date + description: Дата начала временного диапазона для экспорта результатов + new: + type: boolean + description: Экспортировать ли только новые результаты? + responses: + "200": + description: CSV-файл, содержащий результаты теста + content: + text/csv: + schema: + type: string + format: binary + "400": + description: Bad request. Invalid quiz ID or request body. + "500": + description: Internal server error. Failed to export quiz results. + + /result/{resultID}: + get: + summary: возвращает все ответы по resultID (answerID) в рамках сессии resultID + description: возвращает все ответы по resultID (answerID) в рамках сессии resultID + parameters: + - in: path + name: resultID + required: true + description: resultID (answerID), в рамках сессии которого нужно вернуть все ответы + schema: + type: string + responses: + '200': + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Answer' + '400': + description: Invalid result ID provided + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Failed to retrieve result answers + content: + application/json: + schema: + type: object + properties: + message: + type: string diff --git a/server.crt b/server.crt new file mode 100644 index 0000000..33910ed --- /dev/null +++ b/server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUTZYPqWpnMp0w/hf7OjuNm0FwanYwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAxMjUwMjU2MzBaFw0yMzAx +MjUwMjU2MzBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCl/UGvlDrkg9Mwqj2rvGkv9jvZ8Z8ZZe6ZHb/KR56Y +jgyIKow+bqxpduLXclezaUdG9XFQaR+FD1nK5lW6oUrX8Vcd59ImKBV+cCfMwjCH +sYrjheTQyoFY/o3+7bt8nyjnmYG0Z0dQqXSquHngKeoiVqkNGzZHSMLuRuRZFHu/ +SUj+7WOJsraIWK/8yt0dxjhGA/TMK4MlI2tdJ2vEaP9QPIBoWSGIpEa1myjV9XoG +qOtUQzjeRuMHG8Wp+4JzIsNdl1Gg+N3dBEQSExyVv0cLAJouKy2zmR2Eio3nXhWW +2kq+x3gjPlf8RDZJz+x/SsDmWvgGOcGTWcSLOdn/fa7VAgMBAAGjUzBRMB0GA1Ud +DgQWBBS315REqIh/ACzxxAK3EiZ1zTNSWDAfBgNVHSMEGDAWgBS315REqIh/ACzx +xAK3EiZ1zTNSWDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBW +pxle1BCaYtVRwT/05XXBFRfB5Mog8ip6JvWpWcixwx7qsCIsZd4VN2tenuvSQFTr +o9c2PCnU4fJihIiNZqX9X3UzH4WFmkqv1FMeDPPplzs4yxLxBjDDFmGBXLotZYpN +kjq/G1OYdbCssv1oz7S6flqQ1YyzW/Cwx/jkz15uUiqZhDPGj+HX4Fcdl2h3TF9Z +mS1s6VDXLlQM3zCcwVJo3Twryc8IjJWkA3VPfACpJF9dBj8RJW42P92+kBI3fhCL +nhdBxrAvrXK+HT4jdjnYxVDVWTVYx8Acwlr36yTDcucRJpnVN9zZgr8DuL+Ag4FX +xkL9kbdEGt6XnlV8K9Z8 +-----END CERTIFICATE----- diff --git a/server.key b/server.key new file mode 100644 index 0000000..c4bffe5 --- /dev/null +++ b/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCl/UGvlDrkg9Mw +qj2rvGkv9jvZ8Z8ZZe6ZHb/KR56YjgyIKow+bqxpduLXclezaUdG9XFQaR+FD1nK +5lW6oUrX8Vcd59ImKBV+cCfMwjCHsYrjheTQyoFY/o3+7bt8nyjnmYG0Z0dQqXSq +uHngKeoiVqkNGzZHSMLuRuRZFHu/SUj+7WOJsraIWK/8yt0dxjhGA/TMK4MlI2td +J2vEaP9QPIBoWSGIpEa1myjV9XoGqOtUQzjeRuMHG8Wp+4JzIsNdl1Gg+N3dBEQS +ExyVv0cLAJouKy2zmR2Eio3nXhWW2kq+x3gjPlf8RDZJz+x/SsDmWvgGOcGTWcSL +Odn/fa7VAgMBAAECggEAK1ALbNJkD67qU7+IeiCe3foFohgvaMQ1X1To79KCatJe +KMXZdI0Shi6UgJ2AjvtAsx5mpBHaPLFFv/Ecm6dszS5XiiEsr/IzsZt4dzCQ00eb +s0UD8Qt2dw9tZAEKtDkfxldDBh0Q+dZN8CJVAw5M3wr378iTAETXQK/U8/2pUL9V +6kR899Qb1hnK2NK0vQxvg7JoFxsc8DzLBc3q4UuzvzN1EnUDMhxnCiSR36HaX3CQ +c18bSvzchv5HNA+C7oKnvHRcMMfFsbJQZsgXR5oW5pTIsBDPJVW4Ob7DKIZ5r7J9 +5Ew38dHlE8bYmtp+rRgarj/W/PLEu55s+N09mncL3QKBgQDdCeHq9IwRJM5g4+0w +0cRbqU1zsr1jEAPDorLviRi9+GBqUPAwMEEJ3GHZqYofMyAlEmpqHzrJ/Its0IX4 +eQ+oZfSuRW+XUnlrhgSxqZv5bXOVIHHaMNpumx5bg86/2O/Cgr44qezsHazr+cWB +O7cH7Jwe1axGpZrFmJ+yfe7MNwKBgQDAPlx8CTJEh9FfLjg7gKOHMSfMsIh8k4Dh +dU9dXFA5g/FmOf01rlIf9qVBwemn14wRx/+o035qRSEDWfMua7PF/yI/KXo9Gmcg +U4XNNE1cbPLH7w7kEp9Xx7XsyiOX76S4Aa6Oh383PYQjWb1cb1oug4kWhkJGuRCO +1XeznRfPUwKBgQClm5fmf57J4Xe25E57+05RFsHu44Y9yGvpkebazS76djNWtJmg +oSa5iqLNf9b6lUWKQQf1xrw3Rb34oysdBScUFn+Caoe3KpXKV7kAm4E/GiA1JP/o +aB3gLNxL2K940cKhS2/V6IqYRYufqZ2uu9zgVQiLma/fpogVb8qG4w+ddQKBgH7w +7srsm6fK80LhVzpbQCAGIlyz+Pwhvkmh180zJrpD2MQC6KNov0vh5s+cBvjQiOo4 +4SWOufvcN+dpnv15GtVUGj4Q2mm8pHohDMoqjibIviutPGz3qwYglo2MEKLEGEV0 +vtnYuPU2Bqn9GOAPm0H5ky0vbFYc38UhShBtXAqHAoGACD57o0rl3nBUoHyk9kuI +6EYFV+r6NJhHu+oDrMUNExG59G+nIZ/GujYQCGB8ncCZa+R+CmUdDpOQ88tWbuGC +WEdmGyGqWesuSp5mj5171jf5GdoqWvH+N5frD7TGCWUuWF4KCGnMvbcTx/KxEbr7 +Jzr59ImXdRbFP1sL9CibBw8= +-----END PRIVATE KEY-----