diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5c8d7c8e..f4e032de 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,10 +15,10 @@ jobs: name: Mod runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: '1.20' - - uses: actions/checkout@v4 + go-version-file: 'go.mod' - uses: actions/cache@v3.3.2 with: path: ~/go/pkg/mod @@ -31,10 +31,10 @@ jobs: env: GOCACHE: "/tmp/go/cache" steps: + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: '1.20' - - uses: actions/checkout@v4 + go-version-file: 'go.mod' - uses: actions/cache@v3.3.2 with: path: ~/go/pkg/mod @@ -66,10 +66,10 @@ jobs: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: anke-to steps: + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: '1.20' - - uses: actions/checkout@v4 + go-version-file: 'go.mod' - uses: actions/cache@v3.3.2 with: path: ~/go/pkg/mod @@ -97,10 +97,10 @@ jobs: name: Lint runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: '1.20' - - uses: actions/checkout@v4 + go-version-file: 'go.mod' - name: golangci-lint uses: reviewdog/action-golangci-lint@v2.3 with: diff --git a/.gitignore b/.gitignore index fa996640..e0647f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ mock_* *.png # End of https://www.gitignore.io/api/go + +.vscode/settings.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 82a44dec..7f125e40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # syntax = docker/dockerfile:1.3.0 # build backend -FROM golang:1.20.7-alpine as server-build +FROM golang:1.22.2-alpine as server-build RUN --mount=type=cache,target=/var/cache/apk \ apk add --update git diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index 24f74ca2..7e452281 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.7-alpine as build-step +FROM golang:1.22.2-alpine as build-step RUN apk add --update --no-cache ca-certificates git WORKDIR /go/src/github.com/traPtitech/anke-to diff --git a/docker/staging/Dockerfile b/docker/staging/Dockerfile index 2478edd7..4743501d 100644 --- a/docker/staging/Dockerfile +++ b/docker/staging/Dockerfile @@ -1,7 +1,7 @@ # syntax = docker/dockerfile:1.3.0 # build backend -FROM golang:1.20.7-alpine as server-build +FROM golang:1.22.2-alpine as server-build RUN --mount=type=cache,target=/var/cache/apk \ apk add --update git diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile index 71351a61..92a5ef39 100644 --- a/docker/test/Dockerfile +++ b/docker/test/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.7-alpine as builder +FROM golang:1.22.2-alpine as builder RUN apk add --update --no-cache ca-certificates git \ && apk add --no-cache gcc libc-dev \ && apk add --no-cache openssl diff --git a/docker/tuning/Dockerfile.server b/docker/tuning/Dockerfile.server index 38e5f11e..eed7c56e 100644 --- a/docker/tuning/Dockerfile.server +++ b/docker/tuning/Dockerfile.server @@ -1,4 +1,4 @@ -FROM golang:1.20.7-alpine as build-step +FROM golang:1.22.2-alpine as build-step RUN apk add --update --no-cache ca-certificates git WORKDIR /go/src/github.com/traPtitech/anke-to diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index e00c0971..f0cb40ce 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1,8 +1,8 @@ openapi: 3.0.0 servers: - - url: 'https://anke-to.trap.jp/api' + - url: "https://anke-to.trap.jp/api" description: production - - url: 'http://localhost:8080/api' + - url: "http://localhost:8080/api" description: local info: title: anke-to API @@ -10,457 +10,324 @@ info: description: anke-to API contact: name: traP - url: 'https://github.com/traPtitech/anke-to' + url: "https://github.com/traPtitech/anke-to" security: - application: - read - write -tags: +tags: # TODO: リソースの分類でつけなおす - name: questionnaire - - name: question - name: response - - name: user - - name: group - - name: result -paths: - /questionnaires: +paths: # TODO 変数の命名を確認する + /questionnaires: # TODO: 取得個数可変でもいいかも get: operationId: getQuestionnaires tags: - questionnaire - description: 与えられた条件を満たす20件以下のアンケートのリストを取得します. + description: 与えられた条件を満たす20件以下のアンケートのリストを取得します。 parameters: - - $ref: '#/components/parameters/sortInQuery' - - $ref: '#/components/parameters/searchInQuery' - - $ref: '#/components/parameters/pageInQuery' - - $ref: '#/components/parameters/nontargetedInQuery' + - $ref: "#/components/parameters/sortInQuery" + - $ref: "#/components/parameters/searchInQuery" + - $ref: "#/components/parameters/pageInQuery" + - $ref: "#/components/parameters/onlyTargetingMeInQuery" + - $ref: "#/components/parameters/onlyAdministratedByMeInQuery" responses: - '200': + "200": description: 正常に取得できました。アンケートの配列を返します。 content: application/json: schema: - $ref: '#/components/schemas/QuestionnairesWithPageMax' - '400': + $ref: "#/components/schemas/QuestionnaireList" + "400": description: 与えられた情報の形式が異なります - '500': + "500": description: アンケートを正常に取得できませんでした - '503': + "503": description: SQLの実行時間が3sを超えた場合。主に正規表現が原因。 post: operationId: postQuestionnaire tags: - questionnaire - description: 新しいアンケートを作成します. + description: 新しいアンケートを作成します。 requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/NewQuestionnaire' + $ref: "#/components/schemas/NewQuestionnaire" responses: - '201': - description: 正常にアンケートを作成できました.作成されたアンケートを返します. + "201": + description: 正常にアンケートを作成できました。作成されたアンケートを返します。 content: application/json: schema: - $ref: '#/components/schemas/NewQuestionnaireResponse' - '400': + $ref: "#/components/schemas/QuestionnaireDetail" + "400": description: 与えられた情報の形式が異なります - '500': + "500": description: アンケートを正常に作成できませんでした - - '/questionnaires/{questionnaireID}': + /questionnaires/{questionnaireID}: get: operationId: getQuestionnaire tags: - questionnaire description: アンケートの情報を取得します。 parameters: - - $ref: '#/components/parameters/questionnaireIDInPath' + - $ref: "#/components/parameters/questionnaireIDInPath" responses: - '200': + "200": description: 正常に取得できました。 content: application/json: schema: - $ref: '#/components/schemas/QuestionnaireByID' - '400': + $ref: "#/components/schemas/QuestionnaireDetail" + "400": description: アンケートのIDが無効です - '404': + "404": description: アンケートが存在しません - '500': + "500": description: アンケートを正常に取得できませんでした patch: operationId: editQuestionnaire tags: - questionnaire - description: アンケートの情報を変更します. + description: アンケートの情報を変更します。 parameters: - - $ref: '#/components/parameters/questionnaireIDInPath' + - $ref: "#/components/parameters/questionnaireIDInPath" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/NewQuestionnaire' + $ref: "#/components/schemas/QuestionnaireDetail" responses: - '200': - description: 正常にアンケートを変更できました. - '400': + "200": + description: 正常にアンケートを変更できました。 + "400": description: アンケートのIDが無効です - '500': + "500": description: 正常にアンケートを変更できませんでした delete: - operationId: delteQuestionnaire + operationId: deleteQuestionnaire tags: - questionnaire - description: アンケートを削除します. + description: アンケートを削除します。 parameters: - - $ref: '#/components/parameters/questionnaireIDInPath' + - $ref: "#/components/parameters/questionnaireIDInPath" responses: - '200': - description: 正常にアンケートを削除できました. - '400': + "200": + description: 正常にアンケートを削除できました。 + "400": description: アンケートのIDが無効です - '500': + "500": description: アンケートの削除ができませんでした - '/questionnaires/{questionnaireID}/questions': + /questionnaires/{questionnaireID}/myRemindStatus: get: - operationId: getQuestions + operationId: getQuestionnaireMyRemindStatus tags: - questionnaire - description: アンケートに含まれる質問のリストを取得します。 + description: 自分に対するリマインドが有効かどうかを取得します。 parameters: - - $ref: '#/components/parameters/questionnaireIDInPath' + - $ref: "#/components/parameters/questionnaireIDInPath" responses: - '200': + "200": description: 正常に取得できました。 content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/QuestionDetails' - '400': + $ref: "#/components/schemas/QuestionnaireIsRemindEnabled" + "400": description: アンケートのIDが無効です - '500': - description: 質問のリストを取得できませんでした - post: - operationId: PostQuestionByQuestionnaireID - tags: - - questionnaire - parameters: - - $ref: '#/components/parameters/questionnaireIDInPath' - description: 新しい質問を作成します. - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/NewQuestion' - responses: - '201': - description: 正常に質問を作成できました.作成された質問を返します. - content: - application/json: - schema: - $ref: '#/components/schemas/Question' - '400': - description: 正常に作成できませんでした。リクエストが不正です。 - '500': - description: 正常に作成できません。主に正規表現が原因。 - '/questions/{questionID}': + "404": + description: アンケートが存在しません + "500": + description: リマインド設定を正常に取得できませんでした patch: - operationId: editQuestion + operationId: editQuestionnaireMyRemindStatus tags: - - question - description: 質問を変更します. + - questionnaire + description: 自分に対するリマインドが有効かどうかを変更します。 parameters: - - $ref: '#/components/parameters/questionIDInPath' + - $ref: "#/components/parameters/questionnaireIDInPath" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/NewQuestion' + $ref: "#/components/schemas/QuestionnaireIsRemindEnabled" responses: - '200': - description: 正常に質問を変更できました. - '400': - description: 正常に変更できませんでした。リクエストが不正です。 - '500': - description: 正常に変更できませんでした。主に正規表現が原因。 - delete: - operationId: deleteQuestion + "200": + description: 正常に変更できました。 + "400": + description: アンケートのIDが無効です + "404": + description: アンケートが存在しません + "500": + description: リマインド設定を正常に変更できませんでした + /questionnaires/{questionnaireID}/responses: + get: + operationId: getQuestionnaireResponses tags: - - question - description: 質問を削除します. + - questionnaire + description: アンケートの全ての回答を取得します。アンケートが匿名回答の場合、取得できません。 parameters: - - $ref: '#/components/parameters/questionIDInPath' + - $ref: "#/components/parameters/questionnaireIDInPath" + - $ref: "#/components/parameters/responseSortInQuery" + - $ref: "#/components/parameters/onlyMyResponseInQuery" responses: - '200': - description: 正常に質問を削除できました。 - '500': - description: 正常に削除できませんでした。存在しない質問です。 - /responses: + "200": + description: 正常に取得できました。 + content: + application/json: + schema: + $ref: "#/components/schemas/Responses" + "400": + description: アンケートのIDが無効です + "403": + description: アンケートが匿名回答のため回答を取得できません + "404": + description: アンケートが存在しません + "500": + description: 回答を正常に取得できませんでした post: - operationId: postResponse + operationId: postQuestionnaireResponse tags: - - response - description: 新しい回答を作成します. + - questionnaire + description: 新しい回答を作成します。アンケートが複数回答可能でない場合、過去の回答が削除されます。 + parameters: + - $ref: "#/components/parameters/questionnaireIDInPath" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/PostResponse' + $ref: "#/components/schemas/NewResponse" responses: - '201': - description: 正常に回答を作成できました.作成された回答を返します. + "201": + description: 正常に回答を作成できました。作成された回答を返します。 content: application/json: schema: - $ref: '#/components/schemas/ResponseDetails' - '400': + $ref: "#/components/schemas/Response" + "400": description: 与えられた情報の形式が異なります - '404': - description: アンケートの回答の期限がきれたため回答が存在しません - '405': + "404": + description: アンケートが存在しません + "422": description: 回答期限が過ぎたため回答できません - '500': + "500": description: 正常に回答が作成できませんでした - '/responses/{responseID}': + /questionnaires/{questionnaireID}/result: + get: + operationId: getQuestionnaireResult + tags: + - questionnaire + description: アンケートの回答を集計した結果を取得します。回答者の情報は含まれず、アンケートが匿名回答であっても取得できます。 + parameters: + - $ref: "#/components/parameters/questionnaireIDInPath" + responses: + "200": + description: 正常に取得できました。 + content: + application/json: + schema: + $ref: "#/components/schemas/Result" + "400": + description: アンケートのIDが無効です + "403": + description: 結果を閲覧する権限がありません。 + "404": + description: アンケートが存在しません + "500": + description: アンケートの結果を正常に取得できませんでした + /responses/{responseID}: get: - operationId: getResponses + operationId: getResponse tags: - response - description: あるresponseIDを持つ回答に含まれる全ての質問に対する自分の回答を取得します + description: 回答を取得します。 parameters: - - $ref: '#/components/parameters/responseIDInPath' + - $ref: "#/components/parameters/responseIDInPath" responses: - '200': + "200": description: 正常に取得できました。 content: application/json: schema: - $ref: '#/components/schemas/Response' - '400': - description: responseIDが数値に変換できませんでした - '404': - description: アンケートの回答の期限がきれたため回答が存在しません - '500': - description: responseIDを取得できませんでした + $ref: "#/components/schemas/Response" + "400": + description: responseIDが無効です + "403": + description: 回答を閲覧する権限がありません。 + "404": + description: 回答が存在しません + "500": + description: 回答を正常に取得できませんでした patch: operationId: editResponse tags: - response - description: 回答を変更します. + description: 回答を変更します。 parameters: - - $ref: '#/components/parameters/responseIDInPath' + - $ref: "#/components/parameters/responseIDInPath" requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/NewResponse' + $ref: "#/components/schemas/Response" responses: - '200': - description: 正常に回答を変更できました. - '400': + "200": + description: 正常に回答を変更できました + "400": description: 与えられた回答の情報が異なります - '404': + "403": + description: 回答を変更する権限がありません + "404": description: アンケートの回答の期限がきれたため回答が存在しません - '405': + "405": description: 回答期限が過ぎたため回答できません - '500': + "500": description: responseIDを取得できませんでした delete: operationId: deleteResponse tags: - response - description: 回答を削除します. + description: 回答を削除します parameters: - - $ref: '#/components/parameters/responseIDInPath' + - $ref: "#/components/parameters/responseIDInPath" responses: - '200': - description: 正常に回答を削除できました. - '400': + "200": + description: 正常に回答を削除できました + "400": description: 与えられた回答の情報が異なります - '404': + "403": + description: 回答を削除する権限がありません。 + "404": description: アンケートの回答の期限がきれたため回答が存在しません - '405': - description: 回答期限が過ぎたため回答できません - '500': + "405": + description: 回答期限が過ぎたため回答を削除できません + "500": description: responseIDを取得できませんでした - /users: - get: - operationId: getUsers - tags: - - user - summary: 未実装 - description: (botおよび除名されたユーザーを除く、全ての) ユーザーのtraQIDのリストを取得します。 - responses: - '200': - description: 正常に取得できました.ユーザーの配列を返します. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/User' - /users/me: - get: - operationId: getUsersMe - tags: - - user - description: 自分のユーザー情報を取得します - responses: - '200': - description: 正常に取得できました。 - content: - application/json: - schema: - $ref: '#/components/schemas/Me' - '500': - description: Userが取得できませんでした - /users/me/responses: + /responses/myResponses: get: operationId: getMyResponses tags: - - user + - response description: 自分のすべての回答のリストを取得します。 - responses: - '200': - description: 正常に取得できました。回答の配列を返します。 - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ResponseSummary' - '500': - description: Userが取得できませんでした - '/users/me/responses/{questionnaireID}': - get: - operationId: getMyResponsesByID - tags: - - user parameters: - - $ref: '#/components/parameters/questionnaireIDInPath' - description: 特定のquestionnaireIdを持つアンケートに対する自分のすべての回答のリストを取得します。 + - $ref: "#/components/parameters/responseSortInQuery" responses: - '200': + "200": description: 正常に取得できました。回答の配列を返します。 content: application/json: schema: type: array items: - $ref: '#/components/schemas/ResponseSummary' - '400': - description: questionnaireIDの型が数値ではありません - '500': - description: 回答のリストを取得できませんでした - /users/me/targeted: - get: - operationId: getTargetedQuestionnaire - tags: - - user - description: 自分が対象になっている アンケートのリストを取得します。 - parameters: - - $ref: '#/components/parameters/sortInQuery' - responses: - '200': - description: 正常に取得できました。アンケートの配列を返します。 - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/QuestionnaireMyTargeted' - '500': - description: 自分のUserIDが取得できませんでした - /users/{traQID}/targeted: - get: - operationId: getTargettedQuestionnairesBytraQID - tags: - - user - description: ユーザが対象になっているアンケートのリストを取得します。 - parameters: - - $ref: '#/components/parameters/sortInQuery' - - $ref: '#/components/parameters/answeredInQuery' - - $ref: '#/components/parameters/traQIDInPath' - responses: - '200': - description: 正常に取得できました。アンケートの配列を返します。 - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/QuestionnaireMyTargeted' - '400': - description: 与えらえた情報の形式が異なります - '500': - description: 対象となっているアンケートのリストを取得できませんでした - /users/me/administrates: - get: - operationId: getMyQuestionnaire - tags: - - user - description: 自分が管理者になっているアンケートのリストを取得します。 - responses: - '200': - description: 正常に取得できました。アンケートの配列を返します。 - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/QuestionnaireMyAdministrates' - '500': - description: 自分が管理者となっているアンケートのリストを取得できませんでした - /groups: - get: - operationId: getGroups - tags: - - group - summary: 未実装 - description: (全ての) グループのリストを取得します - responses: - '200': - description: 正常に取得できました.グループの配列を返します. - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Group' - '/results/{questionnaireID}': - get: - operationId: getResults - tags: - - result - parameters: - - $ref: '#/components/parameters/questionnaireIDInPath' - - $ref: '#/components/parameters/responseSortInQuery' - description: あるquestionnaireIDを持つアンケートの結果をすべて取得します。 - responses: - '200': - description: 正常に取得できました。アンケートの各質問に対する結果の配列を返します。 - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ResponseResult' - '400': - description: questionnaireIDの型を数値に変換できませんでした - '403': - description: 結果を閲覧する権限がありません。 - '500': - description: アンケートの回答の詳細情報一覧が取得できませんでした + $ref: "#/components/schemas/ResponsesWithQuestionnaireInfo" + "500": + description: 自分の回答のリストを取得できませんでした components: parameters: answeredInQuery: @@ -468,7 +335,7 @@ components: in: query description: 回答したもの(answered)か未回答のもの(unanswered)かを選別 schema: - $ref: '#/components/schemas/AnsweredType' + $ref: "#/components/schemas/AnsweredType" sortInQuery: name: sort in: query @@ -477,7 +344,7 @@ components: タイトルの降順 "-title", 更新日時が新しい "modified_at", 更新日時が古い "-modified_at" ) schema: - $ref: '#/components/schemas/SortType' + $ref: "#/components/schemas/SortType" responseSortInQuery: name: sort in: query @@ -486,7 +353,7 @@ components: タイトルの降順 "-title", 更新日時が新しい "modified_at", 更新日時が古い "-modified_at" ) schema: - $ref: '#/components/schemas/ResponseSortType' + $ref: "#/components/schemas/ResponseSortType" searchInQuery: name: search in: query @@ -499,11 +366,25 @@ components: description: 何ページ目か (未定義の場合は1ページ目) schema: type: integer - nontargetedInQuery: - name: nontargeted + onlyTargetingMeInQuery: + name: onlyTargetingMe + in: query + description: | + 自分がターゲットになっているもののみ取得 (true), ターゲットになっているものも含めてすべて取得 (false)。デフォルトはfalse。 + schema: + type: boolean + onlyAdministratedByMeInQuery: + name: onlyAdministratedByMe in: query description: | - 自分がターゲットになっていないもののみ取得 (true), ターゲットになっているものも含めてすべて取得 (false)。デフォルトはfalse。 + 自分が管理者になっていないもののみ取得 (true), 管理者になっているものも含めてすべて取得 (false)。デフォルトはfalse。 + schema: + type: boolean + onlyMyResponseInQuery: + name: onlyMyResponse + in: query + description: | + 自分の回答のみ取得 (true), 自分の回答以外も含めてすべて取得 (false)。デフォルトはfalse。 schema: type: boolean questionnaireIDInPath: @@ -538,7 +419,7 @@ components: traQ ID(ex:mazrean) schema: type: string - schemas: + schemas: # TODO: description, exampleを確認する AnsweredType: type: string description: アンケート検索時に回答済みかの状態での絞り込み @@ -584,451 +465,555 @@ components: - ModifiedAtDESC ResShareType: type: string - example: public + example: anyone enum: - - administrators + - admins - respondents - - public + - anyone description: | - アンケートの結果を, 運営は見られる ("administrators"), 回答済みの人は見られる ("respondents") 誰でも見られる ("public") + アンケートの結果を, 運営は見られる ("admins"), 回答済みの人は見られる ("respondents") 誰でも見られる ("anyone") + + QuestionnaireBase: + allOf: + - $ref: "#/components/schemas/QuestionnaireTitle" + - $ref: "#/components/schemas/QuestionnaireDescription" + - $ref: "#/components/schemas/QuestionnaireResponseDueDateTime" + - $ref: "#/components/schemas/QuestionnaireResponseViewableBy" + - $ref: "#/components/schemas/QuestionnaireIsAnonymous" + - $ref: "#/components/schemas/QuestionnaireIsAllowingMultipleResponses" + - $ref: "#/components/schemas/QuestionnaireIsPublished" + - $ref: "#/components/schemas/QuestionnaireTargetsAndAdmins" NewQuestionnaire: + allOf: + - $ref: "#/components/schemas/QuestionnaireBase" + - properties: + questions: + type: array + items: + $ref: "#/components/schemas/NewQuestion" + required: + - questions + QuestionnaireDetail: + allOf: + - $ref: "#/components/schemas/QuestionnaireID" + - $ref: "#/components/schemas/QuestionnaireBase" + - $ref: "#/components/schemas/QuestionnaireCreatedAt" + - $ref: "#/components/schemas/QuestionnaireModifiedAt" + - properties: + questions: + type: array + items: + $ref: "#/components/schemas/Question" + respondents: + $ref: "#/components/schemas/Users" + description: | + 回答者の一覧。匿名回答の場合はnull。 + required: + - questions + - respondents + QuestionnaireSummary: # ResponseCountとRespondentCountを入れてもいいかも + allOf: + - $ref: "#/components/schemas/QuestionnaireID" + - $ref: "#/components/schemas/QuestionnaireTitle" + - $ref: "#/components/schemas/QuestionnaireDescription" + - $ref: "#/components/schemas/QuestionnaireResponseDueDateTime" + - $ref: "#/components/schemas/QuestionnaireResponseViewableBy" + - $ref: "#/components/schemas/QuestionnaireIsAnonymous" + - $ref: "#/components/schemas/QuestionnaireIsAllowingMultipleResponses" + - $ref: "#/components/schemas/QuestionnaireIsPublished" + - $ref: "#/components/schemas/QuestionnaireIsTargetingMe" + - $ref: "#/components/schemas/QuestionnaireCreatedAt" + - $ref: "#/components/schemas/QuestionnaireModifiedAt" + - properties: + has_my_draft: + type: boolean + description: 下書きが存在する + has_my_response: + type: boolean + description: 回答が存在する + responded_date_time_by_me: + type: string + format: date-time + all_responded: + type: boolean + example: true + description: | + すべての対象者が回答済みの場合 true を返す。それ以外は false を返す。 (対象者が存在しない場合は true を返す) + required: + - has_my_draft + - has_my_response + - all_responded + QuestionnaireList: type: object properties: - title: - type: string - example: 第1回集会らん☆ぷろ募集アンケート - description: - type: string - example: 第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー! - res_time_limit: - type: string - format: date-time - res_shared_to: - $ref: '#/components/schemas/ResShareType' - targets: - $ref: '#/components/schemas/Users' - administrators: - $ref: '#/components/schemas/Users' + page_max: + type: integer + example: 1 + description: | + 合計のページ数 + questionnaires: + type: array + items: + $ref: "#/components/schemas/QuestionnaireSummary" required: - - title - - description - - res_time_limit - - res_shared_to - - targets - - administrators - NewQuestionnaireResponse: - allOf: - - $ref: '#/components/schemas/QuestionnaireUser' - Questionnaire: + - page_max + - questionnaires + QuestionnaireID: type: object properties: - questionnaireID: + questionnaire_id: type: integer example: 1 + required: + - questionnaire_id + QuestionnaireTitle: + type: object + properties: title: type: string example: 第1回集会らん☆ぷろ募集アンケート + required: + - title + QuestionnaireDescription: + type: object + properties: description: type: string example: 第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー! - res_time_limit: + required: + - description + QuestionnaireResponseDueDateTime: + type: object + properties: + response_due_date_time: type: string format: date-time + example: 2020-01-01T00:00:00+09:00 + description: | + 回答期限。この日時を過ぎたら回答できなくなる。nullの場合は回答期限なし。 + QuestionnaireResponseViewableBy: + type: object + properties: + response_viewable_by: + $ref: "#/components/schemas/ResShareType" + required: + - response_viewable_by + QuestionnaireIsAnonymous: + type: object + properties: + is_anonymous: + type: boolean + example: true + description: | + 匿名回答かどうか + required: + - is_anonymous + QuestionnaireIsAllowingMultipleResponses: + type: object + properties: + is_allowing_multiple_responses: + type: boolean + example: true + description: | + 一人が複数回回答できるかどうか + required: + - is_allowing_multiple_responses + QuestionnaireIsPublished: + type: object + properties: + is_published: + type: boolean + example: true + description: | + アンケートが公開されているかどうか + required: + - is_published + QuestionnaireIsTargetingMe: + type: object + properties: + is_targeting_me: + type: boolean + example: true + description: | + 自分がターゲットになっているかどうか + required: + - is_targeting_me + QuestionnaireCreatedAt: + type: object + properties: created_at: type: string format: date-time + example: 2020-01-01T00:00:00+09:00 + required: + - created_at + QuestionnaireModifiedAt: + type: object + properties: modified_at: type: string format: date-time - res_shared_to: - $ref: '#/components/schemas/ResShareType' + example: 2020-01-01T00:00:00+09:00 required: - - questionnaireID - - title - - description - - res_time_limit - - created_at - modified_at - - res_shared_to + QuestionnaireTargetsAndAdmins: + type: object + properties: + targets: + $ref: "#/components/schemas/UsersAndGroups" + admins: + $ref: "#/components/schemas/UsersAndGroups" + required: - targets - QuestionnaireForList: + - admins + QuestionnaireHasMyResponse: + type: object + properties: + has_response: + type: boolean + description: 回答済みあるいは下書きが存在する + required: + - has_response + QuestionnaireIsRemindEnabled: + type: object + properties: + is_remind_enabled: + type: boolean + description: | + 自分に対するリマインドが有効かどうか。ユーザーが対象者でありかつ回答していない場合、この値がtrueであればリマインドが送信される。 + required: + - is_remind_enabled + NewQuestion: allOf: - - $ref: '#/components/schemas/Questionnaire' - - type: object - properties: - is_targeted: - type: boolean - example: true - description: | - 自分がターゲットになっているかどうか + - $ref: "#/components/schemas/QuestionBase" + - $ref: "#/components/schemas/QuestionSettingsByType" + Question: + allOf: + - $ref: "#/components/schemas/QuestionBase" + - $ref: "#/components/schemas/QuestionSettingsByType" + - properties: + question_id: + type: integer + example: 1 + created_at: + type: string + format: date-time + example: 2020-01-01T00:00:00+09:00 required: - - is_targeted - QuestionnairesWithPageMax: + - question_id + - created_at + QuestionBase: type: object properties: - page_max: + questionnaire_id: type: integer - description: 最大ページ数 - questionnaires: - type: array - items: - $ref: '#/components/schemas/QuestionnaireForList' + example: 1 + title: + type: string + description: + type: string + is_required: + type: boolean + description: | + 回答必須かどうか required: - - page_max - - questionnaires - QuestionnaireByID: + - questionnaire_id + - title + - description + - is_required + QuestionSettingsByType: + oneOf: + - $ref: "#/components/schemas/QuestionSettingsText" + - $ref: "#/components/schemas/QuestionSettingsTextLong" + - $ref: "#/components/schemas/QuestionSettingsNumber" + - $ref: "#/components/schemas/QuestionSettingsSingleChoice" + - $ref: "#/components/schemas/QuestionSettingsMultipleChoice" + - $ref: "#/components/schemas/QuestionSettingsScale" + QuestionSettingsText: allOf: - - $ref: '#/components/schemas/QuestionnaireUser' + - $ref: "#/components/schemas/QuestionTypeText" - type: object properties: - respondents: - $ref: '#/components/schemas/Users' + max_length: + type: integer + QuestionSettingsTextLong: + allOf: + - $ref: "#/components/schemas/QuestionTypeTextLong" + - type: object + properties: + max_length: + type: number + QuestionSettingsNumber: + allOf: + - $ref: "#/components/schemas/QuestionTypeNumber" + - type: object + properties: + min_value: + type: integer + max_value: + type: integer + QuestionSettingsSingleChoice: + allOf: + - $ref: "#/components/schemas/QuestionTypeSingleChoice" + - type: object + properties: + options: + type: array + items: + type: string required: - - respondents - QuestionnaireMyTargeted: + - options + QuestionSettingsMultipleChoice: allOf: - - $ref: '#/components/schemas/Questionnaire' - - type: object - properties: - responded_at: - type: string - format: date-time - has_response: - type: boolean - description: 回答済みあるいは下書きが存在する - required: - - responded_at - - has_response - QuestionnaireMyAdministrates: - allOf: - - $ref: '#/components/schemas/QuestionnaireUser' + - $ref: "#/components/schemas/QuestionTypeMultipleChoice" - type: object properties: - all_responded: - type: boolean - example: true - description: | - 回答必須でない場合、またはすべてのターゲットが回答済みの場合、true を返す。それ以外はfalseを返す。 - respondents: - $ref: '#/components/schemas/Users' + options: + type: array + items: + type: string required: - - all_responded - - respondents - QuestionnaireUser: + - options + QuestionSettingsScale: allOf: - - $ref: '#/components/schemas/Questionnaire' - - type: object - properties: - targets: - $ref: '#/components/schemas/Users' - administrators: - $ref: '#/components/schemas/Users' - required: - - targets - - administrators - QuestionType: - type: string - example: Text - enum: - - Text - - TextArea - - Number - - MultipleChoice - - Checkbox - - LinearScale - description: | - どのタイプの質問か ("Text", "TextArea", "Number", "MultipleChoice", "Checkbox", "LinearScale") - QuestionBase: + - $ref: "#/components/schemas/QuestionTypeScale" + - type: object + properties: + min_value: + type: integer + max_value: + type: integer + min_label: + type: string + max_label: + type: string + required: + - min_value + - max_value + QuestionTypeText: type: object properties: - page_num: - type: integer - example: 1 - description: | - アンケートの何ページ目の質問か - question_num: - type: integer - example: 1 - description: | - アンケートの質問のうち、何問目か question_type: - $ref: '#/components/schemas/QuestionType' - body: type: string - example: 質問文 - is_required: - type: boolean - example: true - description: | - 回答必須かどうか - options: - type: array - items: - type: string - example: 選択肢1 - scale_label_right: - type: string - example: そう思わない - scale_label_left: + enum: [Text] + required: + - question_type + QuestionTypeTextLong: + type: object + properties: + question_type: type: string - example: そう思う - scale_min: - type: integer - example: 1 - scale_max: - type: integer - example: 5 - regex_pattern: + enum: [TextLong] + required: + - question_type + QuestionTypeNumber: + type: object + properties: + question_type: type: string - example: '' - min_bound: + enum: [Number] + required: + - question_type + QuestionTypeSingleChoice: + type: object + properties: + question_type: type: string - example: '' - max_bound: + enum: [SingleChoice] + required: + - question_type + QuestionTypeMultipleChoice: + type: object + properties: + question_type: type: string - example: '' + enum: [MultipleChoice] required: - - page_num - - question_num - question_type - - body - - is_required - - options - - scale_label_right - - scale_label_left - - scale_min - - scale_max - NewQuestion: - allOf: - - $ref: '#/components/schemas/QuestionBase' - - type: object - properties: - questionnaireID: - type: integer - example: 1 - required: - - questionnaireID - Question: - allOf: - - $ref: '#/components/schemas/NewQuestion' - - type: object - properties: - questionID: - type: integer - example: 1 - required: - - questionID - QuestionDetails: - allOf: - - $ref: '#/components/schemas/QuestionBase' - - type: object - properties: - questionID: - type: integer - example: 1 - created_at: - type: string - format: date-time - required: - - questionID - - created_at - NewResponse: + QuestionTypeScale: type: object properties: - questionnaireID: - type: integer - example: 1 - body: - type: array - items: - $ref: '#/components/schemas/ResponseBody' - submitted_at: + question_type: type: string - format: date-time + enum: [Scale] required: - - temporarily - - questionnaireID - - body - PostResponse: + - question_type + NewResponse: type: object properties: - questionnaireID: - type: integer - example: 1 + is_draft: + type: boolean + example: true body: type: array items: - $ref: '#/components/schemas/ResponseBody' - temporarily: - type: boolean - example: true - submitted_at: - type: string - format: date-time + $ref: "#/components/schemas/ResponseBody" required: - - temporarily - - questionnaireID + - is_draft - body Response: allOf: - - $ref: '#/components/schemas/NewResponse' + - $ref: "#/components/schemas/QuestionnaireID" - type: object properties: + response_id: + type: integer + example: 1 + respondent: + $ref: "#/components/schemas/TraqId" + submitted_at: + type: string + format: date-time + example: 2020-01-01T00:00:00+09:00 modified_at: type: string format: date-time + example: 2020-01-01T00:00:00+09:00 required: + - response_id + - respondent + - submitted_at - modified_at - ResponseDetails: + - $ref: "#/components/schemas/NewResponse" + Responses: + type: array + items: + $ref: "#/components/schemas/Response" + ResponsesWithQuestionnaireInfo: + type: array + items: + allOf: + - $ref: "#/components/schemas/Response" + - type: object + properties: + questionnaire_info: + allOf: + - $ref: "#/components/schemas/QuestionnaireTitle" + - $ref: "#/components/schemas/QuestionnaireResponseDueDateTime" + - $ref: "#/components/schemas/QuestionnaireCreatedAt" + - $ref: "#/components/schemas/QuestionnaireModifiedAt" + - $ref: "#/components/schemas/QuestionnaireIsTargetingMe" + ResponseBody: + oneOf: + - $ref: "#/components/schemas/ResponseBodyText" + - $ref: "#/components/schemas/ResponseBodyTextLong" + - $ref: "#/components/schemas/ResponseBodyNumber" + - $ref: "#/components/schemas/ResponseBodySingleChoice" + - $ref: "#/components/schemas/ResponseBodyMultipleChoice" + - $ref: "#/components/schemas/ResponseBodyScale" + ResponseBodyText: + allOf: + - $ref: "#/components/schemas/QuestionTypeText" + - $ref: "#/components/schemas/ResponseBodyBaseString" + ResponseBodyTextLong: + allOf: + - $ref: "#/components/schemas/QuestionTypeTextLong" + - $ref: "#/components/schemas/ResponseBodyBaseString" + ResponseBodyNumber: + allOf: + - $ref: "#/components/schemas/QuestionTypeNumber" + - $ref: "#/components/schemas/ResponseBodyBaseNumber" + ResponseBodySingleChoice: allOf: - - $ref: '#/components/schemas/NewResponse' + - $ref: "#/components/schemas/QuestionTypeSingleChoice" + - $ref: "#/components/schemas/ResponseBodyBaseInteger" + - description: | + 選択肢のインデックス + ResponseBodyMultipleChoice: + allOf: + - $ref: "#/components/schemas/QuestionTypeMultipleChoice" - type: object properties: - responseID: - type: integer - example: 1 + answer: + type: array + items: + type: integer + description: | + 選択肢のインデックスの配列 required: - - responseID - ResponseSummary: - type: object - properties: - responseID: - type: integer - example: 1 - questionnaireID: - type: integer - example: 1 - questionnaire_title: - type: string - example: 第1回集会らん☆ぷろ募集アンケート - description: - type: string - example: 2017年度入学学部生 - res_time_limit: - type: string - format: date-time - submitted_at: - type: string - format: date-time - modified_at: - type: string - format: date-time - required: - - responseID - - questionnaireID - - questionnaire_title - - modified_at - ResponseBody: + - answer + ResponseBodyScale: + allOf: + - $ref: "#/components/schemas/QuestionTypeScale" + - $ref: "#/components/schemas/ResponseBodyBaseInteger" + ResponseBodyBaseString: type: object properties: - questionID: - type: integer - example: 1 - question_type: - $ref: '#/components/schemas/QuestionType' - response: + answer: type: string - example: リマインダーBOTを作った話 - option_response: - type: array - items: - type: string - example: 選択肢1 required: - - questionID - - question_type - ResponseResult: - allOf: - - $ref: '#/components/schemas/Response' - - type: object - properties: - traqID: - type: string - example: lolico - responseID: - type: integer - example: 1 - required: - - traqID - - responseID - required: - - submitted_at - Users: - type: array - items: - type: string - example: lolico - User: + - answer + ResponseBodyBaseNumber: type: object properties: - userId: - type: string - format: uuid - traqID: - type: string - example: lolico - displayName: - type: string - example: ロリ子 - iconFileId: - type: string - format: uuid - twitterId: - type: string - example: trapyojo + answer: + type: number required: - - userId - - traqID - - displayName - - iconFileId - - twitterId - Me: + - answer + ResponseBodyBaseInteger: type: object properties: - traqID: - type: string - example: lolico + answer: + type: integer required: - - traqID - Group: + - answer + Result: + type: array + items: + allOf: + - $ref: "#/components/schemas/QuestionnaireID" + - type: object + properties: + response_id: + type: integer + example: 1 + submitted_at: + type: string + format: date-time + example: 2020-01-01T00:00:00+09:00 + modified_at: + type: string + format: date-time + example: 2020-01-01T00:00:00+09:00 + required: + - response_id + - respondent + - submitted_at + - modified_at + - $ref: "#/components/schemas/NewResponse" + UsersAndGroups: type: object properties: - groupId: - type: string - format: uuid - name: - type: string - example: 17B - description: - type: string - example: 2017年度入学学部生 - adminUser: - type: string - example: lolico - members: - type: array - items: - $ref: '#/components/schemas/Users' - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time + users: + $ref: "#/components/schemas/Users" + groups: + $ref: "#/components/schemas/Groups" required: - - groupId - - name - - description - - adminUser - - members - - createdAt - - updatedAt + - users + - groups + Users: + type: array + items: + $ref: "#/components/schemas/TraqId" + TraqId: + type: string + example: cp20 + description: | + traQ ID + Groups: + type: array + items: + type: string + example: 1 + description: | + Group UUID securitySchemes: application: type: oauth2 flows: - clientCredentials: - tokenUrl: 'http://example.com/oauth/token' + authorizationCode: + authorizationUrl: "https://q.trap.jp/api/v3/oauth2/authorize" + tokenUrl: "https://q.trap.jp/api/v3/oauth2/token" scopes: write: allows modifying resources read: allows reading resources diff --git a/go.mod b/go.mod index bc570d66..5358018d 100644 --- a/go.mod +++ b/go.mod @@ -50,17 +50,19 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - gorm.io/gorm v1.22.5 + gorm.io/gorm v1.25.5 ) require ( - github.com/jinzhu/now v1.1.4 // indirect - gorm.io/driver/mysql v1.2.3 + github.com/jinzhu/now v1.1.5 // indirect + gorm.io/driver/mysql v1.5.2 ) require ( - github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/prometheus/client_golang v1.12.1 gopkg.in/guregu/null.v4 v4.0.0 gorm.io/plugin/prometheus v0.0.0-20210820101226-2a49866f83ee ) + +require github.com/go-gormigrate/gormigrate/v2 v2.1.1 // indirect diff --git a/go.sum b/go.sum index 3562645f..83e0cc26 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gormigrate/gormigrate/v2 v2.1.1 h1:eGS0WTFRV30r103lU8JNXY27KbviRnqqIDobW3EV3iY= +github.com/go-gormigrate/gormigrate/v2 v2.1.1/go.mod h1:L7nJ620PFDKei9QOhJzqA8kRCk+E3UbV2f5gv+1ndLc= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -101,6 +103,8 @@ github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX9 github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -191,6 +195,8 @@ github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -705,10 +711,15 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y= gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo= +gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= +gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= gorm.io/gorm v1.21.13/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk= gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU= gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/plugin/prometheus v0.0.0-20210820101226-2a49866f83ee h1:UhePWayrSEXncx1x7UdBNvUfbuxrQOk9LV44glIJElQ= gorm.io/plugin/prometheus v0.0.0-20210820101226-2a49866f83ee/go.mod h1:tu0ajC4OlzwAK6aSL4NQjAvvbBeWcVbv2X285icuEm0= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index ced14f98..f5457edc 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,7 @@ func main() { panic(err) } - err = model.Migrate() + _, err = model.Migrate() if err != nil { panic(err) } diff --git a/model/current.go b/model/current.go new file mode 100644 index 00000000..9b3fea3e --- /dev/null +++ b/model/current.go @@ -0,0 +1,24 @@ +package model + +import ( + "github.com/go-gormigrate/gormigrate/v2" +) + +// Migrations is all db migrations +func Migrations() []*gormigrate.Migration { + return []*gormigrate.Migration{} +} + +func AllTables() []interface{} { + return []interface{}{ + &Questionnaires{}, + &Questions{}, + &Respondents{}, + &Responses{}, + &Administrators{}, + &Options{}, + &ScaleLabels{}, + &Targets{}, + &Validations{}, + } +} diff --git a/model/db.go b/model/db.go index 918f99fd..04281c32 100755 --- a/model/db.go +++ b/model/db.go @@ -4,26 +4,14 @@ import ( "fmt" "os" + "github.com/go-gormigrate/gormigrate/v2" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" "gorm.io/plugin/prometheus" ) -var ( - db *gorm.DB - allTables = []interface{}{ - Questionnaires{}, - Questions{}, - Respondents{}, - Responses{}, - Administrators{}, - Options{}, - ScaleLabels{}, - Targets{}, - Validations{}, - } -) +var db *gorm.DB // EstablishConnection DBと接続 func EstablishConnection(isProduction bool) error { @@ -83,12 +71,24 @@ func EstablishConnection(isProduction bool) error { return nil } -// Migrate DBのMigrationを行う -func Migrate() error { - err := db.AutoMigrate(allTables...) - if err != nil { - return fmt.Errorf("failed in table's migration: %w", err) - } +func Migrate() (init bool, err error) { + m := gormigrate.New(db.Session(&gorm.Session{}), gormigrate.DefaultOptions, Migrations()) - return nil + m.InitSchema(func(db *gorm.DB) error { + init = true + + return db.AutoMigrate(AllTables()...) + }) + err = m.Migrate() + return } + +// Migrate DBのMigrationを行う +// func Migrate() error { +// err := db.AutoMigrate(allTables...) +// if err != nil { +// return fmt.Errorf("failed in table's migration: %w", err) +// } + +// return nil +// } diff --git a/model/db_test.go b/model/db_test.go index 57e1a66a..7de6d521 100644 --- a/model/db_test.go +++ b/model/db_test.go @@ -23,14 +23,14 @@ var ( targetImpl = new(Target) ) -//TestMain テストのmain +// TestMain テストのmain func TestMain(m *testing.M) { err := EstablishConnection(true) if err != nil { panic(err) } - err = Migrate() + _, err = Migrate() if err != nil { panic(err) } diff --git a/model/questionnaires_impl.go b/model/questionnaires_impl.go index 5aefbb78..e7756bc7 100755 --- a/model/questionnaires_impl.go +++ b/model/questionnaires_impl.go @@ -19,7 +19,7 @@ func NewQuestionnaire() *Questionnaire { return new(Questionnaire) } -//Questionnaires questionnairesテーブルの構造体 +// Questionnaires questionnairesテーブルの構造体 type Questionnaires struct { ID int `json:"questionnaireID" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"` Title string `json:"title" gorm:"type:char(50);size:50;not null"` @@ -44,20 +44,20 @@ func (questionnaire *Questionnaires) BeforeCreate(tx *gorm.DB) error { return nil } -//BeforeUpdate Update時に自動でmodified_atを現在時刻に +// BeforeUpdate Update時に自動でmodified_atを現在時刻に func (questionnaire *Questionnaires) BeforeUpdate(tx *gorm.DB) error { questionnaire.ModifiedAt = time.Now() return nil } -//QuestionnaireInfo Questionnaireにtargetかの情報追加 +// QuestionnaireInfo Questionnaireにtargetかの情報追加 type QuestionnaireInfo struct { Questionnaires IsTargeted bool `json:"is_targeted" gorm:"type:boolean"` } -//QuestionnaireDetail Questionnaireの詳細 +// QuestionnaireDetail Questionnaireの詳細 type QuestionnaireDetail struct { Targets []string Respondents []string @@ -65,7 +65,7 @@ type QuestionnaireDetail struct { Questionnaires } -//TargettedQuestionnaire targetになっているアンケートの情報 +// TargettedQuestionnaire targetになっているアンケートの情報 type TargettedQuestionnaire struct { Questionnaires RespondedAt null.Time `json:"responded_at"` @@ -78,7 +78,7 @@ type ResponseReadPrivilegeInfo struct { IsRespondent bool } -//InsertQuestionnaire アンケートの追加 +// InsertQuestionnaire アンケートの追加 func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string) (int, error) { db, err := getTx(ctx) if err != nil { @@ -109,7 +109,7 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des return questionnaire.ID, nil } -//UpdateQuestionnaire アンケートの更新 +// UpdateQuestionnaire アンケートの更新 func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int) error { db, err := getTx(ctx) if err != nil { @@ -148,7 +148,7 @@ func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, des return nil } -//DeleteQuestionnaire アンケートの削除 +// DeleteQuestionnaire アンケートの削除 func (*Questionnaire) DeleteQuestionnaire(ctx context.Context, questionnaireID int) error { db, err := getTx(ctx) if err != nil { @@ -167,8 +167,10 @@ func (*Questionnaire) DeleteQuestionnaire(ctx context.Context, questionnaireID i return nil } -/*GetQuestionnaires アンケートの一覧 -2つ目の戻り値はページ数の最大値*/ +/* +GetQuestionnaires アンケートの一覧 +2つ目の戻り値はページ数の最大値 +*/ func (*Questionnaire) GetQuestionnaires(ctx context.Context, userID string, sort string, search string, pageNum int, nontargeted bool) ([]QuestionnaireInfo, int, error) { ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() @@ -182,6 +184,7 @@ func (*Questionnaire) GetQuestionnaires(ctx context.Context, userID string, sort query := db. Table("questionnaires"). + Where("deleted_at IS NULL"). Joins("LEFT OUTER JOIN targets ON questionnaires.id = targets.questionnaire_id") query, err = setQuestionnairesOrder(query, sort) @@ -263,7 +266,7 @@ func (*Questionnaire) GetAdminQuestionnaires(ctx context.Context, userID string) return questionnaires, nil } -//GetQuestionnaireInfo アンケートの詳細な情報取得 +// GetQuestionnaireInfo アンケートの詳細な情報取得 func (*Questionnaire) GetQuestionnaireInfo(ctx context.Context, questionnaireID int) (*Questionnaires, []string, []string, []string, error) { db, err := getTx(ctx) if err != nil { @@ -315,7 +318,7 @@ func (*Questionnaire) GetQuestionnaireInfo(ctx context.Context, questionnaireID return &questionnaire, targets, administrators, respondents, nil } -//GetTargettedQuestionnaires targetになっているアンケートの取得 +// GetTargettedQuestionnaires targetになっているアンケートの取得 func (*Questionnaire) GetTargettedQuestionnaires(ctx context.Context, userID string, answered string, sort string) ([]TargettedQuestionnaire, error) { db, err := getTx(ctx) if err != nil { @@ -359,7 +362,7 @@ func (*Questionnaire) GetTargettedQuestionnaires(ctx context.Context, userID str return questionnaires, nil } -//GetQuestionnaireLimit アンケートの回答期限の取得 +// GetQuestionnaireLimit アンケートの回答期限の取得 func (*Questionnaire) GetQuestionnaireLimit(ctx context.Context, questionnaireID int) (null.Time, error) { db, err := getTx(ctx) if err != nil { diff --git a/model/questionnaires_test.go b/model/questionnaires_test.go index d8a3b3d8..16b80506 100644 --- a/model/questionnaires_test.go +++ b/model/questionnaires_test.go @@ -3,6 +3,7 @@ package model import ( "context" "errors" + "fmt" "math" "sort" "strings" @@ -346,7 +347,6 @@ func setupQuestionnairesTest(t *testing.T) { func insertQuestionnaireTest(t *testing.T) { t.Helper() - t.Parallel() assertion := assert.New(t) @@ -483,7 +483,6 @@ func insertQuestionnaireTest(t *testing.T) { func updateQuestionnaireTest(t *testing.T) { t.Helper() - t.Parallel() assertion := assert.New(t) @@ -740,7 +739,6 @@ func updateQuestionnaireTest(t *testing.T) { func deleteQuestionnaireTest(t *testing.T) { t.Helper() - t.Parallel() assertion := assert.New(t) @@ -1096,6 +1094,15 @@ func getQuestionnairesTest(t *testing.T) { } if len(testCase.args.search) == 0 && !testCase.args.nontargeted { + fmt.Println(testCase.description) + fmt.Println(questionnaireNum) + fmt.Println(pageMax) + var allNum int64 + db. + Session(&gorm.Session{NewDB: true}). + Model(&Questionnaires{}). + Count(&allNum) + fmt.Println(allNum) assertion.Equal((questionnaireNum+19)/20, int64(pageMax), testCase.description, "pageMax") assertion.Len(questionnaires, int(math.Min(float64(questionnaireNum-20*(int64(testCase.pageNum)-1)), 20.0)), testCase.description, "page") } diff --git a/model/questions_test.go b/model/questions_test.go index 71880ee8..5e0392bc 100644 --- a/model/questions_test.go +++ b/model/questions_test.go @@ -503,29 +503,6 @@ func updateQuestionTest(t *testing.T) { }, }, }, - { - description: "questionnaireID: valid->valid", - before: before{ - Questions: Questions{ - QuestionnaireID: questionnaireDatas[0].ID, - PageNum: 1, - QuestionNum: 1, - Type: "TextArea", - Body: "自由記述欄", - IsRequired: false, - }, - }, - after: after{ - Questions: Questions{ - QuestionnaireID: questionnaireDatas[1].ID, - PageNum: 1, - QuestionNum: 1, - Type: "TextArea", - Body: "自由記述欄", - IsRequired: false, - }, - }, - }, { description: "questionnaireID: valid->invalid", before: before{ diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index 5db7416f..8e934895 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -1143,7 +1143,7 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { buf := bytes.NewBuffer(nil) err := json.NewEncoder(buf).Encode(test.request) if err != nil { - t.Errorf("failed to encode request: %w", err) + t.Errorf("failed to encode request: %v", err) } request = buf diff --git a/router/responses.go b/router/responses.go index 81d11d64..fde97cfd 100644 --- a/router/responses.go +++ b/router/responses.go @@ -323,9 +323,9 @@ func (r *Response) EditResponse(c echo.Context) error { c.Logger().Errorf("failed to get scale labels: %+v", err) return echo.NewHTTPError(http.StatusInternalServerError, err) } - scaleLabelMap := make(map[int]*model.ScaleLabels, len(scaleLabels)) + scaleLabelMap := make(map[int]model.ScaleLabels, len(scaleLabels)) for _, label := range scaleLabels { - scaleLabelMap[label.QuestionID] = &label + scaleLabelMap[label.QuestionID] = label } // LinearScaleのパターンマッチ @@ -334,9 +334,9 @@ func (r *Response) EditResponse(c echo.Context) error { case "LinearScale": label, ok := scaleLabelMap[body.QuestionID] if !ok { - label = &model.ScaleLabels{} + label = model.ScaleLabels{} } - if err := r.CheckScaleLabel(*label, body.Body.ValueOrZero()); err != nil { + if err := r.CheckScaleLabel(label, body.Body.ValueOrZero()); err != nil { c.Logger().Infof("invalid scale label: %+v", err) return echo.NewHTTPError(http.StatusBadRequest, err) }