diff --git a/libs/clients/tax-api/.eslintrc.json b/libs/clients/tax-api/.eslintrc.json new file mode 100644 index 00000000..3230caf3 --- /dev/null +++ b/libs/clients/tax-api/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": "error" + } + } + ] +} diff --git a/libs/clients/tax-api/README.md b/libs/clients/tax-api/README.md new file mode 100644 index 00000000..d8d6344b --- /dev/null +++ b/libs/clients/tax-api/README.md @@ -0,0 +1,11 @@ +# clients-tax-api + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build clients-tax-api` to build the library. + +## Running unit tests + +Run `nx test clients-tax-api` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/clients/tax-api/jest.config.ts b/libs/clients/tax-api/jest.config.ts new file mode 100644 index 00000000..f5922dd6 --- /dev/null +++ b/libs/clients/tax-api/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'clients-tax-api', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/clients/tax-api', +} diff --git a/libs/clients/tax-api/package.json b/libs/clients/tax-api/package.json new file mode 100644 index 00000000..97b02051 --- /dev/null +++ b/libs/clients/tax-api/package.json @@ -0,0 +1,11 @@ +{ + "name": "clients/tax-api", + "version": "0.0.1", + "dependencies": { + "tslib": "^2.3.0" + }, + "type": "commonjs", + "main": "./src/index.js", + "typings": "./src/index.d.ts", + "private": true +} diff --git a/libs/clients/tax-api/project.json b/libs/clients/tax-api/project.json new file mode 100644 index 00000000..43f2b9ec --- /dev/null +++ b/libs/clients/tax-api/project.json @@ -0,0 +1,29 @@ +{ + "name": "clients-tax-api", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/clients/tax-api/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/clients/tax-api", + "main": "libs/clients/tax-api/src/index.ts", + "tsConfig": "libs/clients/tax-api/tsconfig.lib.json", + "assets": ["libs/clients/tax-api/*.md"] + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/clients/tax-api/jest.config.ts" + } + } + } +} diff --git a/libs/clients/tax-api/src/index.ts b/libs/clients/tax-api/src/index.ts new file mode 100644 index 00000000..aef79d2a --- /dev/null +++ b/libs/clients/tax-api/src/index.ts @@ -0,0 +1 @@ +export * from './lib/clients-tax-api' diff --git a/libs/clients/tax-api/src/lib/clients/tax/clientConfig.json b/libs/clients/tax-api/src/lib/clients/tax/clientConfig.json new file mode 100644 index 00000000..4377b8d0 --- /dev/null +++ b/libs/clients/tax-api/src/lib/clients/tax/clientConfig.json @@ -0,0 +1,440 @@ +{ + "openapi": "3.0.0", + "paths": { + "/v1/me/taxes": { + "get": { + "description": "New endpoint", + "responses": { + "200": { + "description": "New response", + "content": { + "application/json": { + "schema": { + "": "" + } + } + } + } + } + }, + "get": { + "operationId": "MeTaxController_getAllTaxes", + "description": "Get all tax submissions for a user", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxDto" } + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + }, + "401": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + }, + "403": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + } + }, + "tags": ["me/taxes"] + } + }, + "/v1/me/taxes/income": { + "get": { + "operationId": "MeTaxController_getIncomes", + "description": "Get overview of taxes submitted by a user", + "parameters": [ + { + "name": "locale", + "required": false, + "in": "query", + "schema": { "enum": ["en", "is"], "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaxesTaxDto" + } + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + }, + "401": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + }, + "403": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HttpProblemResponse" } + } + } + } + }, + "tags": ["me/taxes"] + } + }, + "/*": { + "get": { + "operationId": "VisualizerController_getVisualizer", + "parameters": [], + "responses": { "200": { "description": "" } } + } + } + }, + "info": { + "title": "Tax Authorities API", + "description": "The api provides access to information about individuals' tax status and their administered taxes.", + "version": "1.0", + "contact": {} + }, + "tags": [], + "servers": [], + "components": { + "securitySchemes": { + "oauth2": { + "type": "oauth2", + "flows": { + "authorizationCode": { + "authorizationUrl": "https://identity-server.dev01.devland.is/connect/authorize", + "tokenUrl": "https://identity-server.dev01.devland.is/connect/token", + "scopes": { + "openid": "openid", + "tax": "tax", + "@landlaeknir.is/taxes:admin": "Manage tax data", + "@landlaeknir.is/taxes": "Get tax data for a user", + "@landlaeknir.is/taxes:read": "Get tax data" + } + } + } + } + }, + "schemas": { + "TaxDto": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "nationalId": { "type": "string", "example": "1234567890" }, + "taxDate": { + "format": "date-time", + "type": "string", + "example": "2021-01-01" + }, + "taxLocation": { "type": "string" }, + "taxAge": { + "type": "object", + "example": { "years": 1, "months": 2 } + }, + "generalComment": { "type": "string" }, + "rejected": { "type": "boolean", "example": false } + }, + "required": [ + "id", + "nationalId", + "vaccineCode", + "vaccineCodingSystem", + "vaccineCodeDescription", + "vaccineCodeDescriptionShort", + "taxDate", + "taxLocation", + "taxAge", + "rejected" + ] + }, + "HttpProblemResponse": { + "type": "object", + "properties": { + "type": { + "type": "object", + "description": "A URI reference that identifies the problem type" + }, + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem type" + }, + "status": { "type": "number", "description": "The HTTP status code" }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem" + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem." + } + }, + "required": ["type", "title"] + }, + "TaxesStatusColor": { + "type": "string", + "enum": ["green", "yellow", "red"] + }, + "TaxesTaxDto": { + "type": "object", + "properties": { + "diseaseId": { "type": "string", "example": "tetanus" }, + "diseaseName": { "type": "string", "example": "Stífkrampi" }, + "diseaseDescription": { "type": "string" }, + "isFeatured": { + "type": "boolean", + "description": "Is the tax scheduled as in \"almenn bólusetning\"" + }, + "taxStatus": { + "type": "string", + "example": "tax", + "enum": [ + "valid", + "expired", + "complete", + "incomplete" + ] + }, + "taxStatusName": { "type": "string", "example": "Í gildi" }, + "taxStatusColor": { + "$ref": "#/components/schemas/TaxesStatusColor" + }, + "lastTaxDate": { "format": "date-time", "type": "string" }, + "taxs": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxDto" } + }, + "comments": { "type": "array", "items": { "type": "string" } } + }, + "required": [ + "diseaseId", + "diseaseName", + "isFeatured", + "taxs", + "comments" + ] + }, + "TaxDto": { + "type": "object", + "properties": { + "code": { "type": "string" }, + "codingSystem": { "type": "string", "example": "ATC" }, + "diseaseId": { "type": "string" }, + "useInCalculations": { "type": "boolean" } + }, + "required": ["code", "codingSystem", "diseaseId", "useInCalculations"] + }, + "Locale": { "type": "string", "enum": ["en", "is"] }, + "DiseaseRuleTranslationDto": { + "type": "object", + "properties": { + "locale": { "$ref": "#/components/schemas/Locale" }, + "comment": { "type": "string" } + }, + "required": ["locale", "comment"] + }, + "TaxRuleDto": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "diseaseId": { "type": "string" }, + "order": { "type": "number" }, + "type": { "type": "object" }, + "vaccineCodes": { "type": "string" }, + "cond1Type": { "type": "string" }, + "cond1Min": { "type": "number" }, + "cond1Max": { "type": "number" }, + "cond2Type": { "type": "string" }, + "cond2Min": { "type": "number" }, + "cond2Max": { "type": "number" }, + "cond3Type": { "type": "string" }, + "cond3Min": { "type": "number" }, + "cond3Max": { "type": "number" }, + "status": { "type": "string" }, + "statusName": { "type": "string" }, + "internalComment": { "type": "string" }, + "publicComment": { "type": "string" }, + "translations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DiseaseRuleTranslationDto" + } + } + }, + "required": ["id", "diseaseId", "order", "type", "translations"] + }, + "TaxTranslationDto": { + "type": "object", + "properties": { + "locale": { "$ref": "#/components/schemas/Locale" }, + "name": { "type": "string" }, + "description": { "type": "object" } + }, + "required": ["locale", "name", "description"] + }, + "DiseaseDto": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "description": { "type": "string" }, + "isFeatured": { "type": "boolean" }, + "isVisible": { "type": "boolean" }, + "hideIfNoTaxs": { "type": "boolean" }, + "taxes": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxDto" } + }, + "rules": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxRuleDto" } + }, + "translations": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxTranslationDto" } + } + }, + "required": [ + "id", + "name", + "isFeatured", + "isVisible", + "hideIfNoTaxs", + "vaccines", + "rules", + "translations" + ] + }, + "UpdateDiseaseDto": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "description": { "type": "string" }, + "isFeatured": { "type": "boolean" }, + "isVisible": { "type": "boolean" }, + "hideIfNoTaxs": { "type": "boolean" }, + "vaccines": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxDto" } + }, + "rules": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxRuleDto" } + }, + "translations": { + "type": "array", + "items": { "$ref": "#/components/schemas/TaxTranslationDto" } + } + } + }, + "CreateTaxRuleDto": { + "type": "object", + "properties": { + "order": { "type": "number" }, + "type": { "type": "object" }, + "vaccineCodes": { "type": "string" }, + "cond1Type": { "type": "string" }, + "cond1Min": { "type": "number" }, + "cond1Max": { "type": "number" }, + "cond2Type": { "type": "string" }, + "cond2Min": { "type": "number" }, + "cond2Max": { "type": "number" }, + "cond3Type": { "type": "string" }, + "cond3Min": { "type": "number" }, + "cond3Max": { "type": "number" }, + "status": { "type": "string" }, + "statusName": { "type": "string" }, + "internalComment": { "type": "string" }, + "publicComment": { "type": "string" }, + "translations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DiseaseRuleTranslationDto" + } + } + }, + "required": ["order", "type", "translations"] + }, + "UpdateTaxRuleDto": { + "type": "object", + "properties": { + "id": { "type": "number" }, + "diseaseId": { "type": "string" }, + "order": { "type": "number" }, + "type": { "type": "object" }, + "vaccineCodes": { "type": "string" }, + "cond1Type": { "type": "string" }, + "cond1Min": { "type": "number" }, + "cond1Max": { "type": "number" }, + "cond2Type": { "type": "string" }, + "cond2Min": { "type": "number" }, + "cond2Max": { "type": "number" }, + "cond3Type": { "type": "string" }, + "cond3Min": { "type": "number" }, + "cond3Max": { "type": "number" }, + "status": { "type": "string" }, + "statusName": { "type": "string" }, + "internalComment": { "type": "string" }, + "publicComment": { "type": "string" }, + "translations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DiseaseRuleTranslationDto" + } + } + } + } + } + } +} diff --git a/libs/clients/tax-api/src/lib/clients/tax/index.ts b/libs/clients/tax-api/src/lib/clients/tax/index.ts new file mode 100644 index 00000000..895afb75 --- /dev/null +++ b/libs/clients/tax-api/src/lib/clients/tax/index.ts @@ -0,0 +1,3 @@ +export { TaxService } from './tax.service' +export * from './tax.config' +export * from './gen/fetch/models' diff --git a/libs/clients/tax-api/src/lib/clients/tax/tax.config.ts b/libs/clients/tax-api/src/lib/clients/tax/tax.config.ts new file mode 100644 index 00000000..df9af6fe --- /dev/null +++ b/libs/clients/tax-api/src/lib/clients/tax/tax.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from '@island.is/nest/config' +import * as z from 'zod' + +const schema = z.object({ + xroadPath: z.string(), + scope: z.array(z.string()), +}) + + +/** + * @todo configure The TaxClientConfig + */ +export const TaxClientConfig = defineConfig< + z.infer +>({ + name: 'TaxClientConfig', + schema, + load(env) { + return { + xroadPath: env.required( + 'XROAD_TAX_PATH', + 'IS-DEV/GOV/10015/EmbaettiLandlaeknis-Protected/tax-v1', + ), + scope: ['@landlaeknir.is/tax'], + } + }, +}) diff --git a/libs/clients/tax-api/src/lib/clients/tax/tax.provider.ts b/libs/clients/tax-api/src/lib/clients/tax/tax.provider.ts new file mode 100644 index 00000000..c2999f32 --- /dev/null +++ b/libs/clients/tax-api/src/lib/clients/tax/tax.provider.ts @@ -0,0 +1,51 @@ +import { createEnhancedFetch } from '@island.is/clients/middlewares' +import { + ConfigType, + IdsClientConfig, + LazyDuringDevScope, + XRoadConfig, +} from '@island.is/nest/config' +import { TaxClientConfig } from './tax.config' +import { Configuration, MeTaxApi } from './gen/fetch' + +/** + * @todo The TaxApiProvider needs to be configured + */ +export const TaxApiProvider = { + provide: MeTaxApi, + scope: LazyDuringDevScope, + useFactory: ( + xRoadConfig: ConfigType, + config: ConfigType, + idsClientConfig: ConfigType, + ) => { + return new MeTaxApi( + new Configuration({ + fetchApi: createEnhancedFetch({ + name: 'clients-tax', + organizationSlug: 'landlaeknir', + logErrorResponseBody: true, + autoAuth: idsClientConfig.isConfigured + ? { + mode: 'tokenExchange', + issuer: idsClientConfig.issuer, + clientId: idsClientConfig.clientId, + clientSecret: idsClientConfig.clientSecret, + scope: config.scope, + } + : undefined, + }), + basePath: `${xRoadConfig.xRoadBasePath}/r1/${config.xroadPath}`, + headers: { + 'X-Road-Client': xRoadConfig.xRoadClient, + Accept: 'application/json', + }, + }), + ) + }, + inject: [ + XRoadConfig.KEY, + TaxClientConfig.KEY, + IdsClientConfig.KEY, + ], +} diff --git a/libs/clients/tax-api/src/lib/clients/tax/tax.service.ts b/libs/clients/tax-api/src/lib/clients/tax/tax.service.ts new file mode 100644 index 00000000..87ed16e2 --- /dev/null +++ b/libs/clients/tax-api/src/lib/clients/tax/tax.service.ts @@ -0,0 +1,68 @@ +import { AuthMiddleware, Auth } from '@island.is/auth-nest-tools' +import { handle404 } from '@island.is/clients/middlewares' +import { Inject, Injectable } from '@nestjs/common' + +/** + * @todo define and adpat the clientConfig.js + * @todo run yarn codegen and use the correct imports + */ +import { + TaxDto, + MeTaxControllerGetTaxEnum, + MeTaxApi, +} from './gen/fetch' +import { LOGGER_PROVIDER } from '@island.is/logging' +import type { Logger } from '@island.is/logging' + +const LOG_CATEGORY = 'tax-api' +@Injectable() +export class TaxService { + constructor( + @Inject(LOGGER_PROVIDER) private readonly logger: Logger, + private readonly taxApi: MeTaxApi, + ) {} + + private taxApiWithAuth(auth: Auth) { + return this.taxApi.withMiddleware(new AuthMiddleware(auth)) + } + + public async getTax( + auth: Auth, + ): Promise | null> { + const taxes = await this.taxApiWithAuth(auth) + .meTaxControllerGetTax() + .catch(handle404) + + if (!taxes) { + this.logger.debug('No tax returned', { + category: LOG_CATEGORY, + }) + return null + } + + return taxes + } + + public async getTaxDetail( + auth: Auth, + locale: string, + ): Promise | null> { + const taxes = await this.taxApiWithAuth(auth) + .meTaxControllerGetTax({ + locale: + locale === 'is' + ? MeTaxControllerGetTaxEnum.Is + : MeTaxControllerGetTaxEnum.En, + }) + .catch(handle404) + + if (!taxes) { + this.logger.debug('No taxes returned', { + category: LOG_CATEGORY, + }) + return null + } + + return taxes + } +} diff --git a/libs/clients/tax-api/tsconfig.json b/libs/clients/tax-api/tsconfig.json new file mode 100644 index 00000000..8122543a --- /dev/null +++ b/libs/clients/tax-api/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/clients/tax-api/tsconfig.lib.json b/libs/clients/tax-api/tsconfig.lib.json new file mode 100644 index 00000000..4befa7f0 --- /dev/null +++ b/libs/clients/tax-api/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/clients/tax-api/tsconfig.spec.json b/libs/clients/tax-api/tsconfig.spec.json new file mode 100644 index 00000000..69a251f3 --- /dev/null +++ b/libs/clients/tax-api/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index d8ac04da..e0718ff1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1174,6 +1174,7 @@ "libs/skilavottord/consts/src/index.ts" ], "@island.is/skilavottord/types": ["libs/skilavottord/types/src/index.ts"], + "@island.is/tax/*": ["apps/tax/*"], "@island.is/testing/containers": ["libs/testing/containers/src/index.ts"], "@island.is/testing/e2e": ["libs/testing/e2e/src/index.ts"], "@island.is/testing/fixtures": ["libs/testing/fixtures/src/index.ts"], @@ -1182,7 +1183,6 @@ "@island.is/university-gateway": ["libs/university-gateway/src/index.ts"], "@island.is/user-monitoring": ["libs/user-monitoring/src/index.ts"], "@island.is/web/*": ["apps/web/*"], - "@island.is/tax/*": ["apps/tax/*"], "@island.is/web/component*": ["apps/web/component*/real.ts"], "api/domains/financial-statement-cemetery": [ "libs/api/domains/financial-statement-cemetery/src/index.ts" @@ -1196,6 +1196,7 @@ "application/templates/name-of-application": [ "libs/application/templates/name-of-application/src/index.ts" ], + "clients/tax-api": ["libs/clients/tax-api/src/index.ts"], "delegation-admin": ["libs/portals/admin/delegation-admin/src/index.ts"] } }