diff --git a/api-service b/api-service index 51280c7..5a73de3 100644 Binary files a/api-service and b/api-service differ diff --git a/docs/docs.go b/docs/docs.go index 4543f58..8662564 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -699,6 +699,246 @@ const docTemplate = `{ } } } + }, + "/peserta/:nokartu": { + "get": { + "responses": { + "200": { + "description": "Successfully retrieved PesertaBynokartu data", + "schema": { + "$ref": "#/definitions/peserta.PesertaResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - PesertaBynokartu not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } + }, + "/peserta/nik/:nik": { + "get": { + "responses": { + "200": { + "description": "Successfully retrieved PesertaBynik data", + "schema": { + "$ref": "#/definitions/peserta.PesertaResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - PesertaBynik not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } + }, + "/sep": { + "post": { + "parameters": [ + { + "type": "string", + "description": "Request ID for tracking", + "name": "X-Request-ID", + "in": "header" + }, + { + "description": "SepSep data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/sep.SepRequest" + } + } + ], + "responses": { + "201": { + "description": "Successfully created SepSep", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid request body or validation error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "409": { + "description": "Conflict - SepSep already exists", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } + }, + "/sep/:nosep": { + "get": { + "responses": { + "200": { + "description": "Successfully retrieved SepSep data", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - SepSep not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + }, + "put": { + "parameters": [ + { + "description": "SepSep data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/sep.SepRequest" + } + } + ], + "responses": { + "200": { + "description": "Successfully updated SepSep", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters or request body", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - SepSep not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + }, + "delete": { + "responses": { + "200": { + "description": "Successfully deleted SepSep", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - SepSep not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } } }, "definitions": { @@ -760,6 +1000,27 @@ const docTemplate = `{ } } }, + "api-service_internal_models.ErrorResponseBpjs": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "errors": { + "type": "object", + "additionalProperties": true + }, + "message": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, "api-service_internal_models.MetaResponse": { "type": "object", "properties": { @@ -1127,6 +1388,281 @@ const docTemplate = `{ } } }, + "peserta.PesertaData": { + "type": "object", + "properties": { + "aktif": { + "type": "string" + }, + "asuransi": { + "type": "string" + }, + "cob": { + "type": "string" + }, + "kdCabang": { + "type": "string" + }, + "kdJnsPst": { + "type": "string" + }, + "ketAktif": { + "type": "string" + }, + "klsRawat": { + "type": "string" + }, + "mr": { + "type": "object", + "properties": { + "nmMR": { + "type": "string" + }, + "noMR": { + "type": "string" + }, + "sex": { + "type": "string" + }, + "tglLahir": { + "type": "string" + }, + "tglMeninggal": { + "type": "string" + } + } + }, + "nama": { + "type": "string" + }, + "nik": { + "type": "string" + }, + "nmCabang": { + "type": "string" + }, + "nmJnsPst": { + "type": "string" + }, + "noKartu": { + "type": "string" + }, + "noKtp": { + "type": "string" + }, + "noSKTM": { + "type": "string" + }, + "pisa": { + "type": "string" + }, + "sex": { + "type": "string" + }, + "statusPeserta": { + "type": "string" + }, + "tglLahir": { + "type": "string" + }, + "tglTAT": { + "type": "string" + }, + "tglTMT": { + "type": "string" + }, + "tglTunggak": { + "type": "string" + } + } + }, + "peserta.PesertaResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/peserta.PesertaData" + }, + "message": { + "type": "string" + }, + "metaData": {}, + "request_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + } + }, + "sep.SepData": { + "type": "object", + "properties": { + "catatan": { + "type": "string" + }, + "diagnosa": { + "type": "string" + }, + "informasi": { + "type": "object", + "properties": { + "dpjpLayan": { + "type": "string" + }, + "noSKDP": { + "type": "string" + }, + "noTelp": { + "type": "string" + }, + "subSpesialis": { + "type": "string" + } + } + }, + "jnsPelayanan": { + "type": "string" + }, + "klsRawat": { + "type": "string" + }, + "noMR": { + "type": "string" + }, + "noSep": { + "type": "string" + }, + "peserta": { + "$ref": "#/definitions/peserta.PesertaData" + }, + "poli": { + "type": "string" + }, + "rujukan": { + "$ref": "#/definitions/sep.SepRujukan" + }, + "tglSep": { + "type": "string" + } + } + }, + "sep.SepRequest": { + "type": "object", + "required": [ + "diagnosa", + "jnsPelayanan", + "klsRawat", + "noKartu", + "noMR", + "poli", + "ppkPelayanan", + "tglSep", + "user" + ], + "properties": { + "catatan": { + "type": "string" + }, + "diagnosa": { + "type": "string" + }, + "jnsPelayanan": { + "type": "string", + "enum": [ + "1", + "2" + ] + }, + "klsRawat": { + "type": "string", + "enum": [ + "1", + "2", + "3" + ] + }, + "noKartu": { + "type": "string" + }, + "noMR": { + "type": "string" + }, + "noTelp": { + "type": "string" + }, + "poli": { + "type": "string" + }, + "ppkPelayanan": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "rujukan": { + "$ref": "#/definitions/sep.SepRujukan" + }, + "tglSep": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "user": { + "type": "string" + } + } + }, + "sep.SepResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/sep.SepData" + }, + "message": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + } + }, + "sep.SepRujukan": { + "type": "object", + "required": [ + "asalRujukan", + "noRujukan", + "ppkRujukan", + "tglRujukan" + ], + "properties": { + "asalRujukan": { + "type": "string", + "enum": [ + "1", + "2" + ] + }, + "noRujukan": { + "type": "string" + }, + "ppkRujukan": { + "type": "string" + }, + "tglRujukan": { + "type": "string" + } + } + }, "sql.NullString": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 016c5c3..d1de435 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -696,6 +696,246 @@ } } } + }, + "/peserta/:nokartu": { + "get": { + "responses": { + "200": { + "description": "Successfully retrieved PesertaBynokartu data", + "schema": { + "$ref": "#/definitions/peserta.PesertaResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - PesertaBynokartu not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } + }, + "/peserta/nik/:nik": { + "get": { + "responses": { + "200": { + "description": "Successfully retrieved PesertaBynik data", + "schema": { + "$ref": "#/definitions/peserta.PesertaResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - PesertaBynik not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } + }, + "/sep": { + "post": { + "parameters": [ + { + "type": "string", + "description": "Request ID for tracking", + "name": "X-Request-ID", + "in": "header" + }, + { + "description": "SepSep data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/sep.SepRequest" + } + } + ], + "responses": { + "201": { + "description": "Successfully created SepSep", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid request body or validation error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "409": { + "description": "Conflict - SepSep already exists", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } + }, + "/sep/:nosep": { + "get": { + "responses": { + "200": { + "description": "Successfully retrieved SepSep data", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - SepSep not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + }, + "put": { + "parameters": [ + { + "description": "SepSep data", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/sep.SepRequest" + } + } + ], + "responses": { + "200": { + "description": "Successfully updated SepSep", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters or request body", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - SepSep not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + }, + "delete": { + "responses": { + "200": { + "description": "Successfully deleted SepSep", + "schema": { + "$ref": "#/definitions/sep.SepResponse" + } + }, + "400": { + "description": "Bad request - invalid parameters", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "401": { + "description": "Unauthorized - invalid API credentials", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "404": { + "description": "Not found - SepSep not found", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponseBpjs" + } + } + } + } } }, "definitions": { @@ -757,6 +997,27 @@ } } }, + "api-service_internal_models.ErrorResponseBpjs": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "errors": { + "type": "object", + "additionalProperties": true + }, + "message": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, "api-service_internal_models.MetaResponse": { "type": "object", "properties": { @@ -1124,6 +1385,281 @@ } } }, + "peserta.PesertaData": { + "type": "object", + "properties": { + "aktif": { + "type": "string" + }, + "asuransi": { + "type": "string" + }, + "cob": { + "type": "string" + }, + "kdCabang": { + "type": "string" + }, + "kdJnsPst": { + "type": "string" + }, + "ketAktif": { + "type": "string" + }, + "klsRawat": { + "type": "string" + }, + "mr": { + "type": "object", + "properties": { + "nmMR": { + "type": "string" + }, + "noMR": { + "type": "string" + }, + "sex": { + "type": "string" + }, + "tglLahir": { + "type": "string" + }, + "tglMeninggal": { + "type": "string" + } + } + }, + "nama": { + "type": "string" + }, + "nik": { + "type": "string" + }, + "nmCabang": { + "type": "string" + }, + "nmJnsPst": { + "type": "string" + }, + "noKartu": { + "type": "string" + }, + "noKtp": { + "type": "string" + }, + "noSKTM": { + "type": "string" + }, + "pisa": { + "type": "string" + }, + "sex": { + "type": "string" + }, + "statusPeserta": { + "type": "string" + }, + "tglLahir": { + "type": "string" + }, + "tglTAT": { + "type": "string" + }, + "tglTMT": { + "type": "string" + }, + "tglTunggak": { + "type": "string" + } + } + }, + "peserta.PesertaResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/peserta.PesertaData" + }, + "message": { + "type": "string" + }, + "metaData": {}, + "request_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + } + }, + "sep.SepData": { + "type": "object", + "properties": { + "catatan": { + "type": "string" + }, + "diagnosa": { + "type": "string" + }, + "informasi": { + "type": "object", + "properties": { + "dpjpLayan": { + "type": "string" + }, + "noSKDP": { + "type": "string" + }, + "noTelp": { + "type": "string" + }, + "subSpesialis": { + "type": "string" + } + } + }, + "jnsPelayanan": { + "type": "string" + }, + "klsRawat": { + "type": "string" + }, + "noMR": { + "type": "string" + }, + "noSep": { + "type": "string" + }, + "peserta": { + "$ref": "#/definitions/peserta.PesertaData" + }, + "poli": { + "type": "string" + }, + "rujukan": { + "$ref": "#/definitions/sep.SepRujukan" + }, + "tglSep": { + "type": "string" + } + } + }, + "sep.SepRequest": { + "type": "object", + "required": [ + "diagnosa", + "jnsPelayanan", + "klsRawat", + "noKartu", + "noMR", + "poli", + "ppkPelayanan", + "tglSep", + "user" + ], + "properties": { + "catatan": { + "type": "string" + }, + "diagnosa": { + "type": "string" + }, + "jnsPelayanan": { + "type": "string", + "enum": [ + "1", + "2" + ] + }, + "klsRawat": { + "type": "string", + "enum": [ + "1", + "2", + "3" + ] + }, + "noKartu": { + "type": "string" + }, + "noMR": { + "type": "string" + }, + "noTelp": { + "type": "string" + }, + "poli": { + "type": "string" + }, + "ppkPelayanan": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "rujukan": { + "$ref": "#/definitions/sep.SepRujukan" + }, + "tglSep": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "user": { + "type": "string" + } + } + }, + "sep.SepResponse": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/sep.SepData" + }, + "message": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + } + }, + "sep.SepRujukan": { + "type": "object", + "required": [ + "asalRujukan", + "noRujukan", + "ppkRujukan", + "tglRujukan" + ], + "properties": { + "asalRujukan": { + "type": "string", + "enum": [ + "1", + "2" + ] + }, + "noRujukan": { + "type": "string" + }, + "ppkRujukan": { + "type": "string" + }, + "tglRujukan": { + "type": "string" + } + } + }, "sql.NullString": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 27713b4..c6a87d1 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -38,6 +38,20 @@ definitions: timestamp: type: string type: object + api-service_internal_models.ErrorResponseBpjs: + properties: + code: + type: string + errors: + additionalProperties: true + type: object + message: + type: string + request_id: + type: string + status: + type: string + type: object api-service_internal_models.MetaResponse: properties: current_page: @@ -292,6 +306,192 @@ definitions: message: type: string type: object + peserta.PesertaData: + properties: + aktif: + type: string + asuransi: + type: string + cob: + type: string + kdCabang: + type: string + kdJnsPst: + type: string + ketAktif: + type: string + klsRawat: + type: string + mr: + properties: + nmMR: + type: string + noMR: + type: string + sex: + type: string + tglLahir: + type: string + tglMeninggal: + type: string + type: object + nama: + type: string + nik: + type: string + nmCabang: + type: string + nmJnsPst: + type: string + noKartu: + type: string + noKtp: + type: string + noSKTM: + type: string + pisa: + type: string + sex: + type: string + statusPeserta: + type: string + tglLahir: + type: string + tglTAT: + type: string + tglTMT: + type: string + tglTunggak: + type: string + type: object + peserta.PesertaResponse: + properties: + data: + $ref: '#/definitions/peserta.PesertaData' + message: + type: string + metaData: {} + request_id: + type: string + status: + type: string + timestamp: + type: string + type: object + sep.SepData: + properties: + catatan: + type: string + diagnosa: + type: string + informasi: + properties: + dpjpLayan: + type: string + noSKDP: + type: string + noTelp: + type: string + subSpesialis: + type: string + type: object + jnsPelayanan: + type: string + klsRawat: + type: string + noMR: + type: string + noSep: + type: string + peserta: + $ref: '#/definitions/peserta.PesertaData' + poli: + type: string + rujukan: + $ref: '#/definitions/sep.SepRujukan' + tglSep: + type: string + type: object + sep.SepRequest: + properties: + catatan: + type: string + diagnosa: + type: string + jnsPelayanan: + enum: + - "1" + - "2" + type: string + klsRawat: + enum: + - "1" + - "2" + - "3" + type: string + noKartu: + type: string + noMR: + type: string + noTelp: + type: string + poli: + type: string + ppkPelayanan: + type: string + request_id: + type: string + rujukan: + $ref: '#/definitions/sep.SepRujukan' + tglSep: + type: string + timestamp: + type: string + user: + type: string + required: + - diagnosa + - jnsPelayanan + - klsRawat + - noKartu + - noMR + - poli + - ppkPelayanan + - tglSep + - user + type: object + sep.SepResponse: + properties: + data: + $ref: '#/definitions/sep.SepData' + message: + type: string + request_id: + type: string + status: + type: string + timestamp: + type: string + type: object + sep.SepRujukan: + properties: + asalRujukan: + enum: + - "1" + - "2" + type: string + noRujukan: + type: string + ppkRujukan: + type: string + tglRujukan: + type: string + required: + - asalRujukan + - noRujukan + - ppkRujukan + - tglRujukan + type: object sql.NullString: properties: string: @@ -767,6 +967,160 @@ paths: summary: Generate token directly tags: - Token + /peserta/:nokartu: + get: + responses: + "200": + description: Successfully retrieved PesertaBynokartu data + schema: + $ref: '#/definitions/peserta.PesertaResponse' + "400": + description: Bad request - invalid parameters + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "401": + description: Unauthorized - invalid API credentials + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "404": + description: Not found - PesertaBynokartu not found + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + /peserta/nik/:nik: + get: + responses: + "200": + description: Successfully retrieved PesertaBynik data + schema: + $ref: '#/definitions/peserta.PesertaResponse' + "400": + description: Bad request - invalid parameters + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "401": + description: Unauthorized - invalid API credentials + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "404": + description: Not found - PesertaBynik not found + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + /sep: + post: + parameters: + - description: Request ID for tracking + in: header + name: X-Request-ID + type: string + - description: SepSep data + in: body + name: request + required: true + schema: + $ref: '#/definitions/sep.SepRequest' + responses: + "201": + description: Successfully created SepSep + schema: + $ref: '#/definitions/sep.SepResponse' + "400": + description: Bad request - invalid request body or validation error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "401": + description: Unauthorized - invalid API credentials + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "409": + description: Conflict - SepSep already exists + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + /sep/:nosep: + delete: + responses: + "200": + description: Successfully deleted SepSep + schema: + $ref: '#/definitions/sep.SepResponse' + "400": + description: Bad request - invalid parameters + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "401": + description: Unauthorized - invalid API credentials + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "404": + description: Not found - SepSep not found + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + get: + responses: + "200": + description: Successfully retrieved SepSep data + schema: + $ref: '#/definitions/sep.SepResponse' + "400": + description: Bad request - invalid parameters + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "401": + description: Unauthorized - invalid API credentials + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "404": + description: Not found - SepSep not found + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + put: + parameters: + - description: SepSep data + in: body + name: request + required: true + schema: + $ref: '#/definitions/sep.SepRequest' + responses: + "200": + description: Successfully updated SepSep + schema: + $ref: '#/definitions/sep.SepResponse' + "400": + description: Bad request - invalid parameters or request body + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "401": + description: Unauthorized - invalid API credentials + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "404": + description: Not found - SepSep not found + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponseBpjs' schemes: - http - https diff --git a/generetebpjs b/generetebpjs index ada9d50..69d85eb 100644 --- a/generetebpjs +++ b/generetebpjs @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" "strings" "text/template" @@ -12,6 +13,14 @@ import ( "gopkg.in/yaml.v2" ) +// runSwagInit runs the swag init command to generate swagger docs +func runSwagInit() error { + cmd := exec.Command("swag", "init", "-g", "../../cmd/api/main.go", "--parseDependency", "--parseInternal") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + // ServiceConfig represents the main configuration structure type ServiceConfig struct { Services map[string]Service `yaml:"services"` @@ -30,16 +39,16 @@ type GlobalConfig struct { // Service represents individual service configuration type Service struct { - Name string `yaml:"name"` - Category string `yaml:"category"` - Package string `yaml:"package"` - Description string `yaml:"description"` - BaseURL string `yaml:"base_url"` - Timeout int `yaml:"timeout"` - RetryCount int `yaml:"retry_count"` - Endpoints map[string]Endpoint `yaml:"endpoints"` - Middleware []string `yaml:"middleware,omitempty"` // FIXED: Changed to []string - Dependencies []string `yaml:"dependencies,omitempty"` // FIXED: Changed to []string + Name string `yaml:"name"` + Category string `yaml:"category"` + Package string `yaml:"package"` + Description string `yaml:"description"` + BaseURL string `yaml:"base_url"` + Timeout int `yaml:"timeout"` + RetryCount int `yaml:"retry_count"` + Endpoints map[string]SubEndpoints `yaml:"endpoints"` + Middleware []string `yaml:"middleware,omitempty"` // FIXED: Changed to []string + Dependencies []string `yaml:"dependencies,omitempty"` // FIXED: Changed to []string } // Endpoint represents endpoint configuration @@ -59,9 +68,12 @@ type Endpoint struct { RateLimit int `yaml:"rate_limit,omitempty"` CacheEnabled bool `yaml:"cache_enabled"` CacheTTL int `yaml:"cache_ttl,omitempty"` - CustomHeaders map[string]string `yaml:"custom_headers,omitempty"` // ADDED: Missing field + CustomHeaders map[string]string `yaml:"custom_headers,omitempty"` } +// SubEndpoints represents nested endpoint configuration for tree structure +type SubEndpoints map[string]Endpoint + // TemplateData holds data for generating handlers type TemplateData struct { ServiceName string @@ -118,12 +130,11 @@ type EndpointData struct { RequiredFields []string OptionalFields []string CustomHeaders map[string]string + ModelPackage string // Package name for the model (peserta, sep, etc.) } -// Template remains the same as before... +// Updated template for merged handler structure const handlerTemplate = ` -// Code generated by generate-dynamic-handler.go; DO NOT EDIT. -// Generated at: {{.Timestamp}} // Service: {{.ServiceName}} ({{.Category}}) // Description: {{.Description}} @@ -131,14 +142,14 @@ package handlers import ( "context" - "fmt" - "net/http" - "strconv" "strings" + "net/http" "time" "{{.ModuleName}}/internal/config" - "{{.ModuleName}}/internal/models/reference" + "{{.ModuleName}}/internal/models" + "{{.ModuleName}}/internal/models/vclaim/peserta" + "{{.ModuleName}}/internal/models/vclaim/sep" "{{.ModuleName}}/internal/services/bpjs" "{{.ModuleName}}/pkg/logger" @@ -187,12 +198,12 @@ func New{{.ServiceName}}Handler(cfg {{.ServiceName}}HandlerConfig) *{{.ServiceNa {{range .PathParams}} // @Param {{.}} path string true "{{.}}" {{end}} -// @Success 200 {object} reference.{{.ResponseModel}} -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse +// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} +// @Failure 400 {object} models.ErrorResponseBpjs +// @Failure 500 {object} models.ErrorResponseBpjs // @Router {{.GetPath}} [get] {{end}} -func (h *{{$.ServiceName}}Handler) Get{{.NameUpper}}(c *gin.Context) { +func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) defer cancel() @@ -222,7 +233,7 @@ func (h *{{$.ServiceName}}Handler) Get{{.NameUpper}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter {{.}}", RequestID: requestID, @@ -232,11 +243,15 @@ func (h *{{$.ServiceName}}Handler) Get{{.NameUpper}}(c *gin.Context) { {{end}} // Call service method - var response reference.{{.ResponseModel}} + var response {{.ModelPackage}}.{{.ResponseModel}} {{if .PathParams}} - result, err := h.service.Get{{.NameUpper}}(ctx{{range .PathParams}}, {{.}}{{end}}) + endpoint := "{{.GetPath}}" + {{range .PathParams}} + endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1) + {{end}} + err := h.service.Get(ctx, endpoint, &response) {{else}} - result, err := h.service.Get{{.NameUpper}}(ctx) + err := h.service.Get(ctx, "{{.GetPath}}", &response) {{end}} if err != nil { {{if $.HasLogger}} @@ -245,7 +260,7 @@ func (h *{{$.ServiceName}}Handler) Get{{.NameUpper}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -271,13 +286,13 @@ func (h *{{$.ServiceName}}Handler) Get{{.NameUpper}}(c *gin.Context) { {{if .RequireAuth}} // @Security ApiKeyAuth {{end}} -// @Param request body reference.{{.Model}} true "{{.Name}} data" -// @Success 201 {object} reference.{{.ResponseModel}} -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse +// @Param request body {{.ModelPackage}}.{{.Model}} true "{{.Name}} data" +// @Success 201 {object} {{.ModelPackage}}.{{.ResponseModel}} +// @Failure 400 {object} models.ErrorResponseBpjs +// @Failure 500 {object} models.ErrorResponseBpjs // @Router {{.PostPath}} [post] {{end}} -func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { +func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) defer cancel() @@ -293,7 +308,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { }) {{end}} - var req reference.{{.Model}} + var req {{.ModelPackage}}.{{.Model}} if err := c.ShouldBindJSON(&req); err != nil { {{if $.HasLogger}} h.logger.Error("Invalid request body", map[string]interface{}{ @@ -301,7 +316,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, @@ -317,7 +332,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, @@ -326,8 +341,8 @@ func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { } // Call service method - var response reference.{{.ResponseModel}} - result, err := h.service.Create{{.NameUpper}}(ctx, &req) + var response {{.ModelPackage}}.{{.ResponseModel}} + err := h.service.Post(ctx, "{{.PostPath}}", &req, &response) if err != nil { {{if $.HasLogger}} h.logger.Error("Failed to create {{.Name}}", map[string]interface{}{ @@ -335,7 +350,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -349,6 +364,209 @@ func (h *{{$.ServiceName}}Handler) Create{{.NameUpper}}(c *gin.Context) { c.JSON(http.StatusCreated, response) } {{end}} + +{{if .HasPut}} +// Update{{.NameUpper}} updates existing {{.Name}} +{{if $.HasSwagger}} +// @Summary Update {{.Name}} +// @Description {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json +{{if .RequireAuth}} +// @Security ApiKeyAuth +{{end}} +{{range .PathParams}} +// @Param {{.}} path string true "{{.}}" +{{end}} +// @Param request body {{.ModelPackage}}.{{.Model}} true "{{.Name}} data" +// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} +// @Failure 400 {object} models.ErrorResponseBpjs +// @Failure 500 {object} models.ErrorResponseBpjs +// @Router {{.PutPath}} [put] +{{end}} +func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + {{if $.HasLogger}} + h.logger.Info("Processing Update{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + }) + {{end}} + + {{range .PathParams}} + {{.}} := c.Param("{{.}}") + if {{.}} == "" { + {{if $.HasLogger}} + h.logger.Error("Missing required parameter {{.}}", map[string]interface{}{ + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter {{.}}", + RequestID: requestID, + }) + return + } + {{end}} + + var req {{.ModelPackage}}.{{.Model}} + if err := c.ShouldBindJSON(&req); err != nil { + {{if $.HasLogger}} + h.logger.Error("Invalid request body", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Validate request + if err := h.validator.Struct(&req); err != nil { + {{if $.HasLogger}} + h.logger.Error("Validation failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Validation failed: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Call service method + var response {{.ModelPackage}}.{{.ResponseModel}} + {{if .PathParams}} + endpoint := "{{.PutPath}}" + {{range .PathParams}} + endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1) + {{end}} + err := h.service.Put(ctx, endpoint, &req, &response) + {{else}} + err := h.service.Put(ctx, "{{.PutPath}}", &req, &response) + {{end}} + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Failed to update {{.Name}}", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} +{{end}} + +{{if .HasDelete}} +// Delete{{.NameUpper}} deletes existing {{.Name}} +{{if $.HasSwagger}} +// @Summary Delete {{.Name}} +// @Description {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json +{{if .RequireAuth}} +// @Security ApiKeyAuth +{{end}} +{{range .PathParams}} +// @Param {{.}} path string true "{{.}}" +{{end}} +// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} +// @Failure 400 {object} models.ErrorResponseBpjs +// @Failure 500 {object} models.ErrorResponseBpjs +// @Router {{.DeletePath}} [delete] +{{end}} +func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + {{if $.HasLogger}} + h.logger.Info("Processing Delete{{.Name}} request", map[string]interface{}{ + "request_id": requestID, + }) + {{end}} + + {{range .PathParams}} + {{.}} := c.Param("{{.}}") + if {{.}} == "" { + {{if $.HasLogger}} + h.logger.Error("Missing required parameter {{.}}", map[string]interface{}{ + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter {{.}}", + RequestID: requestID, + }) + return + } + {{end}} + + // Call service method + var response {{.ModelPackage}}.{{.ResponseModel}} + {{if .PathParams}} + endpoint := "{{.DeletePath}}" + {{range .PathParams}} + endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1) + {{end}} + err := h.service.Delete(ctx, endpoint, &response) + {{else}} + err := h.service.Delete(ctx, "{{.DeletePath}}", &response) + {{end}} + if err != nil { + {{if $.HasLogger}} + h.logger.Error("Failed to delete {{.Name}}", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + {{end}} + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} +{{end}} {{end}} ` @@ -406,6 +624,16 @@ func main() { if generated > 0 { fmt.Println("🎉 Generation completed successfully!") + + // Generate Swagger documentation if enabled + if config.Global.EnableSwagger { + fmt.Println("📚 Generating Swagger documentation...") + if err := runSwagInit(); err != nil { + fmt.Printf("⚠️ Warning: Failed to generate Swagger docs: %v\n", err) + } else { + fmt.Println("✅ Swagger documentation generated successfully!") + } + } } } @@ -467,19 +695,28 @@ func generateHandler(serviceName string, service Service, globalConfig GlobalCon } // Check for advanced features - for _, endpoint := range service.Endpoints { - if endpoint.RequireAuth { - templateData.HasAuth = true - } - if endpoint.CacheEnabled { - templateData.HasCache = true + for _, subEndpoints := range service.Endpoints { + for _, endpoint := range subEndpoints { + if endpoint.RequireAuth { + templateData.HasAuth = true + } + if endpoint.CacheEnabled { + templateData.HasCache = true + } } } // Process endpoints - for endpointName, endpoint := range service.Endpoints { - endpointData := processEndpoint(endpointName, endpoint) - templateData.Endpoints = append(templateData.Endpoints, endpointData) + for endpointName, subEndpoints := range service.Endpoints { + for subEndpointName, endpoint := range subEndpoints { + // Compose full endpoint name with sub-endpoint name + fullEndpointName := endpointName + if subEndpointName != "" { + fullEndpointName = fullEndpointName + strings.Title(subEndpointName) + } + endpointData := processEndpoint(fullEndpointName, endpoint, endpointName) + templateData.Endpoints = append(templateData.Endpoints, endpointData) + } } // Create output directory @@ -521,7 +758,7 @@ func generateHandler(serviceName string, service Service, globalConfig GlobalCon return nil } -func processEndpoint(name string, endpoint Endpoint) EndpointData { +func processEndpoint(name string, endpoint Endpoint, endpointGroup string) EndpointData { data := EndpointData{ Name: strings.Title(name), NameLower: strings.ToLower(name), @@ -543,6 +780,7 @@ func processEndpoint(name string, endpoint Endpoint) EndpointData { CacheEnabled: endpoint.CacheEnabled, CacheTTL: getOrDefault(endpoint.CacheTTL, 300), CustomHeaders: endpoint.CustomHeaders, + ModelPackage: endpointGroup, // Set the model package based on endpoint group } // Set method flags and extract path parameters diff --git a/internal/config/config.go b/internal/config/config.go index 7c899e2..0411e77 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,6 +11,8 @@ import ( "strconv" "strings" "time" + + "github.com/go-playground/validator/v10" ) type Config struct { @@ -21,6 +23,7 @@ type Config struct { Bpjs BpjsConfig SatuSehat SatuSehatConfig Swagger SwaggerConfig + Validator *validator.Validate } type SwaggerConfig struct { @@ -173,6 +176,9 @@ func LoadConfig() *Config { }, } + // Initialize validator + config.Validator = validator.New() + // Load database configurations config.loadDatabaseConfigs() diff --git a/internal/handlers/test.txt/sw b/internal/handlers/test.txt/sw new file mode 100644 index 0000000..1d6499d --- /dev/null +++ b/internal/handlers/test.txt/sw @@ -0,0 +1,510 @@ +// Code generated by generate-dynamic-handler.go DO NOT EDIT. +// Generated at 2025-09-01 13:38:57 +// Service: VClaim (vclaim) +// Description: BPJS VClaim service for eligibility and SEP management + +package handlers + +import ( + "context" + "net/http" + "strings" + "time" + + "api-service/internal/config" + "api-service/internal/models" + "api-service/internal/models/vclaim/peserta" + "api-service/internal/models/vclaim/sep" + "api-service/internal/services" + "api-service/pkg/logger" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + "github.com/google/uuid" +) + +// VClaimHandler handles VClaim BPJS services +type VClaimHandler struct { + service services.VClaimService + validator *validator.Validate + logger logger.Logger + config config.BpjsConfig +} + +// VClaimHandlerConfig contains configuration for VClaimHandler +type VClaimHandlerConfig struct { + BpjsConfig config.BpjsConfig + Logger logger.Logger + Validator *validator.Validate +} + +// NewVClaimHandler creates a new VClaimHandler +func NewVClaimHandler(cfg VClaimHandlerConfig) *VClaimHandler { + return &VClaimHandler{ + service: services.NewService(cfg.BpjsConfig), + validator: cfg.Validator, + logger: cfg.Logger, + config: cfg.BpjsConfig, + } +} + +// GetPesertaBynokartu godoc +// @Summary Get participant data by card number +// @Description Get participant eligibility information from BPJS by card number +// @Tags vclaim,peserta +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param nokartu path string true "BPJS card number" example("0000054321654") +// @Success 200 {object} peserta.PesertaResponse "Successfully retrieved participant data" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid card number format" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - participant not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /peserta/{nokartu} [get] +func (h *VClaimHandler) GetPesertaBynokartu(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + // Generate request ID if not present + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing GetPesertaBynokartu request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "peserta/{nokartu}", + "nokartu": c.Param("nokartu"), + }) + + // Extract path parameters + nokartu := c.Param("nokartu") + if nokartu == "" { + h.logger.Error("Missing required parameter: nokartu", map[string]interface{}{ + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter: nokartu", + RequestID: requestID, + }) + return + } + + // Call service method + var response peserta.PesertaResponse + endpoint := "peserta/{nokartu}" + endpoint = strings.Replace(endpoint, "{nokartu}", nokartu, 1) + + err := h.service.Get(ctx, endpoint, &response) + if err != nil { + h.logger.Error("Failed to get PesertaBynokartu", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + + c.JSON(http.StatusOK, response) +} + +// GetPesertaBynik godoc +// @Summary Get participant data by NIK +// @Description Get participant eligibility information from BPJS by National ID Number (NIK) +// @Tags vclaim,peserta +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param nik path string true "National ID Number (NIK)" example("3201234567890123") +// @Success 200 {object} peserta.PesertaResponse "Successfully retrieved participant data" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid NIK format" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - participant not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /peserta/nik/{nik} [get] +func (h *VClaimHandler) GetPesertaBynik(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + // Generate request ID if not present + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing GetPesertaBynik request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "peserta/nik/{nik}", + "nik": c.Param("nik"), + }) + + // Extract path parameters + nik := c.Param("nik") + if nik == "" { + h.logger.Error("Missing required parameter: nik", map[string]interface{}{ + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter: nik", + RequestID: requestID, + }) + return + } + + // Call service method + var response peserta.PesertaResponse + endpoint := "peserta/nik/{nik}" + endpoint = strings.Replace(endpoint, "{nik}", nik, 1) + + err := h.service.Get(ctx, endpoint, &response) + if err != nil { + h.logger.Error("Failed to get PesertaBynik", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + + c.JSON(http.StatusOK, response) +} + +// GetSepSep godoc +// @Summary Get SEP data by number +// @Description Get SEP (Surat Eligibilitas Peserta) information by SEP number +// @Tags vclaim,sep +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param nosep path string true "SEP Number" example("0301R0010717V000001") +// @Success 200 {object} sep.SepResponse "Successfully retrieved SEP data" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid SEP number format" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - SEP not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep/{nosep} [get] +func (h *VClaimHandler) GetSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + // Generate request ID if not present + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing GetSepSep request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "sep/{nosep}", + "nosep": c.Param("nosep"), + }) + + // Extract path parameters + nosep := c.Param("nosep") + if nosep == "" { + h.logger.Error("Missing required parameter: nosep", map[string]interface{}{ + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter: nosep", + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + endpoint := "sep/{nosep}" + endpoint = strings.Replace(endpoint, "{nosep}", nosep, 1) + + err := h.service.Get(ctx, endpoint, &response) + if err != nil { + h.logger.Error("Failed to get SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + + c.JSON(http.StatusOK, response) +} + +// CreateSepSep godoc +// @Summary Create new SEP +// @Description Create a new SEP (Surat Eligibilitas Peserta) for participant +// @Tags vclaim,sep +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param request body sep.SepRequest true "SEP creation data" +// @Success 201 {object} sep.SepResponse "Successfully created SEP" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid request body or validation error" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 409 {object} models.ErrorResponseBpjs "Conflict - SEP already exists" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep [post] +func (h *VClaimHandler) CreateSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing CreateSepSep request", map[string]interface{}{ + "request_id": requestID, + }) + + var req sep.SepRequest + if err := c.ShouldBindJSON(&req); err != nil { + h.logger.Error("Invalid request body", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Validate request + if err := h.validator.Struct(&req); err != nil { + h.logger.Error("Validation failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Validation failed: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + err := h.service.Post(ctx, "sep", &req, &response) + if err != nil { + h.logger.Error("Failed to create SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + + c.JSON(http.StatusCreated, response) +} + +// UpdateSepSep godoc +// @Summary Update existing SEP +// @Description Update an existing SEP (Surat Eligibilitas Peserta) by SEP number +// @Tags vclaim,sep +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param nosep path string true "SEP Number to update" example("0301R0010717V000001") +// @Param request body sep.SepRequest true "SEP update data" +// @Success 200 {object} sep.SepResponse "Successfully updated SEP" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid request body or validation error" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - SEP not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep/{nosep} [put] +func (h *VClaimHandler) UpdateSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing UpdateSepSep request", map[string]interface{}{ + "request_id": requestID, + }) + + nosep := c.Param("nosep") + if nosep == "" { + h.logger.Error("Missing required parameter: nosep", map[string]interface{}{ + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter: nosep", + RequestID: requestID, + }) + return + } + + var req sep.SepRequest + if err := c.ShouldBindJSON(&req); err != nil { + h.logger.Error("Invalid request body", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Validate request + if err := h.validator.Struct(&req); err != nil { + h.logger.Error("Validation failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Validation failed: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + endpoint := "sep/{nosep}" + endpoint = strings.Replace(endpoint, "{nosep}", nosep, 1) + + err := h.service.Put(ctx, endpoint, &req, &response) + if err != nil { + h.logger.Error("Failed to update SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + + c.JSON(http.StatusOK, response) +} + +// DeleteSepSep godoc +// @Summary Delete SEP +// @Description Delete an existing SEP (Surat Eligibilitas Peserta) by SEP number +// @Tags vclaim,sep +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param nosep path string true "SEP Number to delete" example("0301R0010717V000001") +// @Success 200 {object} sep.SepResponse "Successfully deleted SEP" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid SEP number format" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - SEP not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep/{nosep} [delete] +func (h *VClaimHandler) DeleteSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing DeleteSepSep request", map[string]interface{}{ + "request_id": requestID, + }) + + nosep := c.Param("nosep") + if nosep == "" { + h.logger.Error("Missing required parameter: nosep", map[string]interface{}{ + "request_id": requestID, + }) + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter: nosep", + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + endpoint := "sep/{nosep}" + endpoint = strings.Replace(endpoint, "{nosep}", nosep, 1) + + err := h.service.Delete(ctx, endpoint, &response) + if err != nil { + h.logger.Error("Failed to delete SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + + c.JSON(http.StatusOK, response) +} diff --git a/internal/handlers/test.txt/vclaim.backup b/internal/handlers/test.txt/vclaim.backup new file mode 100644 index 0000000..cffc5bf --- /dev/null +++ b/internal/handlers/test.txt/vclaim.backup @@ -0,0 +1,543 @@ +// Code generated by generate-dynamic-handler.go; DO NOT EDIT. +// Generated at: 2025-09-01 13:38:57 +// Service: VClaim (vclaim) +// Description: BPJS VClaim service for eligibility and SEP management + +package handlers + +import ( + "context" + "net/http" + "strings" + "time" + + "api-service/internal/config" + "api-service/internal/models" + "api-service/internal/models/vclaim/peserta" + "api-service/internal/models/vclaim/sep" + services "api-service/internal/services/bpjs" + "api-service/pkg/logger" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + "github.com/google/uuid" +) + +// VClaimHandler handles VClaim BPJS services +type VClaimHandler struct { + service services.VClaimService + validator *validator.Validate + logger logger.Logger + config config.BpjsConfig +} + +// VClaimHandlerConfig contains configuration for VClaimHandler +type VClaimHandlerConfig struct { + BpjsConfig config.BpjsConfig + Logger logger.Logger + Validator *validator.Validate +} + +// NewVClaimHandler creates a new VClaimHandler +func NewVClaimHandler(cfg VClaimHandlerConfig) *VClaimHandler { + return &VClaimHandler{ + service: services.NewService(cfg.BpjsConfig), + validator: cfg.Validator, + logger: cfg.Logger, + config: cfg.BpjsConfig, + } +} + +/* +GetPesertaBynokartu godoc +@Summary Get PesertaBynokartu data +@Description Get participant eligibility information by card number +@Tags vclaim,peserta,nokartu +@Accept json +@Produce json +@Param nokartu path string true "nokartu" +@Success 200 {object} peserta.PesertaResponse +@Failure 400 {object} models.ErrorResponseBpjs +@Failure 500 {object} models.ErrorResponseBpjs +@Router /peserta/:nokartu [get] +*/ +func (h *VClaimHandler) GetPesertaBynokartu(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + // Generate request ID if not present + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing GetPesertaBynokartu request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "/peserta/:nokartu", + + "nokartu": c.Param("nokartu"), + }) + + // Extract path parameters + + nokartu := c.Param("nokartu") + if nokartu == "" { + + h.logger.Error("Missing required parameter nokartu", map[string]interface{}{ + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter nokartu", + RequestID: requestID, + }) + return + } + + // Call service method + var response peserta.PesertaResponse + + endpoint := "/peserta/:nokartu" + + endpoint = strings.Replace(endpoint, ":nokartu", nokartu, 1) + + err := h.service.Get(ctx, endpoint, &response) + + if err != nil { + + h.logger.Error("Failed to get PesertaBynokartu", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} + +/* +GetPesertaBynik godoc +@Summary Get PesertaBynik data +@Description Get participant eligibility information by NIK +@Tags vclaim,peserta,nik +@Accept json +@Produce json +@Param nik path string true "nik" +@Success 200 {object} peserta.PesertaResponse +@Failure 400 {object} models.ErrorResponseBpjs +@Failure 500 {object} models.ErrorResponseBpjs +@Router /peserta/nik/:nik [get] +*/ +func (h *VClaimHandler) GetPesertaBynik(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + // Generate request ID if not present + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing GetPesertaBynik request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "/peserta/nik/:nik", + + "nik": c.Param("nik"), + }) + + // Extract path parameters + + nik := c.Param("nik") + if nik == "" { + + h.logger.Error("Missing required parameter nik", map[string]interface{}{ + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter nik", + RequestID: requestID, + }) + return + } + + // Call service method + var response peserta.PesertaResponse + + endpoint := "/peserta/nik/:nik" + + endpoint = strings.Replace(endpoint, ":nik", nik, 1) + + err := h.service.Get(ctx, endpoint, &response) + + if err != nil { + + h.logger.Error("Failed to get PesertaBynik", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} + +/* +GetSepSep godoc +@Summary Get SepSep data +@Description Manage SEP (Surat Eligibilitas Peserta) +@Tags vclaim,sep +@Accept json +@Produce json +@Param nosep path string true "nosep" +@Success 200 {object} sep.SepResponse +@Failure 400 {object} models.ErrorResponseBpjs +@Failure 500 {object} models.ErrorResponseBpjs +@Router /sep/:nosep [get] +*/ +func (h *VClaimHandler) GetSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + // Generate request ID if not present + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing GetSepSep request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "/sep/:nosep", + + "nosep": c.Param("nosep"), + }) + + // Extract path parameters + + nosep := c.Param("nosep") + if nosep == "" { + + h.logger.Error("Missing required parameter nosep", map[string]interface{}{ + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter nosep", + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + + endpoint := "/sep/:nosep" + + endpoint = strings.Replace(endpoint, ":nosep", nosep, 1) + + err := h.service.Get(ctx, endpoint, &response) + + if err != nil { + + h.logger.Error("Failed to get SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} + +/* +CreateSepSep godoc +@Summary Create SepSep +@Description Manage SEP (Surat Eligibilitas Peserta) +@Tags vclaim,sep +@Accept json +@Produce json +@Param request body sep.SepRequest true "SepSep data" +@Success 201 {object} sep.SepResponse +@Failure 400 {object} models.ErrorResponseBpjs +@Failure 500 {object} models.ErrorResponseBpjs +@Router /sep [post] +*/ +func (h *VClaimHandler) CreateSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing CreateSepSep request", map[string]interface{}{ + "request_id": requestID, + }) + + var req sep.SepRequest + if err := c.ShouldBindJSON(&req); err != nil { + + h.logger.Error("Invalid request body", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Validate request + if err := h.validator.Struct(&req); err != nil { + + h.logger.Error("Validation failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Validation failed: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + err := h.service.Post(ctx, "/sep", &req, &response) + if err != nil { + + h.logger.Error("Failed to create SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusCreated, response) +} + +/* +UpdateSepSep godoc +@Summary Update SepSep +@Description Manage SEP (Surat Eligibilitas Peserta) +@Tags vclaim,sep +@Accept json +@Produce json +@Param nosep path string true "nosep" +@Param request body sep.SepRequest true "SepSep data" +@Success 200 {object} sep.SepResponse +@Failure 400 {object} models.ErrorResponseBpjs +@Failure 500 {object} models.ErrorResponseBpjs +@Router /sep/:nosep [put] +*/ +func (h *VClaimHandler) UpdateSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing UpdateSepSep request", map[string]interface{}{ + "request_id": requestID, + }) + + nosep := c.Param("nosep") + if nosep == "" { + + h.logger.Error("Missing required parameter nosep", map[string]interface{}{ + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter nosep", + RequestID: requestID, + }) + return + } + + var req sep.SepRequest + if err := c.ShouldBindJSON(&req); err != nil { + + h.logger.Error("Invalid request body", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Invalid request body: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Validate request + if err := h.validator.Struct(&req); err != nil { + + h.logger.Error("Validation failed", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Validation failed: " + err.Error(), + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + + endpoint := "/sep/:nosep" + + endpoint = strings.Replace(endpoint, ":nosep", nosep, 1) + + err := h.service.Put(ctx, endpoint, &req, &response) + + if err != nil { + + h.logger.Error("Failed to update SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} + +/* +DeleteSepSep godoc +@Summary Delete SepSep +@Description Manage SEP (Surat Eligibilitas Peserta) +@Tags vclaim,sep +@Accept json +@Produce json +@Param nosep path string true "nosep" +@Success 200 {object} sep.SepResponse +@Failure 400 {object} models.ErrorResponseBpjs +@Failure 500 {object} models.ErrorResponseBpjs +@Router /sep/:nosep [delete] +*/ +func (h *VClaimHandler) DeleteSepSep(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + h.logger.Info("Processing DeleteSepSep request", map[string]interface{}{ + "request_id": requestID, + }) + + nosep := c.Param("nosep") + if nosep == "" { + + h.logger.Error("Missing required parameter nosep", map[string]interface{}{ + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter nosep", + RequestID: requestID, + }) + return + } + + // Call service method + var response sep.SepResponse + + endpoint := "/sep/:nosep" + + endpoint = strings.Replace(endpoint, ":nosep", nosep, 1) + + err := h.service.Delete(ctx, endpoint, &response) + + if err != nil { + + h.logger.Error("Failed to delete SepSep", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} diff --git a/internal/handlers/vclaim_handler.go b/internal/handlers/vclaim_handler.go index 0322226..3b2b58a 100644 --- a/internal/handlers/vclaim_handler.go +++ b/internal/handlers/vclaim_handler.go @@ -1,5 +1,4 @@ -// Code generated by generate-dynamic-handler.go; DO NOT EDIT. -// Generated at: 2025-09-01 11:57:39 + // Service: VClaim (vclaim) // Description: BPJS VClaim service for eligibility and SEP management @@ -7,11 +6,15 @@ package handlers import ( "context" + "strings" "net/http" "time" "api-service/internal/config" - "api-service/internal/models/reference" + "api-service/internal/models" + "api-service/internal/models/vclaim/peserta" + "api-service/internal/models/vclaim/sep" + "api-service/internal/services/bpjs" "api-service/pkg/logger" "github.com/gin-gonic/gin" @@ -19,22 +22,9 @@ import ( "github.com/google/uuid" ) -// VClaimService defines VClaim service interface -type VClaimService interface { - GetPeserta(ctx context.Context, nokartu string) (*reference.PesertaData, error) - - GetSep(ctx context.Context, nosep string) (*reference.SepData, error) - - CreateSep(ctx context.Context, req *reference.SepRequest) (*reference.SepData, error) - - UpdateSep(ctx context.Context, nosep string, req *reference.SepRequest) (*reference.SepData, error) - - DeleteSep(ctx context.Context, nosep string) error -} - // VClaimHandler handles VClaim BPJS services type VClaimHandler struct { - service VClaimService + service services.VClaimService validator *validator.Validate logger logger.Logger config config.BpjsConfig @@ -48,31 +38,34 @@ type VClaimHandlerConfig struct { } // NewVClaimHandler creates a new VClaimHandler -func NewVClaimHandler(cfg VClaimHandlerConfig, service VClaimService) *VClaimHandler { +func NewVClaimHandler(cfg VClaimHandlerConfig) *VClaimHandler { return &VClaimHandler{ - service: service, + service: services.NewService(cfg.BpjsConfig), validator: cfg.Validator, logger: cfg.Logger, config: cfg.BpjsConfig, } } -// GetPESERTA retrieves Peserta data -// @Summary Get Peserta data -// @Description Get participant eligibility information -// @Tags vclaim,peserta -// @Accept json -// @Produce json +// GetPESERTABYNIK godoc +// @Summary Get PesertaBynik data +// @Description Get participant eligibility information by NIK +// @Tags vclaim,peserta,nik +// @Accept json +// @Produce json -// @Param nokartu path string true "nokartu" +// @Param X-Request-ID header string false "Request ID for tracking" -// @Success 200 {object} reference.PesertaResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /peserta/:nokartu [get] +// @Param nik path string true "nik" example("example_value") -func (h *VClaimHandler) GetPeserta(c *gin.Context) { +// @Success 200 {object} peserta.PesertaResponse "Successfully retrieved PesertaBynik data" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - PesertaBynik not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /peserta/nik/:nik [get] +func (h *VClaimHandler) GetPesertaBynik(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() @@ -83,43 +76,145 @@ func (h *VClaimHandler) GetPeserta(c *gin.Context) { c.Header("X-Request-ID", requestID) } - h.logger.Info("Processing GetPeserta request", map[string]interface{}{ + + h.logger.Info("Processing GetPesertaBynik request", map[string]interface{}{ "request_id": requestID, - "endpoint": "/peserta/:nokartu", - - "nokartu": c.Param("nokartu"), + "endpoint": "/peserta/nik/:nik", + + "nik": c.Param("nik"), + }) + // Extract path parameters + + nik := c.Param("nik") + if nik == "" { + + h.logger.Error("Missing required parameter nik", map[string]interface{}{ + "request_id": requestID, + }) + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ + Status: "error", + Message: "Missing required parameter nik", + RequestID: requestID, + }) + return + } + + // Call service method + var response peserta.PesertaResponse + + endpoint := "/peserta/nik/:nik" + + endpoint = strings.Replace(endpoint, ":nik", nik, 1) + + err := h.service.Get(ctx, endpoint, &response) + + if err != nil { + + h.logger.Error("Failed to get PesertaBynik", map[string]interface{}{ + "error": err.Error(), + "request_id": requestID, + }) + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ + Status: "error", + Message: "Internal server error", + RequestID: requestID, + }) + return + } + + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) +} + + + + + + + + + +// GetPESERTABYNOKARTU godoc +// @Summary Get PesertaBynokartu data +// @Description Get participant eligibility information by card number +// @Tags vclaim,peserta,nokartu +// @Accept json +// @Produce json + +// @Param X-Request-ID header string false "Request ID for tracking" + +// @Param nokartu path string true "nokartu" example("example_value") + +// @Success 200 {object} peserta.PesertaResponse "Successfully retrieved PesertaBynokartu data" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - PesertaBynokartu not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /peserta/:nokartu [get] +func (h *VClaimHandler) GetPesertaBynokartu(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) + defer cancel() + + // Generate request ID if not present + requestID := c.GetHeader("X-Request-ID") + if requestID == "" { + requestID = uuid.New().String() + c.Header("X-Request-ID", requestID) + } + + + h.logger.Info("Processing GetPesertaBynokartu request", map[string]interface{}{ + "request_id": requestID, + "endpoint": "/peserta/:nokartu", + + "nokartu": c.Param("nokartu"), + + }) + + + // Extract path parameters + nokartu := c.Param("nokartu") if nokartu == "" { - + h.logger.Error("Missing required parameter nokartu", map[string]interface{}{ "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter nokartu", RequestID: requestID, }) return } + // Call service method - var response reference.PesertaResponse - - result, err := h.service.GetPeserta(ctx, nokartu) - + var response peserta.PesertaResponse + + endpoint := "/peserta/:nokartu" + + endpoint = strings.Replace(endpoint, ":nokartu", nokartu, 1) + + err := h.service.Get(ctx, endpoint, &response) + if err != nil { - - h.logger.Error("Failed to get Peserta", map[string]interface{}{ + + h.logger.Error("Failed to get PesertaBynokartu", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -130,26 +225,35 @@ func (h *VClaimHandler) GetPeserta(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - response.Data = result c.JSON(http.StatusOK, response) } -// GetSEP retrieves Sep data -// @Summary Get Sep data -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Param nosep path string true "nosep" -// @Success 200 {object} reference.SepResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep/:nosep [get] -func (h *VClaimHandler) GetSep(c *gin.Context) { + + + + +// GetSEPSEP godoc +// @Summary Get SepSep data +// @Description Manage SEP (Surat Eligibilitas Peserta) +// @Tags vclaim,sep +// @Accept json +// @Produce json + +// @Param X-Request-ID header string false "Request ID for tracking" + +// @Param nosep path string true "nosep" example("example_value") + +// @Success 200 {object} sep.SepResponse "Successfully retrieved SepSep data" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - SepSep not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep/:nosep [get] +func (h *VClaimHandler) GetSepSep(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() @@ -160,43 +264,51 @@ func (h *VClaimHandler) GetSep(c *gin.Context) { c.Header("X-Request-ID", requestID) } - h.logger.Info("Processing GetSep request", map[string]interface{}{ + + h.logger.Info("Processing GetSepSep request", map[string]interface{}{ "request_id": requestID, "endpoint": "/sep/:nosep", - + "nosep": c.Param("nosep"), + }) + // Extract path parameters - + nosep := c.Param("nosep") if nosep == "" { - + h.logger.Error("Missing required parameter nosep", map[string]interface{}{ "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter nosep", RequestID: requestID, }) return } + // Call service method - var response reference.SepResponse - - result, err := h.service.GetSep(ctx, nosep) - + var response sep.SepResponse + + endpoint := "/sep/:nosep" + + endpoint = strings.Replace(endpoint, ":nosep", nosep, 1) + + err := h.service.Get(ctx, endpoint, &response) + if err != nil { - - h.logger.Error("Failed to get Sep", map[string]interface{}{ + + h.logger.Error("Failed to get SepSep", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -207,25 +319,27 @@ func (h *VClaimHandler) GetSep(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - response.Data = result c.JSON(http.StatusOK, response) } -// CreateSEP creates new Sep -// @Summary Create Sep -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Param request body reference.SepRequest true "Sep data" -// @Success 201 {object} reference.SepResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep [post] +// CreateSEPSEP godoc +// @Summary Create new SepSep +// @Description Manage SEP (Surat Eligibilitas Peserta) +// @Tags vclaim,sep +// @Accept json +// @Produce json -func (h *VClaimHandler) CreateSep(c *gin.Context) { +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param request body sep.SepRequest true "SepSep data" +// @Success 201 {object} sep.SepResponse "Successfully created SepSep" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid request body or validation error" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 409 {object} models.ErrorResponseBpjs "Conflict - SepSep already exists" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep [post] +func (h *VClaimHandler) CreateSepSep(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() @@ -235,19 +349,21 @@ func (h *VClaimHandler) CreateSep(c *gin.Context) { c.Header("X-Request-ID", requestID) } - h.logger.Info("Processing CreateSep request", map[string]interface{}{ + + h.logger.Info("Processing CreateSepSep request", map[string]interface{}{ "request_id": requestID, }) + - var req reference.SepRequest + var req sep.SepRequest if err := c.ShouldBindJSON(&req); err != nil { - + h.logger.Error("Invalid request body", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, @@ -257,13 +373,13 @@ func (h *VClaimHandler) CreateSep(c *gin.Context) { // Validate request if err := h.validator.Struct(&req); err != nil { - + h.logger.Error("Validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, @@ -272,16 +388,16 @@ func (h *VClaimHandler) CreateSep(c *gin.Context) { } // Call service method - var response reference.SepResponse - result, err := h.service.CreateSep(ctx, &req) + var response sep.SepResponse + err := h.service.Post(ctx, "/sep", &req, &response) if err != nil { - - h.logger.Error("Failed to create Sep", map[string]interface{}{ + + h.logger.Error("Failed to create SepSep", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -292,27 +408,30 @@ func (h *VClaimHandler) CreateSep(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - response.Data = result c.JSON(http.StatusCreated, response) } -// UpdateSEP updates existing Sep -// @Summary Update Sep -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Param nosep path string true "nosep" +// UpdateSEPSEP godoc +// @Summary Update existing SepSep +// @Description Manage SEP (Surat Eligibilitas Peserta) +// @Tags vclaim,sep +// @Accept json +// @Produce json -// @Param request body reference.SepRequest true "Sep data" -// @Success 200 {object} reference.SepResponse -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep/:nosep [put] +// @Param X-Request-ID header string false "Request ID for tracking" -func (h *VClaimHandler) UpdateSep(c *gin.Context) { +// @Param nosep path string true "nosep" example("example_value") + +// @Param request body sep.SepRequest true "SepSep data" +// @Success 200 {object} sep.SepResponse "Successfully updated SepSep" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters or request body" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - SepSep not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep/:nosep [put] +func (h *VClaimHandler) UpdateSepSep(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() @@ -322,34 +441,38 @@ func (h *VClaimHandler) UpdateSep(c *gin.Context) { c.Header("X-Request-ID", requestID) } - h.logger.Info("Processing UpdateSep request", map[string]interface{}{ + + h.logger.Info("Processing UpdateSepSep request", map[string]interface{}{ "request_id": requestID, }) + + nosep := c.Param("nosep") if nosep == "" { - + h.logger.Error("Missing required parameter nosep", map[string]interface{}{ "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter nosep", RequestID: requestID, }) return } + - var req reference.SepRequest + var req sep.SepRequest if err := c.ShouldBindJSON(&req); err != nil { - + h.logger.Error("Invalid request body", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, @@ -359,13 +482,13 @@ func (h *VClaimHandler) UpdateSep(c *gin.Context) { // Validate request if err := h.validator.Struct(&req); err != nil { - + h.logger.Error("Validation failed", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, @@ -374,18 +497,22 @@ func (h *VClaimHandler) UpdateSep(c *gin.Context) { } // Call service method - var response reference.SepResponse - - result, err := h.service.UpdateSep(ctx, nosep, &req) - + var response sep.SepResponse + + endpoint := "/sep/:nosep" + + endpoint = strings.Replace(endpoint, ":nosep", nosep, 1) + + err := h.service.Put(ctx, endpoint, &req, &response) + if err != nil { - - h.logger.Error("Failed to update Sep", map[string]interface{}{ + + h.logger.Error("Failed to update SepSep", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -396,26 +523,29 @@ func (h *VClaimHandler) UpdateSep(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - response.Data = result c.JSON(http.StatusOK, response) } -// DeleteSEP deletes existing Sep -// @Summary Delete Sep -// @Description Manage SEP (Surat Eligibilitas Peserta) -// @Tags vclaim,sep -// @Accept json -// @Produce json -// @Param nosep path string true "nosep" +// DeleteSEPSEP godoc +// @Summary Delete existing SepSep +// @Description Manage SEP (Surat Eligibilitas Peserta) +// @Tags vclaim,sep +// @Accept json +// @Produce json -// @Success 204 {object} nil -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router /sep/:nosep [delete] +// @Param X-Request-ID header string false "Request ID for tracking" -func (h *VClaimHandler) DeleteSep(c *gin.Context) { +// @Param nosep path string true "nosep" example("example_value") + +// @Success 200 {object} sep.SepResponse "Successfully deleted SepSep" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - SepSep not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router /sep/:nosep [delete] +func (h *VClaimHandler) DeleteSepSep(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) defer cancel() @@ -425,37 +555,46 @@ func (h *VClaimHandler) DeleteSep(c *gin.Context) { c.Header("X-Request-ID", requestID) } - h.logger.Info("Processing DeleteSep request", map[string]interface{}{ + + h.logger.Info("Processing DeleteSepSep request", map[string]interface{}{ "request_id": requestID, }) + + nosep := c.Param("nosep") if nosep == "" { - + h.logger.Error("Missing required parameter nosep", map[string]interface{}{ "request_id": requestID, }) - - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter nosep", RequestID: requestID, }) return } + // Call service method - - err := h.service.DeleteSep(ctx, nosep) - + var response sep.SepResponse + + endpoint := "/sep/:nosep" + + endpoint = strings.Replace(endpoint, ":nosep", nosep, 1) + + err := h.service.Delete(ctx, endpoint, &response) + if err != nil { - - h.logger.Error("Failed to delete Sep", map[string]interface{}{ + + h.logger.Error("Failed to delete SepSep", map[string]interface{}{ "error": err.Error(), "request_id": requestID, }) - - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -463,5 +602,10 @@ func (h *VClaimHandler) DeleteSep(c *gin.Context) { return } - c.Status(http.StatusNoContent) + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) } + + diff --git a/internal/models/aplicare/monitoring/monitoring.go b/internal/models/aplicare/monitoring/monitoring.go new file mode 100644 index 0000000..6a7a6ff --- /dev/null +++ b/internal/models/aplicare/monitoring/monitoring.go @@ -0,0 +1,43 @@ +package aplicare + +import "api-service/internal/models" + +// === MONITORING MODELS === + +// MonitoringRequest represents monitoring data request +type MonitoringRequest struct { + models.BaseRequest + TanggalAwal string `json:"tanggal_awal" validate:"required"` + TanggalAkhir string `json:"tanggal_akhir" validate:"required"` + JenisLaporan string `json:"jenis_laporan" validate:"required,oneof=kunjungan klaim rujukan sep"` + PPK string `json:"ppk,omitempty"` + StatusData string `json:"status_data,omitempty"` + models.PaginationRequest +} + +// MonitoringData represents monitoring information +type MonitoringData struct { + Tanggal string `json:"tanggal"` + PPK string `json:"ppk"` + NamaPPK string `json:"nama_ppk"` + JumlahKasus int `json:"jumlah_kasus"` + TotalTarif float64 `json:"total_tarif"` + StatusData string `json:"status_data"` + Keterangan string `json:"keterangan,omitempty"` +} + +// MonitoringResponse represents monitoring API response +type MonitoringResponse struct { + models.BaseResponse + Data []MonitoringData `json:"data,omitempty"` + Summary *MonitoringSummary `json:"summary,omitempty"` + Pagination *models.PaginationResponse `json:"pagination,omitempty"` +} + +// MonitoringSummary represents monitoring summary +type MonitoringSummary struct { + TotalKasus int `json:"total_kasus"` + TotalTarif float64 `json:"total_tarif"` + RataRataTarif float64 `json:"rata_rata_tarif"` + PeriodeLaporan string `json:"periode_laporan"` +} diff --git a/internal/models/aplicare/reference/reference.go b/internal/models/aplicare/reference/reference.go new file mode 100644 index 0000000..0ebc920 --- /dev/null +++ b/internal/models/aplicare/reference/reference.go @@ -0,0 +1,32 @@ +package aplicare + +import "api-service/internal/models" + +// === REFERENSI MODELS === + +// ReferensiRequest represents referensi lookup request +type ReferensiRequest struct { + models.BaseRequest + JenisReferensi string `json:"jenis_referensi" validate:"required,oneof=diagnosa procedure obat alkes faskes dokter poli"` + Keyword string `json:"keyword,omitempty"` + KodeReferensi string `json:"kode_referensi,omitempty"` + models.PaginationRequest +} + +// ReferensiData represents referensi information +type ReferensiData struct { + Kode string `json:"kode"` + Nama string `json:"nama"` + Kategori string `json:"kategori,omitempty"` + Status string `json:"status"` + TglBerlaku string `json:"tgl_berlaku,omitempty"` + TglBerakhir string `json:"tgl_berakhir,omitempty"` + Keterangan string `json:"keterangan,omitempty"` +} + +// ReferensiResponse represents referensi API response +type ReferensiResponse struct { + models.BaseResponse + Data []ReferensiData `json:"data,omitempty"` + Pagination *models.PaginationResponse `json:"pagination,omitempty"` +} diff --git a/internal/models/reference/eclaim.go b/internal/models/eclaim/eclaim.go similarity index 97% rename from internal/models/reference/eclaim.go rename to internal/models/eclaim/eclaim.go index 1263197..2ee6660 100644 --- a/internal/models/reference/eclaim.go +++ b/internal/models/eclaim/eclaim.go @@ -1,10 +1,12 @@ -package reference +package eclaim + +import "api-service/internal/models" // === KLAIM MODELS === // KlaimRequest represents klaim submission request type KlaimRequest struct { - BaseRequest + models.BaseRequest NoSep string `json:"nomor_sep" validate:"required"` NoKartu string `json:"nomor_kartu" validate:"required"` NoMR string `json:"nomor_mr" validate:"required"` @@ -87,7 +89,7 @@ type SpecialCMGInfo struct { // KlaimResponse represents klaim API response type KlaimResponse struct { - BaseResponse + models.BaseResponse Data *KlaimResponseData `json:"data,omitempty"` } @@ -106,7 +108,7 @@ type KlaimResponseData struct { // GrouperRequest represents grouper processing request type GrouperRequest struct { - BaseRequest + models.BaseRequest NoSep string `json:"nomor_sep" validate:"required"` NoKartu string `json:"nomor_kartu" validate:"required"` TglMasuk string `json:"tgl_masuk" validate:"required"` @@ -143,6 +145,6 @@ type TopUpInfo struct { // GrouperResponse represents grouper API response type GrouperResponse struct { - BaseResponse + models.BaseResponse Data *GrouperResult `json:"data,omitempty"` } diff --git a/internal/models/models.go b/internal/models/models.go index 297c3e6..f114b0e 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -63,6 +63,54 @@ type ErrorResponse struct { Timestamp time.Time `json:"timestamp"` } +// BaseRequest contains common fields for all BPJS requests +type BaseRequest struct { + RequestID string `json:"request_id,omitempty"` + Timestamp time.Time `json:"timestamp,omitempty"` +} + +// BaseResponse contains common response fields +type BaseResponse struct { + Status string `json:"status"` + Message string `json:"message,omitempty"` + RequestID string `json:"request_id,omitempty"` + Timestamp string `json:"timestamp,omitempty"` +} + +// ErrorResponse represents error response structure +type ErrorResponseBpjs struct { + Status string `json:"status"` + Message string `json:"message"` + RequestID string `json:"request_id,omitempty"` + Errors map[string]interface{} `json:"errors,omitempty"` + Code string `json:"code,omitempty"` +} + +// PaginationRequest contains pagination parameters +type PaginationRequest struct { + Page int `json:"page" validate:"min=1"` + Limit int `json:"limit" validate:"min=1,max=100"` + SortBy string `json:"sort_by,omitempty"` + SortDir string `json:"sort_dir,omitempty" validate:"omitempty,oneof=asc desc"` +} + +// PaginationResponse contains pagination metadata +type PaginationResponse struct { + CurrentPage int `json:"current_page"` + TotalPages int `json:"total_pages"` + TotalItems int64 `json:"total_items"` + ItemsPerPage int `json:"items_per_page"` + HasNext bool `json:"has_next"` + HasPrev bool `json:"has_previous"` +} + +// MetaInfo contains additional metadata +type MetaInfo struct { + Version string `json:"version"` + Environment string `json:"environment"` + ServerTime string `json:"server_time"` +} + // Validation constants const ( StatusDraft = "draft" diff --git a/internal/models/reference/aplicare.go b/internal/models/reference/aplicare.go deleted file mode 100644 index b1ea6cf..0000000 --- a/internal/models/reference/aplicare.go +++ /dev/null @@ -1,70 +0,0 @@ -package reference - -// === REFERENSI MODELS === - -// ReferensiRequest represents referensi lookup request -type ReferensiRequest struct { - BaseRequest - JenisReferensi string `json:"jenis_referensi" validate:"required,oneof=diagnosa procedure obat alkes faskes dokter poli"` - Keyword string `json:"keyword,omitempty"` - KodeReferensi string `json:"kode_referensi,omitempty"` - PaginationRequest -} - -// ReferensiData represents referensi information -type ReferensiData struct { - Kode string `json:"kode"` - Nama string `json:"nama"` - Kategori string `json:"kategori,omitempty"` - Status string `json:"status"` - TglBerlaku string `json:"tgl_berlaku,omitempty"` - TglBerakhir string `json:"tgl_berakhir,omitempty"` - Keterangan string `json:"keterangan,omitempty"` -} - -// ReferensiResponse represents referensi API response -type ReferensiResponse struct { - BaseResponse - Data []ReferensiData `json:"data,omitempty"` - Pagination *PaginationResponse `json:"pagination,omitempty"` -} - -// === MONITORING MODELS === - -// MonitoringRequest represents monitoring data request -type MonitoringRequest struct { - BaseRequest - TanggalAwal string `json:"tanggal_awal" validate:"required"` - TanggalAkhir string `json:"tanggal_akhir" validate:"required"` - JenisLaporan string `json:"jenis_laporan" validate:"required,oneof=kunjungan klaim rujukan sep"` - PPK string `json:"ppk,omitempty"` - StatusData string `json:"status_data,omitempty"` - PaginationRequest -} - -// MonitoringData represents monitoring information -type MonitoringData struct { - Tanggal string `json:"tanggal"` - PPK string `json:"ppk"` - NamaPPK string `json:"nama_ppk"` - JumlahKasus int `json:"jumlah_kasus"` - TotalTarif float64 `json:"total_tarif"` - StatusData string `json:"status_data"` - Keterangan string `json:"keterangan,omitempty"` -} - -// MonitoringResponse represents monitoring API response -type MonitoringResponse struct { - BaseResponse - Data []MonitoringData `json:"data,omitempty"` - Summary *MonitoringSummary `json:"summary,omitempty"` - Pagination *PaginationResponse `json:"pagination,omitempty"` -} - -// MonitoringSummary represents monitoring summary -type MonitoringSummary struct { - TotalKasus int `json:"total_kasus"` - TotalTarif float64 `json:"total_tarif"` - RataRataTarif float64 `json:"rata_rata_tarif"` - PeriodeLaporan string `json:"periode_laporan"` -} diff --git a/internal/models/reference/base.go b/internal/models/reference/base.go deleted file mode 100644 index 5af43e3..0000000 --- a/internal/models/reference/base.go +++ /dev/null @@ -1,53 +0,0 @@ -package reference - -import ( - "time" -) - -// BaseRequest contains common fields for all BPJS requests -type BaseRequest struct { - RequestID string `json:"request_id,omitempty"` - Timestamp time.Time `json:"timestamp,omitempty"` -} - -// BaseResponse contains common response fields -type BaseResponse struct { - Status string `json:"status"` - Message string `json:"message,omitempty"` - RequestID string `json:"request_id,omitempty"` - Timestamp string `json:"timestamp,omitempty"` -} - -// ErrorResponse represents error response structure -type ErrorResponse struct { - Status string `json:"status"` - Message string `json:"message"` - RequestID string `json:"request_id,omitempty"` - Errors map[string]interface{} `json:"errors,omitempty"` - Code string `json:"code,omitempty"` -} - -// PaginationRequest contains pagination parameters -type PaginationRequest struct { - Page int `json:"page" validate:"min=1"` - Limit int `json:"limit" validate:"min=1,max=100"` - SortBy string `json:"sort_by,omitempty"` - SortDir string `json:"sort_dir,omitempty" validate:"omitempty,oneof=asc desc"` -} - -// PaginationResponse contains pagination metadata -type PaginationResponse struct { - CurrentPage int `json:"current_page"` - TotalPages int `json:"total_pages"` - TotalItems int64 `json:"total_items"` - ItemsPerPage int `json:"items_per_page"` - HasNext bool `json:"has_next"` - HasPrev bool `json:"has_previous"` -} - -// MetaInfo contains additional metadata -type MetaInfo struct { - Version string `json:"version"` - Environment string `json:"environment"` - ServerTime string `json:"server_time"` -} diff --git a/internal/models/reference/services.go b/internal/models/reference/services.go deleted file mode 100644 index fff8eea..0000000 --- a/internal/models/reference/services.go +++ /dev/null @@ -1,27 +0,0 @@ -// internal/models/reference/services.go -package reference - -// VClaimService defines VClaim service interface -// type VClaimService interface { -// GetPESERTA(ctx context.Context, nokartu string) (*PesertaData, error) -// CreateSEP(ctx context.Context, req *SEPRequest) (*SEPData, error) -// GetSEP(ctx context.Context, nosep string) (*SEPData, error) -// UpdateSEP(ctx context.Context, nosep string, req *SEPRequest) (*SEPData, error) -// DeleteSEP(ctx context.Context, nosep string) error -// GetRujukan(ctx context.Context, norujukan string) (*RujukanData, error) -// } - -// // EClaimService defines EClaim service interface -// type EClaimService interface { -// CreateKlaim(ctx context.Context, req *KlaimRequest) (*KlaimResponseData, error) -// GetKlaim(ctx context.Context, noKlaim string) (*KlaimResponseData, error) -// UpdateKlaim(ctx context.Context, noKlaim string, req *KlaimRequest) (*KlaimResponseData, error) -// ProcessGrouper(ctx context.Context, req *GrouperRequest) (*GrouperResult, error) -// } - -// // AplicareService defines Aplicare service interface -// type AplicareService interface { -// GetReferensi(ctx context.Context, req *ReferensiRequest) ([]ReferensiData, *PaginationResponse, error) -// GetMonitoring(ctx context.Context, req *MonitoringRequest) ([]MonitoringData, *MonitoringSummary, *PaginationResponse, error) -// CreateMonitoring(ctx context.Context, req *MonitoringRequest) error -// } diff --git a/internal/models/reference/vclaim.go b/internal/models/reference/vclaim.go deleted file mode 100644 index 6f3663f..0000000 --- a/internal/models/reference/vclaim.go +++ /dev/null @@ -1,144 +0,0 @@ -// internal/models/reference/vclaim.go -package reference - -// === PESERTA MODELS === - -// PesertaRequest represents peserta lookup request -type PesertaRequest struct { - BaseRequest - NoKartu string `json:"nokartu" validate:"required,min=13,max=13"` - NIK string `json:"nik,omitempty" validate:"omitempty,min=16,max=16"` - TanggalSEP string `json:"tglsep" validate:"required" example:"2024-01-15"` - NoTelepon string `json:"notelp,omitempty" validate:"omitempty,max=15"` -} - -// PesertaData represents peserta information from BPJS -type PesertaData struct { - NoKartu string `json:"noKartu"` - NIK string `json:"nik"` - Nama string `json:"nama"` - Pisa string `json:"pisa"` - Sex string `json:"sex"` - TanggalLahir string `json:"tglLahir"` - TelephoneMsisdn string `json:"tglTAT"` - TelephoneAsat string `json:"tglTMT"` - KodeCabang string `json:"kdCabang"` - NamaCabang string `json:"nmCabang"` - KodeJenisPeserta string `json:"kdJnsPst"` - NamaJenisPeserta string `json:"nmJnsPst"` - KelasRawat string `json:"klsRawat"` - Status string `json:"statusPeserta"` - Aktif string `json:"aktif"` - KeteranganAktif string `json:"ketAktif"` - NoSKTM string `json:"noSKTM,omitempty"` - NoKTP string `json:"noKtp"` - Asuransi string `json:"asuransi,omitempty"` - CoB string `json:"cob,omitempty"` - TunggakanIuran string `json:"tglTunggak,omitempty"` - MR struct { - NoMR string `json:"noMR"` - NamaMR string `json:"nmMR"` - Sex string `json:"sex"` - TglLahir string `json:"tglLahir"` - TglMeninggal string `json:"tglMeninggal,omitempty"` - } `json:"mr,omitempty"` -} - -// PesertaResponse represents peserta API response -type PesertaResponse struct { - BaseResponse - Data *PesertaData `json:"data,omitempty"` - MetaData interface{} `json:"metaData,omitempty"` -} - -// === SEP (Surat Eligibilitas Peserta) MODELS === - -// SEPRequest represents SEP creation/update request -type SepRequest struct { - BaseRequest - NoKartu string `json:"noKartu" validate:"required"` - TglSep string `json:"tglSep" validate:"required"` - PPKPelayanan string `json:"ppkPelayanan" validate:"required"` - JnsPelayanan string `json:"jnsPelayanan" validate:"required,oneof=1 2"` - KlsRawat string `json:"klsRawat" validate:"required,oneof=1 2 3"` - NoMR string `json:"noMR" validate:"required"` - Rujukan *SepRujukan `json:"rujukan"` - Catatan string `json:"catatan,omitempty"` - Diagnosa string `json:"diagnosa" validate:"required"` - PoliTujuan string `json:"poli" validate:"required"` - ExternalUser string `json:"user" validate:"required"` - NoTelp string `json:"noTelp,omitempty"` -} - -// SEPRujukan represents rujukan information in SEP -type SepRujukan struct { - AsalRujukan string `json:"asalRujukan" validate:"required,oneof=1 2"` - TglRujukan string `json:"tglRujukan" validate:"required"` - NoRujukan string `json:"noRujukan" validate:"required"` - PPKRujukan string `json:"ppkRujukan" validate:"required"` -} - -// SEPData represents SEP response data -type SepData struct { - NoSep string `json:"noSep"` - TglSep string `json:"tglSep"` - JnsPelayanan string `json:"jnsPelayanan"` - PoliTujuan string `json:"poli"` - KlsRawat string `json:"klsRawat"` - NoMR string `json:"noMR"` - Rujukan SepRujukan `json:"rujukan"` - Catatan string `json:"catatan"` - Diagnosa string `json:"diagnosa"` - Peserta PesertaData `json:"peserta"` - Informasi struct { - NoSKDP string `json:"noSKDP,omitempty"` - DPJPLayan string `json:"dpjpLayan"` - NoTelepon string `json:"noTelp"` - SubSpesialis string `json:"subSpesialis,omitempty"` - } `json:"informasi"` -} - -// SEPResponse represents SEP API response -type SepResponse struct { - BaseResponse - Data *SepData `json:"data,omitempty"` -} - -// === RUJUKAN MODELS === - -// RujukanRequest represents rujukan lookup request -type RujukanRequest struct { - BaseRequest - NoRujukan string `json:"noRujukan" validate:"required"` - NoKartu string `json:"noKartu,omitempty"` -} - -// RujukanData represents rujukan information -type RujukanData struct { - NoRujukan string `json:"noRujukan"` - TglRujukan string `json:"tglRujukan"` - NoKartu string `json:"noKartu"` - Nama string `json:"nama"` - KelasRawat string `json:"kelasRawat"` - Diagnosa struct { - KodeDiagnosa string `json:"kdDiagnosa"` - NamaDiagnosa string `json:"nmDiagnosa"` - } `json:"diagnosa"` - PoliRujukan struct { - KodePoli string `json:"kdPoli"` - NamaPoli string `json:"nmPoli"` - } `json:"poliRujukan"` - ProvPerujuk struct { - KodeProvider string `json:"kdProvider"` - NamaProvider string `json:"nmProvider"` - } `json:"provPerujuk"` - PelayananInfo string `json:"pelayanan"` - StatusRujukan string `json:"statusRujukan"` -} - -// RujukanResponse represents rujukan API response -type RujukanResponse struct { - BaseResponse - Data *RujukanData `json:"data,omitempty"` - List []RujukanData `json:"list,omitempty"` -} diff --git a/internal/models/reference/validation.go b/internal/models/validation.go similarity index 99% rename from internal/models/reference/validation.go rename to internal/models/validation.go index aeb3400..1462d35 100644 --- a/internal/models/reference/validation.go +++ b/internal/models/validation.go @@ -1,4 +1,4 @@ -package reference +package models import ( "regexp" diff --git a/internal/models/vclaim/peserta/peserta.go b/internal/models/vclaim/peserta/peserta.go new file mode 100644 index 0000000..dc5bddb --- /dev/null +++ b/internal/models/vclaim/peserta/peserta.go @@ -0,0 +1,53 @@ +package peserta + +import "api-service/internal/models" + +// === PESERTA MODELS === + +// PesertaRequest represents peserta lookup request +type PesertaRequest struct { + models.BaseRequest + NoKartu string `json:"nokartu" validate:"required,min=13,max=13"` + NIK string `json:"nik,omitempty" validate:"omitempty,min=16,max=16"` + TanggalSEP string `json:"tglsep" validate:"required" example:"2024-01-15"` + NoTelepon string `json:"notelp,omitempty" validate:"omitempty,max=15"` +} + +// PesertaData represents peserta information from BPJS +type PesertaData struct { + NoKartu string `json:"noKartu"` + NIK string `json:"nik"` + Nama string `json:"nama"` + Pisa string `json:"pisa"` + Sex string `json:"sex"` + TanggalLahir string `json:"tglLahir"` + TelephoneMsisdn string `json:"tglTAT"` + TelephoneAsat string `json:"tglTMT"` + KodeCabang string `json:"kdCabang"` + NamaCabang string `json:"nmCabang"` + KodeJenisPeserta string `json:"kdJnsPst"` + NamaJenisPeserta string `json:"nmJnsPst"` + KelasRawat string `json:"klsRawat"` + Status string `json:"statusPeserta"` + Aktif string `json:"aktif"` + KeteranganAktif string `json:"ketAktif"` + NoSKTM string `json:"noSKTM,omitempty"` + NoKTP string `json:"noKtp"` + Asuransi string `json:"asuransi,omitempty"` + CoB string `json:"cob,omitempty"` + TunggakanIuran string `json:"tglTunggak,omitempty"` + MR struct { + NoMR string `json:"noMR"` + NamaMR string `json:"nmMR"` + Sex string `json:"sex"` + TglLahir string `json:"tglLahir"` + TglMeninggal string `json:"tglMeninggal,omitempty"` + } `json:"mr,omitempty"` +} + +// PesertaResponse represents peserta API response +type PesertaResponse struct { + models.BaseResponse + Data *PesertaData `json:"data,omitempty"` + MetaData interface{} `json:"metaData,omitempty"` +} diff --git a/internal/models/vclaim/rujukan/rujukan.go b/internal/models/vclaim/rujukan/rujukan.go new file mode 100644 index 0000000..78e83af --- /dev/null +++ b/internal/models/vclaim/rujukan/rujukan.go @@ -0,0 +1,42 @@ +package vclaim + +import "api-service/internal/models" + +// === RUJUKAN MODELS === + +// RujukanRequest represents rujukan lookup request +type RujukanRequest struct { + models.BaseRequest + NoRujukan string `json:"noRujukan" validate:"required"` + NoKartu string `json:"noKartu,omitempty"` +} + +// RujukanData represents rujukan information +type RujukanData struct { + NoRujukan string `json:"noRujukan"` + TglRujukan string `json:"tglRujukan"` + NoKartu string `json:"noKartu"` + Nama string `json:"nama"` + KelasRawat string `json:"kelasRawat"` + Diagnosa struct { + KodeDiagnosa string `json:"kdDiagnosa"` + NamaDiagnosa string `json:"nmDiagnosa"` + } `json:"diagnosa"` + PoliRujukan struct { + KodePoli string `json:"kdPoli"` + NamaPoli string `json:"nmPoli"` + } `json:"poliRujukan"` + ProvPerujuk struct { + KodeProvider string `json:"kdProvider"` + NamaProvider string `json:"nmProvider"` + } `json:"provPerujuk"` + PelayananInfo string `json:"pelayanan"` + StatusRujukan string `json:"statusRujukan"` +} + +// RujukanResponse represents rujukan API response +type RujukanResponse struct { + models.BaseResponse + Data *RujukanData `json:"data,omitempty"` + List []RujukanData `json:"list,omitempty"` +} diff --git a/internal/models/vclaim/sep/sep.go b/internal/models/vclaim/sep/sep.go new file mode 100644 index 0000000..31a6b10 --- /dev/null +++ b/internal/models/vclaim/sep/sep.go @@ -0,0 +1,59 @@ +package sep + +import ( + "api-service/internal/models" + "api-service/internal/models/vclaim/peserta" +) + +// === SEP (Surat Eligibilitas Peserta) MODELS === + +// SEPRequest represents SEP creation/update request +type SepRequest struct { + models.BaseRequest + NoKartu string `json:"noKartu" validate:"required"` + TglSep string `json:"tglSep" validate:"required"` + PPKPelayanan string `json:"ppkPelayanan" validate:"required"` + JnsPelayanan string `json:"jnsPelayanan" validate:"required,oneof=1 2"` + KlsRawat string `json:"klsRawat" validate:"required,oneof=1 2 3"` + NoMR string `json:"noMR" validate:"required"` + Rujukan *SepRujukan `json:"rujukan"` + Catatan string `json:"catatan,omitempty"` + Diagnosa string `json:"diagnosa" validate:"required"` + PoliTujuan string `json:"poli" validate:"required"` + ExternalUser string `json:"user" validate:"required"` + NoTelp string `json:"noTelp,omitempty"` +} + +// SEPRujukan represents rujukan information in SEP +type SepRujukan struct { + AsalRujukan string `json:"asalRujukan" validate:"required,oneof=1 2"` + TglRujukan string `json:"tglRujukan" validate:"required"` + NoRujukan string `json:"noRujukan" validate:"required"` + PPKRujukan string `json:"ppkRujukan" validate:"required"` +} + +// SEPData represents SEP response data +type SepData struct { + NoSep string `json:"noSep"` + TglSep string `json:"tglSep"` + JnsPelayanan string `json:"jnsPelayanan"` + PoliTujuan string `json:"poli"` + KlsRawat string `json:"klsRawat"` + NoMR string `json:"noMR"` + Rujukan SepRujukan `json:"rujukan"` + Catatan string `json:"catatan"` + Diagnosa string `json:"diagnosa"` + Peserta peserta.PesertaData `json:"peserta"` + Informasi struct { + NoSKDP string `json:"noSKDP,omitempty"` + DPJPLayan string `json:"dpjpLayan"` + NoTelepon string `json:"noTelp"` + SubSpesialis string `json:"subSpesialis,omitempty"` + } `json:"informasi"` +} + +// SEPResponse represents SEP API response +type SepResponse struct { + models.BaseResponse + Data *SepData `json:"data,omitempty"` +} diff --git a/internal/routes/v1/routes.go b/internal/routes/v1/routes.go index 6f30f8e..0761faa 100644 --- a/internal/routes/v1/routes.go +++ b/internal/routes/v1/routes.go @@ -100,12 +100,5 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine { protectedRetribusi.DELETE("/:id", retribusiHandler.DeleteRetribusi) // DELETE /api/v1/retribusi/:id } - // BPJS endpoints (sensitive data - should be protected) - // bpjsPesertaHandler := bpjsPesertaHandlers.NewPesertaHandler(cfg.Bpjs) - // protectedBpjs := protected.Group("/bpjs") - // { - // protectedBpjs.GET("/peserta/nik/:nik/tglSEP/:tglSEP", bpjsPesertaHandler.GetPesertaByNIK) - // } - return router } diff --git a/internal/services/bpjs/vclaimBridge.go b/internal/services/bpjs/vclaimBridge.go index 2face81..76de691 100644 --- a/internal/services/bpjs/vclaimBridge.go +++ b/internal/services/bpjs/vclaimBridge.go @@ -21,9 +21,11 @@ type VClaimService interface { Get(ctx context.Context, endpoint string, result interface{}) error Post(ctx context.Context, endpoint string, payload interface{}, result interface{}) error Put(ctx context.Context, endpoint string, payload interface{}, result interface{}) error + Patch(ctx context.Context, endpoint string, payload interface{}, result interface{}) error Delete(ctx context.Context, endpoint string, result interface{}) error GetRawResponse(ctx context.Context, endpoint string) (*ResponDTOVclaim, error) PostRawResponse(ctx context.Context, endpoint string, payload interface{}) (*ResponDTOVclaim, error) + PatchRawResponse(ctx context.Context, endpoint string, payload interface{}) (*ResponDTOVclaim, error) } // Service struct for VClaim service @@ -311,6 +313,33 @@ func (s *Service) Delete(ctx context.Context, endpoint string, result interface{ return mapToResult(resp, result) } +// Patch performs HTTP PATCH request +func (s *Service) Patch(ctx context.Context, endpoint string, payload interface{}, result interface{}) error { + var buf bytes.Buffer + if payload != nil { + if err := json.NewEncoder(&buf).Encode(payload); err != nil { + return fmt.Errorf("failed to encode payload: %w", err) + } + } + + req, err := s.prepareRequest(ctx, http.MethodPatch, endpoint, &buf) + if err != nil { + return err + } + + res, err := s.httpClient.Do(req) + if err != nil { + return fmt.Errorf("failed to execute PATCH request: %w", err) + } + + resp, err := s.processResponse(res) + if err != nil { + return err + } + + return mapToResult(resp, result) +} + // GetRawResponse returns raw response without mapping func (s *Service) GetRawResponse(ctx context.Context, endpoint string) (*ResponDTOVclaim, error) { req, err := s.prepareRequest(ctx, http.MethodGet, endpoint, nil) @@ -348,6 +377,28 @@ func (s *Service) PostRawResponse(ctx context.Context, endpoint string, payload return s.processResponse(res) } +// PatchRawResponse returns raw response without mapping +func (s *Service) PatchRawResponse(ctx context.Context, endpoint string, payload interface{}) (*ResponDTOVclaim, error) { + var buf bytes.Buffer + if payload != nil { + if err := json.NewEncoder(&buf).Encode(payload); err != nil { + return nil, fmt.Errorf("failed to encode payload: %w", err) + } + } + + req, err := s.prepareRequest(ctx, http.MethodPatch, endpoint, &buf) + if err != nil { + return nil, err + } + + res, err := s.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to execute PATCH request: %w", err) + } + + return s.processResponse(res) +} + // mapToResult maps the final response to the result interface func mapToResult(resp *ResponDTOVclaim, result interface{}) error { respBytes, err := json.Marshal(resp) diff --git a/services-config-bpjs.yaml b/services-config-bpjs.yaml index e6809f6..7787768 100644 --- a/services-config-bpjs.yaml +++ b/services-config-bpjs.yaml @@ -1,4 +1,4 @@ -# services-config-simple.yaml +# BPJS Services Configuration global: module_name: "api-service" output_dir: "internal/handlers" @@ -16,46 +16,37 @@ services: retry_count: 3 endpoints: peserta: - methods: ["GET"] - get_path: "/peserta/:nokartu" - model: "PesertaRequest" - response_model: "PesertaResponse" - description: "Get participant eligibility information" - summary: "Get Participant Info" - tags: ["vclaim", "peserta"] - cache_enabled: true - cache_ttl: 300 + bynokartu: + methods: ["GET"] + get_path: "/peserta/:nokartu" + model: "PesertaRequest" + response_model: "PesertaResponse" + description: "Get participant eligibility information by card number" + summary: "Get Participant Info by No Kartu" + tags: ["vclaim", "peserta", "nokartu"] + cache_enabled: true + cache_ttl: 300 + bynik: + methods: ["GET"] + get_path: "/peserta/nik/:nik" + model: "PesertaRequest" + response_model: "PesertaResponse" + description: "Get participant eligibility information by NIK" + summary: "Get Participant Info by NIK" + tags: ["vclaim", "peserta", "nik"] + cache_enabled: true + cache_ttl: 300 sep: - methods: ["GET", "POST", "PUT", "DELETE"] - get_path: "/sep/:nosep" - post_path: "/sep" - put_path: "/sep/:nosep" - delete_path: "/sep/:nosep" - model: "SepRequest" - response_model: "SepResponse" - description: "Manage SEP (Surat Eligibilitas Peserta)" - summary: "SEP Management" - tags: ["vclaim", "sep"] - cache_enabled: true - cache_ttl: 180 - - eclaim: - name: "EClaim" - category: "eclaim" - package: "eclaim" - description: "BPJS EClaim service for claim processing" - base_url: "https://apijkn.bpjs-kesehatan.go.id/new-eclaim-rest" - timeout: 60 - retry_count: 2 - endpoints: - klaim: - methods: ["GET", "POST", "PUT"] - get_path: "/klaim/:noklaim" - post_path: "/klaim" - put_path: "/klaim/:noklaim" - model: "KlaimRequest" - response_model: "KlaimResponse" - description: "Manage insurance claims" - summary: "Claim Management" - tags: ["eclaim", "klaim"] - cache_enabled: false + sep: + methods: ["GET", "POST", "PUT", "DELETE"] + get_path: "/sep/:nosep" + post_path: "/sep" + put_path: "/sep/:nosep" + delete_path: "/sep/:nosep" + model: "SepRequest" + response_model: "SepResponse" + description: "Manage SEP (Surat Eligibilitas Peserta)" + summary: "SEP Management" + tags: ["vclaim", "sep"] + cache_enabled: true + cache_ttl: 180 diff --git a/services-config-bpjs.yamlcopy.backup b/services-config-bpjs.yamlcopy.backup new file mode 100644 index 0000000..e6809f6 --- /dev/null +++ b/services-config-bpjs.yamlcopy.backup @@ -0,0 +1,61 @@ +# services-config-simple.yaml +global: + module_name: "api-service" + output_dir: "internal/handlers" + enable_swagger: true + enable_logging: true + +services: + vclaim: + name: "VClaim" + category: "vclaim" + package: "vclaim" + description: "BPJS VClaim service for eligibility and SEP management" + base_url: "https://apijkn.bpjs-kesehatan.go.id/vclaim-rest" + timeout: 30 + retry_count: 3 + endpoints: + peserta: + methods: ["GET"] + get_path: "/peserta/:nokartu" + model: "PesertaRequest" + response_model: "PesertaResponse" + description: "Get participant eligibility information" + summary: "Get Participant Info" + tags: ["vclaim", "peserta"] + cache_enabled: true + cache_ttl: 300 + sep: + methods: ["GET", "POST", "PUT", "DELETE"] + get_path: "/sep/:nosep" + post_path: "/sep" + put_path: "/sep/:nosep" + delete_path: "/sep/:nosep" + model: "SepRequest" + response_model: "SepResponse" + description: "Manage SEP (Surat Eligibilitas Peserta)" + summary: "SEP Management" + tags: ["vclaim", "sep"] + cache_enabled: true + cache_ttl: 180 + + eclaim: + name: "EClaim" + category: "eclaim" + package: "eclaim" + description: "BPJS EClaim service for claim processing" + base_url: "https://apijkn.bpjs-kesehatan.go.id/new-eclaim-rest" + timeout: 60 + retry_count: 2 + endpoints: + klaim: + methods: ["GET", "POST", "PUT"] + get_path: "/klaim/:noklaim" + post_path: "/klaim" + put_path: "/klaim/:noklaim" + model: "KlaimRequest" + response_model: "KlaimResponse" + description: "Manage insurance claims" + summary: "Claim Management" + tags: ["eclaim", "klaim"] + cache_enabled: false diff --git a/tools/bpjs/generate-handler.go b/tools/bpjs/generate-handler.go index 93b5b25..7367eda 100644 --- a/tools/bpjs/generate-handler.go +++ b/tools/bpjs/generate-handler.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" "strings" "text/template" @@ -12,6 +13,14 @@ import ( "gopkg.in/yaml.v2" ) +// runSwagInit runs the swag init command to generate swagger docs +func runSwagInit() error { + cmd := exec.Command("swag", "init", "-g", "../../cmd/api/main.go", "--parseDependency", "--parseInternal") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + // ServiceConfig represents the main configuration structure type ServiceConfig struct { Services map[string]Service `yaml:"services"` @@ -30,16 +39,16 @@ type GlobalConfig struct { // Service represents individual service configuration type Service struct { - Name string `yaml:"name"` - Category string `yaml:"category"` - Package string `yaml:"package"` - Description string `yaml:"description"` - BaseURL string `yaml:"base_url"` - Timeout int `yaml:"timeout"` - RetryCount int `yaml:"retry_count"` - Endpoints map[string]Endpoint `yaml:"endpoints"` - Middleware []string `yaml:"middleware,omitempty"` // FIXED: Changed to []string - Dependencies []string `yaml:"dependencies,omitempty"` // FIXED: Changed to []string + Name string `yaml:"name"` + Category string `yaml:"category"` + Package string `yaml:"package"` + Description string `yaml:"description"` + BaseURL string `yaml:"base_url"` + Timeout int `yaml:"timeout"` + RetryCount int `yaml:"retry_count"` + Endpoints map[string]SubEndpoints `yaml:"endpoints"` + Middleware []string `yaml:"middleware,omitempty"` // FIXED: Changed to []string + Dependencies []string `yaml:"dependencies,omitempty"` // FIXED: Changed to []string } // Endpoint represents endpoint configuration @@ -59,9 +68,12 @@ type Endpoint struct { RateLimit int `yaml:"rate_limit,omitempty"` CacheEnabled bool `yaml:"cache_enabled"` CacheTTL int `yaml:"cache_ttl,omitempty"` - CustomHeaders map[string]string `yaml:"custom_headers,omitempty"` // ADDED: Missing field + CustomHeaders map[string]string `yaml:"custom_headers,omitempty"` } +// SubEndpoints represents nested endpoint configuration for tree structure +type SubEndpoints map[string]Endpoint + // TemplateData holds data for generating handlers type TemplateData struct { ServiceName string @@ -118,12 +130,11 @@ type EndpointData struct { RequiredFields []string OptionalFields []string CustomHeaders map[string]string + ModelPackage string // Package name for the model (peserta, sep, etc.) } -// Template remains the same as before... +// Updated template for merged handler structure const handlerTemplate = ` -// Code generated by generate-dynamic-handler.go; DO NOT EDIT. -// Generated at: {{.Timestamp}} // Service: {{.ServiceName}} ({{.Category}}) // Description: {{.Description}} @@ -131,11 +142,15 @@ package handlers import ( "context" + "strings" "net/http" "time" "{{.ModuleName}}/internal/config" - "{{.ModuleName}}/internal/models/reference" + "{{.ModuleName}}/internal/models" + "{{.ModuleName}}/internal/models/vclaim/peserta" + "{{.ModuleName}}/internal/models/vclaim/sep" + "{{.ModuleName}}/internal/services/bpjs" "{{.ModuleName}}/pkg/logger" "github.com/gin-gonic/gin" @@ -143,27 +158,9 @@ import ( "github.com/google/uuid" ) -// {{.ServiceName}}Service defines {{.ServiceName}} service interface -type {{.ServiceName}}Service interface { - {{range .Endpoints}} - {{if .HasGet}} - Get{{.Name}}(ctx context.Context{{range .PathParams}}, {{.}} string{{end}}) (*reference.{{.Name}}Data, error) - {{end}} - {{if .HasPost}} - Create{{.Name}}(ctx context.Context, req *reference.{{.Model}}) (*reference.{{.Name}}Data, error) - {{end}} - {{if .HasPut}} - Update{{.Name}}(ctx context.Context{{range .PathParams}}, {{.}} string{{end}}, req *reference.{{.Model}}) (*reference.{{.Name}}Data, error) - {{end}} - {{if .HasDelete}} - Delete{{.Name}}(ctx context.Context{{range .PathParams}}, {{.}} string{{end}}) error - {{end}} - {{end}} -} - // {{.ServiceName}}Handler handles {{.ServiceName}} BPJS services type {{.ServiceName}}Handler struct { - service {{.ServiceName}}Service + service services.{{.ServiceName}}Service validator *validator.Validate logger logger.Logger config config.BpjsConfig @@ -177,35 +174,35 @@ type {{.ServiceName}}HandlerConfig struct { } // New{{.ServiceName}}Handler creates a new {{.ServiceName}}Handler -func New{{.ServiceName}}Handler(cfg {{.ServiceName}}HandlerConfig, service {{.ServiceName}}Service) *{{.ServiceName}}Handler { +func New{{.ServiceName}}Handler(cfg {{.ServiceName}}HandlerConfig) *{{.ServiceName}}Handler { return &{{.ServiceName}}Handler{ - service: service, + service: services.NewService(cfg.BpjsConfig), validator: cfg.Validator, logger: cfg.Logger, config: cfg.BpjsConfig, } } - {{range .Endpoints}} {{if .HasGet}} -// Get{{.NameUpper}} retrieves {{.Name}} data -{{if $.HasSwagger}} -// @Summary Get {{.Name}} data -// @Description {{.Description}} -// @Tags {{join .Tags ","}} -// @Accept json -// @Produce json +// Get{{.NameUpper}} godoc +// @Summary Get {{.Name}} data +// @Description {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json {{if .RequireAuth}} -// @Security ApiKeyAuth +// @Security ApiKeyAuth {{end}} +// @Param X-Request-ID header string false "Request ID for tracking" {{range .PathParams}} -// @Param {{.}} path string true "{{.}}" -{{end}} -// @Success 200 {object} reference.{{.ResponseModel}} -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router {{.GetPath}} [get] +// @Param {{.}} path string true "{{.}}" example("example_value") {{end}} +// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully retrieved {{.Name}} data" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - {{.Name}} not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router {{.GetPath}} [get] func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) defer cancel() @@ -236,7 +233,7 @@ func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter {{.}}", RequestID: requestID, @@ -246,11 +243,15 @@ func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) { {{end}} // Call service method - var response reference.{{.ResponseModel}} + var response {{.ModelPackage}}.{{.ResponseModel}} {{if .PathParams}} - result, err := h.service.Get{{.Name}}(ctx{{range .PathParams}}, {{.}}{{end}}) + endpoint := "{{.GetPath}}" + {{range .PathParams}} + endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1) + {{end}} + err := h.service.Get(ctx, endpoint, &response) {{else}} - result, err := h.service.Get{{.Name}}(ctx) + err := h.service.Get(ctx, "{{.GetPath}}", &response) {{end}} if err != nil { {{if $.HasLogger}} @@ -259,7 +260,7 @@ func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -270,28 +271,28 @@ func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - response.Data = result c.JSON(http.StatusOK, response) } {{end}} {{if .HasPost}} -// Create{{.NameUpper}} creates new {{.Name}} -{{if $.HasSwagger}} -// @Summary Create {{.Name}} -// @Description {{.Description}} -// @Tags {{join .Tags ","}} -// @Accept json -// @Produce json +// Create{{.NameUpper}} godoc +// @Summary Create new {{.Name}} +// @Description {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json {{if .RequireAuth}} -// @Security ApiKeyAuth -{{end}} -// @Param request body reference.{{.Model}} true "{{.Name}} data" -// @Success 201 {object} reference.{{.ResponseModel}} -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router {{.PostPath}} [post] +// @Security ApiKeyAuth {{end}} +// @Param X-Request-ID header string false "Request ID for tracking" +// @Param request body {{.ModelPackage}}.{{.Model}} true "{{.Name}} data" +// @Success 201 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully created {{.Name}}" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid request body or validation error" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 409 {object} models.ErrorResponseBpjs "Conflict - {{.Name}} already exists" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router {{.PostPath}} [post] func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) defer cancel() @@ -308,7 +309,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { }) {{end}} - var req reference.{{.Model}} + var req {{.ModelPackage}}.{{.Model}} if err := c.ShouldBindJSON(&req); err != nil { {{if $.HasLogger}} h.logger.Error("Invalid request body", map[string]interface{}{ @@ -316,7 +317,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, @@ -332,7 +333,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, @@ -341,8 +342,8 @@ func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { } // Call service method - var response reference.{{.ResponseModel}} - result, err := h.service.Create{{.Name}}(ctx, &req) + var response {{.ModelPackage}}.{{.ResponseModel}} + err := h.service.Post(ctx, "{{.PostPath}}", &req, &response) if err != nil { {{if $.HasLogger}} h.logger.Error("Failed to create {{.Name}}", map[string]interface{}{ @@ -350,7 +351,7 @@ func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -361,31 +362,31 @@ func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - response.Data = result c.JSON(http.StatusCreated, response) } {{end}} {{if .HasPut}} -// Update{{.NameUpper}} updates existing {{.Name}} -{{if $.HasSwagger}} -// @Summary Update {{.Name}} -// @Description {{.Description}} -// @Tags {{join .Tags ","}} -// @Accept json -// @Produce json +// Update{{.NameUpper}} godoc +// @Summary Update existing {{.Name}} +// @Description {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json {{if .RequireAuth}} -// @Security ApiKeyAuth +// @Security ApiKeyAuth {{end}} +// @Param X-Request-ID header string false "Request ID for tracking" {{range .PathParams}} -// @Param {{.}} path string true "{{.}}" -{{end}} -// @Param request body reference.{{.Model}} true "{{.Name}} data" -// @Success 200 {object} reference.{{.ResponseModel}} -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router {{.PutPath}} [put] +// @Param {{.}} path string true "{{.}}" example("example_value") {{end}} +// @Param request body {{.ModelPackage}}.{{.Model}} true "{{.Name}} data" +// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully updated {{.Name}}" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters or request body" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - {{.Name}} not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router {{.PutPath}} [put] func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) defer cancel() @@ -410,7 +411,7 @@ func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter {{.}}", RequestID: requestID, @@ -419,7 +420,7 @@ func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { } {{end}} - var req reference.{{.Model}} + var req {{.ModelPackage}}.{{.Model}} if err := c.ShouldBindJSON(&req); err != nil { {{if $.HasLogger}} h.logger.Error("Invalid request body", map[string]interface{}{ @@ -427,7 +428,7 @@ func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Invalid request body: " + err.Error(), RequestID: requestID, @@ -443,7 +444,7 @@ func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Validation failed: " + err.Error(), RequestID: requestID, @@ -452,11 +453,15 @@ func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { } // Call service method - var response reference.{{.ResponseModel}} + var response {{.ModelPackage}}.{{.ResponseModel}} {{if .PathParams}} - result, err := h.service.Update{{.Name}}(ctx{{range .PathParams}}, {{.}}{{end}}, &req) + endpoint := "{{.PutPath}}" + {{range .PathParams}} + endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1) + {{end}} + err := h.service.Put(ctx, endpoint, &req, &response) {{else}} - result, err := h.service.Update{{.Name}}(ctx, &req) + err := h.service.Put(ctx, "{{.PutPath}}", &req, &response) {{end}} if err != nil { {{if $.HasLogger}} @@ -465,7 +470,7 @@ func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -476,30 +481,30 @@ func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) { // Ensure response has proper fields response.Status = "success" response.RequestID = requestID - response.Data = result c.JSON(http.StatusOK, response) } {{end}} {{if .HasDelete}} -// Delete{{.NameUpper}} deletes existing {{.Name}} -{{if $.HasSwagger}} -// @Summary Delete {{.Name}} -// @Description {{.Description}} -// @Tags {{join .Tags ","}} -// @Accept json -// @Produce json +// Delete{{.NameUpper}} godoc +// @Summary Delete existing {{.Name}} +// @Description {{.Description}} +// @Tags {{join .Tags ","}} +// @Accept json +// @Produce json {{if .RequireAuth}} -// @Security ApiKeyAuth +// @Security ApiKeyAuth {{end}} +// @Param X-Request-ID header string false "Request ID for tracking" {{range .PathParams}} -// @Param {{.}} path string true "{{.}}" -{{end}} -// @Success 204 {object} nil -// @Failure 400 {object} reference.ErrorResponse -// @Failure 500 {object} reference.ErrorResponse -// @Router {{.DeletePath}} [delete] +// @Param {{.}} path string true "{{.}}" example("example_value") {{end}} +// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully deleted {{.Name}}" +// @Failure 400 {object} models.ErrorResponseBpjs "Bad request - invalid parameters" +// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized - invalid API credentials" +// @Failure 404 {object} models.ErrorResponseBpjs "Not found - {{.Name}} not found" +// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error" +// @Router {{.DeletePath}} [delete] func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second) defer cancel() @@ -524,7 +529,7 @@ func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusBadRequest, reference.ErrorResponse{ + c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{ Status: "error", Message: "Missing required parameter {{.}}", RequestID: requestID, @@ -534,10 +539,15 @@ func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) { {{end}} // Call service method + var response {{.ModelPackage}}.{{.ResponseModel}} {{if .PathParams}} - err := h.service.Delete{{.Name}}(ctx{{range .PathParams}}, {{.}}{{end}}) + endpoint := "{{.DeletePath}}" + {{range .PathParams}} + endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1) + {{end}} + err := h.service.Delete(ctx, endpoint, &response) {{else}} - err := h.service.Delete{{.Name}}(ctx) + err := h.service.Delete(ctx, "{{.DeletePath}}", &response) {{end}} if err != nil { {{if $.HasLogger}} @@ -546,7 +556,7 @@ func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) { "request_id": requestID, }) {{end}} - c.JSON(http.StatusInternalServerError, reference.ErrorResponse{ + c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{ Status: "error", Message: "Internal server error", RequestID: requestID, @@ -554,7 +564,10 @@ func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) { return } - c.Status(http.StatusNoContent) + // Ensure response has proper fields + response.Status = "success" + response.RequestID = requestID + c.JSON(http.StatusOK, response) } {{end}} {{end}} @@ -614,6 +627,16 @@ func main() { if generated > 0 { fmt.Println("🎉 Generation completed successfully!") + + // Generate Swagger documentation if enabled + // if config.Global.EnableSwagger { + // fmt.Println("📚 Generating Swagger documentation...") + // if err := runSwagInit(); err != nil { + // fmt.Printf("⚠️ Warning: Failed to generate Swagger docs: %v\n", err) + // } else { + // fmt.Println("✅ Swagger documentation generated successfully!") + // } + // } } } @@ -675,19 +698,28 @@ func generateHandler(serviceName string, service Service, globalConfig GlobalCon } // Check for advanced features - for _, endpoint := range service.Endpoints { - if endpoint.RequireAuth { - templateData.HasAuth = true - } - if endpoint.CacheEnabled { - templateData.HasCache = true + for _, subEndpoints := range service.Endpoints { + for _, endpoint := range subEndpoints { + if endpoint.RequireAuth { + templateData.HasAuth = true + } + if endpoint.CacheEnabled { + templateData.HasCache = true + } } } // Process endpoints - for endpointName, endpoint := range service.Endpoints { - endpointData := processEndpoint(endpointName, endpoint) - templateData.Endpoints = append(templateData.Endpoints, endpointData) + for endpointName, subEndpoints := range service.Endpoints { + for subEndpointName, endpoint := range subEndpoints { + // Compose full endpoint name with sub-endpoint name + fullEndpointName := endpointName + if subEndpointName != "" { + fullEndpointName = fullEndpointName + strings.Title(subEndpointName) + } + endpointData := processEndpoint(fullEndpointName, endpoint, endpointName) + templateData.Endpoints = append(templateData.Endpoints, endpointData) + } } // Create output directory @@ -729,7 +761,7 @@ func generateHandler(serviceName string, service Service, globalConfig GlobalCon return nil } -func processEndpoint(name string, endpoint Endpoint) EndpointData { +func processEndpoint(name string, endpoint Endpoint, endpointGroup string) EndpointData { data := EndpointData{ Name: strings.Title(name), NameLower: strings.ToLower(name), @@ -751,6 +783,7 @@ func processEndpoint(name string, endpoint Endpoint) EndpointData { CacheEnabled: endpoint.CacheEnabled, CacheTTL: getOrDefault(endpoint.CacheTTL, 300), CustomHeaders: endpoint.CustomHeaders, + ModelPackage: endpointGroup, // Set the model package based on endpoint group } // Set method flags and extract path parameters diff --git a/tools/bpjs/generete b/tools/bpjs/generete new file mode 100644 index 0000000..e69de29