diff --git a/.env.example b/.env.example index b1d0140ad..cbb81425b 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,9 @@ MAGIC_LINK_TOKEN_VALIDITY= 3 REFRESH_TOKEN_VALIDITY="604800000" # Default validity is 7 days (604800000 ms) in ms ACCESS_TOKEN_VALIDITY="86400000" # Default validity is 1 day (86400000 ms) in ms SESSION_SECRET='add some secret here' +# Reccomended to be true, set to false if you are using http +# Note: Some auth providers may not support http requests +ALLOW_SECURE_COOKIES=true # Hoppscotch App Domain Config REDIRECT_URL="http://localhost:3000" @@ -35,9 +38,20 @@ MICROSOFT_SCOPE="user.read" MICROSOFT_TENANT="common" # Mailer config -MAILER_SMTP_URL="smtps://user@domain.com:pass@smtp.domain.com" +MAILER_SMTP_ENABLE="true" +MAILER_USE_CUSTOM_CONFIGS="false" MAILER_ADDRESS_FROM='"From Name Here" ' +MAILER_SMTP_URL="smtps://user@domain.com:pass@smtp.domain.com" # used if custom mailer configs is false + +# The following are used if custom mailer configs is true +MAILER_SMTP_HOST="smtp.domain.com" +MAILER_SMTP_PORT="587" +MAILER_SMTP_SECURE="true" +MAILER_SMTP_USER="user@domain.com" +MAILER_SMTP_PASSWORD="pass" +MAILER_TLS_REJECT_UNAUTHORIZED="true" + # Rate Limit Config RATE_LIMIT_TTL=60 # In seconds RATE_LIMIT_MAX=100 # Max requests per IP diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 850c04098..6c6eacd42 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,20 +7,15 @@ Please make sure that the pull request is limited to one type (docs, feature, et Closes # -### Description - + + +### What's changed + -### Checks - -- [ ] My pull request adheres to the code style of this project -- [ ] My code requires changes to the documentation -- [ ] I have updated the documentation as required -- [ ] All the tests have passed - -### Additional Information - +### Notes to reviewers + diff --git a/CODEOWNERS b/CODEOWNERS index e7400a2b6..5c837665d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,30 +1,21 @@ # CODEOWNERS is prioritized from bottom to top -# If none of the below matched -* @AndrewBastin @liyasthomas - # Packages /packages/codemirror-lang-graphql/ @AndrewBastin -/packages/hoppscotch-cli/ @AndrewBastin -/packages/hoppscotch-common/ @amk-dev @AndrewBastin +/packages/hoppscotch-cli/ @jamesgeorge007 /packages/hoppscotch-data/ @AndrewBastin -/packages/hoppscotch-js-sandbox/ @AndrewBastin -/packages/hoppscotch-ui/ @anwarulislam -/packages/hoppscotch-web/ @amk-dev -/packages/hoppscotch-selfhost-web/ @amk-dev +/packages/hoppscotch-js-sandbox/ @jamesgeorge007 +/packages/hoppscotch-selfhost-web/ @jamesgeorge007 +/packages/hoppscotch-selfhost-desktop/ @AndrewBastin /packages/hoppscotch-sh-admin/ @JoelJacobStephen -/packages/hoppscotch-backend/ @ankitsridhar16 @balub +/packages/hoppscotch-backend/ @balub -# Sections within Hoppscotch Common -/packages/hoppscotch-common/src/components @anwarulislam -/packages/hoppscotch-common/src/components/collections @nivedin @amk-dev -/packages/hoppscotch-common/src/components/environments @nivedin @amk-dev -/packages/hoppscotch-common/src/composables @amk-dev -/packages/hoppscotch-common/src/modules @AndrewBastin @amk-dev -/packages/hoppscotch-common/src/pages @AndrewBastin @amk-dev -/packages/hoppscotch-common/src/newstore @AndrewBastin @amk-dev +# READMEs and other documentation files +*.md @liyasthomas -README.md @liyasthomas - -# The lockfile has no owner -pnpm-lock.yaml +# Self Host deployment related files +*.Dockerfile @balub +docker-compose.yml @balub +docker-compose.deploy.yml @balub +*.Caddyfile @balub +.dockerignore @balub diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b5d00dc8..ce37ce4b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,4 @@ Please note we have a code of conduct, please follow it in all your interactions build. 2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](https://semver.org). -4. You may merge the Pull Request once you have the sign-off of two other developers, or if you - do not have permission to do that, you may request the second reviewer merge it for you. +3. Make sure you do not expose environment variables or other sensitive information in your PR. diff --git a/SECURITY.md b/SECURITY.md index f7ab50cdf..8930b000d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,19 +4,36 @@ This document outlines security procedures and general policies for the Hoppscot - [Security Policy](#security-policy) - [Reporting a security vulnerability](#reporting-a-security-vulnerability) + - [What is not a valid vulnerability](#what-is-not-a-valid-vulnerability) - [Incident response process](#incident-response-process) ## Reporting a security vulnerability -Report security vulnerabilities by emailing the Hoppscotch Support team at support@hoppscotch.io. +We use [Github Security Advisories](https://github.com/hoppscotch/hoppscotch/security/advisories) to manage vulnerability reports and collaboration. +Someone from the Hoppscotch team shall report to you within 48 hours of the disclosure of the vulnerability in GHSA. If no response was received, please reach out to +Hoppscotch Support at support@hoppscotch.io along with the GHSA advisory link. -The primary security point of contact from Hoppscotch Support team will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. +> NOTE: Since we have multiple open source components, Advisories may move into the relevant repo (for example, an XSS in a UI component might be part of [`@hoppscotch/ui`](https://github.com/hoppscotch/ui)). +> If in doubt, open your report in `hoppscotch/hoppscotch` GHSA. -**Do not create a GitHub issue ticket to report a security vulnerability.** +**Do not create a GitHub issue ticket to report a security vulnerability!** -The Hoppscotch team and community take all security vulnerability reports in Hoppscotch seriously. Thank you for improving the security of Hoppscotch. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. +The Hoppscotch team takes all security vulnerability reports in Hoppscotch seriously. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. -Report security bugs in third-party modules to the person or team maintaining the module. +## What is not a valid vulnerability +We receive many reports about different sections of the Hoppscotch platform. Hence, we have a fine line we have drawn defining what is considered valid vulnerability. +Please refrain from opening an advisory if it describes the following: + +- A vulnerability in a dependency of Hoppscotch (unless you have practical attack with it on the Hoppscotch codebase) +- Reports of vulnerabilities related to old runtimes (like NodeJS) or container images used by the codebase +- Vulnerabilities present when using Hoppscotch in anything other than the defined minimum requirements that Hoppscotch supports. + +Hoppscotch Team ensures security support for: +- Modern Browsers (Chrome/Firefox/Safari/Edge) with versions up to 1 year old. +- Windows versions on or above Windows 10 on Intel and ARM. +- macOS versions dating back up to 2 years on Intel and Apple Silicon. +- Popular Linux distributions with up-to-date packages with preference to x86/64 CPUs. +- Docker/OCI Runtimes (preference to Docker and Podman) dating back up to 1 year. ## Incident response process diff --git a/docker-compose.yml b/docker-compose.yml index b7f3d2f52..7bae7928b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -100,7 +100,7 @@ services: test: [ "CMD-SHELL", - "sh -c 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'" + "sh -c 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'", ] interval: 5s timeout: 5s diff --git a/package.json b/package.json index f4286102b..dbe13220e 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "devDependencies": { "@commitlint/cli": "16.3.0", "@commitlint/config-conventional": "16.2.4", - "@hoppscotch/ui": "0.1.0", + "@hoppscotch/ui": "0.2.0", "@types/node": "17.0.27", "cross-env": "7.0.3", "http-server": "14.1.1", @@ -37,7 +37,7 @@ "vue": "3.3.9" }, "packageExtensions": { - "httpsnippet@3.0.1": { + "@hoppscotch/httpsnippet": { "dependencies": { "ajv": "6.12.3" } diff --git a/packages/hoppscotch-backend/package.json b/packages/hoppscotch-backend/package.json index 60732bd5e..0cbc17d4a 100644 --- a/packages/hoppscotch-backend/package.json +++ b/packages/hoppscotch-backend/package.json @@ -1,6 +1,6 @@ { "name": "hoppscotch-backend", - "version": "2024.3.3", + "version": "2024.7.0", "description": "", "author": "", "private": true, @@ -35,11 +35,14 @@ "@nestjs/passport": "10.0.2", "@nestjs/platform-express": "10.2.7", "@nestjs/schedule": "4.0.1", + "@nestjs/swagger": "7.4.0", "@nestjs/terminus": "10.2.3", "@nestjs/throttler": "5.0.1", "@prisma/client": "5.8.1", "argon2": "0.30.3", "bcrypt": "5.1.0", + "class-transformer": "0.5.1", + "class-validator": "0.14.1", "cookie": "0.5.0", "cookie-parser": "1.4.6", "cron": "3.1.6", diff --git a/packages/hoppscotch-backend/prisma/migrations/20240519093155_add_last_logged_on_to_user/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20240519093155_add_last_logged_on_to_user/migration.sql new file mode 100644 index 000000000..5664c4fc0 --- /dev/null +++ b/packages/hoppscotch-backend/prisma/migrations/20240519093155_add_last_logged_on_to_user/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "lastLoggedOn" TIMESTAMP(3); diff --git a/packages/hoppscotch-backend/prisma/migrations/20240520091033_personal_access_tokens/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20240520091033_personal_access_tokens/migration.sql new file mode 100644 index 000000000..53abda80b --- /dev/null +++ b/packages/hoppscotch-backend/prisma/migrations/20240520091033_personal_access_tokens/migration.sql @@ -0,0 +1,19 @@ + +-- CreateTable +CREATE TABLE "PersonalAccessToken" ( + "id" TEXT NOT NULL, + "userUid" TEXT NOT NULL, + "label" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expiresOn" TIMESTAMP(3), + "createdOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedOn" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "PersonalAccessToken_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "PersonalAccessToken_token_key" ON "PersonalAccessToken"("token"); + +-- AddForeignKey +ALTER TABLE "PersonalAccessToken" ADD CONSTRAINT "PersonalAccessToken_userUid_fkey" FOREIGN KEY ("userUid") REFERENCES "User"("uid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/hoppscotch-backend/prisma/migrations/20240621062554_user_last_active_on/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20240621062554_user_last_active_on/migration.sql new file mode 100644 index 000000000..9e0ff1c93 --- /dev/null +++ b/packages/hoppscotch-backend/prisma/migrations/20240621062554_user_last_active_on/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "lastActiveOn" TIMESTAMP(3); diff --git a/packages/hoppscotch-backend/prisma/migrations/20240726121956_infra_token/migration.sql b/packages/hoppscotch-backend/prisma/migrations/20240726121956_infra_token/migration.sql new file mode 100644 index 000000000..10971ba62 --- /dev/null +++ b/packages/hoppscotch-backend/prisma/migrations/20240726121956_infra_token/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "InfraToken" ( + "id" TEXT NOT NULL, + "creatorUid" TEXT NOT NULL, + "label" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expiresOn" TIMESTAMP(3), + "createdOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "InfraToken_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "InfraToken_token_key" ON "InfraToken"("token"); diff --git a/packages/hoppscotch-backend/prisma/schema.prisma b/packages/hoppscotch-backend/prisma/schema.prisma index 3225d9ccc..b0731c957 100644 --- a/packages/hoppscotch-backend/prisma/schema.prisma +++ b/packages/hoppscotch-backend/prisma/schema.prisma @@ -41,31 +41,31 @@ model TeamInvitation { } model TeamCollection { - id String @id @default(cuid()) + id String @id @default(cuid()) parentID String? data Json? - parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id]) - children TeamCollection[] @relation("TeamCollectionChildParent") + parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id]) + children TeamCollection[] @relation("TeamCollectionChildParent") requests TeamRequest[] teamID String - team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) + team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) title String orderIndex Int - createdOn DateTime @default(now()) @db.Timestamp(3) - updatedOn DateTime @updatedAt @db.Timestamp(3) + createdOn DateTime @default(now()) @db.Timestamp(3) + updatedOn DateTime @updatedAt @db.Timestamp(3) } model TeamRequest { - id String @id @default(cuid()) + id String @id @default(cuid()) collectionID String - collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade) + collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade) teamID String - team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) + team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) title String request Json orderIndex Int - createdOn DateTime @default(now()) @db.Timestamp(3) - updatedOn DateTime @updatedAt @db.Timestamp(3) + createdOn DateTime @default(now()) @db.Timestamp(3) + updatedOn DateTime @updatedAt @db.Timestamp(3) } model Shortcode { @@ -89,24 +89,27 @@ model TeamEnvironment { } model User { - uid String @id @default(cuid()) - displayName String? - email String? @unique - photoURL String? - isAdmin Boolean @default(false) - refreshToken String? - providerAccounts Account[] - VerificationToken VerificationToken[] - settings UserSettings? - UserHistory UserHistory[] - UserEnvironments UserEnvironment[] - userCollections UserCollection[] - userRequests UserRequest[] - currentRESTSession Json? - currentGQLSession Json? - createdOn DateTime @default(now()) @db.Timestamp(3) - invitedUsers InvitedUsers[] - shortcodes Shortcode[] + uid String @id @default(cuid()) + displayName String? + email String? @unique + photoURL String? + isAdmin Boolean @default(false) + refreshToken String? + providerAccounts Account[] + VerificationToken VerificationToken[] + settings UserSettings? + UserHistory UserHistory[] + UserEnvironments UserEnvironment[] + userCollections UserCollection[] + userRequests UserRequest[] + currentRESTSession Json? + currentGQLSession Json? + lastLoggedOn DateTime? @db.Timestamp(3) + lastActiveOn DateTime? @db.Timestamp(3) + createdOn DateTime @default(now()) @db.Timestamp(3) + invitedUsers InvitedUsers[] + shortcodes Shortcode[] + personalAccessTokens PersonalAccessToken[] } model Account { @@ -218,3 +221,24 @@ model InfraConfig { createdOn DateTime @default(now()) @db.Timestamp(3) updatedOn DateTime @updatedAt @db.Timestamp(3) } + +model PersonalAccessToken { + id String @id @default(cuid()) + userUid String + user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) + label String + token String @unique @default(uuid()) + expiresOn DateTime? @db.Timestamp(3) + createdOn DateTime @default(now()) @db.Timestamp(3) + updatedOn DateTime @updatedAt @db.Timestamp(3) +} + +model InfraToken { + id String @id @default(cuid()) + creatorUid String + label String + token String @unique @default(uuid()) + expiresOn DateTime? @db.Timestamp(3) + createdOn DateTime @default(now()) @db.Timestamp(3) + updatedOn DateTime @default(now()) @db.Timestamp(3) +} diff --git a/packages/hoppscotch-backend/src/access-token/access-token.controller.ts b/packages/hoppscotch-backend/src/access-token/access-token.controller.ts new file mode 100644 index 000000000..4b1bd8460 --- /dev/null +++ b/packages/hoppscotch-backend/src/access-token/access-token.controller.ts @@ -0,0 +1,107 @@ +import { + BadRequestException, + Body, + Controller, + Delete, + Get, + HttpStatus, + Param, + ParseIntPipe, + Post, + Query, + UseGuards, + UseInterceptors, +} from '@nestjs/common'; +import { AccessTokenService } from './access-token.service'; +import { CreateAccessTokenDto } from './dto/create-access-token.dto'; +import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; +import * as E from 'fp-ts/Either'; +import { throwHTTPErr } from 'src/utils'; +import { GqlUser } from 'src/decorators/gql-user.decorator'; +import { AuthUser } from 'src/types/AuthUser'; +import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard'; +import { PATAuthGuard } from 'src/guards/rest-pat-auth.guard'; +import { AccessTokenInterceptor } from 'src/interceptors/access-token.interceptor'; +import { TeamEnvironmentsService } from 'src/team-environments/team-environments.service'; +import { TeamCollectionService } from 'src/team-collection/team-collection.service'; +import { ACCESS_TOKENS_INVALID_DATA_ID } from 'src/errors'; +import { createCLIErrorResponse } from './helper'; + +@UseGuards(ThrottlerBehindProxyGuard) +@Controller({ path: 'access-tokens', version: '1' }) +export class AccessTokenController { + constructor( + private readonly accessTokenService: AccessTokenService, + private readonly teamCollectionService: TeamCollectionService, + private readonly teamEnvironmentsService: TeamEnvironmentsService, + ) {} + + @Post('create') + @UseGuards(JwtAuthGuard) + async createPAT( + @GqlUser() user: AuthUser, + @Body() createAccessTokenDto: CreateAccessTokenDto, + ) { + const result = await this.accessTokenService.createPAT( + createAccessTokenDto, + user, + ); + if (E.isLeft(result)) throwHTTPErr(result.left); + return result.right; + } + + @Delete('revoke') + @UseGuards(JwtAuthGuard) + async deletePAT(@Query('id') id: string) { + const result = await this.accessTokenService.deletePAT(id); + + if (E.isLeft(result)) throwHTTPErr(result.left); + return result.right; + } + + @Get('list') + @UseGuards(JwtAuthGuard) + async listAllUserPAT( + @GqlUser() user: AuthUser, + @Query('offset', ParseIntPipe) offset: number, + @Query('limit', ParseIntPipe) limit: number, + ) { + return await this.accessTokenService.listAllUserPAT( + user.uid, + offset, + limit, + ); + } + + @Get('collection/:id') + @UseGuards(PATAuthGuard) + @UseInterceptors(AccessTokenInterceptor) + async fetchCollection(@GqlUser() user: AuthUser, @Param('id') id: string) { + const res = await this.teamCollectionService.getCollectionForCLI( + id, + user.uid, + ); + + if (E.isLeft(res)) + throw new BadRequestException( + createCLIErrorResponse(ACCESS_TOKENS_INVALID_DATA_ID), + ); + return res.right; + } + + @Get('environment/:id') + @UseGuards(PATAuthGuard) + @UseInterceptors(AccessTokenInterceptor) + async fetchEnvironment(@GqlUser() user: AuthUser, @Param('id') id: string) { + const res = await this.teamEnvironmentsService.getTeamEnvironmentForCLI( + id, + user.uid, + ); + + if (E.isLeft(res)) + throw new BadRequestException( + createCLIErrorResponse(ACCESS_TOKENS_INVALID_DATA_ID), + ); + return res.right; + } +} diff --git a/packages/hoppscotch-backend/src/access-token/access-token.module.ts b/packages/hoppscotch-backend/src/access-token/access-token.module.ts new file mode 100644 index 000000000..344f6f07f --- /dev/null +++ b/packages/hoppscotch-backend/src/access-token/access-token.module.ts @@ -0,0 +1,20 @@ +import { Module } from '@nestjs/common'; +import { AccessTokenController } from './access-token.controller'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { AccessTokenService } from './access-token.service'; +import { TeamCollectionModule } from 'src/team-collection/team-collection.module'; +import { TeamEnvironmentsModule } from 'src/team-environments/team-environments.module'; +import { TeamModule } from 'src/team/team.module'; + +@Module({ + imports: [ + PrismaModule, + TeamCollectionModule, + TeamEnvironmentsModule, + TeamModule, + ], + controllers: [AccessTokenController], + providers: [AccessTokenService], + exports: [AccessTokenService], +}) +export class AccessTokenModule {} diff --git a/packages/hoppscotch-backend/src/access-token/access-token.service.spec.ts b/packages/hoppscotch-backend/src/access-token/access-token.service.spec.ts new file mode 100644 index 000000000..0c319c839 --- /dev/null +++ b/packages/hoppscotch-backend/src/access-token/access-token.service.spec.ts @@ -0,0 +1,196 @@ +import { AccessTokenService } from './access-token.service'; +import { mockDeep, mockReset } from 'jest-mock-extended'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { + ACCESS_TOKEN_EXPIRY_INVALID, + ACCESS_TOKEN_LABEL_SHORT, + ACCESS_TOKEN_NOT_FOUND, +} from 'src/errors'; +import { AuthUser } from 'src/types/AuthUser'; +import { PersonalAccessToken } from '@prisma/client'; +import { AccessToken } from 'src/types/AccessToken'; +import { HttpStatus } from '@nestjs/common'; + +const mockPrisma = mockDeep(); + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +const accessTokenService = new AccessTokenService(mockPrisma); + +const currentTime = new Date(); + +const user: AuthUser = { + uid: '123344', + email: 'dwight@dundermifflin.com', + displayName: 'Dwight Schrute', + photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute', + isAdmin: false, + refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + createdOn: currentTime, + currentGQLSession: {}, + currentRESTSession: {}, + lastLoggedOn: currentTime, + lastActiveOn: currentTime, +}; + +const PATCreatedOn = new Date(); +const expiryInDays = 7; +const PATExpiresOn = new Date( + PATCreatedOn.getTime() + expiryInDays * 24 * 60 * 60 * 1000, +); + +const userAccessToken: PersonalAccessToken = { + id: 'skfvhj8uvdfivb', + userUid: user.uid, + label: 'test', + token: '0140e328-b187-4823-ae4b-ed4bec832ac2', + expiresOn: PATExpiresOn, + createdOn: PATCreatedOn, + updatedOn: new Date(), +}; + +const userAccessTokenCasted: AccessToken = { + id: userAccessToken.id, + label: userAccessToken.label, + createdOn: userAccessToken.createdOn, + lastUsedOn: userAccessToken.updatedOn, + expiresOn: userAccessToken.expiresOn, +}; + +beforeEach(() => { + mockReset(mockPrisma); +}); + +describe('AccessTokenService', () => { + describe('createPAT', () => { + test('should throw ACCESS_TOKEN_LABEL_SHORT if label is too short', async () => { + const result = await accessTokenService.createPAT( + { + label: 'a', + expiryInDays: 7, + }, + user, + ); + expect(result).toEqualLeft({ + message: ACCESS_TOKEN_LABEL_SHORT, + statusCode: HttpStatus.BAD_REQUEST, + }); + }); + + test('should throw ACCESS_TOKEN_EXPIRY_INVALID if expiry date is invalid', async () => { + const result = await accessTokenService.createPAT( + { + label: 'test', + expiryInDays: 9, + }, + user, + ); + expect(result).toEqualLeft({ + message: ACCESS_TOKEN_EXPIRY_INVALID, + statusCode: HttpStatus.BAD_REQUEST, + }); + }); + + test('should successfully create a new Access Token', async () => { + mockPrisma.personalAccessToken.create.mockResolvedValueOnce( + userAccessToken, + ); + + const result = await accessTokenService.createPAT( + { + label: userAccessToken.label, + expiryInDays, + }, + user, + ); + expect(result).toEqualRight({ + token: `pat-${userAccessToken.token}`, + info: userAccessTokenCasted, + }); + }); + }); + + describe('deletePAT', () => { + test('should throw ACCESS_TOKEN_NOT_FOUND if Access Token is not found', async () => { + mockPrisma.personalAccessToken.delete.mockRejectedValueOnce( + 'RecordNotFound', + ); + + const result = await accessTokenService.deletePAT(userAccessToken.id); + expect(result).toEqualLeft({ + message: ACCESS_TOKEN_NOT_FOUND, + statusCode: HttpStatus.NOT_FOUND, + }); + }); + + test('should successfully delete a new Access Token', async () => { + mockPrisma.personalAccessToken.delete.mockResolvedValueOnce( + userAccessToken, + ); + + const result = await accessTokenService.deletePAT(userAccessToken.id); + expect(result).toEqualRight(true); + }); + }); + + describe('listAllUserPAT', () => { + test('should successfully return a list of user Access Tokens', async () => { + mockPrisma.personalAccessToken.findMany.mockResolvedValueOnce([ + userAccessToken, + ]); + + const result = await accessTokenService.listAllUserPAT(user.uid, 0, 10); + expect(result).toEqual([userAccessTokenCasted]); + }); + }); + + describe('getUserPAT', () => { + test('should throw ACCESS_TOKEN_NOT_FOUND if Access Token is not found', async () => { + mockPrisma.personalAccessToken.findUniqueOrThrow.mockRejectedValueOnce( + 'NotFoundError', + ); + + const result = await accessTokenService.getUserPAT(userAccessToken.token); + expect(result).toEqualLeft(ACCESS_TOKEN_NOT_FOUND); + }); + + test('should successfully return a user Access Tokens', async () => { + mockPrisma.personalAccessToken.findUniqueOrThrow.mockResolvedValueOnce({ + ...userAccessToken, + user, + } as any); + + const result = await accessTokenService.getUserPAT( + `pat-${userAccessToken.token}`, + ); + expect(result).toEqualRight({ + user, + ...userAccessToken, + } as any); + }); + }); + + describe('updateLastUsedforPAT', () => { + test('should throw ACCESS_TOKEN_NOT_FOUND if Access Token is not found', async () => { + mockPrisma.personalAccessToken.update.mockRejectedValueOnce( + 'RecordNotFound', + ); + + const result = await accessTokenService.updateLastUsedForPAT( + userAccessToken.token, + ); + expect(result).toEqualLeft(ACCESS_TOKEN_NOT_FOUND); + }); + + test('should successfully update lastUsedOn for a user Access Tokens', async () => { + mockPrisma.personalAccessToken.update.mockResolvedValueOnce( + userAccessToken, + ); + + const result = await accessTokenService.updateLastUsedForPAT( + `pat-${userAccessToken.token}`, + ); + expect(result).toEqualRight(userAccessTokenCasted); + }); + }); +}); diff --git a/packages/hoppscotch-backend/src/access-token/access-token.service.ts b/packages/hoppscotch-backend/src/access-token/access-token.service.ts new file mode 100644 index 000000000..e6a997746 --- /dev/null +++ b/packages/hoppscotch-backend/src/access-token/access-token.service.ts @@ -0,0 +1,190 @@ +import { HttpStatus, Injectable } from '@nestjs/common'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { CreateAccessTokenDto } from './dto/create-access-token.dto'; +import { AuthUser } from 'src/types/AuthUser'; +import { calculateExpirationDate, isValidLength } from 'src/utils'; +import * as E from 'fp-ts/Either'; +import { + ACCESS_TOKEN_EXPIRY_INVALID, + ACCESS_TOKEN_LABEL_SHORT, + ACCESS_TOKEN_NOT_FOUND, +} from 'src/errors'; +import { CreateAccessTokenResponse } from './helper'; +import { PersonalAccessToken } from '@prisma/client'; +import { AccessToken } from 'src/types/AccessToken'; +@Injectable() +export class AccessTokenService { + constructor(private readonly prisma: PrismaService) {} + + TITLE_LENGTH = 3; + VALID_TOKEN_DURATIONS = [7, 30, 60, 90]; + TOKEN_PREFIX = 'pat-'; + + /** + * Validate the expiration date of the token + * + * @param expiresOn Number of days the token is valid for + * @returns Boolean indicating if the expiration date is valid + */ + private validateExpirationDate(expiresOn: null | number) { + if (expiresOn === null || this.VALID_TOKEN_DURATIONS.includes(expiresOn)) + return true; + return false; + } + + /** + * Typecast a database PersonalAccessToken to a AccessToken model + * @param token database PersonalAccessToken + * @returns AccessToken model + */ + private cast(token: PersonalAccessToken): AccessToken { + return { + id: token.id, + label: token.label, + createdOn: token.createdOn, + expiresOn: token.expiresOn, + lastUsedOn: token.updatedOn, + }; + } + + /** + * Extract UUID from the token + * + * @param token Personal Access Token + * @returns UUID of the token + */ + private extractUUID(token): string | null { + if (!token.startsWith(this.TOKEN_PREFIX)) return null; + return token.slice(this.TOKEN_PREFIX.length); + } + + /** + * Create a Personal Access Token + * + * @param createAccessTokenDto DTO for creating a Personal Access Token + * @param user AuthUser object + * @returns Either of the created token or error message + */ + async createPAT(createAccessTokenDto: CreateAccessTokenDto, user: AuthUser) { + const isTitleValid = isValidLength( + createAccessTokenDto.label, + this.TITLE_LENGTH, + ); + if (!isTitleValid) + return E.left({ + message: ACCESS_TOKEN_LABEL_SHORT, + statusCode: HttpStatus.BAD_REQUEST, + }); + + if (!this.validateExpirationDate(createAccessTokenDto.expiryInDays)) + return E.left({ + message: ACCESS_TOKEN_EXPIRY_INVALID, + statusCode: HttpStatus.BAD_REQUEST, + }); + + const createdPAT = await this.prisma.personalAccessToken.create({ + data: { + userUid: user.uid, + label: createAccessTokenDto.label, + expiresOn: calculateExpirationDate(createAccessTokenDto.expiryInDays), + }, + }); + + const res: CreateAccessTokenResponse = { + token: `${this.TOKEN_PREFIX}${createdPAT.token}`, + info: this.cast(createdPAT), + }; + + return E.right(res); + } + + /** + * Delete a Personal Access Token + * + * @param accessTokenID ID of the Personal Access Token + * @returns Either of true or error message + */ + async deletePAT(accessTokenID: string) { + try { + await this.prisma.personalAccessToken.delete({ + where: { id: accessTokenID }, + }); + return E.right(true); + } catch { + return E.left({ + message: ACCESS_TOKEN_NOT_FOUND, + statusCode: HttpStatus.NOT_FOUND, + }); + } + } + + /** + * List all Personal Access Tokens of a user + * + * @param userUid UID of the user + * @param offset Offset for pagination + * @param limit Limit for pagination + * @returns Either of the list of Personal Access Tokens or error message + */ + async listAllUserPAT(userUid: string, offset: number, limit: number) { + const userPATs = await this.prisma.personalAccessToken.findMany({ + where: { + userUid: userUid, + }, + skip: offset, + take: limit, + orderBy: { + createdOn: 'desc', + }, + }); + + const userAccessTokenList = userPATs.map((pat) => this.cast(pat)); + + return userAccessTokenList; + } + + /** + * Get a Personal Access Token + * + * @param accessToken Personal Access Token + * @returns Either of the Personal Access Token or error message + */ + async getUserPAT(accessToken: string) { + const extractedToken = this.extractUUID(accessToken); + if (!extractedToken) return E.left(ACCESS_TOKEN_NOT_FOUND); + + try { + const userPAT = await this.prisma.personalAccessToken.findUniqueOrThrow({ + where: { token: extractedToken }, + include: { user: true }, + }); + return E.right(userPAT); + } catch { + return E.left(ACCESS_TOKEN_NOT_FOUND); + } + } + + /** + * Update the last used date of a Personal Access Token + * + * @param token Personal Access Token + * @returns Either of the updated Personal Access Token or error message + */ + async updateLastUsedForPAT(token: string) { + const extractedToken = this.extractUUID(token); + if (!extractedToken) return E.left(ACCESS_TOKEN_NOT_FOUND); + + try { + const updatedAccessToken = await this.prisma.personalAccessToken.update({ + where: { token: extractedToken }, + data: { + updatedOn: new Date(), + }, + }); + + return E.right(this.cast(updatedAccessToken)); + } catch { + return E.left(ACCESS_TOKEN_NOT_FOUND); + } + } +} diff --git a/packages/hoppscotch-backend/src/access-token/dto/create-access-token.dto.ts b/packages/hoppscotch-backend/src/access-token/dto/create-access-token.dto.ts new file mode 100644 index 000000000..d837a16a4 --- /dev/null +++ b/packages/hoppscotch-backend/src/access-token/dto/create-access-token.dto.ts @@ -0,0 +1,5 @@ +// Inputs to create a new PAT +export class CreateAccessTokenDto { + label: string; + expiryInDays: number | null; +} diff --git a/packages/hoppscotch-backend/src/access-token/helper.ts b/packages/hoppscotch-backend/src/access-token/helper.ts new file mode 100644 index 000000000..dc52ed517 --- /dev/null +++ b/packages/hoppscotch-backend/src/access-token/helper.ts @@ -0,0 +1,17 @@ +import { AccessToken } from 'src/types/AccessToken'; + +// Response type of PAT creation method +export type CreateAccessTokenResponse = { + token: string; + info: AccessToken; +}; + +// Response type of any error in PAT module +export type CLIErrorResponse = { + reason: string; +}; + +// Return a CLIErrorResponse object +export function createCLIErrorResponse(reason: string): CLIErrorResponse { + return { reason }; +} diff --git a/packages/hoppscotch-backend/src/admin/admin.service.spec.ts b/packages/hoppscotch-backend/src/admin/admin.service.spec.ts index a7ebc0682..a3bcf3c16 100644 --- a/packages/hoppscotch-backend/src/admin/admin.service.spec.ts +++ b/packages/hoppscotch-backend/src/admin/admin.service.spec.ts @@ -74,6 +74,8 @@ const dbAdminUsers: DbUser[] = [ refreshToken: 'refreshToken', currentRESTSession: '', currentGQLSession: '', + lastLoggedOn: new Date(), + lastActiveOn: new Date(), createdOn: new Date(), }, { @@ -85,20 +87,11 @@ const dbAdminUsers: DbUser[] = [ refreshToken: 'refreshToken', currentRESTSession: '', currentGQLSession: '', + lastLoggedOn: new Date(), + lastActiveOn: new Date(), createdOn: new Date(), }, ]; -const dbNonAminUser: DbUser = { - uid: 'uid 3', - displayName: 'displayName', - email: 'email@email.com', - photoURL: 'photoURL', - isAdmin: false, - refreshToken: 'refreshToken', - currentRESTSession: '', - currentGQLSession: '', - createdOn: new Date(), -}; describe('AdminService', () => { describe('fetchInvitedUsers', () => { @@ -121,6 +114,7 @@ describe('AdminService', () => { NOT: { inviteeEmail: { in: [dbAdminUsers[0].email], + mode: 'insensitive', }, }, }, @@ -229,7 +223,10 @@ describe('AdminService', () => { expect(mockPrisma.invitedUsers.deleteMany).toHaveBeenCalledWith({ where: { - inviteeEmail: { in: [invitedUsers[0].inviteeEmail] }, + inviteeEmail: { + in: [invitedUsers[0].inviteeEmail], + mode: 'insensitive', + }, }, }); expect(result).toEqualRight(true); diff --git a/packages/hoppscotch-backend/src/admin/admin.service.ts b/packages/hoppscotch-backend/src/admin/admin.service.ts index 0bed66132..a55524a87 100644 --- a/packages/hoppscotch-backend/src/admin/admin.service.ts +++ b/packages/hoppscotch-backend/src/admin/admin.service.ts @@ -89,12 +89,17 @@ export class AdminService { adminEmail: string, inviteeEmail: string, ) { - if (inviteeEmail == adminEmail) return E.left(DUPLICATE_EMAIL); + if (inviteeEmail.toLowerCase() == adminEmail.toLowerCase()) { + return E.left(DUPLICATE_EMAIL); + } if (!validateEmail(inviteeEmail)) return E.left(INVALID_EMAIL); const alreadyInvitedUser = await this.prisma.invitedUsers.findFirst({ where: { - inviteeEmail: inviteeEmail, + inviteeEmail: { + equals: inviteeEmail, + mode: 'insensitive', + }, }, }); if (alreadyInvitedUser != null) return E.left(USER_ALREADY_INVITED); @@ -156,10 +161,17 @@ export class AdminService { * @returns an Either of boolean or error string */ async revokeUserInvitations(inviteeEmails: string[]) { + const areAllEmailsValid = inviteeEmails.every((email) => + validateEmail(email), + ); + if (!areAllEmailsValid) { + return E.left(INVALID_EMAIL); + } + try { await this.prisma.invitedUsers.deleteMany({ where: { - inviteeEmail: { in: inviteeEmails }, + inviteeEmail: { in: inviteeEmails, mode: 'insensitive' }, }, }); return E.right(true); @@ -189,6 +201,7 @@ export class AdminService { NOT: { inviteeEmail: { in: userEmailObjs.map((user) => user.email), + mode: 'insensitive', }, }, }, diff --git a/packages/hoppscotch-backend/src/admin/infra.resolver.ts b/packages/hoppscotch-backend/src/admin/infra.resolver.ts index 1c08e9a2f..084f7bb9b 100644 --- a/packages/hoppscotch-backend/src/admin/infra.resolver.ts +++ b/packages/hoppscotch-backend/src/admin/infra.resolver.ts @@ -359,4 +359,23 @@ export class InfraResolver { return true; } + + @Mutation(() => Boolean, { + description: 'Enable or Disable SMTP for sending emails', + }) + @UseGuards(GqlAuthGuard, GqlAdminGuard) + async toggleSMTP( + @Args({ + name: 'status', + type: () => ServiceStatus, + description: 'Toggle SMTP', + }) + status: ServiceStatus, + ) { + const isUpdated = await this.infraConfigService.enableAndDisableSMTP( + status, + ); + if (E.isLeft(isUpdated)) throwErr(isUpdated.left); + return true; + } } diff --git a/packages/hoppscotch-backend/src/app.module.ts b/packages/hoppscotch-backend/src/app.module.ts index 4ba191ad2..f86db38c2 100644 --- a/packages/hoppscotch-backend/src/app.module.ts +++ b/packages/hoppscotch-backend/src/app.module.ts @@ -27,6 +27,9 @@ import { MailerModule } from './mailer/mailer.module'; import { PosthogModule } from './posthog/posthog.module'; import { ScheduleModule } from '@nestjs/schedule'; import { HealthModule } from './health/health.module'; +import { AccessTokenModule } from './access-token/access-token.module'; +import { UserLastActiveOnInterceptor } from './interceptors/user-last-active-on.interceptor'; +import { InfraTokenModule } from './infra-token/infra-token.module'; @Module({ imports: [ @@ -102,8 +105,13 @@ import { HealthModule } from './health/health.module'; PosthogModule, ScheduleModule.forRoot(), HealthModule, + AccessTokenModule, + InfraTokenModule, + ], + providers: [ + GQLComplexityPlugin, + { provide: 'APP_INTERCEPTOR', useClass: UserLastActiveOnInterceptor }, ], - providers: [GQLComplexityPlugin], controllers: [AppController], }) export class AppModule {} diff --git a/packages/hoppscotch-backend/src/auth/auth.controller.ts b/packages/hoppscotch-backend/src/auth/auth.controller.ts index b8cde8f3d..9d652db49 100644 --- a/packages/hoppscotch-backend/src/auth/auth.controller.ts +++ b/packages/hoppscotch-backend/src/auth/auth.controller.ts @@ -7,6 +7,7 @@ import { Request, Res, UseGuards, + UseInterceptors, } from '@nestjs/common'; import { AuthService } from './auth.service'; import { SignInMagicDto } from './dto/signin-magic.dto'; @@ -27,6 +28,7 @@ import { SkipThrottle } from '@nestjs/throttler'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { ConfigService } from '@nestjs/config'; import { throwHTTPErr } from 'src/utils'; +import { UserLastLoginInterceptor } from 'src/interceptors/user-last-login.interceptor'; @UseGuards(ThrottlerBehindProxyGuard) @Controller({ path: 'auth', version: '1' }) @@ -110,6 +112,7 @@ export class AuthController { @Get('google/callback') @SkipThrottle() @UseGuards(GoogleSSOGuard) + @UseInterceptors(UserLastLoginInterceptor) async googleAuthRedirect(@Request() req, @Res() res) { const authTokens = await this.authService.generateAuthTokens(req.user.uid); if (E.isLeft(authTokens)) throwHTTPErr(authTokens.left); @@ -135,6 +138,7 @@ export class AuthController { @Get('github/callback') @SkipThrottle() @UseGuards(GithubSSOGuard) + @UseInterceptors(UserLastLoginInterceptor) async githubAuthRedirect(@Request() req, @Res() res) { const authTokens = await this.authService.generateAuthTokens(req.user.uid); if (E.isLeft(authTokens)) throwHTTPErr(authTokens.left); @@ -160,6 +164,7 @@ export class AuthController { @Get('microsoft/callback') @SkipThrottle() @UseGuards(MicrosoftSSOGuard) + @UseInterceptors(UserLastLoginInterceptor) async microsoftAuthRedirect(@Request() req, @Res() res) { const authTokens = await this.authService.generateAuthTokens(req.user.uid); if (E.isLeft(authTokens)) throwHTTPErr(authTokens.left); diff --git a/packages/hoppscotch-backend/src/auth/auth.service.spec.ts b/packages/hoppscotch-backend/src/auth/auth.service.spec.ts index c8979518b..3df559b86 100644 --- a/packages/hoppscotch-backend/src/auth/auth.service.spec.ts +++ b/packages/hoppscotch-backend/src/auth/auth.service.spec.ts @@ -51,6 +51,8 @@ const user: AuthUser = { photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute', isAdmin: false, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, currentGQLSession: {}, currentRESTSession: {}, @@ -172,9 +174,11 @@ describe('verifyMagicLinkTokens', () => { // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); + mockUser.updateUserRefreshToken.mockResolvedValueOnce(E.right(user)); // deletePasswordlessVerificationToken mockPrisma.verificationToken.delete.mockResolvedValueOnce(passwordlessData); + // usersService.updateUserLastLoggedOn + mockUser.updateUserLastLoggedOn.mockResolvedValue(E.right(true)); const result = await authService.verifyMagicLinkTokens(magicLinkVerify); expect(result).toEqualRight({ @@ -197,9 +201,11 @@ describe('verifyMagicLinkTokens', () => { // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); + mockUser.updateUserRefreshToken.mockResolvedValueOnce(E.right(user)); // deletePasswordlessVerificationToken mockPrisma.verificationToken.delete.mockResolvedValueOnce(passwordlessData); + // usersService.updateUserLastLoggedOn + mockUser.updateUserLastLoggedOn.mockResolvedValue(E.right(true)); const result = await authService.verifyMagicLinkTokens(magicLinkVerify); expect(result).toEqualRight({ @@ -239,7 +245,7 @@ describe('verifyMagicLinkTokens', () => { // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + mockUser.updateUserRefreshToken.mockResolvedValueOnce( E.left(USER_NOT_FOUND), ); @@ -264,7 +270,7 @@ describe('verifyMagicLinkTokens', () => { // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); + mockUser.updateUserRefreshToken.mockResolvedValueOnce(E.right(user)); // deletePasswordlessVerificationToken mockPrisma.verificationToken.delete.mockRejectedValueOnce('RecordNotFound'); @@ -280,7 +286,7 @@ describe('generateAuthTokens', () => { test('Should successfully generate tokens with valid inputs', async () => { mockJWT.sign.mockReturnValue(user.refreshToken); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce(E.right(user)); + mockUser.updateUserRefreshToken.mockResolvedValueOnce(E.right(user)); const result = await authService.generateAuthTokens(user.uid); expect(result).toEqualRight({ @@ -292,7 +298,7 @@ describe('generateAuthTokens', () => { test('Should throw USER_NOT_FOUND when updating refresh tokens fails', async () => { mockJWT.sign.mockReturnValue(user.refreshToken); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + mockUser.updateUserRefreshToken.mockResolvedValueOnce( E.left(USER_NOT_FOUND), ); @@ -319,7 +325,7 @@ describe('refreshAuthTokens', () => { // generateAuthTokens mockJWT.sign.mockReturnValue(user.refreshToken); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + mockUser.updateUserRefreshToken.mockResolvedValueOnce( E.left(USER_NOT_FOUND), ); @@ -348,7 +354,7 @@ describe('refreshAuthTokens', () => { // generateAuthTokens mockJWT.sign.mockReturnValue('sdhjcbjsdhcbshjdcb'); // UpdateUserRefreshToken - mockUser.UpdateUserRefreshToken.mockResolvedValueOnce( + mockUser.updateUserRefreshToken.mockResolvedValueOnce( E.right({ ...user, refreshToken: 'sdhjcbjsdhcbshjdcb', diff --git a/packages/hoppscotch-backend/src/auth/auth.service.ts b/packages/hoppscotch-backend/src/auth/auth.service.ts index 9f5db2ec7..c04bdb59f 100644 --- a/packages/hoppscotch-backend/src/auth/auth.service.ts +++ b/packages/hoppscotch-backend/src/auth/auth.service.ts @@ -112,7 +112,7 @@ export class AuthService { const refreshTokenHash = await argon2.hash(refreshToken); - const updatedUser = await this.usersService.UpdateUserRefreshToken( + const updatedUser = await this.usersService.updateUserRefreshToken( refreshTokenHash, userUid, ); @@ -320,6 +320,8 @@ export class AuthService { statusCode: HttpStatus.NOT_FOUND, }); + this.usersService.updateUserLastLoggedOn(passwordlessTokens.value.userUid); + return E.right(tokens.right); } diff --git a/packages/hoppscotch-backend/src/auth/helper.ts b/packages/hoppscotch-backend/src/auth/helper.ts index bd2a9fcfd..9c5c051d6 100644 --- a/packages/hoppscotch-backend/src/auth/helper.ts +++ b/packages/hoppscotch-backend/src/auth/helper.ts @@ -52,13 +52,13 @@ export const authCookieHandler = ( res.cookie(AuthTokenType.ACCESS_TOKEN, authTokens.access_token, { httpOnly: true, - secure: true, + secure: configService.get('ALLOW_SECURE_COOKIES') === 'true', sameSite: 'lax', maxAge: accessTokenValidity, }); res.cookie(AuthTokenType.REFRESH_TOKEN, authTokens.refresh_token, { httpOnly: true, - secure: true, + secure: configService.get('ALLOW_SECURE_COOKIES') === 'true', sameSite: 'lax', maxAge: refreshTokenValidity, }); diff --git a/packages/hoppscotch-backend/src/decorators/bearer-token.decorator.ts b/packages/hoppscotch-backend/src/decorators/bearer-token.decorator.ts new file mode 100644 index 000000000..648ed549f --- /dev/null +++ b/packages/hoppscotch-backend/src/decorators/bearer-token.decorator.ts @@ -0,0 +1,15 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +/** + ** Decorator to fetch refresh_token from cookie + */ +export const BearerToken = createParamDecorator( + (data: unknown, context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + + // authorization token will be "Bearer " + const authorization = request.headers['authorization']; + // Remove "Bearer " and return the token only + return authorization.split(' ')[1]; + }, +); diff --git a/packages/hoppscotch-backend/src/errors.ts b/packages/hoppscotch-backend/src/errors.ts index 208a80464..1d424c066 100644 --- a/packages/hoppscotch-backend/src/errors.ts +++ b/packages/hoppscotch-backend/src/errors.ts @@ -678,6 +678,19 @@ export const MAILER_SMTP_URL_UNDEFINED = 'mailer/smtp_url_undefined' as const; export const MAILER_FROM_ADDRESS_UNDEFINED = 'mailer/from_address_undefined' as const; +/** + * MAILER_SMTP_USER environment variable is not defined + * (MailerModule) + */ +export const MAILER_SMTP_USER_UNDEFINED = 'mailer/smtp_user_undefined' as const; + +/** + * MAILER_SMTP_PASSWORD environment variable is not defined + * (MailerModule) + */ +export const MAILER_SMTP_PASSWORD_UNDEFINED = + 'mailer/smtp_password_undefined' as const; + /** * SharedRequest invalid request JSON format * (ShortcodeService) @@ -761,3 +774,82 @@ export const POSTHOG_CLIENT_NOT_INITIALIZED = 'posthog/client_not_initialized'; * Inputs supplied are invalid */ export const INVALID_PARAMS = 'invalid_parameters' as const; + +/** + * The provided label for the access-token is short (less than 3 characters) + * (AccessTokenService) + */ +export const ACCESS_TOKEN_LABEL_SHORT = 'access_token/label_too_short'; + +/** + * The provided expiryInDays value is not valid + * (AccessTokenService) + */ +export const ACCESS_TOKEN_EXPIRY_INVALID = 'access_token/expiry_days_invalid'; + +/** + * The provided PAT ID is invalid + * (AccessTokenService) + */ +export const ACCESS_TOKEN_NOT_FOUND = 'access_token/access_token_not_found'; + +/** + * AccessTokens is expired + * (AccessTokenService) + */ +export const ACCESS_TOKEN_EXPIRED = 'TOKEN_EXPIRED'; + +/** + * AccessTokens is invalid + * (AccessTokenService) + */ +export const ACCESS_TOKEN_INVALID = 'TOKEN_INVALID'; + +/** + * AccessTokens is invalid + * (AccessTokenService) + */ +export const ACCESS_TOKENS_INVALID_DATA_ID = 'INVALID_ID'; + +/** + * The provided label for the infra-token is short (less than 3 characters) + * (InfraTokenService) + */ +export const INFRA_TOKEN_LABEL_SHORT = 'infra_token/label_too_short'; + +/** + * The provided expiryInDays value is not valid + * (InfraTokenService) + */ +export const INFRA_TOKEN_EXPIRY_INVALID = 'infra_token/expiry_days_invalid'; + +/** + * The provided Infra Token ID is invalid + * (InfraTokenService) + */ +export const INFRA_TOKEN_NOT_FOUND = 'infra_token/infra_token_not_found'; + +/** + * Authorization missing in header (Check 'Authorization' Header) + * (InfraTokenGuard) + */ +export const INFRA_TOKEN_HEADER_MISSING = + 'infra_token/authorization_token_missing'; + +/** + * Infra Token is invalid + * (InfraTokenGuard) + */ +export const INFRA_TOKEN_INVALID_TOKEN = 'infra_token/invalid_token'; + +/** + * Infra Token is expired + * (InfraTokenGuard) + */ +export const INFRA_TOKEN_EXPIRED = 'infra_token/expired'; + +/** + * Token creator not found + * (InfraTokenService) + */ +export const INFRA_TOKEN_CREATOR_NOT_FOUND = 'infra_token/creator_not_found'; diff --git a/packages/hoppscotch-backend/src/gql-schema.ts b/packages/hoppscotch-backend/src/gql-schema.ts index bd2434e2f..87d6dffd0 100644 --- a/packages/hoppscotch-backend/src/gql-schema.ts +++ b/packages/hoppscotch-backend/src/gql-schema.ts @@ -28,6 +28,8 @@ import { UserEnvsUserResolver } from './user-environment/user.resolver'; import { UserHistoryUserResolver } from './user-history/user.resolver'; import { UserSettingsUserResolver } from './user-settings/user.resolver'; import { InfraResolver } from './admin/infra.resolver'; +import { InfraConfigResolver } from './infra-config/infra-config.resolver'; +import { InfraTokenResolver } from './infra-token/infra-token.resolver'; /** * All the resolvers present in the application. @@ -58,6 +60,8 @@ const RESOLVERS = [ UserRequestUserCollectionResolver, UserSettingsResolver, UserSettingsUserResolver, + InfraConfigResolver, + InfraTokenResolver, ]; /** diff --git a/packages/hoppscotch-backend/src/guards/infra-token.guard.ts b/packages/hoppscotch-backend/src/guards/infra-token.guard.ts new file mode 100644 index 000000000..e627b3fcb --- /dev/null +++ b/packages/hoppscotch-backend/src/guards/infra-token.guard.ts @@ -0,0 +1,47 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { DateTime } from 'luxon'; +import { + INFRA_TOKEN_EXPIRED, + INFRA_TOKEN_HEADER_MISSING, + INFRA_TOKEN_INVALID_TOKEN, +} from 'src/errors'; + +@Injectable() +export class InfraTokenGuard implements CanActivate { + constructor(private readonly prisma: PrismaService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const authorization = request.headers['authorization']; + + if (!authorization) + throw new UnauthorizedException(INFRA_TOKEN_HEADER_MISSING); + + if (!authorization.startsWith('Bearer ')) + throw new UnauthorizedException(INFRA_TOKEN_INVALID_TOKEN); + + const token = authorization.split(' ')[1]; + + if (!token) throw new UnauthorizedException(INFRA_TOKEN_INVALID_TOKEN); + + const infraToken = await this.prisma.infraToken.findUnique({ + where: { token }, + }); + + if (infraToken === null) + throw new UnauthorizedException(INFRA_TOKEN_INVALID_TOKEN); + + const currentTime = DateTime.now().toISO(); + if (currentTime > infraToken.expiresOn?.toISOString()) { + throw new UnauthorizedException(INFRA_TOKEN_EXPIRED); + } + + return true; + } +} diff --git a/packages/hoppscotch-backend/src/guards/rest-pat-auth.guard.ts b/packages/hoppscotch-backend/src/guards/rest-pat-auth.guard.ts new file mode 100644 index 000000000..d59e77af6 --- /dev/null +++ b/packages/hoppscotch-backend/src/guards/rest-pat-auth.guard.ts @@ -0,0 +1,48 @@ +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common'; +import { Request } from 'express'; +import { AccessTokenService } from 'src/access-token/access-token.service'; +import * as E from 'fp-ts/Either'; +import { DateTime } from 'luxon'; +import { ACCESS_TOKEN_EXPIRED, ACCESS_TOKEN_INVALID } from 'src/errors'; +import { createCLIErrorResponse } from 'src/access-token/helper'; +@Injectable() +export class PATAuthGuard implements CanActivate { + constructor(private accessTokenService: AccessTokenService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + if (!token) { + throw new BadRequestException( + createCLIErrorResponse(ACCESS_TOKEN_INVALID), + ); + } + + const userAccessToken = await this.accessTokenService.getUserPAT(token); + if (E.isLeft(userAccessToken)) + throw new BadRequestException( + createCLIErrorResponse(ACCESS_TOKEN_INVALID), + ); + request.user = userAccessToken.right.user; + + const accessToken = userAccessToken.right; + if (accessToken.expiresOn === null) return true; + + const today = DateTime.now().toISO(); + if (accessToken.expiresOn.toISOString() > today) return true; + + throw new BadRequestException( + createCLIErrorResponse(ACCESS_TOKEN_EXPIRED), + ); + } + + private extractTokenFromHeader(request: Request): string | undefined { + const [type, token] = request.headers.authorization?.split(' ') ?? []; + return type === 'Bearer' ? token : undefined; + } +} diff --git a/packages/hoppscotch-backend/src/infra-config/helper.ts b/packages/hoppscotch-backend/src/infra-config/helper.ts index 9238788a6..f5909873d 100644 --- a/packages/hoppscotch-backend/src/infra-config/helper.ts +++ b/packages/hoppscotch-backend/src/infra-config/helper.ts @@ -33,10 +33,17 @@ const AuthProviderConfigurations = { InfraConfigEnum.MICROSOFT_SCOPE, InfraConfigEnum.MICROSOFT_TENANT, ], - [AuthProvider.EMAIL]: [ - InfraConfigEnum.MAILER_SMTP_URL, - InfraConfigEnum.MAILER_ADDRESS_FROM, - ], + [AuthProvider.EMAIL]: !!process.env.MAILER_USE_CUSTOM_CONFIGS + ? [ + InfraConfigEnum.MAILER_SMTP_HOST, + InfraConfigEnum.MAILER_SMTP_PORT, + InfraConfigEnum.MAILER_SMTP_SECURE, + InfraConfigEnum.MAILER_SMTP_USER, + InfraConfigEnum.MAILER_SMTP_PASSWORD, + InfraConfigEnum.MAILER_TLS_REJECT_UNAUTHORIZED, + InfraConfigEnum.MAILER_ADDRESS_FROM, + ] + : [InfraConfigEnum.MAILER_SMTP_URL, InfraConfigEnum.MAILER_ADDRESS_FROM], }; /** @@ -75,6 +82,14 @@ export async function getDefaultInfraConfigs(): Promise< // Prepare rows for 'infra_config' table with default values (from .env) for each 'name' const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [ + { + name: InfraConfigEnum.MAILER_SMTP_ENABLE, + value: process.env.MAILER_SMTP_ENABLE ?? 'true', + }, + { + name: InfraConfigEnum.MAILER_USE_CUSTOM_CONFIGS, + value: process.env.MAILER_USE_CUSTOM_CONFIGS ?? 'false', + }, { name: InfraConfigEnum.MAILER_SMTP_URL, value: process.env.MAILER_SMTP_URL, @@ -83,6 +98,30 @@ export async function getDefaultInfraConfigs(): Promise< name: InfraConfigEnum.MAILER_ADDRESS_FROM, value: process.env.MAILER_ADDRESS_FROM, }, + { + name: InfraConfigEnum.MAILER_SMTP_HOST, + value: process.env.MAILER_SMTP_HOST, + }, + { + name: InfraConfigEnum.MAILER_SMTP_PORT, + value: process.env.MAILER_SMTP_PORT, + }, + { + name: InfraConfigEnum.MAILER_SMTP_SECURE, + value: process.env.MAILER_SMTP_SECURE, + }, + { + name: InfraConfigEnum.MAILER_SMTP_USER, + value: process.env.MAILER_SMTP_USER, + }, + { + name: InfraConfigEnum.MAILER_SMTP_PASSWORD, + value: process.env.MAILER_SMTP_PASSWORD, + }, + { + name: InfraConfigEnum.MAILER_TLS_REJECT_UNAUTHORIZED, + value: process.env.MAILER_TLS_REJECT_UNAUTHORIZED, + }, { name: InfraConfigEnum.GOOGLE_CLIENT_ID, value: process.env.GOOGLE_CLIENT_ID, diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts index cfb0ee3a2..71eb26f7c 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common'; import { InfraConfigService } from './infra-config.service'; import { PrismaModule } from 'src/prisma/prisma.module'; import { SiteController } from './infra-config.controller'; +import { InfraConfigResolver } from './infra-config.resolver'; @Module({ imports: [PrismaModule], - providers: [InfraConfigService], + providers: [InfraConfigResolver, InfraConfigService], exports: [InfraConfigService], controllers: [SiteController], }) diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.resolver.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.resolver.ts new file mode 100644 index 000000000..4f062abdc --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.resolver.ts @@ -0,0 +1,20 @@ +import { UseGuards } from '@nestjs/common'; +import { Query, Resolver } from '@nestjs/graphql'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { InfraConfig } from './infra-config.model'; +import { InfraConfigService } from './infra-config.service'; +import { GqlAuthGuard } from 'src/guards/gql-auth.guard'; + +@UseGuards(GqlThrottlerGuard) +@Resolver(() => InfraConfig) +export class InfraConfigResolver { + constructor(private infraConfigService: InfraConfigService) {} + + @Query(() => Boolean, { + description: 'Check if the SMTP is enabled or not', + }) + @UseGuards(GqlAuthGuard) + isSMTPEnabled() { + return this.infraConfigService.isSMTPEnabled(); + } +} diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts index a6e5f8186..168f37546 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts @@ -43,6 +43,7 @@ export class InfraConfigService implements OnModuleInit { InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION, InfraConfigEnum.ANALYTICS_USER_ID, InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP, + InfraConfigEnum.MAILER_SMTP_ENABLE, ]; // Following fields can not be fetched by `infraConfigs` Query. Use dedicated queries for these fields instead. EXCLUDE_FROM_FETCH_CONFIGS = [ @@ -196,7 +197,20 @@ export class InfraConfigService implements OnModuleInit { configMap.MICROSOFT_TENANT ); case AuthProvider.EMAIL: - return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM; + if (configMap.MAILER_SMTP_ENABLE !== 'true') return false; + if (configMap.MAILER_USE_CUSTOM_CONFIGS === 'true') { + return ( + configMap.MAILER_SMTP_HOST && + configMap.MAILER_SMTP_PORT && + configMap.MAILER_SMTP_SECURE && + configMap.MAILER_SMTP_USER && + configMap.MAILER_SMTP_PASSWORD && + configMap.MAILER_TLS_REJECT_UNAUTHORIZED && + configMap.MAILER_ADDRESS_FROM + ); + } else { + return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM; + } default: return false; } @@ -218,6 +232,47 @@ export class InfraConfigService implements OnModuleInit { return E.right(isUpdated.right.value === 'true'); } + /** + * Enable or Disable SMTP + * @param status Status to enable or disable + * @returns Either true or an error + */ + async enableAndDisableSMTP(status: ServiceStatus) { + const isUpdated = await this.toggleServiceStatus( + InfraConfigEnum.MAILER_SMTP_ENABLE, + status, + true, + ); + if (E.isLeft(isUpdated)) return E.left(isUpdated.left); + + if (status === ServiceStatus.DISABLE) { + this.enableAndDisableSSO([{ provider: AuthProvider.EMAIL, status }]); + } + return E.right(true); + } + + /** + * Enable or Disable Service (i.e. ALLOW_AUDIT_LOGS, ALLOW_ANALYTICS_COLLECTION, ALLOW_DOMAIN_WHITELISTING, SITE_PROTECTION) + * @param configName Name of the InfraConfigEnum + * @param status Status to enable or disable + * @param restartEnabled If true, restart the app after updating the InfraConfig + * @returns Either true or an error + */ + async toggleServiceStatus( + configName: InfraConfigEnum, + status: ServiceStatus, + restartEnabled = false, + ) { + const isUpdated = await this.update( + configName, + status === ServiceStatus.ENABLE ? 'true' : 'false', + restartEnabled, + ); + if (E.isLeft(isUpdated)) return E.left(isUpdated.left); + + return E.right(true); + } + /** * Enable or Disable SSO for login/signup * @param provider Auth Provider to enable or disable @@ -316,6 +371,16 @@ export class InfraConfigService implements OnModuleInit { .split(','); } + /** + * Check if SMTP is enabled or not + * @returns boolean + */ + isSMTPEnabled() { + return ( + this.configService.get('INFRA.MAILER_SMTP_ENABLE') === 'true' + ); + } + /** * Reset all the InfraConfigs to their default values (from .env) */ @@ -363,6 +428,20 @@ export class InfraConfigService implements OnModuleInit { ) { for (let i = 0; i < infraConfigs.length; i++) { switch (infraConfigs[i].name) { + case InfraConfigEnum.MAILER_SMTP_ENABLE: + if ( + infraConfigs[i].value !== 'true' && + infraConfigs[i].value !== 'false' + ) + return E.left(INFRA_CONFIG_INVALID_INPUT); + break; + case InfraConfigEnum.MAILER_USE_CUSTOM_CONFIGS: + if ( + infraConfigs[i].value !== 'true' && + infraConfigs[i].value !== 'false' + ) + return E.left(INFRA_CONFIG_INVALID_INPUT); + break; case InfraConfigEnum.MAILER_SMTP_URL: const isValidUrl = validateSMTPUrl(infraConfigs[i].value); if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT); @@ -371,6 +450,32 @@ export class InfraConfigService implements OnModuleInit { const isValidEmail = validateSMTPEmail(infraConfigs[i].value); if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT); break; + case InfraConfigEnum.MAILER_SMTP_HOST: + if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); + break; + case InfraConfigEnum.MAILER_SMTP_PORT: + if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); + break; + case InfraConfigEnum.MAILER_SMTP_SECURE: + if ( + infraConfigs[i].value !== 'true' && + infraConfigs[i].value !== 'false' + ) + return E.left(INFRA_CONFIG_INVALID_INPUT); + break; + case InfraConfigEnum.MAILER_SMTP_USER: + if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); + break; + case InfraConfigEnum.MAILER_SMTP_PASSWORD: + if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); + break; + case InfraConfigEnum.MAILER_TLS_REJECT_UNAUTHORIZED: + if ( + infraConfigs[i].value !== 'true' && + infraConfigs[i].value !== 'false' + ) + return E.left(INFRA_CONFIG_INVALID_INPUT); + break; case InfraConfigEnum.GOOGLE_CLIENT_ID: if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); break; diff --git a/packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts b/packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts new file mode 100644 index 000000000..67b12e5c9 --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts @@ -0,0 +1,248 @@ +import { + Body, + Controller, + Delete, + Get, + HttpStatus, + Param, + Patch, + Post, + Query, + UseGuards, + UseInterceptors, +} from '@nestjs/common'; +import { plainToInstance } from 'class-transformer'; +import { AdminService } from 'src/admin/admin.service'; +import { InfraTokenGuard } from 'src/guards/infra-token.guard'; +import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard'; +import { + DeleteUserInvitationRequest, + DeleteUserInvitationResponse, + ExceptionResponse, + GetUserInvitationResponse, + GetUsersRequestQuery, + GetUserResponse, + UpdateUserRequest, + UpdateUserAdminStatusRequest, + UpdateUserAdminStatusResponse, + CreateUserInvitationRequest, + CreateUserInvitationResponse, +} from './request-response.dto'; +import * as E from 'fp-ts/Either'; +import * as O from 'fp-ts/Option'; +import { OffsetPaginationArgs } from 'src/types/input-types.args'; +import { + ApiBadRequestResponse, + ApiCreatedResponse, + ApiNotFoundResponse, + ApiOkResponse, + ApiSecurity, + ApiTags, +} from '@nestjs/swagger'; +import { throwHTTPErr } from 'src/utils'; +import { UserService } from 'src/user/user.service'; +import { + INFRA_TOKEN_CREATOR_NOT_FOUND, + USER_NOT_FOUND, + USERS_NOT_FOUND, +} from 'src/errors'; +import { InfraTokenService } from './infra-token.service'; +import { InfraTokenInterceptor } from 'src/interceptors/infra-token.interceptor'; +import { BearerToken } from 'src/decorators/bearer-token.decorator'; + +@ApiTags('User Management API') +@ApiSecurity('infra-token') +@UseGuards(ThrottlerBehindProxyGuard, InfraTokenGuard) +@UseInterceptors(InfraTokenInterceptor) +@Controller({ path: 'infra', version: '1' }) +export class InfraTokensController { + constructor( + private readonly infraTokenService: InfraTokenService, + private readonly adminService: AdminService, + private readonly userService: UserService, + ) {} + + @Post('user-invitations') + @ApiCreatedResponse({ + description: 'Create a user invitation', + type: CreateUserInvitationResponse, + }) + @ApiBadRequestResponse({ type: ExceptionResponse }) + @ApiNotFoundResponse({ type: ExceptionResponse }) + async createUserInvitation( + @BearerToken() token: string, + @Body() dto: CreateUserInvitationRequest, + ) { + const createdInvitations = + await this.infraTokenService.createUserInvitation(token, dto); + + if (E.isLeft(createdInvitations)) { + const statusCode = + (createdInvitations.left as string) === INFRA_TOKEN_CREATOR_NOT_FOUND + ? HttpStatus.NOT_FOUND + : HttpStatus.BAD_REQUEST; + + throwHTTPErr({ message: createdInvitations.left, statusCode }); + } + + return plainToInstance( + CreateUserInvitationResponse, + { invitationLink: process.env.VITE_BASE_URL }, + { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }, + ); + } + + @Get('user-invitations') + @ApiOkResponse({ + description: 'Get pending user invitations', + type: [GetUserInvitationResponse], + }) + async getPendingUserInvitation( + @Query() paginationQuery: OffsetPaginationArgs, + ) { + const pendingInvitedUsers = await this.adminService.fetchInvitedUsers( + paginationQuery, + ); + + return plainToInstance(GetUserInvitationResponse, pendingInvitedUsers, { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }); + } + + @Delete('user-invitations') + @ApiOkResponse({ + description: 'Delete a pending user invitation', + type: DeleteUserInvitationResponse, + }) + @ApiBadRequestResponse({ type: ExceptionResponse }) + async deleteUserInvitation(@Body() dto: DeleteUserInvitationRequest) { + const isDeleted = await this.adminService.revokeUserInvitations( + dto.inviteeEmails, + ); + + if (E.isLeft(isDeleted)) { + throwHTTPErr({ + message: isDeleted.left, + statusCode: HttpStatus.BAD_REQUEST, + }); + } + + return plainToInstance( + DeleteUserInvitationResponse, + { message: isDeleted.right }, + { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }, + ); + } + + @Get('users') + @ApiOkResponse({ + description: 'Get users list', + type: [GetUserResponse], + }) + async getUsers(@Query() query: GetUsersRequestQuery) { + const users = await this.userService.fetchAllUsersV2(query.searchString, { + take: query.take, + skip: query.skip, + }); + + return plainToInstance(GetUserResponse, users, { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }); + } + + @Get('users/:uid') + @ApiOkResponse({ + description: 'Get user details', + type: GetUserResponse, + }) + @ApiNotFoundResponse({ type: ExceptionResponse }) + async getUser(@Param('uid') uid: string) { + const user = await this.userService.findUserById(uid); + + if (O.isNone(user)) { + throwHTTPErr({ + message: USER_NOT_FOUND, + statusCode: HttpStatus.NOT_FOUND, + }); + } + + return plainToInstance(GetUserResponse, user.value, { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }); + } + + @Patch('users/:uid') + @ApiOkResponse({ + description: 'Update user display name', + type: GetUserResponse, + }) + @ApiBadRequestResponse({ type: ExceptionResponse }) + @ApiNotFoundResponse({ type: ExceptionResponse }) + async updateUser(@Param('uid') uid: string, @Body() body: UpdateUserRequest) { + const updatedUser = await this.userService.updateUserDisplayName( + uid, + body.displayName, + ); + + if (E.isLeft(updatedUser)) { + const statusCode = + (updatedUser.left as string) === USER_NOT_FOUND + ? HttpStatus.NOT_FOUND + : HttpStatus.BAD_REQUEST; + + throwHTTPErr({ message: updatedUser.left, statusCode }); + } + + return plainToInstance(GetUserResponse, updatedUser.right, { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }); + } + + @Patch('users/:uid/admin-status') + @ApiOkResponse({ + description: 'Update user admin status', + type: UpdateUserAdminStatusResponse, + }) + @ApiBadRequestResponse({ type: ExceptionResponse }) + @ApiNotFoundResponse({ type: ExceptionResponse }) + async updateUserAdminStatus( + @Param('uid') uid: string, + @Body() body: UpdateUserAdminStatusRequest, + ) { + let updatedUser; + + if (body.isAdmin) { + updatedUser = await this.adminService.makeUsersAdmin([uid]); + } else { + updatedUser = await this.adminService.demoteUsersByAdmin([uid]); + } + + if (E.isLeft(updatedUser)) { + const statusCode = + (updatedUser.left as string) === USERS_NOT_FOUND + ? HttpStatus.NOT_FOUND + : HttpStatus.BAD_REQUEST; + + throwHTTPErr({ message: updatedUser.left as string, statusCode }); + } + + return plainToInstance( + UpdateUserAdminStatusResponse, + { message: updatedUser.right }, + { + excludeExtraneousValues: true, + enableImplicitConversion: true, + }, + ); + } +} diff --git a/packages/hoppscotch-backend/src/infra-token/infra-token.model.ts b/packages/hoppscotch-backend/src/infra-token/infra-token.model.ts new file mode 100644 index 000000000..9790830fe --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-token/infra-token.model.ts @@ -0,0 +1,43 @@ +import { Field, ID, ObjectType } from '@nestjs/graphql'; + +@ObjectType() +export class InfraToken { + @Field(() => ID, { + description: 'ID of the infra token', + }) + id: string; + + @Field(() => String, { + description: 'Label of the infra token', + }) + label: string; + + @Field(() => Date, { + description: 'Date when the infra token was created', + }) + createdOn: Date; + + @Field(() => Date, { + description: 'Date when the infra token expires', + nullable: true, + }) + expiresOn: Date; + + @Field(() => Date, { + description: 'Date when the infra token was last used', + }) + lastUsedOn: Date; +} + +@ObjectType() +export class CreateInfraTokenResponse { + @Field(() => String, { + description: 'The infra token', + }) + token: string; + + @Field(() => InfraToken, { + description: 'Infra token info', + }) + info: InfraToken; +} diff --git a/packages/hoppscotch-backend/src/infra-token/infra-token.module.ts b/packages/hoppscotch-backend/src/infra-token/infra-token.module.ts new file mode 100644 index 000000000..a8e6f58c8 --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-token/infra-token.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { InfraTokenResolver } from './infra-token.resolver'; +import { InfraTokenService } from './infra-token.service'; +import { InfraTokensController } from './infra-token.controller'; +import { AdminModule } from 'src/admin/admin.module'; +import { UserModule } from 'src/user/user.module'; + +@Module({ + imports: [PrismaModule, AdminModule, UserModule], + controllers: [InfraTokensController], + providers: [InfraTokenResolver, InfraTokenService], +}) +export class InfraTokenModule {} diff --git a/packages/hoppscotch-backend/src/infra-token/infra-token.resolver.ts b/packages/hoppscotch-backend/src/infra-token/infra-token.resolver.ts new file mode 100644 index 000000000..c342732e0 --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-token/infra-token.resolver.ts @@ -0,0 +1,68 @@ +import { Args, ID, Mutation, Query, Resolver } from '@nestjs/graphql'; +import { CreateInfraTokenResponse, InfraToken } from './infra-token.model'; +import { UseGuards } from '@nestjs/common'; +import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; +import { InfraTokenService } from './infra-token.service'; +import { GqlAuthGuard } from 'src/guards/gql-auth.guard'; +import { GqlAdminGuard } from 'src/admin/guards/gql-admin.guard'; +import { OffsetPaginationArgs } from 'src/types/input-types.args'; +import { GqlAdmin } from 'src/admin/decorators/gql-admin.decorator'; +import { Admin } from 'src/admin/admin.model'; +import * as E from 'fp-ts/Either'; +import { throwErr } from 'src/utils'; + +@UseGuards(GqlThrottlerGuard) +@Resolver(() => InfraToken) +export class InfraTokenResolver { + constructor(private readonly infraTokenService: InfraTokenService) {} + + /* Query */ + + @Query(() => [InfraToken], { + description: 'Get list of infra tokens', + }) + @UseGuards(GqlAuthGuard, GqlAdminGuard) + infraTokens(@Args() args: OffsetPaginationArgs) { + return this.infraTokenService.getAll(args.take, args.skip); + } + + /* Mutations */ + + @Mutation(() => CreateInfraTokenResponse, { + description: 'Create a new infra token', + }) + @UseGuards(GqlAuthGuard, GqlAdminGuard) + async createInfraToken( + @GqlAdmin() admin: Admin, + @Args({ name: 'label', description: 'Label of the token' }) label: string, + @Args({ + name: 'expiryInDays', + description: 'Number of days the token is valid for', + nullable: true, + }) + expiryInDays: number, + ) { + const infraToken = await this.infraTokenService.create( + label, + expiryInDays, + admin, + ); + + if (E.isLeft(infraToken)) throwErr(infraToken.left); + return infraToken.right; + } + + @Mutation(() => Boolean, { + description: 'Revoke an infra token', + }) + @UseGuards(GqlAuthGuard, GqlAdminGuard) + async revokeInfraToken( + @Args({ name: 'id', type: () => ID, description: 'ID of the infra token' }) + id: string, + ) { + const res = await this.infraTokenService.revoke(id); + + if (E.isLeft(res)) throwErr(res.left); + return res.right; + } +} diff --git a/packages/hoppscotch-backend/src/infra-token/infra-token.service.ts b/packages/hoppscotch-backend/src/infra-token/infra-token.service.ts new file mode 100644 index 000000000..887dfb350 --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-token/infra-token.service.ts @@ -0,0 +1,160 @@ +import { Injectable } from '@nestjs/common'; +import { InfraToken as dbInfraToken } from '@prisma/client'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { CreateInfraTokenResponse, InfraToken } from './infra-token.model'; +import { calculateExpirationDate, isValidLength } from 'src/utils'; +import { Admin } from 'src/admin/admin.model'; +import { + INFRA_TOKEN_CREATOR_NOT_FOUND, + INFRA_TOKEN_EXPIRY_INVALID, + INFRA_TOKEN_LABEL_SHORT, + INFRA_TOKEN_NOT_FOUND, +} from 'src/errors'; +import * as E from 'fp-ts/Either'; +import { CreateUserInvitationRequest } from './request-response.dto'; +import { AdminService } from 'src/admin/admin.service'; + +@Injectable() +export class InfraTokenService { + constructor( + private readonly prisma: PrismaService, + private readonly adminService: AdminService, + ) {} + + TITLE_LENGTH = 3; + VALID_TOKEN_DURATIONS = [7, 30, 60, 90]; + + /** + * Validate the expiration date of the token + * + * @param expiresOn Number of days the token is valid for + * @returns Boolean indicating if the expiration date is valid + */ + private validateExpirationDate(expiresOn: null | number) { + if (expiresOn === null || this.VALID_TOKEN_DURATIONS.includes(expiresOn)) + return true; + return false; + } + + /** + * Typecast a database InfraToken to a InfraToken model + * @param dbInfraToken database InfraToken + * @returns InfraToken model + */ + private cast(dbInfraToken: dbInfraToken): InfraToken { + return { + id: dbInfraToken.id, + label: dbInfraToken.label, + createdOn: dbInfraToken.createdOn, + expiresOn: dbInfraToken.expiresOn, + lastUsedOn: dbInfraToken.updatedOn, + }; + } + + /** + * Fetch all infra tokens with pagination + * @param take take for pagination + * @param skip skip for pagination + * @returns List of InfraToken models + */ + async getAll(take = 10, skip = 0) { + const infraTokens = await this.prisma.infraToken.findMany({ + take, + skip, + orderBy: { createdOn: 'desc' }, + }); + + return infraTokens.map((token) => this.cast(token)); + } + + /** + * Create a new infra token + * @param label label of the token + * @param expiryInDays expiry duration of the token + * @param admin admin who created the token + * @returns Either of error message or CreateInfraTokenResponse + */ + async create(label: string, expiryInDays: number, admin: Admin) { + if (!isValidLength(label, this.TITLE_LENGTH)) { + return E.left(INFRA_TOKEN_LABEL_SHORT); + } + + if (!this.validateExpirationDate(expiryInDays ?? null)) { + return E.left(INFRA_TOKEN_EXPIRY_INVALID); + } + + const createdInfraToken = await this.prisma.infraToken.create({ + data: { + creatorUid: admin.uid, + label, + expiresOn: calculateExpirationDate(expiryInDays ?? null) ?? undefined, + }, + }); + + const res: CreateInfraTokenResponse = { + token: createdInfraToken.token, + info: this.cast(createdInfraToken), + }; + + return E.right(res); + } + + /** + * Revoke an infra token + * @param id ID of the infra token + * @returns Either of error or true + */ + async revoke(id: string) { + try { + await this.prisma.infraToken.delete({ + where: { id }, + }); + } catch (error) { + return E.left(INFRA_TOKEN_NOT_FOUND); + } + return E.right(true); + } + + /** + * Update the last used on of an infra token + * @param token token to update + * @returns Either of error or InfraToken + */ + async updateLastUsedOn(token: string) { + try { + const infraToken = await this.prisma.infraToken.update({ + where: { token }, + data: { updatedOn: new Date() }, + }); + return E.right(this.cast(infraToken)); + } catch (error) { + return E.left(INFRA_TOKEN_NOT_FOUND); + } + } + + /** + * Create a user invitation using an infra token + * @param token token used to create the invitation + * @param dto CreateUserInvitationRequest + * @returns Either of error or InvitedUser + */ + async createUserInvitation(token: string, dto: CreateUserInvitationRequest) { + const infraToken = await this.prisma.infraToken.findUnique({ + where: { token }, + }); + + const tokenCreator = await this.prisma.user.findUnique({ + where: { uid: infraToken.creatorUid }, + }); + if (!tokenCreator) return E.left(INFRA_TOKEN_CREATOR_NOT_FOUND); + + const invitedUser = await this.adminService.inviteUserToSignInViaEmail( + tokenCreator.uid, + tokenCreator.email, + dto.inviteeEmail, + ); + if (E.isLeft(invitedUser)) return E.left(invitedUser.left); + + return E.right(invitedUser); + } +} diff --git a/packages/hoppscotch-backend/src/infra-token/request-response.dto.ts b/packages/hoppscotch-backend/src/infra-token/request-response.dto.ts new file mode 100644 index 000000000..9bacfbf12 --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-token/request-response.dto.ts @@ -0,0 +1,115 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Expose, Transform, Type } from 'class-transformer'; +import { + ArrayMinSize, + IsArray, + IsBoolean, + IsEmail, + IsNotEmpty, + IsOptional, + IsString, + MinLength, +} from 'class-validator'; +import { OffsetPaginationArgs } from 'src/types/input-types.args'; + +// POST v1/infra/user-invitations +export class CreateUserInvitationRequest { + @Type(() => String) + @IsNotEmpty() + @ApiProperty() + inviteeEmail: string; +} +export class CreateUserInvitationResponse { + @ApiProperty() + @Expose() + invitationLink: string; +} + +// GET v1/infra/user-invitations +export class GetUserInvitationResponse { + @ApiProperty() + @Expose() + inviteeEmail: string; + + @ApiProperty() + @Expose() + invitedOn: Date; +} + +// DELETE v1/infra/user-invitations +export class DeleteUserInvitationRequest { + @IsArray() + @ArrayMinSize(1) + @Type(() => String) + @IsNotEmpty() + @ApiProperty() + inviteeEmails: string[]; +} +export class DeleteUserInvitationResponse { + @ApiProperty() + @Expose() + message: string; +} + +// POST v1/infra/users +export class GetUsersRequestQuery extends OffsetPaginationArgs { + @IsOptional() + @IsString() + @MinLength(1) + @ApiPropertyOptional() + searchString: string; +} +export class GetUserResponse { + @ApiProperty() + @Expose() + uid: string; + + @ApiProperty() + @Expose() + displayName: string; + + @ApiProperty() + @Expose() + email: string; + + @ApiProperty() + @Expose() + photoURL: string; + + @ApiProperty() + @Expose() + isAdmin: boolean; +} + +// PATCH v1/infra/users/:uid +export class UpdateUserRequest { + @IsOptional() + @IsString() + @MinLength(1) + @ApiPropertyOptional() + displayName: string; +} + +// PATCH v1/infra/users/:uid/admin-status +export class UpdateUserAdminStatusRequest { + @IsBoolean() + @IsNotEmpty() + @ApiProperty() + isAdmin: boolean; +} +export class UpdateUserAdminStatusResponse { + @ApiProperty() + @Expose() + message: string; +} + +// Used for Swagger doc only, in codebase throwHTTPErr function is used to throw errors +export class ExceptionResponse { + @ApiProperty() + @Expose() + message: string; + + @ApiProperty() + @Expose() + statusCode: number; +} diff --git a/packages/hoppscotch-backend/src/interceptors/access-token.interceptor.ts b/packages/hoppscotch-backend/src/interceptors/access-token.interceptor.ts new file mode 100644 index 000000000..b9e536094 --- /dev/null +++ b/packages/hoppscotch-backend/src/interceptors/access-token.interceptor.ts @@ -0,0 +1,36 @@ +import { + BadRequestException, + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable, map } from 'rxjs'; +import { AccessTokenService } from 'src/access-token/access-token.service'; +import * as E from 'fp-ts/Either'; +import { ACCESS_TOKEN_NOT_FOUND } from 'src/errors'; + +@Injectable() +export class AccessTokenInterceptor implements NestInterceptor { + constructor(private readonly accessTokenService: AccessTokenService) {} + + intercept(context: ExecutionContext, handler: CallHandler): Observable { + const req = context.switchToHttp().getRequest(); + const authHeader = req.headers.authorization; + const token = authHeader && authHeader.split(' ')[1]; + if (!token) { + throw new BadRequestException(ACCESS_TOKEN_NOT_FOUND); + } + + return handler.handle().pipe( + map(async (data) => { + const userAccessToken = + await this.accessTokenService.updateLastUsedForPAT(token); + if (E.isLeft(userAccessToken)) + throw new BadRequestException(userAccessToken.left); + + return data; + }), + ); + } +} diff --git a/packages/hoppscotch-backend/src/interceptors/infra-token.interceptor.ts b/packages/hoppscotch-backend/src/interceptors/infra-token.interceptor.ts new file mode 100644 index 000000000..bfddda748 --- /dev/null +++ b/packages/hoppscotch-backend/src/interceptors/infra-token.interceptor.ts @@ -0,0 +1,30 @@ +import { + BadRequestException, + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { INFRA_TOKEN_NOT_FOUND } from 'src/errors'; +import { InfraTokenService } from 'src/infra-token/infra-token.service'; + +@Injectable() +export class InfraTokenInterceptor implements NestInterceptor { + constructor(private readonly infraTokenService: InfraTokenService) {} + + intercept(context: ExecutionContext, handler: CallHandler): Observable { + const req = context.switchToHttp().getRequest(); + const authHeader = req.headers.authorization; + + if (!authHeader || !authHeader.startsWith('Bearer ')) { + throw new BadRequestException(INFRA_TOKEN_NOT_FOUND); + } + + const token = authHeader.split(' ')[1]; + + this.infraTokenService.updateLastUsedOn(token); + + return handler.handle(); + } +} diff --git a/packages/hoppscotch-backend/src/interceptors/user-last-active-on.interceptor.ts b/packages/hoppscotch-backend/src/interceptors/user-last-active-on.interceptor.ts new file mode 100644 index 000000000..faba05713 --- /dev/null +++ b/packages/hoppscotch-backend/src/interceptors/user-last-active-on.interceptor.ts @@ -0,0 +1,65 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common'; +import { GqlContextType, GqlExecutionContext } from '@nestjs/graphql'; +import { Observable, throwError } from 'rxjs'; +import { catchError, tap } from 'rxjs/operators'; +import { AuthUser } from 'src/types/AuthUser'; +import { UserService } from 'src/user/user.service'; + +@Injectable() +export class UserLastActiveOnInterceptor implements NestInterceptor { + constructor(private userService: UserService) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + if (context.getType() === 'http') { + return this.restHandler(context, next); + } else if (context.getType() === 'graphql') { + return this.graphqlHandler(context, next); + } + } + + restHandler(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + const user: AuthUser = request.user; + + return next.handle().pipe( + tap(() => { + if (user && typeof user === 'object') { + this.userService.updateUserLastActiveOn(user.uid); + } + }), + catchError((error) => { + if (user && typeof user === 'object') { + this.userService.updateUserLastActiveOn(user.uid); + } + return throwError(() => error); + }), + ); + } + + graphqlHandler( + context: ExecutionContext, + next: CallHandler, + ): Observable { + const contextObject = GqlExecutionContext.create(context).getContext(); + const user: AuthUser = contextObject?.req?.user; + + return next.handle().pipe( + tap(() => { + if (user && typeof user === 'object') { + this.userService.updateUserLastActiveOn(user.uid); + } + }), + catchError((error) => { + if (user && typeof user === 'object') { + this.userService.updateUserLastActiveOn(user.uid); + } + return throwError(() => error); + }), + ); + } +} diff --git a/packages/hoppscotch-backend/src/interceptors/user-last-login.interceptor.ts b/packages/hoppscotch-backend/src/interceptors/user-last-login.interceptor.ts new file mode 100644 index 000000000..db019d9e6 --- /dev/null +++ b/packages/hoppscotch-backend/src/interceptors/user-last-login.interceptor.ts @@ -0,0 +1,25 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { AuthUser } from 'src/types/AuthUser'; +import { UserService } from 'src/user/user.service'; + +@Injectable() +export class UserLastLoginInterceptor implements NestInterceptor { + constructor(private userService: UserService) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const user: AuthUser = context.switchToHttp().getRequest().user; + + return next.handle().pipe( + tap(() => { + this.userService.updateUserLastLoggedOn(user.uid); + }), + ); + } +} diff --git a/packages/hoppscotch-backend/src/mailer/helper.ts b/packages/hoppscotch-backend/src/mailer/helper.ts new file mode 100644 index 000000000..1095777d8 --- /dev/null +++ b/packages/hoppscotch-backend/src/mailer/helper.ts @@ -0,0 +1,59 @@ +import { TransportType } from '@nestjs-modules/mailer/dist/interfaces/mailer-options.interface'; +import { + MAILER_SMTP_PASSWORD_UNDEFINED, + MAILER_SMTP_URL_UNDEFINED, + MAILER_SMTP_USER_UNDEFINED, +} from 'src/errors'; +import { throwErr } from 'src/utils'; + +function isSMTPCustomConfigsEnabled(value) { + return value === 'true'; +} + +export function getMailerAddressFrom(env, config): string { + return ( + env.INFRA.MAILER_ADDRESS_FROM ?? + config.get('MAILER_ADDRESS_FROM') ?? + throwErr(MAILER_SMTP_URL_UNDEFINED) + ); +} + +export function getTransportOption(env, config): TransportType { + const useCustomConfigs = isSMTPCustomConfigsEnabled( + env.INFRA.MAILER_USE_CUSTOM_CONFIGS ?? + config.get('MAILER_USE_CUSTOM_CONFIGS'), + ); + + if (!useCustomConfigs) { + console.log('Using simple mailer configuration'); + return ( + env.INFRA.MAILER_SMTP_URL ?? + config.get('MAILER_SMTP_URL') ?? + throwErr(MAILER_SMTP_URL_UNDEFINED) + ); + } else { + console.log('Using advanced mailer configuration'); + return { + host: env.INFRA.MAILER_SMTP_HOST ?? config.get('MAILER_SMTP_HOST'), + port: +env.INFRA.MAILER_SMTP_PORT ?? +config.get('MAILER_SMTP_PORT'), + secure: + (env.INFRA.MAILER_SMTP_SECURE ?? config.get('MAILER_SMTP_SECURE')) === + 'true', + auth: { + user: + env.INFRA.MAILER_SMTP_USER ?? + config.get('MAILER_SMTP_USER') ?? + throwErr(MAILER_SMTP_USER_UNDEFINED), + pass: + env.INFRA.MAILER_SMTP_PASSWORD ?? + config.get('MAILER_SMTP_PASSWORD') ?? + throwErr(MAILER_SMTP_PASSWORD_UNDEFINED), + }, + tls: { + rejectUnauthorized: + (env.INFRA.MAILER_TLS_REJECT_UNAUTHORIZED ?? + config.get('MAILER_TLS_REJECT_UNAUTHORIZED')) === 'true', + }, + }; + } +} diff --git a/packages/hoppscotch-backend/src/mailer/mailer.module.ts b/packages/hoppscotch-backend/src/mailer/mailer.module.ts index a9d422d79..f8d19726f 100644 --- a/packages/hoppscotch-backend/src/mailer/mailer.module.ts +++ b/packages/hoppscotch-backend/src/mailer/mailer.module.ts @@ -2,13 +2,9 @@ import { Global, Module } from '@nestjs/common'; import { MailerModule as NestMailerModule } from '@nestjs-modules/mailer'; import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; import { MailerService } from './mailer.service'; -import { throwErr } from 'src/utils'; -import { - MAILER_FROM_ADDRESS_UNDEFINED, - MAILER_SMTP_URL_UNDEFINED, -} from 'src/errors'; import { ConfigService } from '@nestjs/config'; import { loadInfraConfiguration } from 'src/infra-config/helper'; +import { getMailerAddressFrom, getTransportOption } from './helper'; @Global() @Module({ @@ -18,24 +14,31 @@ import { loadInfraConfiguration } from 'src/infra-config/helper'; }) export class MailerModule { static async register() { + const config = new ConfigService(); const env = await loadInfraConfiguration(); - let mailerSmtpUrl = env.INFRA.MAILER_SMTP_URL; - let mailerAddressFrom = env.INFRA.MAILER_ADDRESS_FROM; - - if (!env.INFRA.MAILER_SMTP_URL || !env.INFRA.MAILER_ADDRESS_FROM) { - const config = new ConfigService(); - mailerSmtpUrl = config.get('MAILER_SMTP_URL'); - mailerAddressFrom = config.get('MAILER_ADDRESS_FROM'); + // If mailer SMTP is DISABLED, return the module without any configuration (service, listener, etc.) + if (env.INFRA.MAILER_SMTP_ENABLE !== 'true') { + console.log('Mailer module is disabled'); + return { + module: MailerModule, + }; } + // If mailer is ENABLED, return the module with configuration (service, etc.) + + // Determine transport configuration based on custom config flag + let transportOption = getTransportOption(env, config); + // Get mailer address from environment or config + const mailerAddressFrom = getMailerAddressFrom(env, config); + return { module: MailerModule, imports: [ NestMailerModule.forRoot({ - transport: mailerSmtpUrl ?? throwErr(MAILER_SMTP_URL_UNDEFINED), + transport: transportOption, defaults: { - from: mailerAddressFrom ?? throwErr(MAILER_FROM_ADDRESS_UNDEFINED), + from: mailerAddressFrom, }, template: { dir: __dirname + '/templates', diff --git a/packages/hoppscotch-backend/src/mailer/mailer.service.ts b/packages/hoppscotch-backend/src/mailer/mailer.service.ts index f71d65fe1..ab2abd1f6 100644 --- a/packages/hoppscotch-backend/src/mailer/mailer.service.ts +++ b/packages/hoppscotch-backend/src/mailer/mailer.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Optional } from '@nestjs/common'; import { AdminUserInvitationMailDescription, MailDescription, @@ -7,10 +7,14 @@ import { import { throwErr } from 'src/utils'; import { EMAIL_FAILED } from 'src/errors'; import { MailerService as NestMailerService } from '@nestjs-modules/mailer'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class MailerService { - constructor(private readonly nestMailerService: NestMailerService) {} + constructor( + @Optional() private readonly nestMailerService: NestMailerService, + private readonly configService: ConfigService, + ) {} /** * Takes an input mail description and spits out the Email subject required for it @@ -42,6 +46,8 @@ export class MailerService { to: string, mailDesc: MailDescription | UserMagicLinkMailDescription, ) { + if (this.configService.get('INFRA.MAILER_SMTP_ENABLE') !== 'true') return; + try { await this.nestMailerService.sendMail({ to, @@ -50,6 +56,7 @@ export class MailerService { context: mailDesc.variables, }); } catch (error) { + console.log('Error from sendEmail:', error); return throwErr(EMAIL_FAILED); } } @@ -64,6 +71,8 @@ export class MailerService { to: string, mailDesc: AdminUserInvitationMailDescription, ) { + if (this.configService.get('INFRA.MAILER_SMTP_ENABLE') !== 'true') return; + try { const res = await this.nestMailerService.sendMail({ to, @@ -73,6 +82,7 @@ export class MailerService { }); return res; } catch (error) { + console.log('Error from sendUserInvitationEmail:', error); return throwErr(EMAIL_FAILED); } } diff --git a/packages/hoppscotch-backend/src/main.ts b/packages/hoppscotch-backend/src/main.ts index a32079c74..c8eed7724 100644 --- a/packages/hoppscotch-backend/src/main.ts +++ b/packages/hoppscotch-backend/src/main.ts @@ -2,11 +2,40 @@ import { NestFactory } from '@nestjs/core'; import { json } from 'express'; import { AppModule } from './app.module'; import * as cookieParser from 'cookie-parser'; -import { VersioningType } from '@nestjs/common'; +import { ValidationPipe, VersioningType } from '@nestjs/common'; import * as session from 'express-session'; import { emitGQLSchemaFile } from './gql-schema'; import { checkEnvironmentAuthProvider } from './utils'; import { ConfigService } from '@nestjs/config'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { InfraTokensController } from './infra-token/infra-token.controller'; +import { InfraTokenModule } from './infra-token/infra-token.module'; + +function setupSwagger(app) { + const swaggerDocPath = '/api-docs'; + + const config = new DocumentBuilder() + .setTitle('Hoppscotch API Documentation') + .setDescription('APIs for external integration') + .addApiKey( + { + type: 'apiKey', + name: 'Authorization', + in: 'header', + scheme: 'bearer', + bearerFormat: 'Bearer', + }, + 'infra-token', + ) + .build(); + + const document = SwaggerModule.createDocument(app, config, { + include: [InfraTokenModule], + }); + SwaggerModule.setup(swaggerDocPath, app, document, { + swaggerOptions: { persistAuthorization: true, ignoreGlobalPrefix: true }, + }); +} async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -53,6 +82,14 @@ async function bootstrap() { type: VersioningType.URI, }); app.use(cookieParser()); + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + }), + ); + + await setupSwagger(app); + await app.listen(configService.get('PORT') || 3170); // Graceful shutdown diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts index 7614fd0a4..a1f0c7efb 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.service.spec.ts @@ -48,6 +48,8 @@ const user: AuthUser = { photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute', isAdmin: false, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: createdOn, + lastActiveOn: createdOn, createdOn: createdOn, currentGQLSession: {}, currentRESTSession: {}, diff --git a/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts b/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts index e6a02fdb1..e86423053 100644 --- a/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts +++ b/packages/hoppscotch-backend/src/shortcode/shortcode.service.ts @@ -299,7 +299,10 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit { where: userEmail ? { User: { - email: userEmail, + email: { + equals: userEmail, + mode: 'insensitive', + }, }, } : undefined, diff --git a/packages/hoppscotch-backend/src/team-collection/helper.ts b/packages/hoppscotch-backend/src/team-collection/helper.ts index 1958a0a50..b88577126 100644 --- a/packages/hoppscotch-backend/src/team-collection/helper.ts +++ b/packages/hoppscotch-backend/src/team-collection/helper.ts @@ -1,3 +1,5 @@ +import { TeamRequest } from '@prisma/client'; + // Type of data returned from the query to obtain all search results export type SearchQueryReturnType = { id: string; @@ -12,3 +14,12 @@ export type ParentTreeQueryReturnType = { parentID: string; title: string; }; +// Type of data returned from the query to fetch collection details from CLI +export type GetCollectionResponse = { + id: string; + data: string | null; + title: string; + parentID: string | null; + folders: GetCollectionResponse[]; + requests: TeamRequest[]; +}; diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts index f5a2ccfb8..4c6b00ea6 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.resolver.ts @@ -331,6 +331,26 @@ export class TeamCollectionResolver { return updatedTeamCollection.right; } + @Mutation(() => Boolean, { + description: 'Duplicate a Team Collection', + }) + @UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard) + @RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR) + async duplicateTeamCollection( + @Args({ + name: 'collectionID', + description: 'ID of the collection', + }) + collectionID: string, + ) { + const duplicatedTeamCollection = + await this.teamCollectionService.duplicateTeamCollection(collectionID); + + if (E.isLeft(duplicatedTeamCollection)) + throwErr(duplicatedTeamCollection.left); + return duplicatedTeamCollection.right; + } + // Subscriptions @Subscription(() => TeamCollection, { diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts index 041e3858c..b21cc7640 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.service.spec.ts @@ -12,6 +12,7 @@ import { TEAM_COL_REORDERING_FAILED, TEAM_COL_SAME_NEXT_COLL, TEAM_INVALID_COLL_ID, + TEAM_MEMBER_NOT_FOUND, TEAM_NOT_OWNER, } from 'src/errors'; import { PrismaService } from 'src/prisma/prisma.service'; @@ -19,15 +20,18 @@ import { PubSubService } from 'src/pubsub/pubsub.service'; import { AuthUser } from 'src/types/AuthUser'; import { TeamCollectionService } from './team-collection.service'; import { TeamCollection } from './team-collection.model'; +import { TeamService } from 'src/team/team.service'; const mockPrisma = mockDeep(); const mockPubSub = mockDeep(); +const mockTeamService = mockDeep(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const teamCollectionService = new TeamCollectionService( mockPrisma, mockPubSub as any, + mockTeamService, ); const currentTime = new Date(); @@ -39,6 +43,8 @@ const user: AuthUser = { photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute', isAdmin: false, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, currentGQLSession: {}, currentRESTSession: {}, @@ -1738,3 +1744,63 @@ describe('updateTeamCollection', () => { }); //ToDo: write test cases for exportCollectionsToJSON + +describe('getCollectionForCLI', () => { + test('should throw TEAM_COLL_NOT_FOUND if collectionID is invalid', async () => { + mockPrisma.teamCollection.findUniqueOrThrow.mockRejectedValueOnce( + 'NotFoundError', + ); + + const result = await teamCollectionService.getCollectionForCLI( + 'invalidID', + user.uid, + ); + expect(result).toEqualLeft(TEAM_COLL_NOT_FOUND); + }); + + test('should throw TEAM_MEMBER_NOT_FOUND if user not in same team', async () => { + mockPrisma.teamCollection.findUniqueOrThrow.mockResolvedValueOnce( + rootTeamCollection, + ); + mockTeamService.getTeamMember.mockResolvedValue(null); + + const result = await teamCollectionService.getCollectionForCLI( + rootTeamCollection.id, + user.uid, + ); + expect(result).toEqualLeft(TEAM_MEMBER_NOT_FOUND); + }); + + // test('should return the TeamCollection data for CLI', async () => { + // mockPrisma.teamCollection.findUniqueOrThrow.mockResolvedValueOnce( + // rootTeamCollection, + // ); + // mockTeamService.getTeamMember.mockResolvedValue({ + // membershipID: 'sdc3sfdv', + // userUid: user.uid, + // role: TeamMemberRole.OWNER, + // }); + + // const result = await teamCollectionService.getCollectionForCLI( + // rootTeamCollection.id, + // user.uid, + // ); + // expect(result).toEqualRight({ + // id: rootTeamCollection.id, + // data: JSON.stringify(rootTeamCollection.data), + // title: rootTeamCollection.title, + // parentID: rootTeamCollection.parentID, + // folders: [ + // { + // id: childTeamCollection.id, + // data: JSON.stringify(childTeamCollection.data), + // title: childTeamCollection.title, + // parentID: childTeamCollection.parentID, + // folders: [], + // requests: [], + // }, + // ], + // requests: [], + // }); + // }); +}); diff --git a/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts b/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts index 76dd45411..093c25393 100644 --- a/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts +++ b/packages/hoppscotch-backend/src/team-collection/team-collection.service.ts @@ -18,23 +18,38 @@ import { TEAM_COL_SEARCH_FAILED, TEAM_REQ_PARENT_TREE_GEN_FAILED, TEAM_COLL_PARENT_TREE_GEN_FAILED, + TEAM_MEMBER_NOT_FOUND, } from '../errors'; import { PubSubService } from '../pubsub/pubsub.service'; -import { escapeSqlLikeString, isValidLength } from 'src/utils'; +import { + escapeSqlLikeString, + isValidLength, + transformCollectionData, +} from 'src/utils'; import * as E from 'fp-ts/Either'; import * as O from 'fp-ts/Option'; -import { Prisma, TeamCollection as DBTeamCollection } from '@prisma/client'; +import { + Prisma, + TeamCollection as DBTeamCollection, + TeamRequest, +} from '@prisma/client'; import { CollectionFolder } from 'src/types/CollectionFolder'; import { stringToJson } from 'src/utils'; import { CollectionSearchNode } from 'src/types/CollectionSearchNode'; -import { ParentTreeQueryReturnType, SearchQueryReturnType } from './helper'; +import { + GetCollectionResponse, + ParentTreeQueryReturnType, + SearchQueryReturnType, +} from './helper'; import { RESTError } from 'src/types/RESTError'; +import { TeamService } from 'src/team/team.service'; @Injectable() export class TeamCollectionService { constructor( private readonly prisma: PrismaService, private readonly pubsub: PubSubService, + private readonly teamService: TeamService, ) {} TITLE_LENGTH = 3; @@ -123,11 +138,13 @@ export class TeamCollectionService { }, }); + const data = transformCollectionData(collection.right.data); + const result: CollectionFolder = { name: collection.right.title, folders: childrenCollectionObjects, requests: requests.map((x) => x.request), - data: JSON.stringify(collection.right.data), + data, }; return E.right(result); @@ -298,11 +315,13 @@ export class TeamCollectionService { * @returns TeamCollection model */ private cast(teamCollection: DBTeamCollection): TeamCollection { + const data = transformCollectionData(teamCollection.data); + return { id: teamCollection.id, title: teamCollection.title, parentID: teamCollection.parentID, - data: !teamCollection.data ? null : JSON.stringify(teamCollection.data), + data, }; } @@ -1344,4 +1363,126 @@ export class TeamCollectionService { return E.left(TEAM_REQ_PARENT_TREE_GEN_FAILED); } } + + /** + * Get all requests in a collection + * + * @param collectionID The Collection ID + * @returns A list of all requests in the collection + */ + private async getAllRequestsInCollection(collectionID: string) { + const dbTeamRequests = await this.prisma.teamRequest.findMany({ + where: { + collectionID: collectionID, + }, + orderBy: { + orderIndex: 'asc', + }, + }); + + const teamRequests = dbTeamRequests.map((tr) => { + return { + id: tr.id, + collectionID: tr.collectionID, + teamID: tr.teamID, + title: tr.title, + request: JSON.stringify(tr.request), + }; + }); + + return teamRequests; + } + + /** + * Get Collection Tree for CLI + * + * @param parentID The parent Collection ID + * @returns Collection tree for CLI + */ + private async getCollectionTreeForCLI(parentID: string | null) { + const childCollections = await this.prisma.teamCollection.findMany({ + where: { parentID }, + orderBy: { orderIndex: 'asc' }, + }); + + const response: GetCollectionResponse[] = []; + + for (const collection of childCollections) { + const folder: GetCollectionResponse = { + id: collection.id, + data: collection.data === null ? null : JSON.stringify(collection.data), + title: collection.title, + parentID: collection.parentID, + folders: await this.getCollectionTreeForCLI(collection.id), + requests: await this.getAllRequestsInCollection(collection.id), + }; + + response.push(folder); + } + + return response; + } + + /** + * Get Collection for CLI + * + * @param collectionID The Collection ID + * @param userUid The User UID + * @returns An Either of the Collection details + */ + async getCollectionForCLI(collectionID: string, userUid: string) { + try { + const collection = await this.prisma.teamCollection.findUniqueOrThrow({ + where: { id: collectionID }, + }); + + const teamMember = await this.teamService.getTeamMember( + collection.teamID, + userUid, + ); + if (!teamMember) return E.left(TEAM_MEMBER_NOT_FOUND); + + return E.right({ + id: collection.id, + data: collection.data === null ? null : JSON.stringify(collection.data), + title: collection.title, + parentID: collection.parentID, + folders: await this.getCollectionTreeForCLI(collection.id), + requests: await this.getAllRequestsInCollection(collection.id), + }); + } catch (error) { + return E.left(TEAM_COLL_NOT_FOUND); + } + } + + /** + * Duplicate a Team Collection + * + * @param collectionID The Collection ID + * @returns Boolean of duplication status + */ + async duplicateTeamCollection(collectionID: string) { + const collection = await this.getCollection(collectionID); + if (E.isLeft(collection)) return E.left(TEAM_INVALID_COLL_ID); + + const collectionJSONObject = await this.exportCollectionToJSONObject( + collection.right.teamID, + collectionID, + ); + if (E.isLeft(collectionJSONObject)) return E.left(TEAM_INVALID_COLL_ID); + + const result = await this.importCollectionsFromJSON( + JSON.stringify([ + { + ...collectionJSONObject.right, + name: `${collection.right.title} - Duplicate`, + }, + ]), + collection.right.teamID, + collection.right.parentID, + ); + if (E.isLeft(result)) return E.left(result.left as string); + + return E.right(true); + } } diff --git a/packages/hoppscotch-backend/src/team-environments/team-environments.service.spec.ts b/packages/hoppscotch-backend/src/team-environments/team-environments.service.spec.ts index 466dddf69..a1f4d0a23 100644 --- a/packages/hoppscotch-backend/src/team-environments/team-environments.service.spec.ts +++ b/packages/hoppscotch-backend/src/team-environments/team-environments.service.spec.ts @@ -6,19 +6,24 @@ import { JSON_INVALID, TEAM_ENVIRONMENT_NOT_FOUND, TEAM_ENVIRONMENT_SHORT_NAME, + TEAM_MEMBER_NOT_FOUND, } from 'src/errors'; +import { TeamService } from 'src/team/team.service'; +import { TeamMemberRole } from 'src/team/team.model'; const mockPrisma = mockDeep(); const mockPubSub = { publish: jest.fn().mockResolvedValue(null), }; +const mockTeamService = mockDeep(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const teamEnvironmentsService = new TeamEnvironmentsService( mockPrisma, mockPubSub as any, + mockTeamService, ); const teamEnvironment = { @@ -380,4 +385,47 @@ describe('TeamEnvironmentsService', () => { expect(result).toEqual(0); }); }); + + describe('getTeamEnvironmentForCLI', () => { + test('should successfully return a TeamEnvironment with valid ID', async () => { + mockPrisma.teamEnvironment.findFirstOrThrow.mockResolvedValueOnce( + teamEnvironment, + ); + mockTeamService.getTeamMember.mockResolvedValue({ + membershipID: 'sdc3sfdv', + userUid: '123454', + role: TeamMemberRole.OWNER, + }); + + const result = await teamEnvironmentsService.getTeamEnvironmentForCLI( + teamEnvironment.id, + '123454', + ); + expect(result).toEqualRight(teamEnvironment); + }); + + test('should throw TEAM_ENVIRONMENT_NOT_FOUND with invalid ID', async () => { + mockPrisma.teamEnvironment.findFirstOrThrow.mockRejectedValueOnce( + 'RejectOnNotFound', + ); + + const result = await teamEnvironmentsService.getTeamEnvironment( + teamEnvironment.id, + ); + expect(result).toEqualLeft(TEAM_ENVIRONMENT_NOT_FOUND); + }); + + test('should throw TEAM_MEMBER_NOT_FOUND if user not in same team', async () => { + mockPrisma.teamEnvironment.findFirstOrThrow.mockResolvedValueOnce( + teamEnvironment, + ); + mockTeamService.getTeamMember.mockResolvedValue(null); + + const result = await teamEnvironmentsService.getTeamEnvironmentForCLI( + teamEnvironment.id, + '333', + ); + expect(result).toEqualLeft(TEAM_MEMBER_NOT_FOUND); + }); + }); }); diff --git a/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts b/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts index 7af9481e5..f2b28b70b 100644 --- a/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts +++ b/packages/hoppscotch-backend/src/team-environments/team-environments.service.ts @@ -6,14 +6,17 @@ import { TeamEnvironment } from './team-environments.model'; import { TEAM_ENVIRONMENT_NOT_FOUND, TEAM_ENVIRONMENT_SHORT_NAME, + TEAM_MEMBER_NOT_FOUND, } from 'src/errors'; import * as E from 'fp-ts/Either'; import { isValidLength } from 'src/utils'; +import { TeamService } from 'src/team/team.service'; @Injectable() export class TeamEnvironmentsService { constructor( private readonly prisma: PrismaService, private readonly pubsub: PubSubService, + private readonly teamService: TeamService, ) {} TITLE_LENGTH = 3; @@ -242,4 +245,30 @@ export class TeamEnvironmentsService { }); return envCount; } + + /** + * Get details of a TeamEnvironment for CLI. + * + * @param id TeamEnvironment ID + * @param userUid User UID + * @returns Either of a TeamEnvironment or error message + */ + async getTeamEnvironmentForCLI(id: string, userUid: string) { + try { + const teamEnvironment = + await this.prisma.teamEnvironment.findFirstOrThrow({ + where: { id }, + }); + + const teamMember = await this.teamService.getTeamMember( + teamEnvironment.teamID, + userUid, + ); + if (!teamMember) return E.left(TEAM_MEMBER_NOT_FOUND); + + return E.right(teamEnvironment); + } catch (error) { + return E.left(TEAM_ENVIRONMENT_NOT_FOUND); + } + } } diff --git a/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts b/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts index f7749070f..db2927499 100644 --- a/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts +++ b/packages/hoppscotch-backend/src/team-invitation/team-invitation.service.ts @@ -75,12 +75,13 @@ export class TeamInvitationService { if (!isEmailValid) return E.left(INVALID_EMAIL); try { - const teamInvite = await this.prisma.teamInvitation.findUniqueOrThrow({ + const teamInvite = await this.prisma.teamInvitation.findFirstOrThrow({ where: { - teamID_inviteeEmail: { - inviteeEmail: inviteeEmail, - teamID: teamID, + inviteeEmail: { + equals: inviteeEmail, + mode: 'insensitive', }, + teamID, }, }); diff --git a/packages/hoppscotch-backend/src/types/AccessToken.ts b/packages/hoppscotch-backend/src/types/AccessToken.ts new file mode 100644 index 000000000..b467eafd3 --- /dev/null +++ b/packages/hoppscotch-backend/src/types/AccessToken.ts @@ -0,0 +1,7 @@ +export type AccessToken = { + id: string; + label: string; + createdOn: Date; + lastUsedOn: Date; + expiresOn: null | Date; +}; diff --git a/packages/hoppscotch-backend/src/types/InfraConfig.ts b/packages/hoppscotch-backend/src/types/InfraConfig.ts index 8c02929bc..8174d47c3 100644 --- a/packages/hoppscotch-backend/src/types/InfraConfig.ts +++ b/packages/hoppscotch-backend/src/types/InfraConfig.ts @@ -1,7 +1,16 @@ export enum InfraConfigEnum { + MAILER_SMTP_ENABLE = 'MAILER_SMTP_ENABLE', + MAILER_USE_CUSTOM_CONFIGS = 'MAILER_USE_CUSTOM_CONFIGS', MAILER_SMTP_URL = 'MAILER_SMTP_URL', MAILER_ADDRESS_FROM = 'MAILER_ADDRESS_FROM', + MAILER_SMTP_HOST = 'MAILER_SMTP_HOST', + MAILER_SMTP_PORT = 'MAILER_SMTP_PORT', + MAILER_SMTP_SECURE = 'MAILER_SMTP_SECURE', + MAILER_SMTP_USER = 'MAILER_SMTP_USER', + MAILER_SMTP_PASSWORD = 'MAILER_SMTP_PASSWORD', + MAILER_TLS_REJECT_UNAUTHORIZED = 'MAILER_TLS_REJECT_UNAUTHORIZED', + GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID', GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET', GOOGLE_CALLBACK_URL = 'GOOGLE_CALLBACK_URL', diff --git a/packages/hoppscotch-backend/src/types/RESTError.ts b/packages/hoppscotch-backend/src/types/RESTError.ts index 367c51dc0..4aa7d1b6e 100644 --- a/packages/hoppscotch-backend/src/types/RESTError.ts +++ b/packages/hoppscotch-backend/src/types/RESTError.ts @@ -5,6 +5,6 @@ import { HttpStatus } from '@nestjs/common'; ** Since its REST we need to return the HTTP status code along with the error message */ export type RESTError = { - message: string; + message: string | Record; statusCode: HttpStatus; }; diff --git a/packages/hoppscotch-backend/src/types/input-types.args.ts b/packages/hoppscotch-backend/src/types/input-types.args.ts index 14a324997..414b60cf9 100644 --- a/packages/hoppscotch-backend/src/types/input-types.args.ts +++ b/packages/hoppscotch-backend/src/types/input-types.args.ts @@ -1,4 +1,7 @@ import { ArgsType, Field, ID, InputType } from '@nestjs/graphql'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNotEmpty, IsOptional } from 'class-validator'; @ArgsType() @InputType() @@ -21,6 +24,10 @@ export class PaginationArgs { @ArgsType() @InputType() export class OffsetPaginationArgs { + @IsOptional() + @IsNotEmpty() + @Type(() => Number) + @ApiPropertyOptional() @Field({ nullable: true, defaultValue: 0, @@ -28,6 +35,10 @@ export class OffsetPaginationArgs { }) skip: number; + @IsOptional() + @IsNotEmpty() + @Type(() => Number) + @ApiPropertyOptional() @Field({ nullable: true, defaultValue: 10, diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts index be0c3530e..8bc2f22e5 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts @@ -390,6 +390,36 @@ export class UserCollectionResolver { return updatedUserCollection.right; } + @Mutation(() => Boolean, { + description: 'Duplicate a User Collection', + }) + @UseGuards(GqlAuthGuard) + async duplicateUserCollection( + @GqlUser() user: AuthUser, + @Args({ + name: 'collectionID', + description: 'ID of the collection', + }) + collectionID: string, + @Args({ + name: 'reqType', + description: 'Type of UserCollection', + type: () => ReqType, + }) + reqType: ReqType, + ) { + const duplicatedUserCollection = + await this.userCollectionService.duplicateUserCollection( + collectionID, + user.uid, + reqType, + ); + + if (E.isLeft(duplicatedUserCollection)) + throwErr(duplicatedUserCollection.left); + return duplicatedUserCollection.right; + } + // Subscriptions @Subscription(() => UserCollection, { description: 'Listen for User Collection Creation', diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.service.spec.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.service.spec.ts index ceb8f3b45..99a81636c 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.service.spec.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.service.spec.ts @@ -38,6 +38,8 @@ const user: AuthUser = { photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute', isAdmin: false, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, currentGQLSession: {}, currentRESTSession: {}, diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts index 06128e0fd..69358854c 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts @@ -25,7 +25,11 @@ import { UserCollectionExportJSONData, } from './user-collections.model'; import { ReqType } from 'src/types/RequestTypes'; -import { isValidLength, stringToJson } from 'src/utils'; +import { + isValidLength, + stringToJson, + transformCollectionData, +} from 'src/utils'; import { CollectionFolder } from 'src/types/CollectionFolder'; @Injectable() @@ -43,13 +47,15 @@ export class UserCollectionService { * @returns UserCollection model */ private cast(collection: UserCollection) { + const data = transformCollectionData(collection.data); + return { id: collection.id, title: collection.title, type: collection.type, parentID: collection.parentID, userID: collection.userUid, - data: !collection.data ? null : JSON.stringify(collection.data), + data, }; } @@ -871,6 +877,8 @@ export class UserCollectionService { }, }); + const data = transformCollectionData(collection.right.data); + const result: CollectionFolder = { id: collection.right.id, name: collection.right.title, @@ -882,7 +890,7 @@ export class UserCollectionService { ...(x.request as Record), // type casting x.request of type Prisma.JSONValue to an object to enable spread }; }), - data: JSON.stringify(collection.right.data), + data, }; return E.right(result); @@ -1138,4 +1146,45 @@ export class UserCollectionService { return E.left(USER_COLL_NOT_FOUND); } } + + /** + * Duplicate a User Collection + * + * @param collectionID The Collection ID + * @returns Boolean of duplication status + */ + async duplicateUserCollection( + collectionID: string, + userID: string, + reqType: DBReqType, + ) { + const collection = await this.getUserCollection(collectionID); + if (E.isLeft(collection)) return E.left(USER_COLL_NOT_FOUND); + + if (collection.right.userUid !== userID) return E.left(USER_NOT_OWNER); + if (collection.right.type !== reqType) + return E.left(USER_COLL_NOT_SAME_TYPE); + + const collectionJSONObject = await this.exportUserCollectionToJSONObject( + collection.right.userUid, + collectionID, + ); + if (E.isLeft(collectionJSONObject)) + return E.left(collectionJSONObject.left); + + const result = await this.importCollectionsFromJSON( + JSON.stringify([ + { + ...collectionJSONObject.right, + name: `${collection.right.title} - Duplicate`, + }, + ]), + userID, + collection.right.parentID, + reqType, + ); + if (E.isLeft(result)) return E.left(result.left as string); + + return E.right(true); + } } diff --git a/packages/hoppscotch-backend/src/user-request/user-request.service.spec.ts b/packages/hoppscotch-backend/src/user-request/user-request.service.spec.ts index 2ff0db6fa..c5adb0d27 100644 --- a/packages/hoppscotch-backend/src/user-request/user-request.service.spec.ts +++ b/packages/hoppscotch-backend/src/user-request/user-request.service.spec.ts @@ -41,6 +41,8 @@ const user: AuthUser = { photoURL: 'https://example.com/photo.png', isAdmin: false, refreshToken: null, + lastLoggedOn: new Date(), + lastActiveOn: new Date(), createdOn: new Date(), currentGQLSession: null, currentRESTSession: null, diff --git a/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts b/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts index 547489407..909f55a2b 100644 --- a/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts +++ b/packages/hoppscotch-backend/src/user-settings/user-settings.service.spec.ts @@ -27,6 +27,8 @@ const user: AuthUser = { refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', currentGQLSession: {}, currentRESTSession: {}, + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }; diff --git a/packages/hoppscotch-backend/src/user/user.model.ts b/packages/hoppscotch-backend/src/user/user.model.ts index 6889e93db..ffe1e6b62 100644 --- a/packages/hoppscotch-backend/src/user/user.model.ts +++ b/packages/hoppscotch-backend/src/user/user.model.ts @@ -30,6 +30,18 @@ export class User { }) isAdmin: boolean; + @Field({ + nullable: true, + description: 'Date when the user last logged in', + }) + lastLoggedOn: Date; + + @Field({ + nullable: true, + description: 'Date when the user last interacted with the app', + }) + lastActiveOn: Date; + @Field({ description: 'Date when the user account was created', }) diff --git a/packages/hoppscotch-backend/src/user/user.service.spec.ts b/packages/hoppscotch-backend/src/user/user.service.spec.ts index b5093831c..87c05c236 100644 --- a/packages/hoppscotch-backend/src/user/user.service.spec.ts +++ b/packages/hoppscotch-backend/src/user/user.service.spec.ts @@ -42,6 +42,8 @@ const user: AuthUser = { currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }; @@ -54,6 +56,8 @@ const adminUser: AuthUser = { currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }; @@ -67,6 +71,8 @@ const users: AuthUser[] = [ currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }, { @@ -78,6 +84,8 @@ const users: AuthUser[] = [ currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }, { @@ -89,6 +97,8 @@ const users: AuthUser[] = [ currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }, ]; @@ -103,6 +113,8 @@ const adminUsers: AuthUser[] = [ currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }, { @@ -114,6 +126,8 @@ const adminUsers: AuthUser[] = [ currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }, { @@ -125,6 +139,8 @@ const adminUsers: AuthUser[] = [ currentRESTSession: {}, currentGQLSession: {}, refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb', + lastLoggedOn: currentTime, + lastActiveOn: currentTime, createdOn: currentTime, }, ]; @@ -149,7 +165,7 @@ beforeEach(() => { describe('UserService', () => { describe('findUserByEmail', () => { test('should successfully return a valid user given a valid email', async () => { - mockPrisma.user.findUniqueOrThrow.mockResolvedValueOnce(user); + mockPrisma.user.findFirst.mockResolvedValueOnce(user); const result = await userService.findUserByEmail( 'dwight@dundermifflin.com', @@ -158,7 +174,7 @@ describe('UserService', () => { }); test('should return a null user given a invalid email', async () => { - mockPrisma.user.findUniqueOrThrow.mockRejectedValueOnce('NotFoundError'); + mockPrisma.user.findFirst.mockResolvedValueOnce(null); const result = await userService.findUserByEmail('jim@dundermifflin.com'); expect(result).resolves.toBeNone; @@ -495,6 +511,26 @@ describe('UserService', () => { }); }); + describe('updateUserLastLoggedOn', () => { + test('should resolve right and update user last logged on', async () => { + const currentTime = new Date(); + mockPrisma.user.update.mockResolvedValueOnce({ + ...user, + lastLoggedOn: currentTime, + }); + + const result = await userService.updateUserLastLoggedOn(user.uid); + expect(result).toEqualRight(true); + }); + + test('should resolve left and error when invalid user uid is passed', async () => { + mockPrisma.user.update.mockRejectedValueOnce('NotFoundError'); + + const result = await userService.updateUserLastLoggedOn('invalidUserUid'); + expect(result).toEqualLeft(USER_NOT_FOUND); + }); + }); + describe('fetchAllUsers', () => { test('should resolve right and return 20 users when cursor is null', async () => { mockPrisma.user.findMany.mockResolvedValueOnce(users); diff --git a/packages/hoppscotch-backend/src/user/user.service.ts b/packages/hoppscotch-backend/src/user/user.service.ts index 26018894b..f28b4167e 100644 --- a/packages/hoppscotch-backend/src/user/user.service.ts +++ b/packages/hoppscotch-backend/src/user/user.service.ts @@ -62,16 +62,16 @@ export class UserService { * @returns Option of found User */ async findUserByEmail(email: string): Promise> { - try { - const user = await this.prisma.user.findUniqueOrThrow({ - where: { - email: email, + const user = await this.prisma.user.findFirst({ + where: { + email: { + equals: email, + mode: 'insensitive', }, - }); - return O.some(user); - } catch (error) { - return O.none; - } + }, + }); + if (!user) return O.none; + return O.some(user); } /** @@ -114,7 +114,7 @@ export class UserService { * @param userUid User uid * @returns Either of User with updated refreshToken */ - async UpdateUserRefreshToken(refreshTokenHash: string, userUid: string) { + async updateUserRefreshToken(refreshTokenHash: string, userUid: string) { try { const user = await this.prisma.user.update({ where: { @@ -174,6 +174,7 @@ export class UserService { displayName: userDisplayName, email: profile.emails[0].value, photoURL: userPhotoURL, + lastLoggedOn: new Date(), providerAccounts: { create: { provider: profile.provider, @@ -221,7 +222,7 @@ export class UserService { } /** - * Update User displayName and photoURL + * Update User displayName and photoURL when logged in via a SSO provider * * @param user User object * @param profile Data received from SSO provider on the users account @@ -236,6 +237,7 @@ export class UserService { data: { displayName: !profile.displayName ? null : profile.displayName, photoURL: !profile.photos ? null : profile.photos[0].value, + lastLoggedOn: new Date(), }, }); return E.right(updatedUser); @@ -289,7 +291,7 @@ export class UserService { } /** - * Update a user's data + * Update a user's displayName * @param userUID User UID * @param displayName User's displayName * @returns a Either of User or error @@ -316,6 +318,38 @@ export class UserService { } } + /** + * Update user's lastLoggedOn timestamp + * @param userUID User UID + */ + async updateUserLastLoggedOn(userUid: string) { + try { + await this.prisma.user.update({ + where: { uid: userUid }, + data: { lastLoggedOn: new Date() }, + }); + return E.right(true); + } catch (e) { + return E.left(USER_NOT_FOUND); + } + } + + /** + * Update user's lastActiveOn timestamp + * @param userUID User UID + */ + async updateUserLastActiveOn(userUid: string) { + try { + await this.prisma.user.update({ + where: { uid: userUid }, + data: { lastActiveOn: new Date() }, + }); + return E.right(true); + } catch (e) { + return E.left(USER_NOT_FOUND); + } + } + /** * Validate and parse currentRESTSession and currentGQLSession * @param sessionData string of the session diff --git a/packages/hoppscotch-backend/src/utils.ts b/packages/hoppscotch-backend/src/utils.ts index f201a37b3..5be926174 100644 --- a/packages/hoppscotch-backend/src/utils.ts +++ b/packages/hoppscotch-backend/src/utils.ts @@ -1,21 +1,21 @@ import { ExecutionContext, HttpException } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { GqlExecutionContext } from '@nestjs/graphql'; +import { Prisma } from '@prisma/client'; +import * as A from 'fp-ts/Array'; +import * as E from 'fp-ts/Either'; import { pipe } from 'fp-ts/lib/function'; import * as O from 'fp-ts/Option'; -import * as TE from 'fp-ts/TaskEither'; import * as T from 'fp-ts/Task'; -import * as E from 'fp-ts/Either'; -import * as A from 'fp-ts/Array'; -import { TeamMemberRole } from './team/team.model'; -import { User } from './user/user.model'; +import * as TE from 'fp-ts/TaskEither'; +import { AuthProvider } from './auth/helper'; import { ENV_EMPTY_AUTH_PROVIDERS, ENV_NOT_FOUND_KEY_AUTH_PROVIDERS, ENV_NOT_SUPPORT_AUTH_PROVIDERS, JSON_INVALID, } from './errors'; -import { AuthProvider } from './auth/helper'; +import { TeamMemberRole } from './team/team.model'; import { RESTError } from './types/RESTError'; /** @@ -286,3 +286,33 @@ export function escapeSqlLikeString(str: string) { } }); } + +/** + * Calculate the expiration date of the token + * + * @param expiresOn Number of days the token is valid for + * @returns Date object of the expiration date + */ +export function calculateExpirationDate(expiresOn: null | number) { + if (expiresOn === null) return null; + return new Date(Date.now() + expiresOn * 24 * 60 * 60 * 1000); +} + +/* + * Transforms the collection level properties (authorization & headers) under the `data` field. + * Preserves `null` values and prevents duplicate stringification. + * + * @param {Prisma.JsonValue} collectionData - The team collection data to transform. + * @returns {string | null} The transformed team collection data as a string. + */ +export function transformCollectionData( + collectionData: Prisma.JsonValue, +): string | null { + if (!collectionData) { + return null; + } + + return typeof collectionData === 'string' + ? collectionData + : JSON.stringify(collectionData); +} diff --git a/packages/hoppscotch-cli/jest.config.ts b/packages/hoppscotch-cli/jest.config.ts deleted file mode 100644 index 39cb61bd5..000000000 --- a/packages/hoppscotch-cli/jest.config.ts +++ /dev/null @@ -1,193 +0,0 @@ -/* - * For a detailed explanation regarding each configuration property, visit: - * https://jestjs.io/docs/configuration - */ - -module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/tmp/jest_rs", - - // Automatically clear mock calls, instances and results before every test - clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: true, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: undefined, - - // The directory where Jest should output its coverage files - // coverageDirectory: undefined, - - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], - - // Indicates which provider should be used to instrument code for coverage - // coverageProvider: "babel", - - // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: undefined, - - // A path to a custom dependency extractor - // dependencyExtractor: undefined, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: undefined, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: undefined, - - // A set of global variables that need to be available in all test environments - // globals: { - // 'ts-jest': { - // useESM: true, - // }, - // }, - - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - // maxWorkers: "50%", - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - moduleFileExtensions: ["js", "ts", "json"], - - // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module - // moduleNameMapper: { - // '^(\\.{1,2}/.*)\\.js$': '$1', - // }, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - preset: "ts-jest/presets/js-with-babel", - - // Run tests from one or more projects - // projects: undefined, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state before every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: undefined, - - // Automatically restore mock state and implementation before every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: undefined, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - setupFilesAfterEnv: ["./jest.setup.ts"], - - // The number of seconds after which a test is considered as slow and reported as such in the results. - // slowTestThreshold: 5, - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: "node", - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - "**/src/__tests__/commands/**/*.*.ts", - ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - testPathIgnorePatterns: ["/node_modules/", "/dist/"], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: undefined, - - // This option allows use of a custom test runner - // testRunner: "jest-circus/runner", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - transform: { - "^.+\\.ts$": "ts-jest", - }, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/", - // "\\.pnp\\.[^\\/]+$" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - verbose: true, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, -}; diff --git a/packages/hoppscotch-cli/jest.setup.ts b/packages/hoppscotch-cli/jest.setup.ts deleted file mode 100644 index 7a33702a4..000000000 --- a/packages/hoppscotch-cli/jest.setup.ts +++ /dev/null @@ -1 +0,0 @@ -import "@relmify/jest-fp-ts"; diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index e69b9b14c..1f82f83b7 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -1,6 +1,6 @@ { "name": "@hoppscotch/cli", - "version": "0.8.0", + "version": "0.10.1", "description": "A CLI to run Hoppscotch test scripts in CI environments.", "homepage": "https://hoppscotch.io", "type": "module", @@ -20,9 +20,9 @@ "debugger": "node debugger.js 9999", "prepublish": "pnpm exec tsup", "prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write", - "test": "pnpm run build && jest && rm -rf dist", + "test": "pnpm run build && vitest run", "do-typecheck": "pnpm exec tsc --noEmit", - "do-test": "pnpm test" + "do-test": "pnpm run test" }, "keywords": [ "cli", @@ -48,6 +48,7 @@ "lodash-es": "4.17.21", "qs": "6.11.2", "verzod": "0.2.2", + "xmlbuilder2": "3.1.1", "zod": "3.22.4" }, "devDependencies": { @@ -55,15 +56,13 @@ "@hoppscotch/js-sandbox": "workspace:^", "@relmify/jest-fp-ts": "2.1.1", "@swc/core": "1.4.2", - "@types/jest": "29.5.12", "@types/lodash-es": "4.17.12", "@types/qs": "6.9.12", "fp-ts": "2.16.2", - "jest": "29.7.0", "prettier": "3.2.5", "qs": "6.11.2", - "ts-jest": "29.1.2", "tsup": "8.0.2", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vitest": "0.34.6" } } diff --git a/packages/hoppscotch-cli/setupFiles.ts b/packages/hoppscotch-cli/setupFiles.ts new file mode 100644 index 000000000..0a1bcdc0b --- /dev/null +++ b/packages/hoppscotch-cli/setupFiles.ts @@ -0,0 +1,15 @@ +// Vitest doesn't work without globals +// Ref: https://github.com/relmify/jest-fp-ts/issues/11 + +import decodeMatchers from "@relmify/jest-fp-ts/dist/decodeMatchers"; +import eitherMatchers from "@relmify/jest-fp-ts/dist/eitherMatchers"; +import optionMatchers from "@relmify/jest-fp-ts/dist/optionMatchers"; +import theseMatchers from "@relmify/jest-fp-ts/dist/theseMatchers"; +import eitherOrTheseMatchers from "@relmify/jest-fp-ts/dist/eitherOrTheseMatchers"; +import { expect } from "vitest"; + +expect.extend(decodeMatchers.matchers); +expect.extend(eitherMatchers.matchers); +expect.extend(optionMatchers.matchers); +expect.extend(theseMatchers.matchers); +expect.extend(eitherOrTheseMatchers.matchers); diff --git a/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts b/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts deleted file mode 100644 index e65fac753..000000000 --- a/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { ExecException } from "child_process"; - -import { HoppErrorCode } from "../../types/errors"; -import { runCLI, getErrorCode, getTestJsonFilePath } from "../utils"; - -describe("Test `hopp test ` command:", () => { - describe("Argument parsing", () => { - test("Errors with the code `INVALID_ARGUMENT` for not supplying enough arguments", async () => { - const args = "test"; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("INVALID_ARGUMENT"); - }); - - test("Errors with the code `INVALID_ARGUMENT` for an invalid command", async () => { - const args = "invalid-arg"; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("INVALID_ARGUMENT"); - }); - }); - - describe("Supplied collection export file validations", () => { - test("Errors with the code `FILE_NOT_FOUND` if the supplied collection export file doesn't exist", async () => { - const args = "test notfound.json"; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("FILE_NOT_FOUND"); - }); - - test("Errors with the code UNKNOWN_ERROR if the supplied collection export file content isn't valid JSON", async () => { - const args = `test ${getTestJsonFilePath("malformed-coll.json", "collection")}`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("UNKNOWN_ERROR"); - }); - - test("Errors with the code `MALFORMED_COLLECTION` if the supplied collection export file content is malformed", async () => { - const args = `test ${getTestJsonFilePath("malformed-coll-2.json", "collection")}`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("MALFORMED_COLLECTION"); - }); - - test("Errors with the code `INVALID_FILE_TYPE` if the supplied collection export file doesn't end with the `.json` extension", async () => { - const args = `test ${getTestJsonFilePath("notjson-coll.txt", "collection")}`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("INVALID_FILE_TYPE"); - }); - - test("Fails if the collection file includes scripts with incorrect API usage and failed assertions", async () => { - const args = `test ${getTestJsonFilePath("fails-coll.json", "collection")}`; - const { error } = await runCLI(args); - - expect(error).not.toBeNull(); - expect(error).toMatchObject({ - code: 1, - }); - }); - }); - - describe("Versioned entities", () => { - describe("Collections & Requests", () => { - const testFixtures = [ - { fileName: "coll-v1-req-v0.json", collVersion: 1, reqVersion: 0 }, - { fileName: "coll-v1-req-v1.json", collVersion: 1, reqVersion: 1 }, - { fileName: "coll-v2-req-v2.json", collVersion: 2, reqVersion: 2 }, - { fileName: "coll-v2-req-v3.json", collVersion: 2, reqVersion: 3 }, - ]; - - testFixtures.forEach(({ collVersion, fileName, reqVersion }) => { - test(`Successfully processes a supplied collection export file where the collection is based on the "v${collVersion}" schema and the request following the "v${reqVersion}" schema`, async () => { - const args = `test ${getTestJsonFilePath(fileName, "collection")}`; - const { error } = await runCLI(args); - - expect(error).toBeNull(); - }); - }); - }); - - describe("Environments", () => { - const testFixtures = [ - { fileName: "env-v0.json", version: 0 }, - { fileName: "env-v1.json", version: 1 }, - ]; - - testFixtures.forEach(({ fileName, version }) => { - test(`Successfully processes the supplied collection and environment export files where the environment is based on the "v${version}" schema`, async () => { - const ENV_PATH = getTestJsonFilePath(fileName, "environment"); - const args = `test ${getTestJsonFilePath("sample-coll.json", "collection")} --env ${ENV_PATH}`; - const { error } = await runCLI(args); - - expect(error).toBeNull(); - }); - }); - }); - }); - - test("Successfully processes a supplied collection export file of the expected format", async () => { - const args = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; - const { error } = await runCLI(args); - - expect(error).toBeNull(); - }); - - test("Successfully inherits headers and authorization set at the root collection", async () => { - const args = `test ${getTestJsonFilePath( - "collection-level-headers-auth-coll.json", - "collection" - )}`; - const { error } = await runCLI(args); - - expect(error).toBeNull(); - }); - - test("Persists environment variables set in the pre-request script for consumption in the test script", async () => { - const args = `test ${getTestJsonFilePath( - "pre-req-script-env-var-persistence-coll.json", - "collection" - )}`; - const { error } = await runCLI(args); - - expect(error).toBeNull(); - }); -}); - -describe("Test `hopp test --env ` command:", () => { - describe("Supplied environment export file validations", () => { - const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; - - test("Errors with the code `INVALID_ARGUMENT` if no file is supplied", async () => { - const args = `${VALID_TEST_ARGS} --env`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("INVALID_ARGUMENT"); - }); - - test("Errors with the code `INVALID_FILE_TYPE` if the supplied environment export file doesn't end with the `.json` extension", async () => { - const args = `${VALID_TEST_ARGS} --env ${getTestJsonFilePath( - "notjson-coll.txt", - "collection" - )}`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("INVALID_FILE_TYPE"); - }); - - test("Errors with the code `FILE_NOT_FOUND` if the supplied environment export file doesn't exist", async () => { - const args = `${VALID_TEST_ARGS} --env notfound.json`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("FILE_NOT_FOUND"); - }); - - test("Errors with the code `MALFORMED_ENV_FILE` on supplying a malformed environment export file", async () => { - const ENV_PATH = getTestJsonFilePath( - "malformed-envs.json", - "environment" - ); - const args = `${VALID_TEST_ARGS} --env ${ENV_PATH}`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("MALFORMED_ENV_FILE"); - }); - - test("Errors with the code `BULK_ENV_FILE` on supplying an environment export file based on the bulk environment export format", async () => { - const ENV_PATH = getTestJsonFilePath("bulk-envs.json", "environment"); - const args = `${VALID_TEST_ARGS} --env ${ENV_PATH}`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("BULK_ENV_FILE"); - }); - }); - - test("Successfully resolves values from the supplied environment export file", async () => { - const TESTS_PATH = getTestJsonFilePath( - "env-flag-tests-coll.json", - "collection" - ); - const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); - const args = `test ${TESTS_PATH} --env ${ENV_PATH}`; - - const { error } = await runCLI(args); - expect(error).toBeNull(); - }); - - test("Successfully resolves environment variables referenced in the request body", async () => { - const COLL_PATH = getTestJsonFilePath( - "req-body-env-vars-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "req-body-env-vars-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; - - const { error } = await runCLI(args); - expect(error).toBeNull(); - }); - - test("Works with shorth `-e` flag", async () => { - const TESTS_PATH = getTestJsonFilePath( - "env-flag-tests-coll.json", - "collection" - ); - const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); - const args = `test ${TESTS_PATH} -e ${ENV_PATH}`; - - const { error } = await runCLI(args); - expect(error).toBeNull(); - }); - - describe("Secret environment variables", () => { - jest.setTimeout(100000); - - // Reads secret environment values from system environment - test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => { - const env = { - ...process.env, - secretBearerToken: "test-token", - secretBasicAuthUsername: "test-user", - secretBasicAuthPassword: "test-pass", - secretQueryParamValue: "secret-query-param-value", - secretBodyValue: "secret-body-value", - secretHeaderValue: "secret-header-value", - }; - - const COLL_PATH = getTestJsonFilePath( - "secret-envs-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath("secret-envs.json", "environment"); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; - - const { error, stdout } = await runCLI(args, { env }); - - expect(stdout).toContain( - "https://httpbin.org/basic-auth/*********/*********" - ); - expect(error).toBeNull(); - }); - - // Prefers values specified in the environment export file over values set in the system environment - test("Successfully picks the values for secret environment variables set directly in the environment export file and persists the environment variables set from the pre-request script", async () => { - const COLL_PATH = getTestJsonFilePath( - "secret-envs-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "secret-supplied-values-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; - - const { error, stdout } = await runCLI(args); - - expect(stdout).toContain( - "https://httpbin.org/basic-auth/*********/*********" - ); - expect(error).toBeNull(); - }); - - // Values set from the scripting context takes the highest precedence - test("Setting values for secret environment variables from the pre-request script overrides values set at the supplied environment export file", async () => { - const COLL_PATH = getTestJsonFilePath( - "secret-envs-persistence-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "secret-supplied-values-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; - - const { error, stdout } = await runCLI(args); - - expect(stdout).toContain( - "https://httpbin.org/basic-auth/*********/*********" - ); - expect(error).toBeNull(); - }); - - test("Persists secret environment variable values set from the pre-request script for consumption in the request and post-request script context", async () => { - const COLL_PATH = getTestJsonFilePath( - "secret-envs-persistence-scripting-coll.json", - "collection" - ); - const ENVS_PATH = getTestJsonFilePath( - "secret-envs-persistence-scripting-envs.json", - "environment" - ); - const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; - - const { error } = await runCLI(args); - expect(error).toBeNull(); - }); - }); -}); - -describe("Test `hopp test --delay ` command:", () => { - const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; - - test("Errors with the code `INVALID_ARGUMENT` on not supplying a delay value", async () => { - const args = `${VALID_TEST_ARGS} --delay`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("INVALID_ARGUMENT"); - }); - - test("Errors with the code `INVALID_ARGUMENT` on supplying an invalid delay value", async () => { - const args = `${VALID_TEST_ARGS} --delay 'NaN'`; - const { stderr } = await runCLI(args); - - const out = getErrorCode(stderr); - expect(out).toBe("INVALID_ARGUMENT"); - }); - - test("Successfully performs delayed request execution for a valid delay value", async () => { - const args = `${VALID_TEST_ARGS} --delay 1`; - const { error } = await runCLI(args); - - expect(error).toBeNull(); - }); - - test("Works with the short `-d` flag", async () => { - const args = `${VALID_TEST_ARGS} -d 1`; - const { error } = await runCLI(args); - - expect(error).toBeNull(); - }); -}); diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap b/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap new file mode 100644 index 000000000..055c9a861 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/commands/__snapshots__/test.spec.ts.snap @@ -0,0 +1,529 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report at the default path 1`] = ` +" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + >\\" +} (ENV_EXPAND_LOOP)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" +`; + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report at the specified path 1`] = ` +" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + >\\" +} (ENV_EXPAND_LOOP)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" +`; + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report for a collection referring to environment variables 1`] = ` +" + + + + + + + + + + + + + + + +" +`; + +exports[`hopp test [options] > Test\`hopp test --env --reporter-junit [path] > Generates a JUnit report for a collection with authorization/headers set at the collection level 1`] = ` +" + + + + + + + + + + + + + + + + + + + + + + + + { + const args = "test notfound.json"; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("FILE_NOT_FOUND"); + }); + + test("Errors with the code UNKNOWN_ERROR if the supplied collection export file content isn't valid JSON", async () => { + const args = `test ${getTestJsonFilePath("malformed-coll.json", "collection")}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("UNKNOWN_ERROR"); + }); + + test("Errors with the code `MALFORMED_COLLECTION` if the supplied collection export file content is malformed", async () => { + const args = `test ${getTestJsonFilePath("malformed-coll-2.json", "collection")}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("MALFORMED_COLLECTION"); + }); + + test("Errors with the code `INVALID_FILE_TYPE` if the supplied collection export file doesn't end with the `.json` extension", async () => { + const args = `test ${getTestJsonFilePath("notjson-coll.txt", "collection")}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_FILE_TYPE"); + }); + + test("Fails if the collection file includes scripts with incorrect API usage and failed assertions", async () => { + const args = `test ${getTestJsonFilePath("fails-coll.json", "collection")}`; + const { error } = await runCLI(args); + + expect(error).not.toBeNull(); + expect(error).toMatchObject({ + code: 1, + }); + }); + }); + + describe("Versioned entities", () => { + describe("Collections & Requests", () => { + const testFixtures = [ + { fileName: "coll-v1-req-v0.json", collVersion: 1, reqVersion: 0 }, + { fileName: "coll-v1-req-v1.json", collVersion: 1, reqVersion: 1 }, + { fileName: "coll-v2-req-v2.json", collVersion: 2, reqVersion: 2 }, + { fileName: "coll-v2-req-v3.json", collVersion: 2, reqVersion: 3 }, + ]; + + testFixtures.forEach(({ collVersion, fileName, reqVersion }) => { + test(`Successfully processes a supplied collection export file where the collection is based on the "v${collVersion}" schema and the request following the "v${reqVersion}" schema`, async () => { + const args = `test ${getTestJsonFilePath(fileName, "collection")}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + }); + + describe("Environments", () => { + const testFixtures = [ + { fileName: "env-v0.json", version: 0 }, + { fileName: "env-v1.json", version: 1 }, + ]; + + testFixtures.forEach(({ fileName, version }) => { + test(`Successfully processes the supplied collection and environment export files where the environment is based on the "v${version}" schema`, async () => { + const ENV_PATH = getTestJsonFilePath(fileName, "environment"); + const args = `test ${getTestJsonFilePath("sample-coll.json", "collection")} --env ${ENV_PATH}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + }); + }); + + test("Successfully processes a supplied collection export file of the expected format", async () => { + const args = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + + test("Successfully inherits/overrides authorization and headers specified at the root collection at deeply nested collections", async () => { + const args = `test ${getTestJsonFilePath( + "collection-level-auth-headers-coll.json", + "collection" + )}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + + test( + "Successfully inherits/overrides authorization and headers at each level with multiple child collections", + async () => { + const args = `test ${getTestJsonFilePath( + "multiple-child-collections-auth-headers-coll.json", + "collection" + )}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }, + { timeout: 50000 } + ); + + test("Persists environment variables set in the pre-request script for consumption in the test script", async () => { + const args = `test ${getTestJsonFilePath( + "pre-req-script-env-var-persistence-coll.json", + "collection" + )}`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + + describe("Test `hopp test --env ` command:", () => { + describe("Supplied environment export file validations", () => { + describe("Argument parsing", () => { + test("Errors with the code `INVALID_ARGUMENT` if no file is supplied", async () => { + const args = `${VALID_TEST_ARGS} --env`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + }); + + test("Errors with the code `INVALID_FILE_TYPE` if the supplied environment export file doesn't end with the `.json` extension", async () => { + const args = `${VALID_TEST_ARGS} --env ${getTestJsonFilePath( + "notjson-coll.txt", + "collection" + )}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_FILE_TYPE"); + }); + + test("Errors with the code `FILE_NOT_FOUND` if the supplied environment export file doesn't exist", async () => { + const args = `${VALID_TEST_ARGS} --env notfound.json`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("FILE_NOT_FOUND"); + }); + + test("Errors with the code `MALFORMED_ENV_FILE` on supplying a malformed environment export file", async () => { + const ENV_PATH = getTestJsonFilePath( + "malformed-envs.json", + "environment" + ); + const args = `${VALID_TEST_ARGS} --env ${ENV_PATH}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("MALFORMED_ENV_FILE"); + }); + + test("Errors with the code `BULK_ENV_FILE` on supplying an environment export file based on the bulk environment export format", async () => { + const ENV_PATH = getTestJsonFilePath("bulk-envs.json", "environment"); + const args = `${VALID_TEST_ARGS} --env ${ENV_PATH}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("BULK_ENV_FILE"); + }); + }); + + test("Successfully resolves values from the supplied environment export file", async () => { + const TESTS_PATH = getTestJsonFilePath( + "env-flag-tests-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); + const args = `test ${TESTS_PATH} --env ${ENV_PATH}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + + test("Successfully resolves environment variables referenced in the request body", async () => { + const COLL_PATH = getTestJsonFilePath( + "req-body-env-vars-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "req-body-env-vars-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + + test("Works with short `-e` flag", async () => { + const TESTS_PATH = getTestJsonFilePath( + "env-flag-tests-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); + const args = `test ${TESTS_PATH} -e ${ENV_PATH}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + + describe( + "Secret environment variables", + () => { + // Reads secret environment values from system environment + test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => { + const env = { + ...process.env, + secretBearerToken: "test-token", + secretBasicAuthUsername: "test-user", + secretBasicAuthPassword: "test-pass", + secretQueryParamValue: "secret-query-param-value", + secretBodyValue: "secret-body-value", + secretHeaderValue: "secret-header-value", + }; + + const COLL_PATH = getTestJsonFilePath( + "secret-envs-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "secret-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + + const { error, stdout } = await runCLI(args, { env }); + + expect(stdout).toContain( + "https://httpbin.org/basic-auth/*********/*********" + ); + expect(error).toBeNull(); + }); + + // Prefers values specified in the environment export file over values set in the system environment + test("Successfully picks the values for secret environment variables set directly in the environment export file and persists the environment variables set from the pre-request script", async () => { + const COLL_PATH = getTestJsonFilePath( + "secret-envs-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "secret-supplied-values-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + + const { error, stdout } = await runCLI(args); + + expect(stdout).toContain( + "https://httpbin.org/basic-auth/*********/*********" + ); + expect(error).toBeNull(); + }); + + // Values set from the scripting context takes the highest precedence + test("Setting values for secret environment variables from the pre-request script overrides values set at the supplied environment export file", async () => { + const COLL_PATH = getTestJsonFilePath( + "secret-envs-persistence-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "secret-supplied-values-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + + const { error, stdout } = await runCLI(args); + + expect(stdout).toContain( + "https://httpbin.org/basic-auth/*********/*********" + ); + expect(error).toBeNull(); + }); + + test("Persists secret environment variable values set from the pre-request script for consumption in the request and post-request script context", async () => { + const COLL_PATH = getTestJsonFilePath( + "secret-envs-persistence-scripting-coll.json", + "collection" + ); + const ENVS_PATH = getTestJsonFilePath( + "secret-envs-persistence-scripting-envs.json", + "environment" + ); + const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + }, + { timeout: 20000 } + ); + }); + + describe("Test `hopp test --delay ` command:", () => { + describe("Argument parsing", () => { + test("Errors with the code `INVALID_ARGUMENT` on not supplying a delay value", async () => { + const args = `${VALID_TEST_ARGS} --delay`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + + test("Errors with the code `INVALID_ARGUMENT` on supplying an invalid delay value", async () => { + const args = `${VALID_TEST_ARGS} --delay 'NaN'`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + }); + + test("Successfully performs delayed request execution for a valid delay value", async () => { + const args = `${VALID_TEST_ARGS} --delay 1`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + + test("Works with the short `-d` flag", async () => { + const args = `${VALID_TEST_ARGS} -d 1`; + const { error } = await runCLI(args); + + expect(error).toBeNull(); + }); + }); + + // Future TODO: Enable once a proper e2e test environment is set up locally + describe.skip("Test `hopp test --env --token --server ` command:", () => { + const { + REQ_BODY_ENV_VARS_COLL_ID, + COLLECTION_LEVEL_HEADERS_AUTH_COLL_ID, + REQ_BODY_ENV_VARS_ENVS_ID, + PERSONAL_ACCESS_TOKEN, + } = process.env; + + if ( + !REQ_BODY_ENV_VARS_COLL_ID || + !COLLECTION_LEVEL_HEADERS_AUTH_COLL_ID || + !REQ_BODY_ENV_VARS_ENVS_ID || + !PERSONAL_ACCESS_TOKEN + ) { + return; + } + + const SERVER_URL = "https://stage-shc.hoppscotch.io/backend"; + + describe("Argument parsing", () => { + test("Errors with the code `INVALID_ARGUMENT` on not supplying a value for the `--token` flag", async () => { + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --token`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + + test("Errors with the code `INVALID_ARGUMENT` on not supplying a value for the `--server` flag", async () => { + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --server`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ARGUMENT"); + }); + }); + + describe("Workspace access validations", () => { + const INVALID_COLLECTION_ID = "invalid-coll-id"; + const INVALID_ENVIRONMENT_ID = "invalid-env-id"; + const INVALID_ACCESS_TOKEN = "invalid-token"; + + test("Errors with the code `TOKEN_INVALID` if the supplied access token is invalid", async () => { + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --token ${INVALID_ACCESS_TOKEN} --server ${SERVER_URL}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("TOKEN_INVALID"); + }); + + test("Errors with the code `INVALID_ID` if the supplied collection ID is invalid", async () => { + const args = `test ${INVALID_COLLECTION_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ID"); + }); + + test("Errors with the code `INVALID_ID` if the supplied environment ID is invalid", async () => { + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --env ${INVALID_ENVIRONMENT_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_ID"); + }); + + test("Errors with the code `INVALID_SERVER_URL` if not supplying a valid SH instance server URL", async () => { + // FE URL of the staging SHC instance + const INVALID_SERVER_URL = "https://stage-shc.hoppscotch.io"; + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --env ${REQ_BODY_ENV_VARS_ENVS_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${INVALID_SERVER_URL}`; + + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("INVALID_SERVER_URL"); + }); + + test("Errors with the code `SERVER_CONNECTION_REFUSED` if supplying an SH instance server URL that doesn't follow URL semantics", async () => { + const INVALID_URL = "invalid-url"; + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --env ${REQ_BODY_ENV_VARS_ENVS_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${INVALID_URL}`; + const { stderr } = await runCLI(args); + + const out = getErrorCode(stderr); + expect(out).toBe("SERVER_CONNECTION_REFUSED"); + }); + }); + + test("Successfully retrieves a collection with the ID", async () => { + const args = `test ${COLLECTION_LEVEL_HEADERS_AUTH_COLL_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + + test("Successfully retrieves collections and environments from a workspace using their respective IDs", async () => { + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --env ${REQ_BODY_ENV_VARS_ENVS_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + + test("Supports specifying collection file path along with environment ID", async () => { + const TESTS_PATH = getTestJsonFilePath( + "req-body-env-vars-coll.json", + "collection" + ); + const args = `test ${TESTS_PATH} --env ${REQ_BODY_ENV_VARS_ENVS_ID} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + + test("Supports specifying environment file path along with collection ID", async () => { + const ENV_PATH = getTestJsonFilePath( + "req-body-env-vars-envs.json", + "environment" + ); + const args = `test ${REQ_BODY_ENV_VARS_COLL_ID} --env ${ENV_PATH} --token ${PERSONAL_ACCESS_TOKEN} --server ${SERVER_URL}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + + test("Supports specifying both collection and environment file paths", async () => { + const TESTS_PATH = getTestJsonFilePath( + "req-body-env-vars-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "req-body-env-vars-envs.json", + "environment" + ); + const args = `test ${TESTS_PATH} --env ${ENV_PATH} --token ${PERSONAL_ACCESS_TOKEN}`; + + const { error } = await runCLI(args); + expect(error).toBeNull(); + }); + }); + + describe("Test`hopp test --env --reporter-junit [path]", () => { + const genPath = path.resolve("hopp-cli-test"); + + // Helper function to replace dynamic values before generating test snapshots + // Currently scoped to JUnit report generation + const replaceDynamicValuesInStr = (input: string): string => + input.replace( + /(time|timestamp)="[^"]+"/g, + (_, attr) => `${attr}="${attr}"` + ); + + beforeAll(() => { + fs.mkdirSync(genPath); + }); + + afterAll(() => { + fs.rmdirSync(genPath, { recursive: true }); + }); + + test("Report export fails with the code `REPORT_EXPORT_FAILED` while encountering an error during path creation", async () => { + const exportPath = "hopp-junit-report.xml"; + + const COLL_PATH = getTestJsonFilePath("passes-coll.json", "collection"); + + const args = `test ${COLL_PATH} --reporter-junit /non-existent-path/report.xml`; + + const { stdout, stderr } = await runCLI(args, { + cwd: path.resolve("hopp-cli-test"), + }); + + const out = getErrorCode(stderr); + expect(out).toBe("REPORT_EXPORT_FAILED"); + + expect(stdout).not.toContain( + `Successfully exported the JUnit report to: ${exportPath}` + ); + }); + + test("Generates a JUnit report at the default path", async () => { + const exportPath = "hopp-junit-report.xml"; + + const COLL_PATH = getTestJsonFilePath( + "test-junit-report-export-coll.json", + "collection" + ); + + const args = `test ${COLL_PATH} --reporter-junit`; + + const { stdout } = await runCLI(args, { + cwd: path.resolve("hopp-cli-test"), + }); + + expect(stdout).not.toContain( + `Overwriting the pre-existing path: ${exportPath}` + ); + + expect(stdout).toContain( + `Successfully exported the JUnit report to: ${exportPath}` + ); + + const fileContents = fs + .readFileSync(path.resolve(genPath, exportPath)) + .toString(); + + expect(replaceDynamicValuesInStr(fileContents)).toMatchSnapshot(); + }); + + test("Generates a JUnit report at the specified path", async () => { + const exportPath = "outer-dir/inner-dir/report.xml"; + + const COLL_PATH = getTestJsonFilePath( + "test-junit-report-export-coll.json", + "collection" + ); + + const args = `test ${COLL_PATH} --reporter-junit ${exportPath}`; + + const { stdout } = await runCLI(args, { + cwd: path.resolve("hopp-cli-test"), + }); + + expect(stdout).not.toContain( + `Overwriting the pre-existing path: ${exportPath}` + ); + + expect(stdout).toContain( + `Successfully exported the JUnit report to: ${exportPath}` + ); + + const fileContents = fs + .readFileSync(path.resolve(genPath, exportPath)) + .toString(); + + expect(replaceDynamicValuesInStr(fileContents)).toMatchSnapshot(); + }); + + test("Generates a JUnit report for a collection with authorization/headers set at the collection level", async () => { + const exportPath = "hopp-junit-report.xml"; + + const COLL_PATH = getTestJsonFilePath( + "collection-level-auth-headers-coll.json", + "collection" + ); + + const args = `test ${COLL_PATH} --reporter-junit`; + + const { stdout } = await runCLI(args, { + cwd: path.resolve("hopp-cli-test"), + }); + + expect(stdout).toContain( + `Overwriting the pre-existing path: ${exportPath}` + ); + + expect(stdout).toContain( + `Successfully exported the JUnit report to: ${exportPath}` + ); + + const fileContents = fs + .readFileSync(path.resolve(genPath, exportPath)) + .toString(); + + expect(replaceDynamicValuesInStr(fileContents)).toMatchSnapshot(); + }); + + test("Generates a JUnit report for a collection referring to environment variables", async () => { + const exportPath = "hopp-junit-report.xml"; + + const COLL_PATH = getTestJsonFilePath( + "req-body-env-vars-coll.json", + "collection" + ); + const ENV_PATH = getTestJsonFilePath( + "req-body-env-vars-envs.json", + "environment" + ); + + const args = `test ${COLL_PATH} --env ${ENV_PATH} --reporter-junit`; + + const { stdout } = await runCLI(args, { + cwd: path.resolve("hopp-cli-test"), + }); + + expect(stdout).toContain( + `Overwriting the pre-existing path: ${exportPath}` + ); + + expect(stdout).toContain( + `Successfully exported the JUnit report to: ${exportPath}` + ); + + const fileContents = fs + .readFileSync(path.resolve(genPath, exportPath)) + .toString(); + + expect(replaceDynamicValuesInStr(fileContents)).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v1-req-v0.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v0.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v1-req-v0.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v1-req-v1.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v1-req-v1.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v1-req-v1.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v2-req-v2.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v2.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v2-req-v2.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v2-req-v3.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/coll-v2-req-v3.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/coll-v2-req-v3.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/collection-level-headers-auth-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/collection-level-auth-headers-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/collection-level-headers-auth-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/collection-level-auth-headers-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/env-flag-tests-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/env-flag-tests-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/env-flag-tests-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/env-flag-tests-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/fails-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/fails-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/fails-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/fails-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/malformed-coll-2.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/malformed-coll-2.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/malformed-coll-2.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/malformed-coll-2.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/malformed-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/malformed-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/malformed-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/malformed-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json new file mode 100644 index 000000000..8e94b7df4 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json @@ -0,0 +1,655 @@ +{ + "v": 2, + "id": "clx1f86hv000010f8szcfya0t", + "name": "Multiple child collections with authorization & headers set at each level", + "folders": [ + { + "v": 2, + "id": "clx1fjgah000110f8a5bs68gd", + "name": "folder-1", + "folders": [ + { + "v": 2, + "id": "clx1fjwmm000410f8l1gkkr1a", + "name": "folder-11", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-11-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-1\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "key", + "value": "Set at folder-11", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1fjyxm000510f8pv90dt43", + "name": "folder-12", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-12-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-12-request", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-12-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-12-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-12-request\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-12", + "active": true + }, + { + "key": "key", + "value": "Set at folder-12", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1fk1cv000610f88kc3aupy", + "name": "folder-13", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "key": "api-key", + "addTo": "HEADERS", + "value": "api-key-value", + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true, + "grantTypeInfo": { + "token": "", + "isPKCE": true, + "clientID": "sfasfa", + "password": "", + "username": "", + "grantType": "AUTHORIZATION_CODE", + "authEndpoint": "asfafs", + "clientSecret": "sfasfasf", + "tokenEndpoint": "asfa", + "codeVerifierMethod": "S256" + } + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-13-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-13-request level", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-13-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-13\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-13-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-13-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "token": "test-token", + "authType": "bearer", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-13", + "active": true + }, + { + "key": "key", + "value": "Set at folder-13", + "active": true + } + ] + } + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-1-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-1\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-1", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1fjk9o000210f8j0573pls", + "name": "folder-2", + "folders": [ + { + "v": 2, + "id": "clx1fk516000710f87sfpw6bo", + "name": "folder-21", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-21-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-2\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "key", + "value": "Set at folder-21", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1fk72t000810f8gfwkpi5y", + "name": "folder-22", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-22-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-22-request", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-22-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-22-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-22-request\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-22", + "active": true + }, + { + "key": "key", + "value": "Set at folder-22", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1fk95g000910f8bunhaoo8", + "name": "folder-23", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-23-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-23-request level", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-23-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-23\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-23-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-23-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "token": "test-token", + "authType": "bearer", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-23", + "active": true + }, + { + "key": "key", + "value": "Set at folder-23", + "active": true + } + ] + } + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-2-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-2-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-2-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-2", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1fjmlq000310f86o4d3w2o", + "name": "folder-3", + "folders": [ + { + "v": 2, + "id": "clx1iwq0p003e10f8u8zg0p85", + "name": "folder-31", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-31-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-3\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [ + { + "key": "key", + "value": "Set at folder-31", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1izut7003m10f894ip59zg", + "name": "folder-32", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "none", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-32-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-32-request", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-32-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(undefined)\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-32-request\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-32-request\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-32", + "active": true + }, + { + "key": "key", + "value": "Set at folder-32", + "active": true + } + ] + }, + { + "v": 2, + "id": "clx1j2ka9003q10f8cdbzpgpg", + "name": "folder-33", + "folders": [], + "requests": [ + { + "v": "4", + "auth": { + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-33-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-33-request level", + "active": true + }, + { + "key": "key", + "value": "Overriden at folder-33-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-33\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Overriden at folder-33-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-33-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "token": "test-token", + "authType": "bearer", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-33", + "active": true + }, + { + "key": "key", + "value": "Set at folder-33", + "active": true + } + ] + } + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "folder-3-request", + "method": "GET", + "params": [], + "headers": [ + { + "key": "Custom-Header-Request-Level", + "value": "New custom header added at the folder-3-request level", + "active": true + }, + { + "key": "key", + "value": "Set at folder-3-request", + "active": true + } + ], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value overriden at folder-3\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n pw.expect(pw.response.body.headers[\"Key\"]).toBe(\"Set at folder-3-request\")\n pw.expect(pw.response.body.headers[\"Custom-Header-Request-Level\"]).toBe(\"New custom header added at the folder-3-request level\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "key": "testuser", + "addTo": "HEADERS", + "value": "testpass", + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value overriden at folder-3", + "active": true + } + ] + } + ], + "requests": [ + { + "v": "4", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "root-collection-request", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://httpbin.org/get", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully inherits authorization/header set at the parent collection level\", () => {\n pw.expect(pw.response.body.headers[\"Authorization\"]).toBe(\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\")\n \n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"Custom header value set at the root collection\")\n pw.expect(pw.response.body.headers[\"Inherited-Header\"]).toBe(\"Inherited header at all levels\")\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "basic", + "password": "testpass", + "username": "testuser", + "authActive": true + }, + "headers": [ + { + "key": "Custom-Header", + "value": "Custom header value set at the root collection", + "active": true + }, + { + "key": "Inherited-Header", + "value": "Inherited header at all levels", + "active": true + } + ] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/notjson-coll.txt b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/notjson-coll.txt similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/notjson-coll.txt rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/notjson-coll.txt diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/passes-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/passes-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/passes-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/passes-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/pre-req-script-env-var-persistence-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/pre-req-script-env-var-persistence-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/pre-req-script-env-var-persistence-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/pre-req-script-env-var-persistence-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/req-body-env-vars-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/req-body-env-vars-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/req-body-env-vars-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/req-body-env-vars-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/sample-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/sample-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/sample-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/sample-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/secret-envs-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/secret-envs-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/secret-envs-persistence-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/secret-envs-persistence-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-scripting-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/secret-envs-persistence-scripting-coll.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/collections/secret-envs-persistence-scripting-coll.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/secret-envs-persistence-scripting-coll.json diff --git a/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/test-junit-report-export-coll.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/test-junit-report-export-coll.json new file mode 100644 index 000000000..081f4f3df --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/collections/test-junit-report-export-coll.json @@ -0,0 +1,150 @@ +{ + "v": 2, + "name": "test-junit-report-export", + "folders": [ + { + "v": 2, + "name": "assertions", + "folders": [], + "requests": [ + { + "v": "5", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "error", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "pw.test(\"`toBeLevelxxx()` error scenarios\", ()=> {\n pw.expect(\"foo\").toBeLevel2xx();\n pw.expect(\"foo\").not.toBeLevel2xx();\n});\n\npw.test(\"`toBeType()` error scenarios\", () => {\n pw.expect(2).toBeType(\"foo\")\n pw.expect(\"2\").toBeType(\"bar\")\n pw.expect(true).toBeType(\"baz\")\n pw.expect({}).toBeType(\"qux\")\n pw.expect(undefined).toBeType(\"quux\")\n \n pw.expect(2).not.toBeType(\"foo\")\n pw.expect(\"2\").not.toBeType(\"bar\")\n pw.expect(true).not.toBeType(\"baz\")\n pw.expect({}).not.toBeType(\"qux\")\n pw.expect(undefined).not.toBeType(\"quux\")\n})\n\npw.test(\"`toHaveLength()` error scenarios\", () => {\n pw.expect(5).toHaveLength(0)\n pw.expect(true).toHaveLength(0)\n\n pw.expect(5).not.toHaveLength(0)\n pw.expect(true).not.toHaveLength(0)\n\n pw.expect([1, 2, 3, 4]).toHaveLength(\"a\")\n\n pw.expect([1, 2, 3, 4]).not.toHaveLength(\"a\")\n})\n\npw.test(\"`toInclude() error scenarios`\", () => {\n pw.expect(5).not.toInclude(0)\n pw.expect(true).not.toInclude(0)\n\n pw.expect([1, 2, 3, 4]).not.toInclude(null)\n\n pw.expect([1, 2, 3, 4]).not.toInclude(undefined)\n})", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "5", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "success", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "\n\n// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check headers\npw.test(\"Check headers\", ()=> {\n pw.expect(pw.response.body.headers[\"accept\"]).toBe(\"application/json, text/plain, */*,image/webp\");\n pw.expect(pw.response.body.headers[\"host\"]).toBe(\"echo.hoppscotch.io\")\n pw.expect(pw.response.body.headers[\"custom-header\"]).toBe(undefined)\n});\n\n// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "5", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "failure", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "\n\n// Check status code is 200\npw.test(\"Simulating failure - Status code is 200\", ()=> {\n pw.expect(pw.response.status).not.toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Simulating failure - Check headers\", ()=> {\n pw.expect(pw.response.body.headers[\"accept\"]).not.toBe(\"application/json, text/plain, */*\");\n pw.expect(pw.response.body.headers[\"host\"]).not.toBe(\"httpbin.org\")\n pw.expect(pw.response.body.headers[\"custom-header\"]).not.toBe(\"value\")\n});\n\n// Check status code is 2xx\npw.test(\"Simulating failure - Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).not.toBeLevel2xx();\n});", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] + }, + { + "v": 2, + "name": "request-level-errors", + "folders": [], + "requests": [ + { + "v": "5", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "invalid-url", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "invalid-url", + "testScript": "pw.test(\"`toBeLevelxxx()` error scenarios\", ()=> {\n pw.expect(\"foo\").toBeLevel2xx();\n pw.expect(\"foo\").not.toBeLevel2xx();\n});\n\npw.test(\"`toBeType()` error scenarios\", () => {\n pw.expect(2).toBeType(\"foo\")\n pw.expect(\"2\").toBeType(\"bar\")\n pw.expect(true).toBeType(\"baz\")\n pw.expect({}).toBeType(\"qux\")\n pw.expect(undefined).toBeType(\"quux\")\n \n pw.expect(2).not.toBeType(\"foo\")\n pw.expect(\"2\").not.toBeType(\"bar\")\n pw.expect(true).not.toBeType(\"baz\")\n pw.expect({}).not.toBeType(\"qux\")\n pw.expect(undefined).not.toBeType(\"quux\")\n})\n\npw.test(\"`toHaveLength()` error scenarios\", () => {\n pw.expect(5).toHaveLength(0)\n pw.expect(true).toHaveLength(0)\n\n pw.expect(5).not.toHaveLength(0)\n pw.expect(true).not.toHaveLength(0)\n\n pw.expect([1, 2, 3, 4]).toHaveLength(\"a\")\n\n pw.expect([1, 2, 3, 4]).not.toHaveLength(\"a\")\n})\n\npw.test(\"`toInclude() error scenarios`\", () => {\n pw.expect(5).not.toInclude(0)\n pw.expect(true).not.toInclude(0)\n\n pw.expect([1, 2, 3, 4]).not.toInclude(null)\n\n pw.expect([1, 2, 3, 4]).not.toInclude(undefined)\n})", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "5", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": null, + "contentType": null + }, + "name": "test-script-reference-error", + "method": "GET", + "params": [], + "headers": [], + "endpoint": "invalid-url", + "testScript": "pw.test(\"Reference error\", () => {\n pw.expect(status).toBe(200);\n})", + "preRequestScript": "", + "requestVariables": [] + }, + { + "v": "5", + "auth": { + "authType": "inherit", + "authActive": true + }, + "body": { + "body": "{\n \"key\": \"<>\"\n}", + "contentType": "application/json" + }, + "name": "non-existent-env-var", + "method": "POST", + "params": [], + "headers": [], + "endpoint": "https://echo.hoppscotch.io", + "testScript": "pw.test(\"`toBeLevelxxx()` error scenarios\", ()=> {\n pw.expect(\"foo\").toBeLevel2xx();\n pw.expect(\"foo\").not.toBeLevel2xx();\n});\n\npw.test(\"`toBeType()` error scenarios\", () => {\n pw.expect(2).toBeType(\"foo\")\n pw.expect(\"2\").toBeType(\"bar\")\n pw.expect(true).toBeType(\"baz\")\n pw.expect({}).toBeType(\"qux\")\n pw.expect(undefined).toBeType(\"quux\")\n \n pw.expect(2).not.toBeType(\"foo\")\n pw.expect(\"2\").not.toBeType(\"bar\")\n pw.expect(true).not.toBeType(\"baz\")\n pw.expect({}).not.toBeType(\"qux\")\n pw.expect(undefined).not.toBeType(\"quux\")\n})\n\npw.test(\"`toHaveLength()` error scenarios\", () => {\n pw.expect(5).toHaveLength(0)\n pw.expect(true).toHaveLength(0)\n\n pw.expect(5).not.toHaveLength(0)\n pw.expect(true).not.toHaveLength(0)\n\n pw.expect([1, 2, 3, 4]).toHaveLength(\"a\")\n\n pw.expect([1, 2, 3, 4]).not.toHaveLength(\"a\")\n})\n\npw.test(\"`toInclude() error scenarios`\", () => {\n pw.expect(5).not.toInclude(0)\n pw.expect(true).not.toInclude(0)\n\n pw.expect([1, 2, 3, 4]).not.toInclude(null)\n\n pw.expect([1, 2, 3, 4]).not.toInclude(undefined)\n})", + "preRequestScript": "", + "requestVariables": [] + } + ], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] + } + ], + "requests": [], + "auth": { + "authType": "inherit", + "authActive": true + }, + "headers": [] +} \ No newline at end of file diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/bulk-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/bulk-envs.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/bulk-envs.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/bulk-envs.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/env-flag-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/env-flag-envs.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/env-flag-envs.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/env-flag-envs.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v0.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/env-v0.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/env-v0.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/env-v0.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/env-v1.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/env-v1.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/env-v1.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/env-v1.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/malformed-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/malformed-envs.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/malformed-envs.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/malformed-envs.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/req-body-env-vars-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/req-body-env-vars-envs.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/req-body-env-vars-envs.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/req-body-env-vars-envs.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs-persistence-scripting-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/secret-envs-persistence-scripting-envs.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs-persistence-scripting-envs.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/secret-envs-persistence-scripting-envs.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/secret-envs.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/secret-envs.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/secret-envs.json diff --git a/packages/hoppscotch-cli/src/__tests__/samples/environments/secret-supplied-values-envs.json b/packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/secret-supplied-values-envs.json similarity index 100% rename from packages/hoppscotch-cli/src/__tests__/samples/environments/secret-supplied-values-envs.json rename to packages/hoppscotch-cli/src/__tests__/e2e/fixtures/environments/secret-supplied-values-envs.json diff --git a/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts new file mode 100644 index 000000000..e2a7d6bff --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts @@ -0,0 +1,758 @@ +import { + CollectionSchemaVersion, + Environment, + EnvironmentSchemaVersion, + HoppCollection, +} from "@hoppscotch/data"; + +import { + WorkspaceCollection, + WorkspaceEnvironment, +} from "../../../utils/workspace-access"; + +export const WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: WorkspaceCollection[] = + [ + { + id: "clx1ldkzs005t10f8rp5u60q7", + data: '{"auth":{"token":"BearerToken","authType":"bearer","authActive":true},"headers":[{"key":"X-Test-Header","value":"Set at root collection","active":true}]}', + title: "CollectionA", + parentID: null, + folders: [ + { + id: "clx1ldkzs005v10f86b9wx4yc", + data: '{"auth":{"authType":"inherit","authActive":true},"headers":[]}', + title: "FolderA", + parentID: "clx1ldkzs005t10f8rp5u60q7", + folders: [ + { + id: "clx1ldkzt005x10f8i0u5lzgj", + data: '{"auth":{"key":"key","addTo":"HEADERS","value":"test-key","authType":"api-key","authActive":true},"headers":[{"key":"X-Test-Header","value":"Overriden at FolderB","active":true}]}', + title: "FolderB", + parentID: "clx1ldkzs005v10f86b9wx4yc", + folders: [ + { + id: "clx1ldkzu005z10f880zx17bg", + data: '{"auth":{"authType":"inherit","authActive":true},"headers":[]}', + title: "FolderC", + parentID: "clx1ldkzt005x10f8i0u5lzgj", + folders: [], + requests: [ + { + id: "clx1ldkzu006010f820vzy13v", + collectionID: "clx1ldkzu005z10f880zx17bg", + teamID: "clws3hg58000011o8h07glsb1", + title: "RequestD", + request: + '{"v":"3","auth":{"authType":"basic","password":"password","username":"username","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestD","method":"GET","params":[],"headers":[{"key":"X-Test-Header","value":"Overriden at RequestD","active":true}],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Overrides auth and headers set at the parent folder\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Overriden at RequestD\\");\\n pw.expect(pw.response.body.headers[\\"authorization\\"]).toBe(\\"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\\");\\n});","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ], + requests: [ + { + id: "clx1ldkzt005y10f82dl8ni8d", + collectionID: "clx1ldkzt005x10f8i0u5lzgj", + teamID: "clws3hg58000011o8h07glsb1", + title: "RequestC", + request: + '{"v":"3","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestC","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Correctly inherits auth and headers from the parent folder\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Overriden at FolderB\\");\\n pw.expect(pw.response.body.headers[\\"key\\"]).toBe(\\"test-key\\");\\n});","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ], + requests: [ + { + id: "clx1ldkzs005w10f8pc2v2boh", + collectionID: "clx1ldkzs005v10f86b9wx4yc", + teamID: "clws3hg58000011o8h07glsb1", + title: "RequestB", + request: + '{"v":"3","id":"clpttpdq00003qp16kut6doqv","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestB","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Correctly inherits auth and headers from the parent folder\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Set at root collection\\");\\n pw.expect(pw.response.body.headers[\\"authorization\\"]).toBe(\\"Bearer BearerToken\\");\\n});","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ], + requests: [ + { + id: "clx1ldkzs005u10f82xd5ho3l", + collectionID: "clx1ldkzs005t10f8rp5u60q7", + teamID: "clws3hg58000011o8h07glsb1", + title: "RequestA", + request: + '{"v":"5","id":"clpttpdq00003qp16kut6doqv","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestA","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Correctly inherits auth and headers from the root collection\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Set at root collection\\");\\n pw.expect(pw.response.body.headers[\\"authorization\\"]).toBe(\\"Bearer BearerToken\\");\\n});","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ]; + +export const TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppCollection[] = + [ + { + v: CollectionSchemaVersion, + id: "clx1ldkzs005t10f8rp5u60q7", + name: "CollectionA", + folders: [ + { + v: CollectionSchemaVersion, + id: "clx1ldkzs005v10f86b9wx4yc", + name: "FolderA", + folders: [ + { + v: CollectionSchemaVersion, + id: "clx1ldkzt005x10f8i0u5lzgj", + name: "FolderB", + folders: [ + { + v: CollectionSchemaVersion, + id: "clx1ldkzu005z10f880zx17bg", + name: "FolderC", + folders: [], + requests: [ + { + v: "3", + auth: { + authType: "basic", + password: "password", + username: "username", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "RequestD", + method: "GET", + params: [], + headers: [ + { + key: "X-Test-Header", + value: "Overriden at RequestD", + active: true, + }, + ], + endpoint: "https://echo.hoppscotch.io", + testScript: + 'pw.test("Overrides auth and headers set at the parent folder", ()=> {\n pw.expect(pw.response.body.headers["x-test-header"]).toBe("Overriden at RequestD");\n pw.expect(pw.response.body.headers["authorization"]).toBe("Basic dXNlcm5hbWU6cGFzc3dvcmQ=");\n});', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [], + }, + ], + requests: [ + { + v: "3", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "RequestC", + method: "GET", + params: [], + headers: [], + endpoint: "https://echo.hoppscotch.io", + testScript: + 'pw.test("Correctly inherits auth and headers from the parent folder", ()=> {\n pw.expect(pw.response.body.headers["x-test-header"]).toBe("Overriden at FolderB");\n pw.expect(pw.response.body.headers["key"]).toBe("test-key");\n});', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + key: "key", + addTo: "HEADERS", + value: "test-key", + authType: "api-key", + authActive: true, + }, + headers: [ + { + key: "X-Test-Header", + value: "Overriden at FolderB", + active: true, + }, + ], + }, + ], + requests: [ + { + v: "3", + id: "clpttpdq00003qp16kut6doqv", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "RequestB", + method: "GET", + params: [], + headers: [], + endpoint: "https://echo.hoppscotch.io", + testScript: + 'pw.test("Correctly inherits auth and headers from the parent folder", ()=> {\n pw.expect(pw.response.body.headers["x-test-header"]).toBe("Set at root collection");\n pw.expect(pw.response.body.headers["authorization"]).toBe("Bearer BearerToken");\n});', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [], + }, + ], + requests: [ + { + v: "5", + id: "clpttpdq00003qp16kut6doqv", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "RequestA", + method: "GET", + params: [], + headers: [], + endpoint: "https://echo.hoppscotch.io", + testScript: + 'pw.test("Correctly inherits auth and headers from the root collection", ()=> {\n pw.expect(pw.response.body.headers["x-test-header"]).toBe("Set at root collection");\n pw.expect(pw.response.body.headers["authorization"]).toBe("Bearer BearerToken");\n});', + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + token: "BearerToken", + authType: "bearer", + authActive: true, + }, + headers: [ + { + key: "X-Test-Header", + value: "Set at root collection", + active: true, + }, + ], + }, + ]; + +export const WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: WorkspaceCollection[] = + [ + { + id: "clx1f86hv000010f8szcfya0t", + data: '{"auth":{"authType":"basic","password":"testpass","username":"testuser","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value set at the root collection","active":true},{"key":"Inherited-Header","value":"Inherited header at all levels","active":true}]}', + title: + "Multiple child collections with authorization & headers set at each level", + parentID: null, + folders: [ + { + id: "clx1fjgah000110f8a5bs68gd", + data: '{"auth":{"authType":"inherit","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-1","active":true}]}', + title: "folder-1", + parentID: "clx1f86hv000010f8szcfya0t", + folders: [ + { + id: "clx1fjwmm000410f8l1gkkr1a", + data: '{"auth":{"authType":"inherit","authActive":true},"headers":[{"key":"key","value":"Set at folder-11","active":true}]}', + title: "folder-11", + parentID: "clx1fjgah000110f8a5bs68gd", + folders: [], + requests: [ + { + id: "clx1gjo1q000p10f8tc3x2u50", + collectionID: "clx1fjwmm000410f8l1gkkr1a", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-11-request", + request: + '{"v":"4","auth":{"authType":"inherit","password":"testpass","username":"testuser","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-11-request","method":"GET","params":[],"headers":[],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-1\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1fjyxm000510f8pv90dt43", + data: '{"auth":{"authType":"none","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-12","active":true},{"key":"key","value":"Set at folder-12","active":true}]}', + title: "folder-12", + parentID: "clx1fjgah000110f8a5bs68gd", + folders: [], + requests: [ + { + id: "clx1glkt5000u10f88q51ioj8", + collectionID: "clx1fjyxm000510f8pv90dt43", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-12-request", + request: + '{"v":"4","auth":{"authType":"none","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-12-request","method":"GET","params":[],"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-12-request","active":true},{"key":"key","value":"Overriden at folder-12-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(undefined)\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-12-request\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n pw.expect(pw.response.body.headers[\\"Key\\"]).toBe(\\"Overriden at folder-12-request\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1fk1cv000610f88kc3aupy", + data: '{"auth":{"token":"test-token","authType":"bearer","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-13","active":true},{"key":"key","value":"Set at folder-13","active":true}]}', + title: "folder-13", + parentID: "clx1fjgah000110f8a5bs68gd", + folders: [], + requests: [ + { + id: "clx1grfir001510f8c4ttiazq", + collectionID: "clx1fk1cv000610f88kc3aupy", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-13-request", + request: + '{"v":"4","auth":{"key":"api-key","addTo":"HEADERS","value":"api-key-value","authType":"basic","password":"testpass","username":"testuser","authActive":true,"grantTypeInfo":{"token":"","isPKCE":true,"clientID":"sfasfa","password":"","username":"","grantType":"AUTHORIZATION_CODE","authEndpoint":"asfafs","clientSecret":"sfasfasf","tokenEndpoint":"asfa","codeVerifierMethod":"S256"}},"body":{"body":null,"contentType":null},"name":"folder-13-request","method":"GET","params":[],"headers":[{"key":"Custom-Header-Request-Level","value":"New custom header added at the folder-13-request level","active":true},{"key":"key","value":"Overriden at folder-13-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-13\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n pw.expect(pw.response.body.headers[\\"Key\\"]).toBe(\\"Overriden at folder-13-request\\")\\n pw.expect(pw.response.body.headers[\\"Custom-Header-Request-Level\\"]).toBe(\\"New custom header added at the folder-13-request level\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ], + requests: [ + { + id: "clx1gebpx000k10f8andzw36z", + collectionID: "clx1fjgah000110f8a5bs68gd", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-1-request", + request: + '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-1-request","method":"GET","params":[],"headers":[],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-1\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1fjk9o000210f8j0573pls", + data: '{"auth":{"authType":"none","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-2","active":true}]}', + title: "folder-2", + parentID: "clx1f86hv000010f8szcfya0t", + folders: [ + { + id: "clx1fk516000710f87sfpw6bo", + data: '{"auth":{"authType":"inherit","authActive":true},"headers":[{"key":"key","value":"Set at folder-21","active":true}]}', + title: "folder-21", + parentID: "clx1fjk9o000210f8j0573pls", + folders: [], + requests: [ + { + id: "clx1hfegy001j10f8ywbozysk", + collectionID: "clx1fk516000710f87sfpw6bo", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-21-request", + request: + '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-21-request","method":"GET","params":[],"headers":[],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(undefined)\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-2\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1fk72t000810f8gfwkpi5y", + data: '{"auth":{"authType":"none","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-22","active":true},{"key":"key","value":"Set at folder-22","active":true}]}', + title: "folder-22", + parentID: "clx1fjk9o000210f8j0573pls", + folders: [], + requests: [ + { + id: "clx1ibfre002k10f86brcb2aa", + collectionID: "clx1fk72t000810f8gfwkpi5y", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-22-request", + request: + '{"v":"4","auth":{"authType":"none","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-22-request","method":"GET","params":[],"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-22-request","active":true},{"key":"key","value":"Overriden at folder-22-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(undefined)\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-22-request\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n pw.expect(pw.response.body.headers[\\"Key\\"]).toBe(\\"Overriden at folder-22-request\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1fk95g000910f8bunhaoo8", + data: '{"auth":{"token":"test-token","authType":"bearer","password":"testpass","username":"testuser","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-23","active":true},{"key":"key","value":"Set at folder-23","active":true}]}', + title: "folder-23", + parentID: "clx1fjk9o000210f8j0573pls", + folders: [], + requests: [ + { + id: "clx1if4w6002n10f8xe4gnf0w", + collectionID: "clx1fk95g000910f8bunhaoo8", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-23-request", + request: + '{"v":"4","auth":{"authType":"basic","password":"testpass","username":"testuser","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-23-request","method":"GET","params":[],"headers":[{"key":"Custom-Header-Request-Level","value":"New custom header added at the folder-23-request level","active":true},{"key":"key","value":"Overriden at folder-23-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-23\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n pw.expect(pw.response.body.headers[\\"Key\\"]).toBe(\\"Overriden at folder-23-request\\")\\n pw.expect(pw.response.body.headers[\\"Custom-Header-Request-Level\\"]).toBe(\\"New custom header added at the folder-23-request level\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ], + requests: [ + { + id: "clx1hbtdj001g10f8y71y869s", + collectionID: "clx1fjk9o000210f8j0573pls", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-2-request", + request: + '{"v":"4","auth":{"authType":"none","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-2-request","method":"GET","params":[],"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-2-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(undefined)\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-2-request\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1fjmlq000310f86o4d3w2o", + data: '{"auth":{"key":"testuser","addTo":"HEADERS","value":"testpass","authType":"basic","password":"testpass","username":"testuser","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-3","active":true}]}', + title: "folder-3", + parentID: "clx1f86hv000010f8szcfya0t", + folders: [ + { + id: "clx1iwq0p003e10f8u8zg0p85", + data: '{"auth":{"authType":"inherit","authActive":true},"headers":[{"key":"key","value":"Set at folder-31","active":true}]}', + title: "folder-31", + parentID: "clx1fjmlq000310f86o4d3w2o", + folders: [], + requests: [ + { + id: "clx1ixdiv003f10f8j6ni375m", + collectionID: "clx1iwq0p003e10f8u8zg0p85", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-31-request", + request: + '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-31-request","method":"GET","params":[],"headers":[],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-3\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1izut7003m10f894ip59zg", + data: '{"auth":{"authType":"none","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-32","active":true},{"key":"key","value":"Set at folder-32","active":true}]}', + title: "folder-32", + parentID: "clx1fjmlq000310f86o4d3w2o", + folders: [], + requests: [ + { + id: "clx1j01dg003n10f8e34khl6v", + collectionID: "clx1izut7003m10f894ip59zg", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-32-request", + request: + '{"v":"4","auth":{"authType":"none","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-32-request","method":"GET","params":[],"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-32-request","active":true},{"key":"key","value":"Overriden at folder-32-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(undefined)\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-32-request\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n pw.expect(pw.response.body.headers[\\"Key\\"]).toBe(\\"Overriden at folder-32-request\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1j2ka9003q10f8cdbzpgpg", + data: '{"auth":{"token":"test-token","authType":"bearer","password":"testpass","username":"testuser","authActive":true},"headers":[{"key":"Custom-Header","value":"Custom header value overriden at folder-33","active":true},{"key":"key","value":"Set at folder-33","active":true}]}', + title: "folder-33", + parentID: "clx1fjmlq000310f86o4d3w2o", + folders: [], + requests: [ + { + id: "clx1j361a003r10f8oly5m2n6", + collectionID: "clx1j2ka9003q10f8cdbzpgpg", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-33-request", + request: + '{"v":"4","auth":{"authType":"basic","password":"testpass","username":"testuser","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-33-request","method":"GET","params":[],"headers":[{"key":"Custom-Header-Request-Level","value":"New custom header added at the folder-33-request level","active":true},{"key":"key","value":"Overriden at folder-33-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-33\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n pw.expect(pw.response.body.headers[\\"Key\\"]).toBe(\\"Overriden at folder-33-request\\")\\n pw.expect(pw.response.body.headers[\\"Custom-Header-Request-Level\\"]).toBe(\\"New custom header added at the folder-33-request level\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ], + requests: [ + { + id: "clx1jk1nq004y10f8fhtxvs02", + collectionID: "clx1fjmlq000310f86o4d3w2o", + teamID: "clws3hg58000011o8h07glsb1", + title: "folder-3-request", + request: + '{"v":"4","auth":{"authType":"basic","password":"testpass","username":"testuser","authActive":true},"body":{"body":null,"contentType":null},"name":"folder-3-request","method":"GET","params":[],"headers":[{"key":"Custom-Header-Request-Level","value":"New custom header added at the folder-3-request level","active":true},{"key":"key","value":"Set at folder-3-request","active":true}],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits/overrides authorization/header set at the parent collection level with new header addition\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value overriden at folder-3\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n pw.expect(pw.response.body.headers[\\"Key\\"]).toBe(\\"Set at folder-3-request\\")\\n pw.expect(pw.response.body.headers[\\"Custom-Header-Request-Level\\"]).toBe(\\"New custom header added at the folder-3-request level\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ], + requests: [ + { + id: "clx1g2pnv000b10f80f0oyp79", + collectionID: "clx1f86hv000010f8szcfya0t", + teamID: "clws3hg58000011o8h07glsb1", + title: "root-collection-request", + request: + '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"root-collection-request","method":"GET","params":[],"headers":[],"endpoint":"https://httpbin.org/get","testScript":"// Check status code is 200\\npw.test(\\"Status code is 200\\", ()=> {\\n pw.expect(pw.response.status).toBe(200);\\n});\\n\\npw.test(\\"Successfully inherits authorization/header set at the parent collection level\\", () => {\\n pw.expect(pw.response.body.headers[\\"Authorization\\"]).toBe(\\"Basic dGVzdHVzZXI6dGVzdHBhc3M=\\")\\n \\n pw.expect(pw.response.body.headers[\\"Custom-Header\\"]).toBe(\\"Custom header value set at the root collection\\")\\n pw.expect(pw.response.body.headers[\\"Inherited-Header\\"]).toBe(\\"Inherited header at all levels\\")\\n})","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + ]; + +// Collections with `data` field set to `null` at certain levels +export const WORKSPACE_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK: WorkspaceCollection[] = + [ + { + id: "clx1kxvao005m10f8luqivrf1", + data: null, + title: "Collection with no authorization/headers set", + parentID: null, + folders: [ + { + id: "clx1kygjt005n10f8m1nkhjux", + data: null, + title: "folder-1", + parentID: "clx1kxvao005m10f8luqivrf1", + folders: [], + requests: [ + { + id: "clx1kz2gk005p10f8ll7ztbnj", + collectionID: "clx1kygjt005n10f8m1nkhjux", + teamID: "clws3hg58000011o8h07glsb1", + title: "req1", + request: + '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"req1","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1kym98005o10f8qg17t9o2", + data: '{"auth":{"authType":"none","authActive":true},"headers":[{"key":"Custom-Header","value":"Set at folder-2","active":true}]}', + title: "folder-2", + parentID: "clx1kxvao005m10f8luqivrf1", + folders: [], + requests: [ + { + id: "clx1kz3m7005q10f8lw3v09l4", + collectionID: "clx1kym98005o10f8qg17t9o2", + teamID: "clws3hg58000011o8h07glsb1", + title: "req2", + request: + '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"req2","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + { + id: "clx1l2bu6005r10f8daynohge", + data: null, + title: "folder-3", + parentID: "clx1kxvao005m10f8luqivrf1", + folders: [], + requests: [], + }, + { + id: "clx1l2eaz005s10f8loetbbeb", + data: '{"auth":{"authType":"none","authActive":true},"headers":[{"key":"Custom-Header","value":"Set at folder-4","active":true}]}', + title: "folder-4", + parentID: "clx1kxvao005m10f8luqivrf1", + folders: [], + requests: [], + }, + ], + requests: [], + }, + ]; + +export const TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK: HoppCollection[] = + [ + { + v: 2, + id: "clx1kxvao005m10f8luqivrf1", + name: "Collection with no authorization/headers set", + folders: [ + { + v: 2, + id: "clx1kygjt005n10f8m1nkhjux", + name: "folder-1", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "req1", + method: "GET", + params: [], + headers: [], + endpoint: "https://echo.hoppscotch.io", + testScript: "", + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [], + }, + { + v: 2, + id: "clx1kym98005o10f8qg17t9o2", + name: "folder-2", + folders: [], + requests: [ + { + v: "4", + auth: { + authType: "inherit", + authActive: true, + }, + body: { + body: null, + contentType: null, + }, + name: "req2", + method: "GET", + params: [], + headers: [], + endpoint: "https://echo.hoppscotch.io", + testScript: "", + preRequestScript: "", + requestVariables: [], + }, + ], + auth: { + authType: "none", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Set at folder-2", + active: true, + }, + ], + }, + { + v: 2, + id: "clx1l2bu6005r10f8daynohge", + name: "folder-3", + folders: [], + requests: [], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [], + }, + { + v: 2, + id: "clx1l2eaz005s10f8loetbbeb", + name: "folder-4", + folders: [], + requests: [], + auth: { + authType: "none", + authActive: true, + }, + headers: [ + { + key: "Custom-Header", + value: "Set at folder-4", + active: true, + }, + ], + }, + ], + requests: [], + auth: { + authType: "inherit", + authActive: true, + }, + headers: [], + }, + ]; + +export const WORKSPACE_ENVIRONMENT_MOCK: WorkspaceEnvironment = { + id: "clwudd68q00079rufju8uo3on", + teamID: "clws3hg58000011o8h07glsb1", + name: "Response body sample", + variables: [ + { + key: "firstName", + value: "John", + secret: false, + }, + { + key: "lastName", + value: "Doe", + secret: false, + }, + { + key: "id", + value: "7", + secret: false, + }, + { + key: "fullName", + value: "<> <>", + secret: false, + }, + { + key: "recursiveVarX", + value: "<>", + secret: false, + }, + { + key: "recursiveVarY", + value: "<>", + secret: false, + }, + { + key: "salutation", + value: "Hello", + secret: false, + }, + { + key: "greetText", + value: "<> <>", + secret: false, + }, + ], +}; + +export const TRANSFORMED_ENVIRONMENT_MOCK: Environment = { + v: EnvironmentSchemaVersion, + id: "clwudd68q00079rufju8uo3on", + name: "Response body sample", + variables: [ + { + key: "firstName", + value: "John", + secret: false, + }, + { + key: "lastName", + value: "Doe", + secret: false, + }, + { + key: "id", + value: "7", + secret: false, + }, + { + key: "fullName", + value: "<> <>", + secret: false, + }, + { + key: "recursiveVarX", + value: "<>", + secret: false, + }, + { + key: "recursiveVarY", + value: "<>", + secret: false, + }, + { + key: "salutation", + value: "Hello", + secret: false, + }, + { + key: "greetText", + value: "<> <>", + secret: false, + }, + ], +}; diff --git a/packages/hoppscotch-cli/src/__tests__/unit/getters.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/getters.spec.ts new file mode 100644 index 000000000..ef3141154 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/unit/getters.spec.ts @@ -0,0 +1,389 @@ +import axios, { AxiosError, AxiosResponse } from "axios"; +import fs from "fs/promises"; +import { describe, expect, test, vi } from "vitest"; + +import { + CollectionSchemaVersion, + Environment, + HoppCollection, + getDefaultRESTRequest, +} from "@hoppscotch/data"; + +import { DEFAULT_DURATION_PRECISION } from "../../utils/constants"; +import { + getDurationInSeconds, + getEffectiveFinalMetaData, + getResourceContents, +} from "../../utils/getters"; +import * as mutators from "../../utils/mutators"; + +import * as workspaceAccessHelpers from "../../utils/workspace-access"; + +describe("getters", () => { + describe("getDurationInSeconds", () => { + const testDurations = [ + { end: [1, 111111111], precision: 1, expected: 1.1 }, + { end: [2, 333333333], precision: 2, expected: 2.33 }, + { + end: [3, 555555555], + precision: DEFAULT_DURATION_PRECISION, + expected: 3.556, + }, + { end: [4, 777777777], precision: 4, expected: 4.7778 }, + ]; + + test.each(testDurations)( + "($end.0 s + $end.1 ns) rounded-off to $expected", + ({ end, precision, expected }) => { + expect(getDurationInSeconds(end as [number, number], precision)).toBe( + expected + ); + } + ); + }); + + describe("getEffectiveFinalMetaData", () => { + const DEFAULT_ENV = { + name: "name", + variables: [{ key: "PARAM", value: "parsed_param" }], + }; + + test("Empty list of meta-data", () => { + expect(getEffectiveFinalMetaData([], DEFAULT_ENV)).toSubsetEqualRight([]); + }); + + test("Non-empty active list of meta-data with unavailable ENV", () => { + expect( + getEffectiveFinalMetaData( + [ + { + active: true, + key: "<>", + value: "<>", + }, + ], + DEFAULT_ENV + ) + ).toSubsetEqualRight([{ active: true, key: "", value: "" }]); + }); + + test("Inactive list of meta-data", () => { + expect( + getEffectiveFinalMetaData( + [{ active: false, key: "KEY", value: "<>" }], + DEFAULT_ENV + ) + ).toSubsetEqualRight([]); + }); + + test("Active list of meta-data", () => { + expect( + getEffectiveFinalMetaData( + [{ active: true, key: "PARAM", value: "<>" }], + DEFAULT_ENV + ) + ).toSubsetEqualRight([ + { active: true, key: "PARAM", value: "parsed_param" }, + ]); + }); + }); + + describe("getResourceContents", () => { + describe("Network call failure", () => { + const args = { + pathOrId: "test-collection-id-or-path", + resourceType: "collection" as const, + accessToken: "test-token", + serverUrl: "test-url", + }; + + const cases = [ + { + description: + "Promise rejects with the code `SERVER_CONNECTION_REFUSED` if the network call fails with the code `ECONNREFUSED`", + args, + axiosMock: { + code: "ECONNREFUSED", + }, + expected: { + code: "SERVER_CONNECTION_REFUSED", + data: args.serverUrl, + }, + }, + { + description: + "Promise rejects with the code `INVALID_SERVER_URL` if the network call fails with the code `ERR_INVALID_URL`", + args, + axiosMock: { + code: "ERR_INVALID_URL", + }, + expected: { + code: "INVALID_SERVER_URL", + data: args.serverUrl, + }, + }, + { + description: + "Promise rejects with the code `INVALID_SERVER_URL` if the network call fails with the code `ENOTFOUND`", + args, + axiosMock: { + code: "ENOTFOUND", + }, + expected: { + code: "INVALID_SERVER_URL", + data: args.serverUrl, + }, + }, + { + description: + "Promise rejects with the code `INVALID_SERVER_URL` if the network call returns a response with a status code of `404`", + args, + axiosMock: { + response: { + status: 404, + }, + }, + expected: { + code: "INVALID_SERVER_URL", + data: args.serverUrl, + }, + }, + { + description: + "Promise rejects with the code `TOKEN_EXPIRED` if the network call fails for the same reason", + args, + axiosMock: { + response: { + data: { + reason: "TOKEN_EXPIRED", + }, + }, + }, + expected: { + code: "TOKEN_EXPIRED", + data: args.accessToken, + }, + }, + { + description: + "Promise rejects with the code `TOKEN_INVALID` if the network call fails for the same reason", + args, + axiosMock: { + response: { + data: { + reason: "TOKEN_INVALID", + }, + }, + }, + expected: { + code: "TOKEN_INVALID", + data: args.accessToken, + }, + }, + { + description: + "Promise rejects with the code `INVALID_ID` if the network call fails for the same reason when the supplied collection ID or path is invalid", + args, + axiosMock: { + response: { + data: { + reason: "INVALID_ID", + }, + }, + }, + expected: { + code: "INVALID_ID", + data: args.pathOrId, + }, + }, + { + description: + "Promise rejects with the code `INVALID_ID` if the network call fails for the same reason when the supplied environment ID or path is invalid", + args: { + ...args, + pathOrId: "test-environment-id-or-path", + resourceType: "environment" as const, + }, + axiosMock: { + response: { + data: { + reason: "INVALID_ID", + }, + }, + }, + expected: { + code: "INVALID_ID", + data: "test-environment-id-or-path", + }, + }, + ]; + + test.each(cases)("$description", ({ args, axiosMock, expected }) => { + const { code, response } = axiosMock; + const axiosErrMessage = code ?? response?.data?.reason; + + vi.spyOn(axios, "get").mockImplementation(() => + Promise.reject( + new AxiosError( + axiosErrMessage, + code, + undefined, + undefined, + response as AxiosResponse + ) + ) + ); + + expect(getResourceContents(args)).rejects.toEqual(expected); + }); + + test("Promise rejects with the code `INVALID_SERVER_URL` if the network call succeeds and the received response content type is not `application/json`", () => { + const expected = { + code: "INVALID_SERVER_URL", + data: args.serverUrl, + }; + + vi.spyOn(axios, "get").mockImplementation(() => + Promise.resolve({ + data: "", + headers: { "content-type": "text/html; charset=UTF-8" }, + }) + ); + + expect(getResourceContents(args)).rejects.toEqual(expected); + }); + + test("Promise rejects with the code `UNKNOWN_ERROR` while encountering an error that is not an instance of `AxiosError`", () => { + const expected = { + code: "UNKNOWN_ERROR", + data: new TypeError("UNKNOWN_ERROR"), + }; + + vi.spyOn(axios, "get").mockImplementation(() => + Promise.reject(new Error("UNKNOWN_ERROR")) + ); + + expect(getResourceContents(args)).rejects.toEqual(expected); + }); + }); + + describe("Success", () => { + test("Proceeds with reading from the file system if the supplied file exists in the path", async () => { + fs.access = vi.fn().mockResolvedValueOnce(undefined); + + const sampleCollectionContents: HoppCollection = { + v: CollectionSchemaVersion, + id: "valid-collection-id", + name: "valid-collection-title", + folders: [], + requests: [], + headers: [], + auth: { + authType: "none", + authActive: false, + }, + }; + + axios.get = vi.fn(); + + vi.spyOn(mutators, "readJsonFile").mockImplementation(() => + Promise.resolve(sampleCollectionContents) + ); + + const pathOrId = "valid-collection-file-path"; + const resourceType = "collection"; + const accessToken = "valid-access-token"; + const serverUrl = "valid-url"; + + const contents = await getResourceContents({ + pathOrId, + accessToken, + serverUrl, + resourceType, + }); + + expect(fs.access).toHaveBeenCalledWith(pathOrId); + expect(axios.get).not.toBeCalled(); + expect(mutators.readJsonFile).toHaveBeenCalledWith(pathOrId, true); + + expect(contents).toEqual(sampleCollectionContents); + }); + + test("Proceeds with the network call if a value for the access token is specified and the supplied path/id is not a valid file path", async () => { + fs.access = vi.fn().mockRejectedValueOnce(undefined); + + const sampleCollectionContents: HoppCollection = { + v: CollectionSchemaVersion, + name: "test-coll", + folders: [], + requests: [getDefaultRESTRequest()], + headers: [], + auth: { + authType: "none", + authActive: false, + }, + }; + + axios.get = vi.fn().mockImplementation(() => + Promise.resolve({ + data: { + id: "clx06ik0o00028t6uwywwnxgg", + data: null, + title: "test-coll", + parentID: null, + folders: [], + requests: [ + { + id: "clx06imin00038t6uynt5vyk4", + collectionID: "clx06ik0o00028t6uwywwnxgg", + teamID: "clwt6r6j10031kc6pu0b08y6e", + title: "req1", + request: + '{"v":"4","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"req1","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"","preRequestScript":"","requestVariables":[]}', + }, + ], + }, + headers: { + "content-type": "application/json", + }, + }) + ); + + vi.spyOn(mutators, "readJsonFile").mockImplementation(() => + Promise.resolve(sampleCollectionContents) + ); + + vi.spyOn( + workspaceAccessHelpers, + "transformWorkspaceCollections" + ).mockImplementation(() => [sampleCollectionContents]); + + const pathOrId = "valid-collection-id"; + const resourceType = "collection"; + const accessToken = "valid-access-token"; + const serverUrl = "valid-url"; + + await getResourceContents({ + pathOrId, + accessToken, + serverUrl, + resourceType, + }); + + expect(fs.access).toHaveBeenCalledWith(pathOrId); + expect(axios.get).toBeCalledWith( + `${serverUrl}/v1/access-tokens/${resourceType}/${pathOrId}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + expect( + workspaceAccessHelpers.transformWorkspaceCollections + ).toBeCalled(); + expect(mutators.readJsonFile).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts new file mode 100644 index 000000000..3677b6747 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts @@ -0,0 +1,57 @@ +import { describe, expect, test } from "vitest"; + +import { + transformWorkspaceCollections, + transformWorkspaceEnvironment, +} from "../../utils/workspace-access"; +import { + TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK, + TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, + TRANSFORMED_ENVIRONMENT_MOCK, + WORKSPACE_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK, + WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, + WORKSPACE_ENVIRONMENT_MOCK, + WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, +} from "./fixtures/workspace-access.mock"; + +import TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK from "../e2e/fixtures/collections/multiple-child-collections-auth-headers-coll.json"; + +describe("workspace-access", () => { + describe("transformWorkspaceCollection", () => { + test("Successfully transforms collection data with deeply nested collections and authorization/headers set at each level to the `HoppCollection` format", () => { + expect( + transformWorkspaceCollections( + WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK + ) + ).toEqual(TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK); + }); + + test("Successfully transforms collection data with multiple child collections and authorization/headers set at each level to the `HoppCollection` format", () => { + expect( + transformWorkspaceCollections( + WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK + ) + ).toEqual([ + TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK, + ]); + }); + + test("Adds the default value for `auth` & `header` fields while transforming collections without authorization/headers set at certain levels", () => { + expect( + transformWorkspaceCollections( + WORKSPACE_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK + ) + ).toEqual( + TRANSFORMED_COLLECTIONS_WITHOUT_AUTH_HEADERS_AT_CERTAIN_LEVELS_MOCK + ); + }); + }); + + describe("transformWorkspaceEnvironment", () => { + test("Successfully transforms environment data conforming to the format received from the network call to the `HoppEnvironment` format", () => { + expect(transformWorkspaceEnvironment(WORKSPACE_ENVIRONMENT_MOCK)).toEqual( + TRANSFORMED_ENVIRONMENT_MOCK + ); + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/utils.ts b/packages/hoppscotch-cli/src/__tests__/utils.ts index 40c4d70f0..c86a655dc 100644 --- a/packages/hoppscotch-cli/src/__tests__/utils.ts +++ b/packages/hoppscotch-cli/src/__tests__/utils.ts @@ -37,7 +37,7 @@ export const getTestJsonFilePath = ( const filePath = resolve( __dirname, - `../../src/__tests__/samples/${kindDir}/${file}` + `../../src/__tests__/e2e/fixtures/${kindDir}/${file}` ); return filePath; }; diff --git a/packages/hoppscotch-cli/src/commands/test.ts b/packages/hoppscotch-cli/src/commands/test.ts index bdc563892..6143f5baf 100644 --- a/packages/hoppscotch-cli/src/commands/test.ts +++ b/packages/hoppscotch-cli/src/commands/test.ts @@ -1,30 +1,34 @@ +import { handleError } from "../handlers/error"; +import { parseDelayOption } from "../options/test/delay"; +import { parseEnvsData } from "../options/test/env"; +import { TestCmdEnvironmentOptions, TestCmdOptions } from "../types/commands"; +import { HoppEnvs } from "../types/request"; +import { isHoppCLIError } from "../utils/checks"; import { collectionsRunner, collectionsRunnerExit, collectionsRunnerResult, } from "../utils/collections"; -import { handleError } from "../handlers/error"; import { parseCollectionData } from "../utils/mutators"; -import { parseEnvsData } from "../options/test/env"; -import { TestCmdOptions } from "../types/commands"; -import { parseDelayOption } from "../options/test/delay"; -import { HoppEnvs } from "../types/request"; -import { isHoppCLIError } from "../utils/checks"; -export const test = (path: string, options: TestCmdOptions) => async () => { +export const test = (pathOrId: string, options: TestCmdOptions) => async () => { try { - const delay = options.delay ? parseDelayOption(options.delay) : 0 - const envs = options.env ? await parseEnvsData(options.env) : { global: [], selected: [] } - const collections = await parseCollectionData(path) + const delay = options.delay ? parseDelayOption(options.delay) : 0; - const report = await collectionsRunner({collections, envs, delay}) - const hasSucceeded = collectionsRunnerResult(report) - collectionsRunnerExit(hasSucceeded) - } catch(e) { - if(isHoppCLIError(e)) { - handleError(e) + const envs = options.env + ? await parseEnvsData(options as TestCmdEnvironmentOptions) + : { global: [], selected: [] }; + + const collections = await parseCollectionData(pathOrId, options); + + const report = await collectionsRunner({ collections, envs, delay }); + const hasSucceeded = collectionsRunnerResult(report, options.reporterJunit); + + collectionsRunnerExit(hasSucceeded); + } catch (e) { + if (isHoppCLIError(e)) { + handleError(e); process.exit(1); - } - else throw e + } else throw e; } }; diff --git a/packages/hoppscotch-cli/src/handlers/error.ts b/packages/hoppscotch-cli/src/handlers/error.ts index 34430cf48..65918136e 100644 --- a/packages/hoppscotch-cli/src/handlers/error.ts +++ b/packages/hoppscotch-cli/src/handlers/error.ts @@ -63,7 +63,7 @@ export const handleError = (error: HoppError) => { ERROR_MSG = `Unable to parse -\n${error.data}`; break; case "INVALID_FILE_TYPE": - ERROR_MSG = `Please provide file of extension type: ${error.data}`; + ERROR_MSG = `Please provide file of extension type .json: ${error.data}`; break; case "REQUEST_ERROR": case "TEST_SCRIPT_ERROR": @@ -82,6 +82,25 @@ export const handleError = (error: HoppError) => { case "TESTS_FAILING": ERROR_MSG = error.data; break; + case "TOKEN_EXPIRED": + ERROR_MSG = `The specified access token is expired. Please provide a valid token: ${error.data}`; + break; + case "TOKEN_INVALID": + ERROR_MSG = `The specified access token is invalid. Please provide a valid token: ${error.data}`; + break; + case "INVALID_ID": + ERROR_MSG = `The specified collection/environment (ID or file path) is invalid or inaccessible. Please ensure the supplied ID or file path is correct: ${error.data}`; + break; + case "INVALID_SERVER_URL": + ERROR_MSG = `Please provide a valid SH instance server URL: ${error.data}`; + break; + case "SERVER_CONNECTION_REFUSED": + ERROR_MSG = `Unable to connect to the server. Please check your network connection or server instance URL and try again: ${error.data}`; + break; + case "REPORT_EXPORT_FAILED": + const moreInfo = error.data ? `: ${error.data}` : S.empty; + ERROR_MSG = `Failed to export the report at ${error.path}${moreInfo}`; + break; } if (!S.isEmpty(ERROR_MSG)) { diff --git a/packages/hoppscotch-cli/src/index.ts b/packages/hoppscotch-cli/src/index.ts index daa71995a..ebd20c01f 100644 --- a/packages/hoppscotch-cli/src/index.ts +++ b/packages/hoppscotch-cli/src/index.ts @@ -18,7 +18,7 @@ const CLI_BEFORE_ALL_TXT = `hopp: The ${accent( )}) ${chalk.black.bold.bgYellowBright(" ALPHA ")} \n`; const CLI_AFTER_ALL_TXT = `\nFor more help, head on to ${accent( - "https://docs.hoppscotch.io/documentation/clients/cli" + "https://docs.hoppscotch.io/documentation/clients/cli/overview" )}`; const program = new Command(); @@ -49,24 +49,47 @@ program.exitOverride().configureOutput({ program .command("test") .argument( - "", - "path to a hoppscotch collection.json file for CI testing" + "", + "path to a hoppscotch collection.json file or collection ID from a workspace for CI testing" + ) + .option( + "-e, --env ", + "path to an environment variables json file or environment ID from a workspace" ) - .option("-e, --env ", "path to an environment variables json file") .option( "-d, --delay ", "delay in milliseconds(ms) between consecutive requests within a collection" ) + .option( + "--token ", + "personal access token to access collections/environments from a workspace" + ) + .option("--server ", "server URL for SH instance") + .option( + "--reporter-junit [path]", + "generate JUnit report optionally specifying the path" + ) .allowExcessArguments(false) .allowUnknownOption(false) .description("running hoppscotch collection.json file") .addHelpText( "after", `\nFor help, head on to ${accent( - "https://docs.hoppscotch.io/documentation/clients/cli#commands" + "https://docs.hoppscotch.io/documentation/clients/cli/overview#commands" )}` ) - .action(async (path, options) => await test(path, options)()); + .action(async (pathOrId, options) => { + const overrides: Record = {}; + + // Choose `hopp-junit-report.xml` as the default value if `reporter-junit` flag is supplied without a value + if (options.reporterJunit === true) { + overrides.reporterJunit = "hopp-junit-report.xml"; + } + + const effectiveOptions = { ...options, ...overrides }; + + await test(pathOrId, effectiveOptions)(); + }); export const cli = async (args: string[]) => { try { diff --git a/packages/hoppscotch-cli/src/options/test/env.ts b/packages/hoppscotch-cli/src/options/test/env.ts index 2cec231a1..d42450344 100644 --- a/packages/hoppscotch-cli/src/options/test/env.ts +++ b/packages/hoppscotch-cli/src/options/test/env.ts @@ -2,21 +2,34 @@ import { Environment } from "@hoppscotch/data"; import { entityReference } from "verzod"; import { z } from "zod"; +import { TestCmdEnvironmentOptions } from "../../types/commands"; import { error } from "../../types/errors"; import { HoppEnvKeyPairObject, HoppEnvPair, HoppEnvs, } from "../../types/request"; -import { readJsonFile } from "../../utils/mutators"; +import { getResourceContents } from "../../utils/getters"; /** - * Parses env json file for given path and validates the parsed env json object - * @param path Path of env.json file to be parsed - * @returns For successful parsing we get HoppEnvs object + * Parses environment data from a given path or ID and returns the data conforming to the latest version of the `Environment` schema. + * + * @param {TestCmdEnvironmentOptions} options Supplied values for CLI flags. + * @param {string} options.env Path of the environment `.json` file to be parsed. + * @param {string} [options.token] Personal access token to fetch workspace environments. + * @param {string} [options.server] server URL for SH instance. + * @returns {Promise} A promise that resolves to the parsed environment object with global and selected environments. */ -export async function parseEnvsData(path: string) { - const contents = await readJsonFile(path); +export async function parseEnvsData(options: TestCmdEnvironmentOptions) { + const { env: pathOrId, token: accessToken, server: serverUrl } = options; + + const contents = await getResourceContents({ + pathOrId, + accessToken, + serverUrl, + resourceType: "environment", + }); + const envPairs: Array> = []; // The legacy key-value pair format that is still supported @@ -33,7 +46,7 @@ export async function parseEnvsData(path: string) { // CLI doesnt support bulk environments export // Hence we check for this case and throw an error if it matches the format if (HoppBulkEnvExportObjectResult.success) { - throw error({ code: "BULK_ENV_FILE", path, data: error }); + throw error({ code: "BULK_ENV_FILE", path: pathOrId, data: error }); } // Checks if the environment file is of the correct format @@ -42,7 +55,7 @@ export async function parseEnvsData(path: string) { !HoppEnvKeyPairResult.success && HoppEnvExportObjectResult.type === "err" ) { - throw error({ code: "MALFORMED_ENV_FILE", path, data: error }); + throw error({ code: "MALFORMED_ENV_FILE", path: pathOrId, data: error }); } if (HoppEnvKeyPairResult.success) { diff --git a/packages/hoppscotch-cli/src/types/commands.ts b/packages/hoppscotch-cli/src/types/commands.ts index 0c8eab855..71fd51f6b 100644 --- a/packages/hoppscotch-cli/src/types/commands.ts +++ b/packages/hoppscotch-cli/src/types/commands.ts @@ -1,6 +1,17 @@ export type TestCmdOptions = { - env: string | undefined; - delay: string | undefined; + env?: string; + delay?: string; + token?: string; + server?: string; + reporterJunit?: string; +}; + +// Consumed in the collection `file_path_or_id` argument action handler +export type TestCmdCollectionOptions = Omit; + +// Consumed in the `--env, -e` flag action handler +export type TestCmdEnvironmentOptions = Omit & { + env: string; }; export type HOPP_ENV_FILE_EXT = "json"; diff --git a/packages/hoppscotch-cli/src/types/errors.ts b/packages/hoppscotch-cli/src/types/errors.ts index 0e75b7447..354b2da4f 100644 --- a/packages/hoppscotch-cli/src/types/errors.ts +++ b/packages/hoppscotch-cli/src/types/errors.ts @@ -26,6 +26,12 @@ type HoppErrors = { MALFORMED_ENV_FILE: HoppErrorPath & HoppErrorData; BULK_ENV_FILE: HoppErrorPath & HoppErrorData; INVALID_FILE_TYPE: HoppErrorData; + TOKEN_EXPIRED: HoppErrorData; + TOKEN_INVALID: HoppErrorData; + INVALID_ID: HoppErrorData; + INVALID_SERVER_URL: HoppErrorData; + SERVER_CONNECTION_REFUSED: HoppErrorData; + REPORT_EXPORT_FAILED: HoppErrorPath & HoppErrorData; }; export type HoppErrorCode = keyof HoppErrors; diff --git a/packages/hoppscotch-cli/src/utils/collections.ts b/packages/hoppscotch-cli/src/utils/collections.ts index c4708c817..9a6ef5ef1 100644 --- a/packages/hoppscotch-cli/src/utils/collections.ts +++ b/packages/hoppscotch-cli/src/utils/collections.ts @@ -27,6 +27,7 @@ import { } from "./display"; import { exceptionColors } from "./getters"; import { getPreRequestMetrics } from "./pre-request"; +import { buildJUnitReport, generateJUnitReportExport } from "./reporters/junit"; import { getRequestMetrics, preProcessRequest, @@ -56,19 +57,22 @@ export const collectionsRunner = async ( // Pop out top-most collection from stack to be processed. const { collection, path } = collectionStack.pop(); - // Processing each request in collection - for (const request of collection.requests) { - const _request = preProcessRequest(request as HoppRESTRequest, collection); - const requestPath = `${path}/${_request.name}`; - const processRequestParams: ProcessRequestParams = { - path: requestPath, - request: _request, - envs, - delay, - }; + // Processing each request in collection + for (const request of collection.requests) { + const _request = preProcessRequest( + request as HoppRESTRequest, + collection + ); + const requestPath = `${path}/${_request.name}`; + const processRequestParams: ProcessRequestParams = { + path: requestPath, + request: _request, + envs, + delay, + }; - // Request processing initiated message. - log(WARN(`\nRunning: ${chalk.bold(requestPath)}`)); + // Request processing initiated message. + log(WARN(`\nRunning: ${chalk.bold(requestPath)}`)); // Processing current request. const result = await processRequest(processRequestParams)(); @@ -78,35 +82,40 @@ export const collectionsRunner = async ( envs.global = global; envs.selected = selected; - // Storing current request's report. - const requestReport = result.report; - requestsReport.push(requestReport); - } - - // Pushing remaining folders realted collection to stack. - for (const folder of collection.folders) { - const updatedFolder: HoppCollection = { ...folder } - - if (updatedFolder.auth?.authType === "inherit") { - updatedFolder.auth = collection.auth; - } - - if (collection.headers?.length) { - // Filter out header entries present in the parent collection under the same name - // This ensures the folder headers take precedence over the collection headers - const filteredHeaders = collection.headers.filter((collectionHeaderEntries) => { - return !updatedFolder.headers.some((folderHeaderEntries) => folderHeaderEntries.key === collectionHeaderEntries.key) - }) - updatedFolder.headers.push(...filteredHeaders); - } - - collectionStack.push({ - path: `${path}/${updatedFolder.name}`, - collection: updatedFolder, - }); - } + // Storing current request's report. + const requestReport = result.report; + requestsReport.push(requestReport); } + // Pushing remaining folders realted collection to stack. + for (const folder of collection.folders) { + const updatedFolder: HoppCollection = { ...folder }; + + if (updatedFolder.auth?.authType === "inherit") { + updatedFolder.auth = collection.auth; + } + + if (collection.headers?.length) { + // Filter out header entries present in the parent collection under the same name + // This ensures the folder headers take precedence over the collection headers + const filteredHeaders = collection.headers.filter( + (collectionHeaderEntries) => { + return !updatedFolder.headers.some( + (folderHeaderEntries) => + folderHeaderEntries.key === collectionHeaderEntries.key + ); + } + ); + updatedFolder.headers.push(...filteredHeaders); + } + + collectionStack.push({ + path: `${path}/${updatedFolder.name}`, + collection: updatedFolder, + }); + } + } + return requestsReport; }; @@ -134,7 +143,8 @@ const getCollectionStack = (collections: HoppCollection[]): CollectionStack[] => * False, if errors occurred or test-cases failed. */ export const collectionsRunnerResult = ( - requestsReport: RequestReport[] + requestsReport: RequestReport[], + reporterJUnitExportPath?: string ): boolean => { const overallTestMetrics = { tests: { failed: 0, passed: 0 }, @@ -152,6 +162,9 @@ export const collectionsRunnerResult = ( }; let finalResult = true; + let totalErroredTestCases = 0; + let totalFailedTestCases = 0; + // Printing requests-report details of failed-tests and errors for (const requestReport of requestsReport) { const { path, tests, errors, result, duration } = requestReport; @@ -165,6 +178,19 @@ export const collectionsRunnerResult = ( printErrorsReport(path, errors); + if (reporterJUnitExportPath) { + const { failedRequestTestCases, erroredRequestTestCases } = + buildJUnitReport({ + path, + tests, + errors, + duration: duration.test, + }); + + totalFailedTestCases += failedRequestTestCases; + totalErroredTestCases += erroredRequestTestCases; + } + /** * Extracting current request report's test-metrics and updating * overall test-metrics. @@ -216,6 +242,19 @@ export const collectionsRunnerResult = ( printRequestsMetrics(overallRequestMetrics); printPreRequestMetrics(overallPreRequestMetrics); + if (reporterJUnitExportPath) { + const totalTestCases = + overallTestMetrics.tests.failed + overallTestMetrics.tests.passed; + + generateJUnitReportExport({ + totalTestCases, + totalFailedTestCases, + totalErroredTestCases, + testDuration: overallTestMetrics.duration, + reporterJUnitExportPath, + }); + } + return finalResult; }; diff --git a/packages/hoppscotch-cli/src/utils/getters.ts b/packages/hoppscotch-cli/src/utils/getters.ts index 5bbb2b8ee..5df063cb1 100644 --- a/packages/hoppscotch-cli/src/utils/getters.ts +++ b/packages/hoppscotch-cli/src/utils/getters.ts @@ -1,18 +1,36 @@ import { - HoppRESTHeader, Environment, - parseTemplateStringE, + HoppCollection, + HoppRESTHeader, HoppRESTParam, + parseTemplateStringE, } from "@hoppscotch/data"; +import axios, { AxiosError } from "axios"; import chalk from "chalk"; -import { pipe } from "fp-ts/function"; import * as A from "fp-ts/Array"; import * as E from "fp-ts/Either"; -import * as S from "fp-ts/string"; import * as O from "fp-ts/Option"; -import { error } from "../types/errors"; +import { pipe } from "fp-ts/function"; +import * as S from "fp-ts/string"; +import fs from "fs/promises"; import { round } from "lodash-es"; + +import { error } from "../types/errors"; import { DEFAULT_DURATION_PRECISION } from "./constants"; +import { readJsonFile } from "./mutators"; +import { + WorkspaceCollection, + WorkspaceEnvironment, + transformWorkspaceCollections, + transformWorkspaceEnvironment, +} from "./workspace-access"; + +type GetResourceContentsParams = { + pathOrId: string; + accessToken?: string; + serverUrl?: string; + resourceType: "collection" | "environment"; +}; /** * Generates template string (status + statusText) with specific color unicodes @@ -134,3 +152,104 @@ export const roundDuration = ( duration: number, precision: number = DEFAULT_DURATION_PRECISION ) => round(duration, precision); + +/** + * Retrieves the contents of a resource (collection or environment) from a local file (export) or a remote server (workspaces). + * + * @param {GetResourceContentsParams} params - The parameters for retrieving resource contents. + * @param {string} params.pathOrId - The path to the local file or the ID for remote retrieval. + * @param {string} [params.accessToken] - The access token for authorizing remote retrieval. + * @param {string} [params.serverUrl] - The SH instance server URL for remote retrieval. Defaults to the cloud instance. + * @param {"collection" | "environment"} params.resourceType - The type of the resource to retrieve. + * @returns {Promise} A promise that resolves to the contents of the resource. + * @throws Will throw an error if the content type of the fetched resource is not `application/json`, + * if there is an issue with the access token, if the server connection is refused, + * or if the server URL is invalid. + */ +export const getResourceContents = async ( + params: GetResourceContentsParams +): Promise => { + const { pathOrId, accessToken, serverUrl, resourceType } = params; + + let contents: unknown | null = null; + let fileExistsInPath = false; + + try { + await fs.access(pathOrId); + fileExistsInPath = true; + } catch (e) { + fileExistsInPath = false; + } + + if (accessToken && !fileExistsInPath) { + const resolvedServerUrl = serverUrl || "https://api.hoppscotch.io"; + + try { + const separator = resolvedServerUrl.endsWith("/") ? "" : "/"; + const resourcePath = + resourceType === "collection" ? "collection" : "environment"; + + const url = `${resolvedServerUrl}${separator}v1/access-tokens/${resourcePath}/${pathOrId}`; + + const { data, headers } = await axios.get(url, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + if (!headers["content-type"].includes("application/json")) { + throw new AxiosError("INVALID_CONTENT_TYPE"); + } + + contents = + resourceType === "collection" + ? transformWorkspaceCollections([data] as WorkspaceCollection[])[0] + : transformWorkspaceEnvironment(data as WorkspaceEnvironment); + } catch (err) { + if (err instanceof AxiosError) { + const axiosErr: AxiosError<{ + reason?: "TOKEN_EXPIRED" | "TOKEN_INVALID" | "INVALID_ID"; + message: string; + statusCode: number; + }> = err; + + const errReason = axiosErr.response?.data?.reason; + + if (errReason) { + throw error({ + code: errReason, + data: ["TOKEN_EXPIRED", "TOKEN_INVALID"].includes(errReason) + ? accessToken + : pathOrId, + }); + } + + if (axiosErr.code === "ECONNREFUSED") { + throw error({ + code: "SERVER_CONNECTION_REFUSED", + data: resolvedServerUrl, + }); + } + + if ( + axiosErr.message === "INVALID_CONTENT_TYPE" || + axiosErr.code === "ERR_INVALID_URL" || + axiosErr.code === "ENOTFOUND" || + axiosErr.code === "ERR_BAD_REQUEST" || + axiosErr.response?.status === 404 + ) { + throw error({ code: "INVALID_SERVER_URL", data: resolvedServerUrl }); + } + } else { + throw error({ code: "UNKNOWN_ERROR", data: err }); + } + } + } + + // Fallback to reading from file if contents are not available + if (contents === null) { + contents = await readJsonFile(pathOrId, fileExistsInPath); + } + + return contents; +}; diff --git a/packages/hoppscotch-cli/src/utils/mutators.ts b/packages/hoppscotch-cli/src/utils/mutators.ts index 516f720e2..6c4ac6547 100644 --- a/packages/hoppscotch-cli/src/utils/mutators.ts +++ b/packages/hoppscotch-cli/src/utils/mutators.ts @@ -1,11 +1,13 @@ -import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; +import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; import fs from "fs/promises"; import { entityReference } from "verzod"; import { z } from "zod"; +import { TestCmdCollectionOptions } from "../types/commands"; import { error } from "../types/errors"; import { FormDataEntry } from "../types/request"; import { isHoppErrnoException } from "./checks"; +import { getResourceContents } from "./getters"; const getValidRequests = ( collections: HoppCollection[], @@ -72,15 +74,26 @@ export const parseErrorMessage = (e: unknown) => { return msg.replace(/\n+$|\s{2,}/g, "").trim(); }; -export async function readJsonFile(path: string): Promise { +/** + * Reads a JSON file from the specified path and returns the parsed content. + * + * @param {string} path - The path to the JSON file. + * @param {boolean} fileExistsInPath - Indicates whether the file exists in the specified path. + * @returns {Promise} A Promise that resolves to the parsed JSON contents. + * @throws {Error} If the file path does not end with `.json`. + * @throws {Error} If the file does not exist in the specified path. + * @throws {Error} If an unknown error occurs while reading or parsing the file. + */ +export async function readJsonFile( + path: string, + fileExistsInPath: boolean +): Promise { if (!path.endsWith(".json")) { throw error({ code: "INVALID_FILE_TYPE", data: path }); } - try { - await fs.access(path); - } catch (e) { - throw error({ code: "FILE_NOT_FOUND", path: path }); + if (!fileExistsInPath) { + throw error({ code: "FILE_NOT_FOUND", path }); } try { @@ -91,15 +104,27 @@ export async function readJsonFile(path: string): Promise { } /** - * Parses collection json file for given path:context.path, and validates - * the parsed collectiona array. - * @param path Collection json file path. - * @returns For successful parsing we get array of HoppCollection, + * Parses collection data from a given path or ID and returns the data conforming to the latest version of the `HoppCollection` schema. + * + * @param pathOrId Collection JSON file path/ID from a workspace. + * @param {TestCmdCollectionOptions} options Supplied values for CLI flags. + * @param {string} [options.token] Personal access token to fetch workspace environments. + * @param {string} [options.server] server URL for SH instance. + * @returns {Promise} A promise that resolves to an array of HoppCollection objects. + * @throws Throws an error if the collection data is malformed. */ export async function parseCollectionData( - path: string + pathOrId: string, + options: TestCmdCollectionOptions ): Promise { - let contents = await readJsonFile(path); + const { token: accessToken, server: serverUrl } = options; + + const contents = await getResourceContents({ + pathOrId, + accessToken, + serverUrl, + resourceType: "collection", + }); const maybeArrayOfCollections: unknown[] = Array.isArray(contents) ? contents @@ -112,10 +137,10 @@ export async function parseCollectionData( if (!collectionSchemaParsedResult.success) { throw error({ code: "MALFORMED_COLLECTION", - path, + path: pathOrId, data: "Please check the collection data.", }); } - return getValidRequests(collectionSchemaParsedResult.data, path); + return getValidRequests(collectionSchemaParsedResult.data, pathOrId); } diff --git a/packages/hoppscotch-cli/src/utils/pre-request.ts b/packages/hoppscotch-cli/src/utils/pre-request.ts index b1a760ab3..f39410644 100644 --- a/packages/hoppscotch-cli/src/utils/pre-request.ts +++ b/packages/hoppscotch-cli/src/utils/pre-request.ts @@ -47,7 +47,10 @@ export const preRequestScriptRunner = ( ), TE.map( ({ selected, global }) => - { name: "Env", variables: [...selected, ...global] } + { + name: "Env", + variables: [...(selected ?? []), ...(global ?? [])], + } ), TE.chainEitherKW((env) => getEffectiveRESTRequest(request, env)), TE.mapLeft((reason) => diff --git a/packages/hoppscotch-cli/src/utils/reporters/junit.ts b/packages/hoppscotch-cli/src/utils/reporters/junit.ts new file mode 100644 index 000000000..a47fdf546 --- /dev/null +++ b/packages/hoppscotch-cli/src/utils/reporters/junit.ts @@ -0,0 +1,178 @@ +import { info, log } from "console"; +import fs from "fs"; +import path from "path"; + +import { create } from "xmlbuilder2"; +import { XMLBuilder } from "xmlbuilder2/lib/interfaces"; +import { TestReport } from "../../interfaces/response"; +import { error, HoppCLIError } from "../../types/errors"; +import { RequestReport } from "../../types/request"; +import { exceptionColors } from "../getters"; + +type BuildJUnitReportArgs = Omit & { + duration: RequestReport["duration"]["test"]; +}; + +type BuildJUnitReportResult = { + failedRequestTestCases: number; + erroredRequestTestCases: number; +}; + +type GenerateJUnitReportExportArgs = { + totalTestCases: number; + totalFailedTestCases: number; + totalErroredTestCases: number; + testDuration: number; + reporterJUnitExportPath: string; +}; + +const { INFO, SUCCESS } = exceptionColors; + +// Create the root XML element +const rootEl = create({ version: "1.0", encoding: "UTF-8" }).ele("testsuites"); + +/** + * Builds a JUnit report based on the provided request report. + * Creates a test suite at the request level populating the XML document structure. + * + * @param {BuildJUnitReportArgs} options - The options to build the JUnit report. + * @param {string} options.path - The path of the request. + * @param {TestReport[]} options.tests - The test suites for the request. + * @param {HoppCLIError[]} options.errors - The errors encountered during the request. + * @param {number} options.duration - Time taken to execute the test suite. + * @returns {BuildJUnitReportResult} An object containing the number of failed and errored test cases. + */ +export const buildJUnitReport = ({ + path, + tests: testSuites, + errors: requestTestSuiteErrors, + duration: testSuiteDuration, +}: BuildJUnitReportArgs): BuildJUnitReportResult => { + let requestTestSuiteError: XMLBuilder | null = null; + + // Create a test suite at the request level + const requestTestSuite = rootEl.ele("testsuite", { + name: path, + time: testSuiteDuration, + timestamp: new Date().toISOString(), + }); + + if (requestTestSuiteErrors.length > 0) { + requestTestSuiteError = requestTestSuite.ele("system-err"); + } + + let systemErrContent = ""; + + requestTestSuiteErrors.forEach((error) => { + let compiledError = error.code; + + if ("data" in error) { + compiledError += ` - ${error.data}`; + } + + // Append each error message with a newline for separation + systemErrContent += `\n${" ".repeat(6)}${compiledError}`; + }); + + // There'll be a single `CDATA` element compiling all the error messages + if (requestTestSuiteError) { + requestTestSuiteError.dat(systemErrContent); + } + + let requestTestCases = 0; + let erroredRequestTestCases = 0; + let failedRequestTestCases = 0; + + // Test suites correspond to `pw.test()` invocations + testSuites.forEach(({ descriptor, expectResults }) => { + requestTestCases += expectResults.length; + + expectResults.forEach(({ status, message }) => { + const testCase = requestTestSuite + .ele("testcase", { + name: `${descriptor} - ${message}`, + }) + .att("classname", path); + + if (status === "fail") { + failedRequestTestCases += 1; + + testCase + .ele("failure") + .att("type", "AssertionFailure") + .att("message", message); + } else if (status === "error") { + erroredRequestTestCases += 1; + + testCase.ele("error").att("message", message); + } + }); + }); + + requestTestSuite.att("tests", requestTestCases.toString()); + requestTestSuite.att("failures", failedRequestTestCases.toString()); + requestTestSuite.att("errors", erroredRequestTestCases.toString()); + + return { + failedRequestTestCases, + erroredRequestTestCases, + }; +}; + +/** + * Generates the built JUnit report export at the specified path. + * + * @param {GenerateJUnitReportExportArgs} options - The options to generate the JUnit report export. + * @param {number} options.totalTestCases - The total number of test cases. + * @param {number} options.totalFailedTestCases - The total number of failed test cases. + * @param {number} options.totalErroredTestCases - The total number of errored test cases. + * @param {number} options.testDuration - The total duration of test cases. + * @param {string} options.reporterJUnitExportPath - The path to export the JUnit report. + * @returns {void} + */ +export const generateJUnitReportExport = ({ + totalTestCases, + totalFailedTestCases, + totalErroredTestCases, + testDuration, + reporterJUnitExportPath, +}: GenerateJUnitReportExportArgs) => { + rootEl + .att("tests", totalTestCases.toString()) + .att("failures", totalFailedTestCases.toString()) + .att("errors", totalErroredTestCases.toString()) + .att("time", testDuration.toString()); + + // Convert the XML structure to a string + const xmlDocString = rootEl.end({ prettyPrint: true }); + + // Write the XML string to the specified path + try { + const resolvedExportPath = path.resolve(reporterJUnitExportPath); + + if (fs.existsSync(resolvedExportPath)) { + info( + INFO(`\nOverwriting the pre-existing path: ${reporterJUnitExportPath}.`) + ); + } + + fs.mkdirSync(path.dirname(resolvedExportPath), { + recursive: true, + }); + + fs.writeFileSync(resolvedExportPath, xmlDocString); + + log( + SUCCESS( + `\nSuccessfully exported the JUnit report to: ${reporterJUnitExportPath}.` + ) + ); + } catch (err) { + const data = err instanceof Error ? err.message : null; + throw error({ + code: "REPORT_EXPORT_FAILED", + data, + path: reporterJUnitExportPath, + }); + } +}; diff --git a/packages/hoppscotch-cli/src/utils/request.ts b/packages/hoppscotch-cli/src/utils/request.ts index 81513bd21..16a02c519 100644 --- a/packages/hoppscotch-cli/src/utils/request.ts +++ b/packages/hoppscotch-cli/src/utils/request.ts @@ -52,10 +52,11 @@ const processVariables = (variable: Environment["variables"][number]) => { * @param envs Global + selected envs used by requests with in collection * @returns Processed envs with each variable processed */ -const processEnvs = (envs: HoppEnvs) => { +const processEnvs = (envs: Partial) => { + // This can take the shape `{ global: undefined, selected: undefined }` when no environment is supplied const processedEnvs = { - global: envs.global.map(processVariables), - selected: envs.selected.map(processVariables), + global: envs.global?.map(processVariables), + selected: envs.selected?.map(processVariables), }; return processedEnvs; @@ -270,7 +271,7 @@ export const processRequest = // Updating report for errors & current result report.errors.push(preRequestRes.left); - report.result = report.result && false; + report.result = report.result; } else { // Updating effective-request and consuming updated envs after pre-request script execution ({ effectiveRequest, updatedEnvs } = preRequestRes.right); @@ -298,7 +299,7 @@ export const processRequest = if (E.isLeft(requestRunnerRes)) { // Updating report for errors & current result report.errors.push(requestRunnerRes.left); - report.result = report.result && false; + report.result = report.result; printRequestRunner.fail(); } else { @@ -321,7 +322,7 @@ export const processRequest = // Updating report with current errors & result. report.errors.push(testRunnerRes.left); - report.result = report.result && false; + report.result = report.result; } else { const { envs, testsReport, duration } = testRunnerRes.right; const _hasFailedTestCases = hasFailedTestCases(testsReport); diff --git a/packages/hoppscotch-cli/src/utils/workspace-access.ts b/packages/hoppscotch-cli/src/utils/workspace-access.ts new file mode 100644 index 000000000..063907596 --- /dev/null +++ b/packages/hoppscotch-cli/src/utils/workspace-access.ts @@ -0,0 +1,101 @@ +import { + CollectionSchemaVersion, + Environment, + EnvironmentSchemaVersion, + HoppCollection, + HoppRESTRequest, +} from "@hoppscotch/data"; + +import { HoppEnvPair } from "../types/request"; + +export interface WorkspaceEnvironment { + id: string; + teamID: string; + name: string; + variables: HoppEnvPair[]; +} + +export interface WorkspaceCollection { + id: string; + data: string | null; + title: string; + parentID: string | null; + folders: WorkspaceCollection[]; + requests: WorkspaceRequest[]; +} + +interface WorkspaceRequest { + id: string; + collectionID: string; + teamID: string; + title: string; + request: string; +} + +/** + * Transforms the incoming list of workspace requests by applying `JSON.parse` to the `request` field. + * + * @param {WorkspaceRequest[]} requests - An array of workspace request objects to be transformed. + * @returns {HoppRESTRequest[]} The transformed array of requests conforming to the `HoppRESTRequest` type. + */ +const transformWorkspaceRequests = ( + requests: WorkspaceRequest[] +): HoppRESTRequest[] => requests.map(({ request }) => JSON.parse(request)); + +/** + * Transforms workspace environment data to the `HoppEnvironment` format. + * + * @param {WorkspaceEnvironment} workspaceEnvironment - The workspace environment object to transform. + * @returns {Environment} The transformed environment object conforming to the `Environment` type. + */ +export const transformWorkspaceEnvironment = ( + workspaceEnvironment: WorkspaceEnvironment +): Environment => { + const { teamID, variables, ...rest } = workspaceEnvironment; + + // Add `secret` field if the data conforms to an older schema + const transformedEnvVars = variables.map((variable) => { + if (!("secret" in variable)) { + return { + ...(variable as HoppEnvPair), + secret: false, + } as HoppEnvPair; + } + + return variable; + }); + + return { + v: EnvironmentSchemaVersion, + variables: transformedEnvVars, + ...rest, + }; +}; + +/** + * Transforms workspace collection data to the `HoppCollection` format. + * + * @param {WorkspaceCollection[]} collections - An array of workspace collection objects to be transformed. + * @returns {HoppCollection[]} The transformed array of collections conforming to the `HoppCollection` type. + */ +export const transformWorkspaceCollections = ( + collections: WorkspaceCollection[] +): HoppCollection[] => { + return collections.map((collection) => { + const { id, title, data, requests, folders } = collection; + + const parsedData = data ? JSON.parse(data) : {}; + const { auth = { authType: "inherit", authActive: true }, headers = [] } = + parsedData; + + return { + v: CollectionSchemaVersion, + id, + name: title, + folders: transformWorkspaceCollections(folders), + requests: transformWorkspaceRequests(requests), + auth, + headers, + }; + }); +}; diff --git a/packages/hoppscotch-cli/tsconfig.json b/packages/hoppscotch-cli/tsconfig.json index 0d8095b07..d360c14f0 100644 --- a/packages/hoppscotch-cli/tsconfig.json +++ b/packages/hoppscotch-cli/tsconfig.json @@ -10,7 +10,8 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "composite": true + "composite": true, + "lib": ["ESNext", "DOM"], }, "files": ["package.json"] } diff --git a/packages/hoppscotch-cli/vitest.config.ts b/packages/hoppscotch-cli/vitest.config.ts new file mode 100644 index 000000000..bac92a0ec --- /dev/null +++ b/packages/hoppscotch-cli/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + setupFiles: ["./setupFiles.ts"], + include: ["**/src/__tests__/**/**/*.{test,spec}.ts"], + exclude: [ + "**/node_modules/**", + "**/dist/**", + "**/src/__tests__/functions/**/*.ts", + ], + }, +}); diff --git a/packages/hoppscotch-common/locales/af.json b/packages/hoppscotch-common/locales/af.json index 8e66dab7a..fc27dea15 100644 --- a/packages/hoppscotch-common/locales/af.json +++ b/packages/hoppscotch-common/locales/af.json @@ -24,8 +24,10 @@ "go_back": "Gaan terug", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etiket", "learn_more": "Leer meer", + "download_here": "Download here", "less": "Less", "more": "Meer", "new": "Nuut", @@ -43,6 +45,7 @@ "search": "Soek", "send": "Stuur", "share": "Share", + "show_secret": "Show secret", "start": "Begin", "starting": "Starting", "stop": "Stop", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Tik 'n opdrag of soek ...", "we_use_cookies": "Ons gebruik koekies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Wat's nuut?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Rekening bestaan met verskillende geloofsbriewe - Meld aan om beide rekeninge te koppel", "all_sign_in_options": "Alle aanmeldopsies", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Gaan voort met e -pos", "continue_with_github": "Gaan voort met GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Gaan voort met Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E -pos", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Wagwoord", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Teken", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Wysig versameling", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Gee 'n geldige naam vir die versameling", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Kies 'n versameling", "select_location": "Kies ligging", + "details": "Details", "select_team": "Kies 'n span", "team_collections": "Spanversamelings" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Weet u seker dat u van Telemetry wil afskakel?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Is u seker dat u hierdie werkruimte wil sinkroniseer?" + "sync": "Is u seker dat u hierdie werkruimte wil sinkroniseer?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protokolle is leeg", + "request_variables": "This request does not have any request variables", "schema": "Koppel aan 'n GraphQL -eindpunt", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Spannaam leeg", "teams": "Spanne is leeg", "tests": "Daar is geen toetse vir hierdie versoek nie", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Kies omgewing", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Veranderlike lys" + "variables": "Variables", + "variable_list": "Veranderlike lys", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Leë versoeknaam", "f12_details": "(F12 vir meer inligting)", "gql_prettify_invalid_query": "Kon nie 'n ongeldige navraag mooi maak nie, los sintaksisfoute op en probeer weer", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Kon nie 'n ongeldige liggaam mooi maak nie, los json -sintaksisfoute op en probeer weer", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Kon nie voorafversoekskrip uitvoer nie", "something_went_wrong": "Iets het verkeerd geloop", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Uitvoer as JSON", "create_secret_gist": "Skep geheime Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gis geskep", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Teken in met GitHub om 'n geheime idee te skep", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gis geskep" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutasies", "schema": "Skema", "subscriptions": "Inskrywings", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Invoer" + "title": "Invoer", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Rou versoeksliggaam", "rename": "Rename Request", "renamed": "Versoek hernoem", + "request_variables": "Request variables", "run": "Hardloop", "save": "Stoor", "save_as": "Stoor as", @@ -557,6 +613,7 @@ "title": "Versoek", "type": "Soort versoek", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Veranderlikes", "view_my_links": "View my links", "copy_link": "Kopieer skakel" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Stel vrae en kry antwoorde", "github": "Follow us on Github", "shortcuts": "Blaai vinniger deur die app", - "team": "Kontak die span", "title": "Ondersteuning", - "twitter": "volg ons op Twitter" + "twitter": "volg ons op Twitter", + "team": "Kontak die span" }, "tab": { "authorization": "Magtiging", @@ -889,6 +953,9 @@ "query": "Navraag", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Toetse", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Sluit aan by die beta -program om toegang tot spanne te kry.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Spanne", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Sluit aan by die beta -program om toegang tot spanne te kry." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/ar.json b/packages/hoppscotch-common/locales/ar.json index 362f87548..02a8c40b4 100644 --- a/packages/hoppscotch-common/locales/ar.json +++ b/packages/hoppscotch-common/locales/ar.json @@ -24,8 +24,10 @@ "go_back": "عد", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "ملصق", "learn_more": "اقرأ أكثر", + "download_here": "Download here", "less": "اقل", "more": "أكثر", "new": "جديد", @@ -43,6 +45,7 @@ "search": "بحث", "send": "ارسل", "share": "Share", + "show_secret": "Show secret", "start": "ابدأ", "starting": "Starting", "stop": "قف", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "اكتب أمرًا أو ابحث ...", "we_use_cookies": "نحن نستخدم ملفات تعريف الارتباط", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "ما هو الجديد؟", + "see_whats_new": "See what’s new", "wiki": "ويكي" }, "auth": { "account_exists": "الحساب موجود ببيانات اعتماد مختلفة - تسجيل الدخول لربط كلا الحسابين", "all_sign_in_options": "كل خيارات تسجيل الدخول", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "تواصل مع البريد الإلكتروني", "continue_with_github": "تواصل مع جيثب", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "تواصل مع جوجل", "continue_with_microsoft": "Continue with Microsoft", "email": "بريد إلكتروني", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "كلمة المرور", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "رمز", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "تحرير المجموعة", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "الرجاء تقديم اسم صالح للمجموعة", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "حدد مجموعة", "select_location": "اختر موقعا", + "details": "Details", "select_team": "اختر فريقًا", "team_collections": "مجموعات الفريق" }, @@ -183,7 +213,8 @@ "remove_telemetry": "هل أنت متأكد أنك تريد الانسحاب من القياس عن بعد؟", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "هل أنت متأكد أنك تريد مزامنة مساحة العمل هذه؟" + "sync": "هل أنت متأكد أنك تريد مزامنة مساحة العمل هذه؟", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "لاتوجد اي دعوات معلقة لهذا الفريق", "profile": "سجل الدخول لرؤية فريقك", "protocols": "البروتوكولات فارغة", + "request_variables": "This request does not have any request variables", "schema": "اتصل بنقطة نهاية GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "اسم الفريق فارغ", "teams": "الفرق فارغة", "tests": "لا توجد اختبارات لهذا الطلب", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "حدد البيئة", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "تحديث بيئة العمل", "value": "Value", "variable": "Variable", - "variable_list": "قائمة متغيرة" + "variables": "Variables", + "variable_list": "قائمة متغيرة", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "اسم الطلب فارغ", "f12_details": "(للحصول على تفاصيل F12)", "gql_prettify_invalid_query": "تعذر تحسين استعلام غير صالح وحل أخطاء بنية الاستعلام وحاول مرة أخرى", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "تعذر تجميل جسم غير صالح وحل أخطاء بناء جملة json وحاول مرة أخرى", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "تعذر تنفيذ نص الطلب المسبق", "something_went_wrong": "هناك خطأ ما", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "تصدير بتنسيق JSON", "create_secret_gist": "إنشاء جوهر سري", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "خلقت الجست", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "تسجيل الدخول باستخدام GitHub لإنشاء جوهر سري", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "خلقت الجست" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "الطفرات", "schema": "مخطط", "subscriptions": "الاشتراكات", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "استيراد مجموعة من ملفHoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "يستورد" + "title": "يستورد", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "نص طلب خام", "rename": "Rename Request", "renamed": "تمت إعادة تسمية الطلب", + "request_variables": "Request variables", "run": "يركض", "save": "يحفظ", "save_as": "حفظ باسم", @@ -557,6 +613,7 @@ "title": "طلب", "type": "نوع الطلب", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "المتغيرات", "view_my_links": "View my links", "copy_link": "نسخ الوصلة" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "اسأل سؤالا وتلقى جوابا", "github": "Follow us on Github", "shortcuts": "تصفح التطبيق بشكل أسرع", - "team": "تواصل مع الفريق", "title": "يدعم", - "twitter": "تابعنا على تويتر" + "twitter": "تابعنا على تويتر", + "team": "تواصل مع الفريق" }, "tab": { "authorization": "تفويض", @@ -889,6 +953,9 @@ "query": "استفسار", "schema": "مخطط", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "مقبس", "sse": "SSE", "tests": "الاختبارات", @@ -917,7 +984,6 @@ "invite_tooltip": "ادعو الناس الى العمل", "invited_to_team": "{owner} قادم بدعوتك للإنضمام الى {team}", "join": "تم قبول الدعوة", - "join_beta": "انضم إلى برنامج بيتا للوصول إلى الفرق.", "join_team": "انصم الى فريق {team}", "joined_team": "لقد انضممت الى فريق {team}", "joined_team_description": "انت الآن عضو في الفريق", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "فرق", "we_sent_invite_link": "لقد أرسلنا رابط دعوة لجميع المدعوين!", - "we_sent_invite_link_description": "اطلب من جميع المدعوين التحقق من صندوق الوارد الخاص بهم. انقر على الرابط للانضمام إلى الفريق." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "اطلب من جميع المدعوين التحقق من صندوق الوارد الخاص بهم. انقر على الرابط للانضمام إلى الفريق.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "انضم إلى برنامج بيتا للوصول إلى الفرق." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/ca.json b/packages/hoppscotch-common/locales/ca.json index b32277c79..0e0363351 100644 --- a/packages/hoppscotch-common/locales/ca.json +++ b/packages/hoppscotch-common/locales/ca.json @@ -24,8 +24,10 @@ "go_back": "Tornar", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etiquetar", "learn_more": "Aprèn més", + "download_here": "Download here", "less": "Menys", "more": "Més", "new": "Novetat", @@ -43,6 +45,7 @@ "search": "Cercar", "send": "Enviar", "share": "Share", + "show_secret": "Show secret", "start": "Començar", "starting": "Starting", "stop": "Aturar", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Escriviu una comanda o cerqueu...", "we_use_cookies": "Utilitzem cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Què hi ha de nou?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "El compte existeix amb credencials diferents - Inicieu sessió per enllaçar els dos comptes", "all_sign_in_options": "Totes les opcions d'inici de sessió", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Continuar amb el correu electrònic", "continue_with_github": "Continuar amb GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Continuar amb Google", "continue_with_microsoft": "Continuar amb Microsoft", "email": "Correu electrònic", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Passar per", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Contrasenya", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Editar la col·lecció", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Proporcioneu un nom vàlid per a la col·lecció", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Seleccionar una col·lecció", "select_location": "Seleccionar la ubicació", + "details": "Details", "select_team": "Seleccionar un equip", "team_collections": "Col·leccions per equips" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Està segur que vol desactivar Telemetry?", "request_change": "Està segur que vol descartar la sol·licitud actual, els canvis no desats es perdran.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Està segur que vol sincronitzar aquest espai de treball?" + "sync": "Està segur que vol sincronitzar aquest espai de treball?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "No hi ha invitacions pendents per a aquest equip", "profile": "Inicia sessió per veure el vostre perfil", "protocols": "Els protocols estan buits", + "request_variables": "This request does not have any request variables", "schema": "Connecta't a un endpoint GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "El nom de l'equip és buit", "teams": "Els equips estan buits", "tests": "No hi ha proves per a aquesta sol·licitud", + "access_tokens": "Access tokens are empty", "shortcodes": "Els shortcodes estan buits" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Seleccioneu un entorn", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Entorn actualitzat", "value": "Value", "variable": "Variable", - "variable_list": "Llista de variables" + "variables": "Variables", + "variable_list": "Llista de variables", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nom de la sol·licitud buida", "f12_details": "(F12 per obtenir més informació)", "gql_prettify_invalid_query": "No s'ha pogut definir una consulta no vàlida, resoldre els errors de sintaxi de la consulta i tornar-ho a provar", @@ -296,6 +336,7 @@ "incorrect_email": "Correu electrònic incorrecte", "invalid_link": "Enllaç invalid", "invalid_link_description": "L'enllaç en que heu fet clic no és vàlid o ha caducat.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSON no vàlid", "json_prettify_invalid_body": "No s'ha pogut personalitzar un cos no vàlid, resol els errors de sintaxi json i tornar-ho a provar", "network_error": "Sembla que hi ha un error de xarxa. Si us plau torna-ho a provar.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "No s'ha pogut executar l'script de sol·licitud prèvia", "something_went_wrong": "Alguna cosa ha anat malament", - "test_script_fail": "No s'ha pogut executar l'script posterior a la sol·licitud" + "test_script_fail": "No s'ha pogut executar l'script posterior a la sol·licitud", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exporta com a JSON", "create_secret_gist": "Crear un Gist secret", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist creat", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Inicieu la sessió amb GitHub per crear un Gisst secret", - "title": "Exportar" + "title": "Exportar", + "success": "Successfully exported", + "gist_created": "Gist creat" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutacions", "schema": "Esquema", "subscriptions": "Subscripcions", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Importar col·leccions des d'un fitxer JSON de col·leccions Hoppscotch", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importació" + "title": "Importació", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Cos de sol·licitud sense processar", "rename": "Rename Request", "renamed": "S'ha canviat el nom de la sol·licitud", + "request_variables": "Request variables", "run": "Executar", "save": "Guardar", "save_as": "Guardar com", @@ -557,6 +613,7 @@ "title": "Sol·licitud", "type": "Tipus de sol·licitud", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variables", "view_my_links": "Visualitzar els meus enllaços", "copy_link": "Copia l'enllaç" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Feu preguntes i obteniu respostes", "github": "Segueix-nos a Github", "shortcuts": "Navega per l'aplicació més ràpidament", - "team": "Poseu-vos en contacte amb l'equip", "title": "Suport", - "twitter": "Segueix-nos a Twitter" + "twitter": "Segueix-nos a Twitter", + "team": "Poseu-vos en contacte amb l'equip" }, "tab": { "authorization": "Autorització", @@ -889,6 +953,9 @@ "query": "Consulta", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Proves", @@ -917,7 +984,6 @@ "invite_tooltip": "Invitar persones a aquest espai de treball", "invited_to_team": "{owner} t'ha invitat a unir-te a {team}", "join": "S'ha acceptat la invitació", - "join_beta": "Uneix-te al programa beta per accedir als equips.", "join_team": "Uneix-te a {team}", "joined_team": "T'has unit a {team}", "joined_team_description": "Ara ets membre d'aquest equip", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Equips", "we_sent_invite_link": "Hem enviat un enllaç d'invitació a tots els convidats!", - "we_sent_invite_link_description": "Demaneu a tots els convidats que comprovin la seva safata d'entrada. Feu clic a l'enllaç per unir-vos a l'equip." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Demaneu a tots els convidats que comprovin la seva safata d'entrada. Feu clic a l'enllaç per unir-vos a l'equip.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Uneix-te al programa beta per accedir als equips." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Accions", "created_on": "Creat el", diff --git a/packages/hoppscotch-common/locales/cn.json b/packages/hoppscotch-common/locales/cn.json index 1fa3ea5d9..d3349ba2f 100644 --- a/packages/hoppscotch-common/locales/cn.json +++ b/packages/hoppscotch-common/locales/cn.json @@ -24,8 +24,10 @@ "go_back": "返回", "go_forward": "前进", "group_by": "分组方式", + "hide_secret": "Hide secret", "label": "标签", "learn_more": "了解更多", + "download_here": "Download here", "less": "更少", "more": "更多", "new": "新增", @@ -43,6 +45,7 @@ "search": "搜索", "send": "发送", "share": "Share", + "show_secret": "Show secret", "start": "开始", "starting": "正在开始", "stop": "停止", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "输入命令或搜索内容……", "we_use_cookies": "我们使用 cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "新增内容", + "see_whats_new": "See what’s new", "wiki": "帮助" }, "auth": { "account_exists": "当前帐号已存在 - 登录以链接两个帐号", "all_sign_in_options": "所有登录选项", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "使用电子邮箱登录", "continue_with_github": "使用 GitHub 登录", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "使用 Google 登录", "continue_with_microsoft": "使用 Microsoft 登录", "email": "电子邮箱地址", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "传递方式", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "密码", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "令牌", @@ -149,6 +177,7 @@ "different_parent": "不能用不同的父类来重新排序集合", "edit": "编辑集合", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "请提供有效的集合名称", "invalid_root_move": "该集合已经在根级了", "moved": "移动完成", @@ -165,6 +194,7 @@ "save_to_collection": "保存至集合", "select": "选择一个集合", "select_location": "选择位置", + "details": "Details", "select_team": "选择一个团队", "team_collections": "团队集合" }, @@ -183,7 +213,8 @@ "remove_telemetry": "你确定要退出遥测服务吗?", "request_change": "你确定你要放弃当前的请求,未保存的修改将被丢失。", "save_unsaved_tab": "你想保存在此标签页中所作的修改吗?", - "sync": "您确定要同步该工作区吗?" + "sync": "您确定要同步该工作区吗?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "添加至参数", @@ -237,13 +268,16 @@ "pending_invites": "此团队无待办邀请", "profile": "登录以查看你的个人资料", "protocols": "协议为空", + "request_variables": "This request does not have any request variables", "schema": "连接至 GraphQL 端点", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "订阅为空", "team_name": "团队名称为空", "teams": "团队为空", "tests": "没有针对该请求的测试", + "access_tokens": "Access tokens are empty", "shortcodes": "短链接为空" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "快速浏览环境", "replace_with_variable": "替换为变量", "scope": "范围", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "选择环境", "set": "设置环境", "set_as_environment": "设置为环境", @@ -278,7 +314,10 @@ "updated": "环境已更新", "value": "值", "variable": "变量", - "variable_list": "变量列表" + "variables": "Variables", + "variable_list": "变量列表", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "危险区域", "delete_account": "您的帐号目前为这些团队的拥有者:", "delete_account_description": "您在删除帐号前必须先将您自己从团队中移除、转移拥有权,或是删除团队。", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "空请求名称", "f12_details": "(F12 详情)", "gql_prettify_invalid_query": "无法美化无效的查询,处理查询语法错误并重试", @@ -308,9 +348,14 @@ "page_not_found": "找不到此頁面", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "代理错误", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "无法执行预请求脚本", "something_went_wrong": "发生了一些错误", - "test_script_fail": "无法执行请求脚本" + "test_script_fail": "无法执行请求脚本", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "导出为 JSON", @@ -343,7 +388,8 @@ "mutations": "变更", "schema": "模式", "subscriptions": "订阅", - "switch_connection": "切换连接" + "switch_connection": "切换连接", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -409,12 +455,17 @@ "json_description": "从 Hoppscotch 的集合文件导入(JSON)", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "导入" + "title": "导入", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "查可能的错误", "environment": { "add_environment": "添加到环境", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "环境变量“{environment}”未找到。" }, "header": { @@ -550,6 +601,7 @@ "raw_body": "原始请求体", "rename": "重命名请求", "renamed": "请求重命名", + "request_variables": "Request variables", "run": "运行", "save": "保存", "save_as": "另存为", @@ -561,6 +613,7 @@ "title": "请求", "type": "请求类型", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "变量", "view_my_links": "查看我的链接", "copy_link": "复制链接" @@ -815,6 +868,13 @@ "new": "创建新团队", "switch_to_personal": "切换到您的个人工作空间", "title": "团队" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -871,9 +931,9 @@ "forum": "答疑解惑", "github": "在 Github 关注我们", "shortcuts": "更快浏览应用", - "team": "与团队保持联系", "title": "支持", - "twitter": "在 Twitter 关注我们" + "twitter": "在 Twitter 关注我们", + "team": "与团队保持联系" }, "tab": { "authorization": "授权", @@ -893,6 +953,9 @@ "query": "查询", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "测试", @@ -921,7 +984,6 @@ "invite_tooltip": "邀请成员加入此工作区", "invited_to_team": "{owner} 邀请你加入 {team}", "join": "邀请已被接受", - "join_beta": "加入 Beta 计划以访问团队。", "join_team": "加入 {team}", "joined_team": "你已加入 {team}", "joined_team_description": "你现在是此团队的成员了", @@ -954,7 +1016,12 @@ "success_invites": "Success invites", "title": "团队", "we_sent_invite_link": "我们向所有受邀者发送了邀请链接!", - "we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。" + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "加入 Beta 计划以访问团队。" }, "team_environment": { "deleted": "已刪除环境", @@ -981,9 +1048,50 @@ "workspace": { "change": "切换工作空间", "personal": "我的工作空间", + "other_workspaces": "My Workspaces", "team": "团队工作空间", "title": "工作空间" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "操作", "created_on": "创建于", diff --git a/packages/hoppscotch-common/locales/cs.json b/packages/hoppscotch-common/locales/cs.json index 42144bf49..e7e3a37b5 100644 --- a/packages/hoppscotch-common/locales/cs.json +++ b/packages/hoppscotch-common/locales/cs.json @@ -24,8 +24,10 @@ "go_back": "Vrať se", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Označení", "learn_more": "Další informace", + "download_here": "Download here", "less": "Less", "more": "Více", "new": "Nový", @@ -43,6 +45,7 @@ "search": "Vyhledávání", "send": "Poslat", "share": "Share", + "show_secret": "Show secret", "start": "Start", "starting": "Starting", "stop": "Stop", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Zadejte příkaz nebo hledejte…", "we_use_cookies": "Používáme cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Co je nového?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Účet existuje s různými pověřeními - Přihlaste se a propojte oba účty", "all_sign_in_options": "Všechny možnosti přihlášení", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Pokračujte e -mailem", "continue_with_github": "Pokračujte na GitHubu", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Pokračovat s Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-mailem", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Heslo", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Žeton", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Upravit sbírku", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Uveďte prosím platný název kolekce", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Vyberte sbírku", "select_location": "Vyberte umístění", + "details": "Details", "select_team": "Vyberte tým", "team_collections": "Týmové sbírky" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Opravdu se chcete odhlásit z telemetrie?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Opravdu chcete synchronizovat tento pracovní prostor?" + "sync": "Opravdu chcete synchronizovat tento pracovní prostor?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protokoly jsou prázdné", + "request_variables": "This request does not have any request variables", "schema": "Připojte se ke koncovému bodu GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Název týmu prázdný", "teams": "Týmy jsou prázdné", "tests": "Pro tento požadavek neexistují žádné testy", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Vyberte prostředí", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Seznam proměnných" + "variables": "Variables", + "variable_list": "Seznam proměnných", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Název prázdného požadavku", "f12_details": "(F12 pro podrobnosti)", "gql_prettify_invalid_query": "Neplatný dotaz nelze předběžně upravit, vyřešit chyby syntaxe dotazu a zkusit to znovu", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Nelze předtifikovat neplatné tělo, vyřešit chyby syntaxe json a zkusit to znovu", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Skript předběžného požadavku nelze spustit", "something_went_wrong": "Něco se pokazilo", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exportovat jako JSON", "create_secret_gist": "Vytvořte tajnou podstatu", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Podstata vytvořena", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Přihlaste se pomocí GitHub a vytvořte tajný seznam", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Podstata vytvořena" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutace", "schema": "Schéma", "subscriptions": "Předplatné", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Import" + "title": "Import", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Raw Request Body", "rename": "Rename Request", "renamed": "Žádost přejmenována", + "request_variables": "Request variables", "run": "Běh", "save": "Uložit", "save_as": "Uložit jako", @@ -557,6 +613,7 @@ "title": "Žádost", "type": "Typ požadavku", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Proměnné", "view_my_links": "View my links", "copy_link": "Kopírovat odkaz" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Ptejte se a získejte odpovědi", "github": "Follow us on Github", "shortcuts": "Procházejte aplikaci rychleji", - "team": "Spojte se s týmem", "title": "Podpěra, podpora", - "twitter": "Sleduj nás na Twitteru" + "twitter": "Sleduj nás na Twitteru", + "team": "Spojte se s týmem" }, "tab": { "authorization": "Povolení", @@ -889,6 +953,9 @@ "query": "Dotaz", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Testy", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Připojte se k beta programu a získejte přístup k týmům.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Týmy", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Připojte se k beta programu a získejte přístup k týmům." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/da.json b/packages/hoppscotch-common/locales/da.json index 019677a6a..500187f98 100644 --- a/packages/hoppscotch-common/locales/da.json +++ b/packages/hoppscotch-common/locales/da.json @@ -24,8 +24,10 @@ "go_back": "Gå tilbage", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etiket", "learn_more": "Lær mere", + "download_here": "Download here", "less": "Less", "more": "Mere", "new": "Ny", @@ -43,6 +45,7 @@ "search": "Søg", "send": "Sende", "share": "Share", + "show_secret": "Show secret", "start": "Start", "starting": "Starting", "stop": "Hold op", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Skriv en kommando eller søg ...", "we_use_cookies": "Vi bruger cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Hvad er nyt?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Kontoen findes med forskellige legitimationsoplysninger - Log ind for at linke begge konti", "all_sign_in_options": "Alle muligheder for login", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Fortsæt med e -mail", "continue_with_github": "Fortsæt med GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Fortsæt med Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E -mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Adgangskode", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Polet", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Rediger samling", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Angiv et gyldigt navn til samlingen", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Vælg en samling", "select_location": "Vælg placering", + "details": "Details", "select_team": "Vælg et hold", "team_collections": "Teamsamlinger" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Er du sikker på, at du vil fravælge telemetri?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Er du sikker på, at du vil synkronisere dette arbejdsområde?" + "sync": "Er du sikker på, at du vil synkronisere dette arbejdsområde?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protokoller er tomme", + "request_variables": "This request does not have any request variables", "schema": "Opret forbindelse til et GraphQL -slutpunkt", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Teamnavn er tomt", "teams": "Hold er tomme", "tests": "Der er ingen test for denne anmodning", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Vælg miljø", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Variabel liste" + "variables": "Variables", + "variable_list": "Variabel liste", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Tom anmodningsnavn", "f12_details": "(F12 for detaljer)", "gql_prettify_invalid_query": "Kunne ikke prætificere en ugyldig forespørgsel, løse forespørgselssyntaksfejl og prøve igen", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Kunne ikke pryde et ugyldigt brødtekst, løse json -syntaksfejl og prøve igen", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Kunne ikke udføre pre-request script", "something_went_wrong": "Noget gik galt", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Eksporter som JSON", "create_secret_gist": "Opret hemmelig Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist skabt", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Log ind med GitHub for at skabe hemmelig kerne", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist skabt" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutationer", "schema": "Skema", "subscriptions": "Abonnementer", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importere" + "title": "Importere", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Raw Request Body", "rename": "Rename Request", "renamed": "Anmodning omdøbt", + "request_variables": "Request variables", "run": "Løb", "save": "Gemme", "save_as": "Gem som", @@ -557,6 +613,7 @@ "title": "Anmodning", "type": "Anmodningstype", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variabler", "view_my_links": "View my links", "copy_link": "Kopier link" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Stil spørgsmål og få svar", "github": "Follow us on Github", "shortcuts": "Gennemse appen hurtigere", - "team": "Kom i kontakt med teamet", "title": "Support", - "twitter": "Følg os på Twitter" + "twitter": "Følg os på Twitter", + "team": "Kom i kontakt med teamet" }, "tab": { "authorization": "Bemyndigelse", @@ -889,6 +953,9 @@ "query": "Forespørgsel", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Test", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Deltag i betaprogrammet for at få adgang til teams.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Hold", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Deltag i betaprogrammet for at få adgang til teams." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/de.json b/packages/hoppscotch-common/locales/de.json index 70d5b3dc7..a4380339d 100644 --- a/packages/hoppscotch-common/locales/de.json +++ b/packages/hoppscotch-common/locales/de.json @@ -1,50 +1,53 @@ { "action": { - "add": "Add", + "add": "Hinzufügen", "autoscroll": "Autoscroll", "cancel": "Abbrechen", "choose_file": "Datei auswählen", "clear": "Zurücksetzen", "clear_all": "Alles zurücksetzen", "clear_history": "Clear all History", - "close": "Close", + "close": "Schließen", "connect": "Verbinden", - "connecting": "Connecting", + "connecting": "Verbinde", "copy": "Kopieren", - "create": "Create", + "create": "Erstellen", "delete": "Löschen", "disconnect": "Trennen", "dismiss": "Verwerfen", "dont_save": "Nicht speichern", "download_file": "Datei herunterladen", - "drag_to_reorder": "Drag to reorder", + "drag_to_reorder": "Ziehen zum Umordnen", "duplicate": "Duplizieren", "edit": "Bearbeiten", "filter": "Filter", "go_back": "Zurück", "go_forward": "Go forward", - "group_by": "Group by", + "group_by": "Gruppiere nach", + "hide_secret": "Hide secret", "label": "Etikett", "learn_more": "Mehr erfahren", + "download_here": "Download here", "less": "Weniger", "more": "Mehr", "new": "Neu", "no": "Nein", - "open_workspace": "Open workspace", + "open_workspace": "Arbeitsbereich öffnen", "paste": "Einfügen", "prettify": "Verschönern", - "properties": "Properties", + "properties": "Eigenschaften", "remove": "Entfernen", - "rename": "Rename", + "rename": "Umbenennen", "restore": "Wiederherstellen", "save": "Speichern", - "scroll_to_bottom": "Scroll to bottom", - "scroll_to_top": "Scroll to top", + "scroll_to_bottom": "Zum Ende scrollen", + "scroll_to_top": "Zum Anfang scrollen", "search": "Suchen", "send": "Senden", - "share": "Share", - "start": "Start", - "starting": "Starting", + "share": "Teilen", + "show_secret": "Show secret", + "start": "Starten", + "starting": "Startet", "stop": "Stopp", "to_close": "zum Schließen", "to_navigate": "zum Navigieren", @@ -95,19 +98,23 @@ "twitter": "Twitter", "type_a_command_search": "Gib einen Befehl ein oder suche…", "we_use_cookies": "Wir verwenden Cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Was gibt's Neues?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Konto existiert mit unterschiedlichen Zugangsdaten - Melde Dich an, um beide Konten zu verknüpfen", "all_sign_in_options": "Alle Anmeldeoptionen", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Mit E-Mail anmelden", "continue_with_github": "Mit GitHub anmelden", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Mit Google anmelden", "continue_with_microsoft": "Mit Microsoft anmelden", "email": "E-Mail-Adresse", "logged_out": "Abgemeldet", - "login": "Anmeldung", + "login": "Anmelden", "login_success": "Erfolgreich eingeloggt", "login_to_hoppscotch": "Anmelden bei Hoppscotch", "logout": "Ausloggen", @@ -135,42 +142,65 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Übertragungsart", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Passwort", - "save_to_inherit": "Please save this request in any collection to inherit the authorization", + "save_to_inherit": "Bitte speichere diese Anfrage in einer Sammlung, um die Autorisierung zu erben", "token": "Token", "type": "Autorisierungsverfahren", "username": "Nutzername" }, "collection": { "created": "Sammlung erstellt", - "different_parent": "Cannot reorder collection with different parent", + "different_parent": "Sammlung kann nicht neu geordnet werden, weil sie ein anderes übergeordnetes Element hat", "edit": "Sammlung bearbeiten", - "import_or_create": "Import or create a collection", + "import_or_create": "Sammlung erstellen oder importieren", + "import_collection": "Import Collection", "invalid_name": "Bitte gib einen gültigen Namen für die Sammlung an", - "invalid_root_move": "Collection already in the root", - "moved": "Moved Successfully", + "invalid_root_move": "Sammlung bereits im Stammverzeichnis", + "moved": "Erfolgreich verschoben", "my_collections": "Meine Sammlungen", "name": "Meine neue Sammlung", - "name_length_insufficient": "Sammlungsname soll mindestens 3 Zeichen lang sein", + "name_length_insufficient": "Sammlungsname muss mindestens 3 Zeichen lang sein", "new": "Neue Sammlung", - "order_changed": "Collection Order Updated", - "properties": "Collection Properties", - "properties_updated": "Collection Properties Updated", + "order_changed": "Reihenfolge der Sammlung aktualisiert", + "properties": "Eigenschaften der Sammlung", + "properties_updated": "Eigenschaften der Sammlung aktualisiert", "renamed": "Sammlung umbenannt", "request_in_use": "Anfrage wird ausgeführt", "save_as": "Speichern als", - "save_to_collection": "Save to Collection", + "save_to_collection": "In Sammlung speichern", "select": "Wähle eine Sammlung", "select_location": "Ort auswählen", + "details": "Details", "select_team": "Wähle ein Team", "team_collections": "Teamsammlungen" }, "confirm": { - "close_unsaved_tab": "Are you sure you want to close this tab?", - "close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.", + "close_unsaved_tab": "Bist du sicher, dass du dieses Tab schließen möchtest?", + "close_unsaved_tabs": "Bist du sicher, dass du alle Tabs schließen möchtest? {count} nicht gespeicherte Tabs gehen verloren.", "exit_team": "Möchtest Du dieses Team wirklich verlassen?", "logout": "Möchtest Du Dich wirklich abmelden?", "remove_collection": "Möchtest Du diese Sammlung wirklich endgültig löschen?", @@ -182,13 +212,14 @@ "remove_team": "Möchtest Du dieses Team wirklich löschen?", "remove_telemetry": "Möchtest Du die Telemetrie wirklich deaktivieren?", "request_change": "Möchtest Du diese Anfrage verwerfen? Ungespeicherte Änderungen gehen verloren.", - "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Möchtest Du diesen Arbeitsbereich wirklich synchronisieren?" + "save_unsaved_tab": "Möchtest du die Änderungen aus diesem Tab speichern?", + "sync": "Möchtest Du diesen Arbeitsbereich wirklich synchronisieren?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { - "add_parameters": "Add to parameters", - "open_request_in_new_tab": "Open request in new tab", - "set_environment_variable": "Set as variable" + "add_parameters": "Zu Parametern hinzufügen", + "open_request_in_new_tab": "Anfrage in neuem Tab öffnen", + "set_environment_variable": "Als Variable setzen" }, "cookies": { "modal": { @@ -234,16 +265,19 @@ "invites": "Einladungsliste ist leer", "members": "Team ist leer", "parameters": "Diese Anfrage hat keine Parameter", - "pending_invites": "Es gibt keine offenen Einladungen für dieses Team", + "pending_invites": "Es gibt keine ausstehenden Einladungen für dieses Team", "profile": "Einloggen um das Profil anzusehen", "protocols": "Protokolle sind leer", + "request_variables": "This request does not have any request variables", "schema": "Verbinden mit einem GraphQL-Endpunkt", - "shared_requests": "Shared requests are empty", - "shared_requests_logout": "Login to view your shared requests or create a new one", - "subscription": "Subscriptions are empty", + "secret_environments": "Secrets are not synced to Hoppscotch", + "shared_requests": "Keine geteilten Abfragen", + "shared_requests_logout": "Melde dich an, um geteilte Anfragen zu sehen oder eine neue zu erstellen", + "subscription": "Keine Abonnements", "team_name": "Teamname leer", - "teams": "Teams sind leer", + "teams": "Keine Teams", "tests": "Es gibt keine Tests für diese Anfrage", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -252,43 +286,49 @@ "create_new": "Neue Umgebung erstellen", "created": "Umgebung erzeugt", "deleted": "Umgebung löschen", - "duplicated": "Environment duplicated", + "duplicated": "Umgebung dupliziert", "edit": "Umgebung bearbeiten", - "empty_variables": "No variables", + "empty_variables": "Keine Variablen", "global": "Global", - "global_variables": "Global variables", - "import_or_create": "Import or create a environment", + "global_variables": "Globale Variablen", + "import_or_create": "Importiere oder erstelle eine Umgebung", "invalid_name": "Bitte gib einen gültigen Namen für die Umgebung an", - "list": "Environment variables", - "my_environments": "My Environments", + "list": "Umgebungsvariablen", + "my_environments": "Meine Umgebungen", "name": "Name", "nested_overflow": "Verschachtelte Umgebungsvariablen sind limitert auf 10 Unterebenen", "new": "Neue Umgebung", - "no_active_environment": "No active environment", + "no_active_environment": "Keine Umgebung ausgewählt", "no_environment": "Keine Umgebung", "no_environment_description": "Es wurden keine Umgebungen ausgewählt. Wähle aus, was mit den untenstehenden Variablen geschehen soll.", - "quick_peek": "Environment Quick Peek", - "replace_with_variable": "Replace with variable", + "quick_peek": "Kurzinfo der Umgebung", + "replace_with_variable": "Mit Variable ersetzen", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Umgebung auswählen", - "set": "Set environment", - "set_as_environment": "Set as environment", - "team_environments": "Team Environments", + "set": "Umgebung setzen", + "set_as_environment": "Als Umgebung setzen", + "team_environments": "Teamumgebungen", "title": "Umgebungen", "updated": "Umgebung aktualisiert", - "value": "Value", + "value": "Wert", "variable": "Variable", - "variable_list": "Variablenliste" + "variables": "Variables", + "variable_list": "Variablenliste", + "properties": "Environment Properties", + "details": "Details" }, "error": { - "authproviders_load_error": "Unable to load auth providers", + "authproviders_load_error": "Auth-Provider können nicht geladen werden", "browser_support_sse": "Dieser Browser scheint keine Unterstützung für die vom Server gesendete Ereignisse zu haben.", "check_console_details": "Einzelheiten findest Du in der Browser-Konsole.", - "check_how_to_add_origin": "Check how you can add an origin", + "check_how_to_add_origin": "Erfahre, wie du eine Quelle hinzufügen kannst", "curl_invalid_format": "cURL ist nicht richtig formatiert", - "danger_zone": "Danger zone", - "delete_account": "Your account is currently an owner in these teams:", - "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "danger_zone": "Gefahrenbereich", + "delete_account": "Dein Konto ist derzeit Besitzer dieser Teams:", + "delete_account_description": "Du musst dich entweder selbst entfernen, den Besitz übertragen oder diese Teams löschen, bevor du dein Konto löschen kannst.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Leerer Anfragename", "f12_details": "(F12 für Details)", "gql_prettify_invalid_query": "Eine ungültige Abfrage konnte nicht verschönert werden. Fehler in der Abfragesyntax beheben und erneut versuchen", @@ -296,28 +336,37 @@ "incorrect_email": "Falsche E-Mail Adresse", "invalid_link": "Falscher Link", "invalid_link_description": "Der verwendete Link ist entweder ungültig oder abgelaufen.", - "json_parsing_failed": "Invalid JSON", + "invalid_embed_link": "The embed does not exist or is invalid.", + "json_parsing_failed": "JSON ungültig", "json_prettify_invalid_body": "Ein ungültiger Text konnte nicht verschönert werden, JSON-Syntaxfehler beheben und erneut versuchen", "network_error": "Netzwerkfehler. Bitte versuche es erneut.", "network_fail": "Anfrage konnte nicht gesendet werden", - "no_collections_to_export": "No collections to export. Please create a collection to get started.", + "no_collections_to_export": "Keine Sammlungen zu exportieren. Bitte erstelle eine Sammlung, um loszulegen.", "no_duration": "Keine Dauer", - "no_environments_to_export": "No environments to export. Please create an environment to get started.", - "no_results_found": "No matches found", - "page_not_found": "This page could not be found", - "please_install_extension": "Please install the extension and add origin to the extension.", + "no_environments_to_export": "Keine Umgebungen zum Exportieren. Bitte erstelle eine Umgebung, um loszulegen.", + "no_results_found": "Keine Ergebnisse gefunden", + "page_not_found": "Diese Seite konnte nicht gefunden werden", + "please_install_extension": "Bitte installiere die Browser-Erweiterung, um Quellen hinzuzufügen.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Pre-Request-Skripte konnte nicht ausgeführt werden", "something_went_wrong": "Etwas ist schief gelaufen", - "test_script_fail": "Testskripts konnten nicht ausgeführt werden" + "test_script_fail": "Testskripts konnten nicht ausgeführt werden", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Als JSON exportieren", - "create_secret_gist": "Geheimen Github Gist erstellen", - "failed": "Something went wrong while exporting", + "create_secret_gist_tooltip_text": "Export as secret Gist", + "secret_gist_success": "Successfully exported as secret Gist", + "success": "Successfully exported", + "create_secret_gist": "Geheimen GitHub Gist erstellen", + "failed": "Beim Exportieren ist etwas schief gelaufen", "gist_created": "Gist erstellt", "require_github": "Melde Dich bei GitHub an, um einen geheimen Gist zu erstellen", - "title": "Export" + "title": "Exportieren" }, "filter": { "all": "All", @@ -333,16 +382,17 @@ "renamed": "Ordner umbenannt" }, "graphql": { - "connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", - "connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", - "connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", + "connection_switch_confirm": "Möchtest du dich mit dem letzten GraphQL-Endpunkt verbinden?", + "connection_switch_new_url": "Wenn du zu einem anderen Tab wechselst, wird die aktive GraphQL-Verbindung getrennt. Die neue Verbindungs-URL lautet", + "connection_switch_url": "Du bist mit einem GraphQL-Endpunkt verbunden, die Verbindungs-URL lautet", "mutations": "Mutationen", "schema": "Schema", "subscriptions": "Abonnements", - "switch_connection": "Switch connection" + "switch_connection": "Verbindung wechseln", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { - "title": "GraphQL Collections" + "title": "GraphQL Sammlungen" }, "group": { "time": "Time", @@ -355,8 +405,8 @@ }, "helpers": { "authorization": "Der Autorisierungsheader wird automatisch generiert, wenn Du die Anfrage sendest.", - "collection_properties_authorization": " This authorization will be set for every request in this collection.", - "collection_properties_header": "This header will be set for every request in this collection.", + "collection_properties_authorization": "Diese Autorisierung wird für jede Anfrage in dieser Sammlung gesetzt.", + "collection_properties_header": "Dieser Header wird für jede Anfrage in dieser Sammlung gesetzt.", "generate_documentation_first": "Zuerst Dokumentation erstellen", "network_fail": "Der API-Endpunkt kann nicht erreicht werden. Überprüfe Deine Netzwerkverbindung und versuche es erneut.", "offline": "Du scheinst offline zu sein. Die Daten in diesem Arbeitsbereich sind möglicherweise nicht aktuell.", @@ -376,59 +426,64 @@ "import": { "collections": "Sammlungen importieren", "curl": "cURL importieren", - "environments_from_gist": "Import From Gist", - "environments_from_gist_description": "Import Hoppscotch Environments From Gist", + "environments_from_gist": "Aus Gist importieren", + "environments_from_gist_description": "Hoppscotch-Umgebungen aus Gist importieren", "failed": "Importieren fehlgeschlagen", - "from_file": "Import from File", - "from_gist": "Von Github Gist importieren", - "from_gist_description": "Von Github Gist URL importieren", - "from_insomnia": "Von Insomnia importieren", + "from_file": "Aus Datei importieren", + "from_gist": "Aus GitHub Gist importieren", + "from_gist_description": "Aus GitHub Gist URL importieren", + "from_insomnia": "Aus Insomnia importieren", "from_insomnia_description": "Insomnia-Sammlung importieren", - "from_json": "Von Hoppscotch importieren", + "from_json": "Aus Hoppscotch importieren", "from_json_description": "Hoppscotch-Sammlung importieren", "from_my_collections": "Aus 'Meine Sammlungen' importieren", "from_my_collections_description": "Meine-Sammlungen-Datei importieren", - "from_openapi": "Von OpenAPI importieren", + "from_openapi": "Aus OpenAPI importieren", "from_openapi_description": "OpenAPI-Spezifikation importieren (YAML/JSON)", - "from_postman": "Von Postman importieren", + "from_postman": "Aus Postman importieren", "from_postman_description": "Postman-Sammlung importieren", - "from_url": "Von URL importieren", + "from_url": "Aus URL importieren", "gist_url": "Gist-URL eingeben", - "gql_collections_from_gist_description": "Import GraphQL Collections From Gist", - "hoppscotch_environment": "Hoppscotch Environment", - "hoppscotch_environment_description": "Import Hoppscotch Environment JSON file", - "import_from_url_invalid_fetch": "Couldn't get data from the url", - "import_from_url_invalid_file_format": "Error while importing collections", - "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", - "import_from_url_success": "Collections Imported", - "insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file", - "json_description": "Hoppscotch Sammlungsdatei (JSON) importieren", - "postman_environment": "Postman Environment", - "postman_environment_description": "Import Postman Environment from a JSON file", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported", + "gql_collections_from_gist_description": "GraphQL-Sammlungen aus Gist importieren", + "hoppscotch_environment": "Hoppscotch-Umgebung", + "hoppscotch_environment_description": "Hoppscotch-Umgebung aus JSON-Datei importieren", + "import_from_url_invalid_fetch": "Konnte keine Daten aus der URL abrufen", + "import_from_url_invalid_file_format": "Fehler beim Importieren von Sammlungen", + "import_from_url_invalid_type": "Typ wird nicht unterstützt. Akzeptierte Werte sind: 'hoppscotch', 'openapi', 'postman', 'insomnia'", + "import_from_url_success": "Sammlungen importiert", + "insomnia_environment_description": "Insomnia-Umgebung aus YAML/JSON-Datei importieren", + "json_description": "Hoppscotch-Sammlung aus JSON-Datei importieren", + "postman_environment": "Postman Umgebung", + "postman_environment_description": "Postman-Umgebung aus einer JSON-Datei importieren", "title": "Importieren" }, "inspections": { - "description": "Inspect possible errors", + "description": "Mögliche Fehler überprüfen", "environment": { - "add_environment": "Add to Environment", - "not_found": "Environment variable “{environment}” not found." + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", + "add_environment": "Zur Umgebung hinzufügen", + "not_found": "Umgebungsvariable “{environment}” nicht gefunden." }, "header": { - "cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." + "cookie": "Der Browser erlaubt Hoppscotch nicht, den Cookie-Header zu setzen. Während wir an der Hoppscotch Desktop App arbeiten (kommt bald), verwende bitte stattdessen den Authorization Header." }, "response": { - "401_error": "Please check your authentication credentials.", - "404_error": "Please check your request URL and method type.", - "cors_error": "Please check your Cross-Origin Resource Sharing configuration.", - "default_error": "Please check your request.", - "network_error": "Please check your network connection." + "401_error": "Bitte überprüfe die Authentifizierungsdaten.", + "404_error": "Bitte überprüfe die URL und die HTTP-Anfragemethode.", + "cors_error": "Bitte überprüfe die Konfiguration für Cross-Origin Resource Sharing (CORS)", + "default_error": "Bitte überprüfe deine Anfrage.", + "network_error": "Bitte überprüfe deine Netzwerkverbindung." }, "title": "Inspector", "url": { - "extension_not_installed": "Extension not installed.", - "extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", - "extention_enable_action": "Enable Browser Extension", - "extention_not_enabled": "Extension not enabled." + "extension_not_installed": "Erweiterung nicht installiert.", + "extension_unknown_origin": "Stelle sicher, dass du die Quelle für den API-Endpunkt zur Liste in der Hoppscotch Browsererweiterungen hinzugefügt hast.", + "extention_enable_action": "Browsererweiterung aktivieren", + "extention_not_enabled": "Browsererweiterungen nicht aktiviert." } }, "layout": { @@ -439,25 +494,25 @@ "row": "Horizontales Layout" }, "modal": { - "close_unsaved_tab": "You have unsaved changes", + "close_unsaved_tab": "Du hast ungespeicherte Änderungen", "collections": "Sammlungen", "confirm": "Aktion bestätigen", - "customize_request": "Customize Request", + "customize_request": "Anfrage anpassen", "edit_request": "Anfrage bearbeiten", - "import_export": "Import / Export", - "share_request": "Share Request" + "import_export": "Importieren / Exportieren", + "share_request": "Anfrage teilen" }, "mqtt": { - "already_subscribed": "You are already subscribed to this topic.", + "already_subscribed": "Du hast dieses Topic bereits abonniert.", "clean_session": "Clean Session", - "clear_input": "Clear input", - "clear_input_on_send": "Clear input on send", + "clear_input": "Eingaben löschen", + "clear_input_on_send": "Eingaben beim Senden löschen", "client_id": "Client ID", - "color": "Pick a color", + "color": "Wähle eine Farbe", "communication": "Kommunikation", - "connection_config": "Connection Config", - "connection_not_authorized": "This MQTT connection does not use any authentication.", - "invalid_topic": "Please provide a topic for the subscription", + "connection_config": "Verbindungseinstellungen", + "connection_not_authorized": "Diese MQTT-Verbindung verwendet keine Authentifizierung.", + "invalid_topic": "Bitte gib ein Topic für das Abonnement an", "keep_alive": "Keep Alive", "log": "Protokoll", "lw_message": "Last-Will Message", @@ -465,16 +520,16 @@ "lw_retain": "Last-Will Retain", "lw_topic": "Last-Will Topic", "message": "Nachricht", - "new": "New Subscription", - "not_connected": "Please start a MQTT connection first.", + "new": "Neues Abonnement", + "not_connected": "Bitte stelle zuerst eine MQTT-Verbindung her.", "publish": "Veröffentlichen", "qos": "QoS", "ssl": "SSL", "subscribe": "Abonnieren", - "topic": "Thema", - "topic_name": "Themenname", - "topic_title": "Thema veröffentlichen / abonnieren", - "unsubscribe": "Abbestellen", + "topic": "Topic", + "topic_name": "Topic-Name", + "topic_title": "Topic veröffentlichen / abonnieren", + "unsubscribe": "Deabonnieren", "url": "URL" }, "navigation": { @@ -520,21 +575,21 @@ "structured": "Structured", "text": "Text" }, - "different_collection": "Cannot reorder requests from different collections", - "duplicated": "Request duplicated", + "different_collection": "Anfragen aus verschiedenen Sammlungen können nicht neu geordnet werden", + "duplicated": "Anfrage dupliziert", "duration": "Dauer", "enter_curl": "cURL eingeben", "generate_code": "Code generieren", "generated_code": "Generierter Code", - "go_to_authorization_tab": "Go to Authorization tab", - "go_to_body_tab": "Go to Body tab", + "go_to_authorization_tab": "Zum Tab 'Autorisierung' wechseln", + "go_to_body_tab": "Zum Tab 'Anfragekörper' wechseln", "header_list": "Header-Liste", "invalid_name": "Bitte gib einen Namen für die Anfrage an", "method": "Methode", - "moved": "Request moved", + "moved": "Anfrage verschoben", "name": "Anfragename", "new": "Neue Anfrage", - "order_changed": "Request Order Updated", + "order_changed": "Reihenfolge der Anfragen aktualisiert", "override": "Überschreiben", "override_help": "Setze Content-Type in Headers", "overriden": "Überschrieben", @@ -544,27 +599,29 @@ "payload": "Nutzlast", "query": "Anfrage", "raw_body": "Roher Anfragetext", - "rename": "Rename Request", + "rename": "Anfrage umbenennen", "renamed": "Anfrage umbenannt", + "request_variables": "Request variables", "run": "Ausführen", "save": "Speichern", "save_as": "Speichern als", "saved": "Anfrage gespeichert", "share": "Teilen", "share_description": "Teile Hoppscotch mit Deinen Freunden", - "share_request": "Share Request", + "share_request": "Anfrage teilen", "stop": "Stop", "title": "Anfrage", "type": "Anfragetyp", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variablen", - "view_my_links": "View my links", + "view_my_links": "Meine Links anzeigen", "copy_link": "Link kopieren" }, "response": { "audio": "Audio", "body": "Antworttext", - "filter_response_body": "Filter JSON response body (uses JSONPath syntax)", + "filter_response_body": "JSON-Antwortkörper filtern (verwendet JSONPath-Syntax)", "headers": "Header", "html": "HTML", "image": "Bild", @@ -583,17 +640,17 @@ "settings": { "accent_color": "Akzentfarbe", "account": "Konto", - "account_deleted": "Your account has been deleted", + "account_deleted": "Dein Konto wurde gelöscht", "account_description": "Passe Deine Kontoeinstellungen an.", "account_email_description": "Deine primäre E-Mail-Adresse.", "account_name_description": "Dies ist Dein Anzeigename.", - "additional": "Additional Settings", + "additional": "Weitere Einstellungen", "background": "Hintergrund", "black_mode": "Schwarz", "choose_language": "Sprache wählen", "dark_mode": "Dunkel", - "delete_account": "Delete account", - "delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.", + "delete_account": "Konto löschen", + "delete_account_description": "Wenn du dein Konto löschst, werden alle Daten dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.", "expand_navigation": "Menüpunkte vergrößern", "experiments": "Experimente", "experiments_notice": "Dies ist eine Sammlung von Experimenten, an denen wir aktuell arbeiten und die sich als nützlich erweisen könnten, Spaß machen, beides oder keines von beiden. Sie sind nicht endgültig und möglicherweise nicht stabil. Wenn also etwas übermäßig Seltsames passiert, gerate nicht in Panik. Schalte das verdammte Ding einfach aus. Scherz beiseite,", @@ -631,31 +688,31 @@ "theme_description": "Passe Dein Anwendungsdesign an.", "use_experimental_url_bar": "Experimentelle URL-Leiste mit Hervorhebung der Umgebung verwenden", "user": "Nutzer", - "verified_email": "Verified email", + "verified_email": "Bestätigte E-Mail-Adresse", "verify_email": "E-Mail-Adresse bestätigen" }, "shared_requests": { "button": "Button", - "button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", - "copy_html": "Copy HTML", - "copy_link": "Copy Link", - "copy_markdown": "Copy Markdown", - "creating_widget": "Creating widget", - "customize": "Customize", - "deleted": "Shared request deleted", - "description": "Select a widget, you can change and customize this later", + "button_info": "Erstelle einen 'Run in Hoppscotch'-Button für deine Website, deinen Blog oder eine README.", + "copy_html": "HTML kopieren", + "copy_link": "Link kopieren", + "copy_markdown": "Markdown kopieren", + "creating_widget": "Widget erstellen", + "customize": "Anpassen", + "deleted": "Geteilte Anfrage gelöscht", + "description": "Wähle ein Widget aus. Du kannst es später noch ändern und anpassen.", "embed": "Embed", - "embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.", + "embed_info": "Füge deiner Website, deinem Blog oder deiner Dokumentation einen kleinen 'Hoppscotch API Playground' hinzu.", "link": "Link", - "link_info": "Create a shareable link to share with anyone on the internet with view access.", - "modified": "Shared request modified", - "not_found": "Shared request not found", - "open_new_tab": "Open in new tab", - "preview": "Preview", - "run_in_hoppscotch": "Run in Hoppscotch", + "link_info": "Erstelle einen Link, den du mit jedem im Internet teilen kannst, der Berechtigungen zur Ansicht bekommt.", + "modified": "Geteilte Anfrage geändert", + "not_found": "Geteilte Anfrage nicht gefunden", + "open_new_tab": "In neuem Tab öffnen", + "preview": "Vorschau", + "run_in_hoppscotch": "In Hoppscotch ausführen", "theme": { - "dark": "Dark", - "light": "Light", + "dark": "Dunkel", + "light": "Hell", "system": "System", "title": "Theme" } @@ -702,20 +759,20 @@ "save_request": "Save Request", "save_to_collections": "In Sammlungen speichern", "send_request": "Anfrage senden", - "share_request": "Share Request", - "show_code": "Generate code snippet", + "share_request": "Anfrage teilen", + "show_code": "Code-Schnipsel generieren", "title": "Anfrage", "copy_request_link": "Anfragelink kopieren" }, "response": { - "copy": "Copy response to clipboard", - "download": "Download response as file", - "title": "Response" + "copy": "Antwort in die Zwischenablage kopieren", + "download": "Antwort als Datei herunterladen", + "title": "Antwort" }, "theme": { "black": "Auf Schwarzes Design wechseln", - "dark": "Auf Dunkles Design wechseln", - "light": "Auf Helles Design wechseln", + "dark": "Auf dunkles Design wechseln", + "light": "Auf helles Design wechseln", "system": "Auf Systemdesign wechseln", "title": "Theme" } @@ -728,89 +785,96 @@ }, "socketio": { "communication": "Kommunikation", - "connection_not_authorized": "This SocketIO connection does not use any authentication.", + "connection_not_authorized": "Diese SocketIO-Verbindung verwendet keine Authentifizierung.", "event_name": "Ereignissname", "events": "Ereigniss", "log": "Protokoll", "url": "URL" }, "spotlight": { - "change_language": "Change Language", + "change_language": "Sprache ändern", "environments": { - "delete": "Delete current environment", - "duplicate": "Duplicate current environment", - "duplicate_global": "Duplicate global environment", - "edit": "Edit current environment", - "edit_global": "Edit global environment", - "new": "Create new environment", - "new_variable": "Create a new environment variable", - "title": "Environments" + "delete": "Aktuelle Umgebung löschen", + "duplicate": "Aktuelle Umgebung duplizieren", + "duplicate_global": "Globale Umgebung duplizieren", + "edit": "Aktuelle Umgebung bearbeiten", + "edit_global": "Globale Umgebung bearbeiten", + "new": "Neue Umgebung erstellen", + "new_variable": "Neue Umgebungsvariable erstellen", + "title": "Umgebungen" }, "general": { - "chat": "Chat with support", - "help_menu": "Help and support", - "open_docs": "Read Documentation", - "open_github": "Open GitHub repository", - "open_keybindings": "Keyboard shortcuts", - "social": "Social", - "title": "General" + "chat": "Chat mit dem Support", + "help_menu": "Hilfe und Support", + "open_docs": "Dokumentation lesen", + "open_github": "GitHub-Repository öffnen", + "open_keybindings": "Tastenkürzel anzeigen", + "social": "Soziale Medien", + "title": "Allgemein" }, "graphql": { - "connect": "Connect to server", - "disconnect": "Disconnect from server" + "connect": "Verbindung zum Server herstellen", + "disconnect": "Verbindung zum Server trennen" }, "miscellaneous": { - "invite": "Invite your friends to Hoppscotch", - "title": "Miscellaneous" + "invite": "Lade deine Freunde zu Hoppscotch ein", + "title": "Verschiedenes" }, "request": { - "save_as_new": "Save as new request", - "select_method": "Select method", - "switch_to": "Switch to", - "tab_authorization": "Authorization tab", - "tab_body": "Body tab", - "tab_headers": "Headers tab", - "tab_parameters": "Parameters tab", - "tab_pre_request_script": "Pre-request script tab", - "tab_query": "Query tab", - "tab_tests": "Tests tab", - "tab_variables": "Variables tab" + "save_as_new": "Als neue Anfrage speichern", + "select_method": "Methode wählen", + "switch_to": "Wechseln zu", + "tab_authorization": "Tab 'Autorisierung'", + "tab_body": "Tab 'Anfragekörper'", + "tab_headers": "Tab 'Header'", + "tab_parameters": "Tab 'Parameter'", + "tab_pre_request_script": "Tab 'Pre-Request-Skripte'", + "tab_query": "Tab 'Anfrage'", + "tab_tests": "Tab 'Tests'", + "tab_variables": "Tab 'Variablen'" }, "response": { - "copy": "Copy response", - "download": "Download response as file", - "title": "Response" + "copy": "Antwort kopieren", + "download": "Antwort als Datei herunterladen", + "title": "Antwort" }, "section": { "interceptor": "Interceptor", - "interface": "Interface", + "interface": "Oberfläche", "theme": "Theme", - "user": "User" + "user": "Benutzer" }, "settings": { - "change_interceptor": "Change Interceptor", - "change_language": "Change Language", + "change_interceptor": "Interceptor ändern", + "change_language": "Sprache ändern", "theme": { - "black": "Black", - "dark": "Dark", - "light": "Light", - "system": "System preference" + "black": "Schwarz", + "dark": "Dunkel", + "light": "Hell", + "system": "System" } }, "tab": { - "close_current": "Close current tab", - "close_others": "Close all other tabs", - "duplicate": "Duplicate current tab", - "new_tab": "Open a new tab", + "close_current": "Aktuelles Tab schließen", + "close_others": "Andere Tabs schließen", + "duplicate": "Tab duplizieren", + "new_tab": "Neues Tab öffnen", "title": "Tabs" }, "workspace": { - "delete": "Delete current team", - "edit": "Edit current team", - "invite": "Invite people to team", - "new": "Create new team", - "switch_to_personal": "Switch to your personal workspace", + "delete": "Aktuelles Team löschen", + "edit": "Aktuelles Team bearbeiten", + "invite": "Leute zum Team einladen", + "new": "Neues Team erstellen", + "switch_to_personal": "Zum eigenen Arbeitsbereich wechseln", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -825,9 +889,9 @@ "connected": "In Verbindung gebracht", "connected_to": "Verbunden mit {name}", "connecting_to": "Verbindung zu {name}...", - "connection_error": "Failed to connect", - "connection_failed": "Connection failed", - "connection_lost": "Connection lost", + "connection_error": "Verbindung nicht möglich", + "connection_failed": "Verbindungsaufbau fehlgeschlagen", + "connection_lost": "Verbindung unterbrochen", "copied_interface_to_clipboard": "Copied {language} interface type to clipboard", "copied_to_clipboard": "In die Zwischenablage kopiert", "deleted": "Gelöscht", @@ -836,27 +900,27 @@ "disconnected": "Getrennt", "disconnected_from": "Verbindung zu {name} getrennt", "docs_generated": "Dokumentation erstellt", - "download_failed": "Download failed", + "download_failed": "Download fehlgeschlagen", "download_started": "Download gestartet", "enabled": "Aktiviert", "file_imported": "Datei importiert", "finished_in": "Fertig in {duration} ms", - "hide": "Hide", + "hide": "Ausblenden", "history_deleted": "Verlauf gelöscht", "linewrap": "Zeilen umbrechen", "loading": "Wird geladen...", - "message_received": "Message: {message} arrived on topic: {topic}", - "mqtt_subscription_failed": "Something went wrong while subscribing to topic: {topic}", + "message_received": "Nachricht: {message} eingegangen auf Topic: {topic}", + "mqtt_subscription_failed": "Beim Abonnieren des Topics '{topic}' ist etwas schiefgelaufen.", "none": "Keiner", "nothing_found": "Nichts gefunden für", - "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", - "published_message": "Published message: {message} to topic: {topic}", - "reconnection_error": "Failed to reconnect", - "show": "Show", - "subscribed_failed": "Failed to subscribe to topic: {topic}", - "subscribed_success": "Successfully subscribed to topic: {topic}", - "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", - "unsubscribed_success": "Successfully unsubscribed from topic: {topic}", + "published_error": "Beim Veröffentlichen der Nachricht '{message}' im Topic '{topic}' ist etwas schiefgelaufen.", + "published_message": "Nachricht '{message}' wurde im Topic '{topic}' veröffentlicht.", + "reconnection_error": "Verbindung fehlgeschlagen", + "show": "Anzeigen", + "subscribed_failed": "Topic '{topic}' konnte nicht abonniert werden.", + "subscribed_success": "Topic '{topic}' wurde erfolgreich abonniert.", + "unsubscribed_failed": "Topic '{topic}' konnte nicht deabonniert werden.", + "unsubscribed_success": "Topic '{topic}' wurde erfolgreich deabonniert.", "waiting_send_request": "Warten auf Anfrage senden" }, "support": { @@ -865,21 +929,21 @@ "community": "Stelle Fragen und helfe anderen", "documentation": "Lese mehr über Hoppscotch", "forum": "Stelle Fragen und erhalte Antworten", - "github": "Folge uns auf Github", + "github": "Folge uns auf GitHub", "shortcuts": "Hoppscotch schneller bedienen", - "team": "Nehme Kontakt mit dem Team auf", "title": "Hilfe", - "twitter": "Folge uns auf Twitter" + "twitter": "Folge uns auf Twitter", + "team": "Nehme Kontakt mit dem Team auf" }, "tab": { "authorization": "Autorisierung", "body": "Anfragekörper", - "close": "Close Tab", - "close_others": "Close other Tabs", + "close": "Tab schließen", + "close_others": "Andere Tabs schließen", "collections": "Sammlungen", "documentation": "Dokumentation", - "duplicate": "Duplicate Tab", - "environments": "Environments", + "duplicate": "Tab duplizieren", + "environments": "Umgebungen", "headers": "Header", "history": "Verlauf", "mqtt": "MQTT", @@ -888,7 +952,10 @@ "queries": "Anfragen", "query": "Anfrage", "schema": "Schema", - "shared_requests": "Shared Requests", + "shared_requests": "Geteilte Anfragen", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Tests", @@ -905,8 +972,8 @@ "email_do_not_match": "E-Mail-Adresse stimmt nicht mit Deinen Kontodaten überein, bitte kontaktiere den Teameigentümer.", "exit": "Team verlassen", "exit_disabled": "Eigentümer können das Team nicht verlassen", - "failed_invites": "Failed invites", - "invalid_coll_id": "Invalid collection ID", + "failed_invites": "Gescheiterte Einladungen", + "invalid_coll_id": "Ungültige Sammlungs-ID", "invalid_email_format": "E-Mail-Format ist ungültig", "invalid_id": "Ungültige Team-ID, bitte kontaktiere den Teameigentümer.", "invalid_invite_link": "Ungültiger Einladungslink.", @@ -914,10 +981,9 @@ "invalid_member_permission": "Bitte erteile dem Teammitglied eine gültige Erlaubnis", "invite": "Einladen", "invite_more": "Mehr einladen", - "invite_tooltip": "Personen zum Arbeitsbereich einladen", + "invite_tooltip": "Personen zum Team einladen", "invited_to_team": "{owner} hat dich zu {team} eingeladen", "join": "Einladung angenommen", - "join_beta": "Nimm am Beta-Programm teil, um auf Teams zuzugreifen.", "join_team": "{team} beitreten", "joined_team": "Du bist {team} beigetreten", "joined_team_description": "Du bist nun ein Mitglied des Teams", @@ -930,7 +996,7 @@ "member_removed": "Benutzer entfernt", "member_role_updated": "Benutzerrollen aktualisiert", "members": "Mitglieder", - "more_members": "+{count} more", + "more_members": "+{count} weitere", "name_length_insufficient": "Der Teamname sollte mindestens 6 Zeichen lang sein", "name_updated": "Teamname aktualisiert", "new": "Neues Team", @@ -938,24 +1004,29 @@ "new_name": "Mein neues Team", "no_access": "Du hast keinen Bearbeitungszugriff auf diese Sammlungen", "no_invite_found": "Einladung nicht gefunden, bitte kontaktiere den Teameigentümer.", - "no_request_found": "Request not found.", + "no_request_found": "Anfrage nicht gefunden.", "not_found": "Team wurde nicht gefunde, bitte kontaktiere den Teameigentümer.", "not_valid_viewer": "Du hast nicht die richtige Berechtigung als Gast, bitte kontaktiere den Teameigentümer.", - "parent_coll_move": "Cannot move collection to a child collection", - "pending_invites": "Wartende Einladungen", + "parent_coll_move": "Sammlung kann nicht in eine untergeordnete Sammlung verschoben werden", + "pending_invites": "Ausstehende Einladungen", "permissions": "Berechtigungen", "same_target_destination": "Same target and destination", "saved": "Team gespeichert", "select_a_team": "Team auswählen", - "success_invites": "Success invites", + "success_invites": "Erfolgreiche Einladungen", "title": "Team", "we_sent_invite_link": "Einladungen wurden an alle E-Mails verschickt!", - "we_sent_invite_link_description": "Bitte alle eingeladenen Personen, ihren Posteingang zu überprüfen. Klicke auf den Link, um dem Team beizutreten." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Bitte alle eingeladenen Personen, ihren Posteingang zu überprüfen und auf den Link zu klicken, um dem Team beizutreten.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Nimm am Beta-Programm teil, um auf Teams zuzugreifen." }, "team_environment": { - "deleted": "Environment Deleted", - "duplicate": "Environment Duplicated", - "not_found": "Environment not found." + "deleted": "Umgebung gelöscht", + "duplicate": "Umgebung dupliziert", + "not_found": "Umgebung nicht gefunden." }, "test": { "failed": "Test fehlgeschlagen", @@ -975,18 +1046,59 @@ "url": "URL" }, "workspace": { - "change": "Change workspace", - "personal": "My Workspace", - "team": "Team Workspace", - "title": "Workspaces" + "change": "Arbeitsbereich wechseln", + "personal": "Mein Arbeitsbereich", + "other_workspaces": "My Workspaces", + "team": "Team Arbeitsbereich", + "title": "Arbeitsbereiche" + }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" }, "shortcodes": { - "actions": "Actions", - "created_on": "Created on", - "deleted": "Shortcode deleted", - "method": "Method", - "not_found": "Shortcode not found", - "short_code": "Short code", + "actions": "Aktionen", + "created_on": "Erstellt am", + "deleted": "Shortcode gelöscht", + "method": "Methode", + "not_found": "Shortcode nicht gefunden", + "short_code": "Shortcode", "url": "URL" } } diff --git a/packages/hoppscotch-common/locales/el.json b/packages/hoppscotch-common/locales/el.json index dcd8bf04f..f2fffece3 100644 --- a/packages/hoppscotch-common/locales/el.json +++ b/packages/hoppscotch-common/locales/el.json @@ -24,8 +24,10 @@ "go_back": "Πήγαινε πίσω", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Επιγραφή", "learn_more": "Μάθε περισσότερα", + "download_here": "Download here", "less": "Less", "more": "Περισσότερο", "new": "Νέος", @@ -43,6 +45,7 @@ "search": "Αναζήτηση", "send": "Στείλετε", "share": "Share", + "show_secret": "Show secret", "start": "Αρχή", "starting": "Starting", "stop": "Να σταματήσει", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Πληκτρολογήστε μια εντολή ή αναζήτηση…", "we_use_cookies": "Χρησιμοποιούμε cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Τι νέα?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Ο λογαριασμός υπάρχει με διαφορετικά διαπιστευτήρια - Συνδεθείτε για να συνδέσετε και τους δύο λογαριασμούς", "all_sign_in_options": "Όλες οι επιλογές σύνδεσης", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Συνεχίστε με το Email", "continue_with_github": "Συνεχίστε με το GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Συνεχίστε με την Google", "continue_with_microsoft": "Συνεχίστε με την Microsoft", "email": "ΗΛΕΚΤΡΟΝΙΚΗ ΔΙΕΥΘΥΝΣΗ", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Κωδικός πρόσβασης", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Ενδειξη", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Επεξεργασία Συλλογής", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για τη συλλογή", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Επιλέξτε μια Συλλογή", "select_location": "Επιλέξτε τοποθεσία", + "details": "Details", "select_team": "Επιλέξτε μια ομάδα", "team_collections": "Συλλογές ομάδων" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Είστε βέβαιοι ότι θέλετε να εξαιρεθείτε από την τηλεμετρία;", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Είστε βέβαιοι ότι θέλετε να συγχρονίσετε αυτόν τον χώρο εργασίας;" + "sync": "Είστε βέβαιοι ότι θέλετε να συγχρονίσετε αυτόν τον χώρο εργασίας;", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "Δεν υπάρχουν εκκρεμείς προσκλήσεις για αυτή την ομάδα", "profile": "Συνδεθείτε για προβολή του προφίλ σας", "protocols": "Τα πρωτόκολλα είναι κενά", + "request_variables": "This request does not have any request variables", "schema": "Συνδεθείτε σε ένα τελικό σημείο GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Το όνομα της ομάδας είναι κενό", "teams": "Οι ομάδες είναι άδειες", "tests": "Δεν υπάρχουν δοκιμές για αυτό το αίτημα", + "access_tokens": "Access tokens are empty", "shortcodes": "Τα Shortcodes είναι κενά" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Επιλέξτε περιβάλλον", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Αναβάθμιση Περιβάλλοντος", "value": "Value", "variable": "Variable", - "variable_list": "Λίστα μεταβλητών" + "variables": "Variables", + "variable_list": "Λίστα μεταβλητών", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Όνομα κενού αιτήματος", "f12_details": "(F12 για λεπτομέρειες)", "gql_prettify_invalid_query": "Δεν ήταν δυνατή η προεπιλογή ενός μη έγκυρου ερωτήματος, η επίλυση σφαλμάτων σύνταξης ερωτήματος και η δοκιμή ξανά", @@ -296,6 +336,7 @@ "incorrect_email": "Λάθος email", "invalid_link": "Μή έγκυρο link", "invalid_link_description": "Ο σύνδεσμος που επιλέξατε έχει λήξει ή δεν είναι έγκυρος.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Μη έγκυρο JSON", "json_prettify_invalid_body": "Δεν ήταν δυνατή η ομορφιά ενός μη έγκυρου σώματος, η επίλυση σφαλμάτων σύνταξης json και η προσπάθεια ξανά", "network_error": "Από ότι φαίνεται υπάρχει ένα σφάλμα δικτύου. Παρακαλούμε προσπαθήστε ξανά.", @@ -307,17 +348,25 @@ "page_not_found": "Αυτή η σελίδα δεν βρέθηκε", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Δεν ήταν δυνατή η εκτέλεση του σεναρίου πριν από το αίτημα", "something_went_wrong": "Κάτι πήγε στραβά", - "test_script_fail": "Δεν μπορεσε να εκτελεστεί το post-request script" + "test_script_fail": "Δεν μπορεσε να εκτελεστεί το post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Εξαγωγή ως JSON", "create_secret_gist": "Δημιουργήστε μυστική ουσία", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Η ουσία δημιουργήθηκε", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Συνδεθείτε με το GitHub για να δημιουργήσετε μυστική ουσία", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Η ουσία δημιουργήθηκε" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Μεταλλάξεις", "schema": "Σχήμα", "subscriptions": "Συνδρομές", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Εισαγωγή συλλογών αρχείο JSON Hoppscotch Collections", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Εισαγωγή" + "title": "Εισαγωγή", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Σώμα Ακατέργαστου Αιτήματος", "rename": "Rename Request", "renamed": "Το αίτημα μετονομάστηκε", + "request_variables": "Request variables", "run": "Τρέξιμο", "save": "Σώσει", "save_as": "Αποθήκευση ως", @@ -557,6 +613,7 @@ "title": "Αίτηση", "type": "Τύπος αιτήματος", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Μεταβλητές", "view_my_links": "Προβολή των links μου", "copy_link": "Αντιγραφή συνδέσμου" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Κάντε ερωτήσεις και λάβετε απαντήσεις", "github": "Follow us on Github", "shortcuts": "Περιηγηθείτε πιο γρήγορα στην εφαρμογή", - "team": "Επικοινωνήστε με την ομάδα", "title": "Υποστήριξη", - "twitter": "Ακολουθήστε μας στο Twitter" + "twitter": "Ακολουθήστε μας στο Twitter", + "team": "Επικοινωνήστε με την ομάδα" }, "tab": { "authorization": "Εξουσιοδότηση", @@ -889,6 +953,9 @@ "query": "Ερώτηση", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Υποδοχή.IO", "sse": "SSE", "tests": "Δοκιμές", @@ -917,7 +984,6 @@ "invite_tooltip": "Πρόσκληση χρηστών σε αυτό το workspace", "invited_to_team": "{owner} σου έκανε πρόσκληση για να μπεις στην {team}", "join": "Πρόσκληση Αποδεκτή", - "join_beta": "Εγγραφείτε στο πρόγραμμα beta για πρόσβαση σε ομάδες.", "join_team": "Γίνε Μέλος {team}", "joined_team": "Μπήκες στην ομάδα: {team}", "joined_team_description": "Είστε πλέον μέλος αυτής της ομάδας", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Της ομάδας", "we_sent_invite_link": "Στείλαμε έναν σύνδεσμο πρόσκλησης σε όλους!", - "we_sent_invite_link_description": "Ζητήστε από όλους όσους στείλατε πρόσκληση να ελέγξουν τα email τους. Click στον σύνδεσμο για εισαγωγή στην ομάδα." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ζητήστε από όλους όσους στείλατε πρόσκληση να ελέγξουν τα email τους. Click στον σύνδεσμο για εισαγωγή στην ομάδα.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Εγγραφείτε στο πρόγραμμα beta για πρόσβαση σε ομάδες." }, "team_environment": { "deleted": "Το περιβάλλον διαγράφηκε", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Δράσεις", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 2ab5f041c..e4bd4437c 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -98,7 +98,9 @@ "twitter": "Twitter", "type_a_command_search": "Type a command or search…", "we_use_cookies": "We use cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "What's new?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { @@ -175,7 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Edit Collection", "import_or_create": "Import or create a collection", - "import_collection":"Import Collection", + "import_collection": "Import Collection", "invalid_name": "Please provide a name for the collection", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -191,7 +193,9 @@ "save_as": "Save as", "save_to_collection": "Save to Collection", "select": "Select a Collection", - "select_location": "Select location" + "select_location": "Select location", + "details": "Details", + "duplicated": "Collection duplicated" }, "confirm": { "close_unsaved_tab": "Are you sure you want to close this tab?", @@ -208,7 +212,8 @@ "remove_telemetry": "Are you sure you want to opt-out of Telemetry?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Would you like to restore your workspace from cloud? This will discard your local progress." + "sync": "Would you like to restore your workspace from cloud? This will discard your local progress.", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -270,7 +275,8 @@ "subscription": "Subscriptions are empty", "team_name": "Workspace name empty", "teams": "You don't belong to any workspaces", - "tests": "There are no tests for this request" + "tests": "There are no tests for this request", + "access_tokens": "Access tokens are empty" }, "environment": { "add_to_global": "Add to Global", @@ -307,7 +313,9 @@ "value": "Value", "variable": "Variable", "variables": "Variables", - "variable_list": "Variable List" + "variable_list": "Variable List", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -342,7 +350,10 @@ "script_fail": "Could not execute pre-request script", "something_went_wrong": "Something went wrong", "test_script_fail": "Could not execute post-request script", - "reading_files": "Error while reading one or more files." + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Export as JSON", @@ -601,7 +612,8 @@ "url": "URL", "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variables", - "view_my_links": "View my links" + "view_my_links": "View my links", + "generate_name_error": "Failed to generate request name." }, "response": { "audio": "Audio", @@ -661,6 +673,7 @@ "short_codes": "Short codes", "short_codes_description": "Short codes which were created by you.", "sidebar_on_left": "Sidebar on left", + "ai_experiments": "AI Experiments", "sync": "Synchronise", "sync_collections": "Collections", "sync_description": "These settings are synced to cloud.", @@ -853,7 +866,7 @@ "switch_to_personal": "Switch to your personal workspace", "title": "Workspaces" }, - "phrases":{ + "phrases": { "try": "Try", "import_collections": "Import collections", "create_environment": "Create environment", @@ -936,6 +949,8 @@ "query": "Query", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", @@ -997,7 +1012,10 @@ "success_invites": "Success invites", "title": "Workspaces", "we_sent_invite_link": "We sent an invite link to all invitees!", + "invite_sent_smtp_disabled": "Invite links generated", "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the workspace.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", "search_title": "Team Requests" }, "team_environment": { @@ -1033,5 +1051,44 @@ "login_to_continue": "Login to continue", "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, + "ai_experiments": { + "generate_request_name": "Generate Request Name Using AI", + "generate_or_modify_request_body": "Generate or Modify Request Body" } } diff --git a/packages/hoppscotch-common/locales/es.json b/packages/hoppscotch-common/locales/es.json index 61a014a1a..48828ca74 100644 --- a/packages/hoppscotch-common/locales/es.json +++ b/packages/hoppscotch-common/locales/es.json @@ -24,8 +24,10 @@ "go_back": "Volver", "go_forward": "Adelante", "group_by": "Agrupar por", + "hide_secret": "Hide secret", "label": "Etiqueta", "learn_more": "Aprender más", + "download_here": "Download here", "less": "Menos", "more": "Más", "new": "Nuevo", @@ -43,6 +45,7 @@ "search": "Buscar", "send": "Enviar", "share": "Share", + "show_secret": "Show secret", "start": "Comenzar", "starting": "Iniciando", "stop": "Detener", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Escribe un comando o buscar algo…", "we_use_cookies": "Usamos cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "¿Qué hay de nuevo?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "La cuenta existe con una credencial diferente - Inicia sesión para vincular ambas cuentas", "all_sign_in_options": "Todas las opciones de inicio de sesión", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Continuar con correo electrónico", "continue_with_github": "Continuar con GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Continuar con Google", "continue_with_microsoft": "Continuar con Microsoft", "email": "Correo electrónico", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No se ha definido ningún punto final de token", "something_went_wrong_on_oauth_redirect": "Algo ha ido mal durante la redirección OAuth", "something_went_wrong_on_token_generation": "Algo salió mal en la generación del token", - "token_generation_oidc_discovery_failed": "Fallo en la generación del token: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Fallo en la generación del token: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pasar por", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Contraseña", "save_to_inherit": "Por favor, guarda esta solicitud en cualquier colección para heredar la autorización", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "No se puede reordenar la colección con un padre diferente", "edit": "Editar colección", "import_or_create": "Importar o crear una colección", + "import_collection": "Import Collection", "invalid_name": "Proporciona un nombre válido para la colección.", "invalid_root_move": "La colección ya está en la raíz", "moved": "Movido con éxito", @@ -165,6 +194,7 @@ "save_to_collection": "Guardar en la colección", "select": "Seleccionar colección", "select_location": "Seleccionar ubicación", + "details": "Details", "select_team": "Seleccionar equipo", "team_collections": "Colecciones de equipos" }, @@ -183,7 +213,8 @@ "remove_telemetry": "¿Estás seguro de que deseas darse de baja de la telemetría?", "request_change": "¿Estás seguro de que deseas descartar la solicitud actual, los cambios no guardados se perderán.", "save_unsaved_tab": "¿Deseas guardar los cambios realizados en esta pestaña?", - "sync": "¿Estás seguro de que deseas sincronizar este espacio de trabajo?" + "sync": "¿Estás seguro de que deseas sincronizar este espacio de trabajo?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Añadir a parámetros", @@ -237,13 +268,16 @@ "pending_invites": "No hay invitaciones pendientes para este equipo", "profile": "Iniciar sesión para ver tu perfil", "protocols": "No hay protocolos", + "request_variables": "This request does not have any request variables", "schema": "Conectarse a un punto final de GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "No hay solicitudes compartidas", "shared_requests_logout": "Iniciar sesión para ver sus solicitudes compartidas o crear una nueva", "subscription": "No hay suscripciones", "team_name": "Nombre del equipo vacío", "teams": "No hay equipos", "tests": "No hay pruebas para esta solicitud", + "access_tokens": "Access tokens are empty", "shortcodes": "Aún no se han creado Shortcodes" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Vistazo rápido al entorno", "replace_with_variable": "Sustituir por variable", "scope": "Ámbito", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Seleccionar entorno", "set": "Establecer entorno", "set_as_environment": "Establecer como entorno", @@ -278,7 +314,10 @@ "updated": "Entorno actualizado", "value": "Valor", "variable": "Variable", - "variable_list": "Lista de variables" + "variables": "Variables", + "variable_list": "Lista de variables", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "No se han podido cargar los proveedores de autenticación", @@ -289,6 +328,7 @@ "danger_zone": "Zona de peligro", "delete_account": "Tu cuenta es actualmente propietaria en estos equipos:", "delete_account_description": "Para poder eliminar tu cuenta, debes darte de baja, transferir la propiedad o eliminar estos equipos.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nombre de solicitud vacío", "f12_details": "(F12 para más detalles)", "gql_prettify_invalid_query": "No se puede aplicar embellecedor a una consulta no válida, resuelve los errores de sintaxis de la consulta y vuelve a intentarlo", @@ -296,6 +336,7 @@ "incorrect_email": "Correo electrónico incorrecto", "invalid_link": "Enlace no válido", "invalid_link_description": "El enlace que has pulsado no es válido o ha caducado.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSON no válido", "json_prettify_invalid_body": "No se puede aplicar embellecedor a un cuerpo inválido, resuelve errores de sintaxis json y vuelve a intentarlo", "network_error": "Parece que hay un error de red. Por favor, inténtalo de nuevo.", @@ -307,17 +348,25 @@ "page_not_found": "No se ha podido encontrar esta página", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Error de proxy", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "No se pudo ejecutar el script de solicitud previa", "something_went_wrong": "Algo salió mal", - "test_script_fail": "No se ha podido ejecutar la secuencia de comandos posterior a la solicitud" + "test_script_fail": "No se ha podido ejecutar la secuencia de comandos posterior a la solicitud", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exportar como JSON", "create_secret_gist": "Crear un Gist secreto", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Algo ha ido mal al exportar", - "gist_created": "Gist creado", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Iniciar sesión con GitHub para crear un Gist secreto", - "title": "Exportar" + "title": "Exportar", + "success": "Successfully exported", + "gist_created": "Gist creado" }, "filter": { "all": "Todos", @@ -339,7 +388,8 @@ "mutations": "Mutaciones", "schema": "Esquema", "subscriptions": "Suscripciones", - "switch_connection": "Cambiar conexión" + "switch_connection": "Cambiar conexión", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "Colecciones de GraphQL" @@ -405,12 +455,17 @@ "json_description": "Importar colecciones desde un archivo JSON de colecciones de Hoppscotch", "postman_environment": "Entorno de Postman", "postman_environment_description": "Importar entorno de Postman desde un archivo JSON", - "title": "Importar" + "title": "Importar", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspeccionar posibles errores", "environment": { "add_environment": "Añadir al Entorno", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "No se ha encontrado la variable de entorno \"{environment}\"." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "cuerpo sin procesar", "rename": "Renombrar solicitud", "renamed": "Solicitud renombrada", + "request_variables": "Request variables", "run": "Ejecutar", "save": "Guardar", "save_as": "Guardar como", @@ -557,6 +613,7 @@ "title": "Solicitud", "type": "Tipo de solicitud", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variables", "view_my_links": "Ver mis enlaces", "copy_link": "Copiar enlace" @@ -811,6 +868,13 @@ "new": "Crear un nuevo equipo", "switch_to_personal": "Cambia a tu espacio de trabajo personal", "title": "Equipos" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Haz preguntas y obtén respuestas", "github": "Síguenos en Github", "shortcuts": "Navega por la aplicación más rápido", - "team": "Ponte en contacto con el equipo", "title": "Ayuda", - "twitter": "Síguenos en Twitter" + "twitter": "Síguenos en Twitter", + "team": "Ponte en contacto con el equipo" }, "tab": { "authorization": "Autorización", @@ -889,6 +953,9 @@ "query": "Consulta", "schema": "Esquema", "shared_requests": "Solicitudes compartidas", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Pruebas", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite a personas a este espacio de trabajo", "invited_to_team": "{owner} te ha invitado al equipo {team}", "join": "Invitación aceptada", - "join_beta": "Únete al programa beta para acceder a los equipos.", "join_team": "Entrar a {team}", "joined_team": "Haz entrado a {team}", "joined_team_description": "Ahora eres miembro de este equipo", @@ -950,7 +1016,12 @@ "success_invites": "Invitaciones con éxito", "title": "Equipos", "we_sent_invite_link": "¡Hemos enviado un enlace de invitación a todos los invitados!", - "we_sent_invite_link_description": "Pide a todos los invitados que revisen su bandeja de entrada. Tienen que hacer clic en el enlace para unirse al equipo." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Pide a todos los invitados que revisen su bandeja de entrada. Tienen que hacer clic en el enlace para unirse al equipo.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Únete al programa beta para acceder a los equipos." }, "team_environment": { "deleted": "Entorno eliminado", @@ -977,9 +1048,50 @@ "workspace": { "change": "Cambiar el espacio de trabajo", "personal": "Mi espacio de trabajo", + "other_workspaces": "My Workspaces", "team": "Espacio de trabajo en equipo", "title": "Espacios de trabajo" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Acciones", "created_on": "Creado el", diff --git a/packages/hoppscotch-common/locales/fi.json b/packages/hoppscotch-common/locales/fi.json index c24d6ede3..03c00e3af 100644 --- a/packages/hoppscotch-common/locales/fi.json +++ b/packages/hoppscotch-common/locales/fi.json @@ -24,8 +24,10 @@ "go_back": "Mene takaisin", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etiketti", "learn_more": "Lue lisää", + "download_here": "Download here", "less": "Less", "more": "Lisää", "new": "Uusi", @@ -43,6 +45,7 @@ "search": "Hae", "send": "Lähettää", "share": "Share", + "show_secret": "Show secret", "start": "alkaa", "starting": "Starting", "stop": "Lopettaa", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Kirjoita komento tai hae…", "we_use_cookies": "Käytämme evästeitä", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Mikä on uutta?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Tili on eri kirjautumistiedoilla - Kirjaudu linkittääksesi molemmat tilit", "all_sign_in_options": "Kaikki kirjautumisvaihtoehdot", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Jatka sähköpostilla", "continue_with_github": "Jatka GitHubilla", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Jatka Googlella", "continue_with_microsoft": "Continue with Microsoft", "email": "Sähköposti", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Salasana", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Muokkaa kokoelmaa", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Anna kokoelmalle kelvollinen nimi", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Valitse kokoelma", "select_location": "Valitse sijainti", + "details": "Details", "select_team": "Valitse joukkue", "team_collections": "Joukkuekokoelmat" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Haluatko varmasti poistaa telemetrian käytöstä?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Haluatko varmasti synkronoida tämän työtilan?" + "sync": "Haluatko varmasti synkronoida tämän työtilan?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protokollat ovat tyhjiä", + "request_variables": "This request does not have any request variables", "schema": "Muodosta yhteys GraphQL -päätepisteeseen", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Joukkueen nimi tyhjä", "teams": "Joukkueet ovat tyhjiä", "tests": "Tätä pyyntöä ei ole testattu", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Valitse ympäristö", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Muuttujien luettelo" + "variables": "Variables", + "variable_list": "Muuttujien luettelo", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Tyhjä pyynnön nimi", "f12_details": "(F12 lisätietoja)", "gql_prettify_invalid_query": "Virheellistä kyselyä ei voitu määrittää, ratkaista kyselyn syntaksivirheet ja yrittää uudelleen", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Virheellistä runkoa ei voitu määrittää, ratkaista json -syntaksivirheitä ja yrittää uudelleen", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Ennakkopyyntöskriptiä ei voitu suorittaa", "something_went_wrong": "Jotain meni pieleen", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Vie JSON -muodossa", "create_secret_gist": "Luo salainen ydin", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist luotu", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Kirjaudu sisään GitHubilla luodaksesi salaisen sisällön", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist luotu" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutaatiot", "schema": "Kaavio", "subscriptions": "Tilaukset", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Tuonti" + "title": "Tuonti", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Raaka pyynnön runko", "rename": "Rename Request", "renamed": "Pyyntö nimettiin uudelleen", + "request_variables": "Request variables", "run": "Juosta", "save": "Tallentaa", "save_as": "Tallenna nimellä", @@ -557,6 +613,7 @@ "title": "Pyyntö", "type": "Pyynnön tyyppi", "url": "URL -osoite", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Muuttujat", "view_my_links": "View my links", "copy_link": "Kopioi linkki" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Esitä kysymyksiä ja saat vastauksia", "github": "Follow us on Github", "shortcuts": "Selaa sovellusta nopeammin", - "team": "Ota yhteyttä tiimiin", "title": "Tuki", - "twitter": "Seuraa meitä Twitterissä" + "twitter": "Seuraa meitä Twitterissä", + "team": "Ota yhteyttä tiimiin" }, "tab": { "authorization": "Valtuutus", @@ -889,6 +953,9 @@ "query": "Kysely", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Pistorasia.IO", "sse": "SSE", "tests": "Testit", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Liity beta -ohjelmaan päästäksesi tiimeihin.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Joukkueet", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Liity beta -ohjelmaan päästäksesi tiimeihin." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/fr.json b/packages/hoppscotch-common/locales/fr.json index 87578467d..40a1ad2f8 100644 --- a/packages/hoppscotch-common/locales/fr.json +++ b/packages/hoppscotch-common/locales/fr.json @@ -24,8 +24,10 @@ "go_back": "Retour", "go_forward": "Avancer", "group_by": "Grouper par", + "hide_secret": "Hide secret", "label": "Étiqueter", "learn_more": "En savoir plus", + "download_here": "Download here", "less": "Moins", "more": "Suite", "new": "Nouveau", @@ -43,6 +45,7 @@ "search": "Chercher", "send": "Envoyer", "share": "Share", + "show_secret": "Show secret", "start": "Démarrer", "starting": "Démarrage", "stop": "Arrêter", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Tapez une commande ou recherchez…", "we_use_cookies": "Nous utilisons des cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Quoi de neuf ?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Le compte existe avec des informations d'identification différentes - Connectez-vous pour lier les deux comptes", "all_sign_in_options": "Toutes les options de connexion", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Continuer par e-mail", "continue_with_github": "Continuer avec GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Continuer avec Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-mail", @@ -123,31 +130,54 @@ "include_in_url": "Inclure dans l'URL", "inherited_from": "Inherited from {auth} from Parent Collection {collection} ", "learn": "Apprendre comment", + "oauth": { + "redirect_auth_server_returned_error": "Le serveur d'authentification a renvoyé un état d'erreur", + "redirect_auth_token_request_failed": "La demande d'obtention du jeton d'authentification a échoué", + "redirect_auth_token_request_invalid_response": "Réponse invalide du point de terminaison Token lors de la demande d'un jeton d'authentification", + "redirect_invalid_state": "Valeur d'état non valide présente dans la redirection", + "redirect_no_auth_code": "Pas de code d'autorisation dans la redirection", + "redirect_no_client_id": "Pas d'ID client défini", + "redirect_no_client_secret": "Pas de secret client défini", + "redirect_no_code_verifier": "Pas de vérificateur de code défini", + "redirect_no_token_endpoint": "Aucun point de terminaison de jeton n'est défini", + "something_went_wrong_on_oauth_redirect": "Quelque chose s'est mal passé lors de la redirection OAuth", + "something_went_wrong_on_token_generation": "Un problème s'est produit lors de la génération des jetons", + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" + }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Mot de passe", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Jeton", "type": "Type d'autorisation", - "username": "Nom d'utilisateur", - "oauth": { - "something_went_wrong_on_token_generation": "Un problème s'est produit lors de la génération des jetons", - "redirect_auth_server_returned_error": "Le serveur d'authentification a renvoyé un état d'erreur", - "redirect_no_auth_code": "Pas de code d'autorisation dans la redirection", - "redirect_invalid_state": "Valeur d'état non valide présente dans la redirection", - "redirect_no_token_endpoint": "Aucun point de terminaison de jeton n'est défini", - "redirect_no_client_id": "Pas d'ID client défini", - "redirect_no_client_secret": "Pas de secret client défini", - "redirect_no_code_verifier": "Pas de vérificateur de code défini", - "redirect_auth_token_request_failed": "La demande d'obtention du jeton d'authentification a échoué", - "redirect_auth_token_request_invalid_response": "Réponse invalide du point de terminaison Token lors de la demande d'un jeton d'authentification", - "something_went_wrong_on_oauth_redirect": "Quelque chose s'est mal passé lors de la redirection OAuth" - } + "username": "Nom d'utilisateur" }, "collection": { "created": "Collection créée", "different_parent": "Impossible de réorganiser une collection dont le parent est différent", "edit": "Modifier la collection", "import_or_create": "Importer ou créer une collection", + "import_collection": "Import Collection", "invalid_name": "Veuillez fournir un nom valide pour la collection", "invalid_root_move": "Collection déjà présente dans la racine", "moved": "Déplacement réussi", @@ -164,6 +194,7 @@ "save_to_collection": "Enregistrer dans la collection", "select": "Sélectionnez une collection", "select_location": "Sélectionnez l'emplacement", + "details": "Details", "select_team": "Sélectionnez une équipe", "team_collections": "Collections de l'équipe" }, @@ -182,7 +213,8 @@ "remove_telemetry": "Êtes-vous sûr de vouloir désactiver la télémétrie ?", "request_change": "Êtes-vous sûr de vouloir rejeter la demande en cours ? Les modifications non enregistrées seront perdues.", "save_unsaved_tab": "Souhaitez-vous enregistrer les modifications apportées dans cet onglet ?", - "sync": "Voulez-vous vraiment synchroniser cet espace de travail ?" + "sync": "Voulez-vous vraiment synchroniser cet espace de travail ?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Ajouter aux paramètres", @@ -191,20 +223,20 @@ }, "cookies": { "modal": { - "new_domain_name": "Nouveau nom de domaine", - "set": "Définir un cookie", - "cookie_string": "Chaîne de caractères de cookie", - "enter_cookie_string": "Saisir la chaîne de caractères du cookie", - "cookie_name": "Nom", - "cookie_value": "Valeur", - "cookie_path": "Chemin d'accès", "cookie_expires": "Expiration", - "managed_tab": "Gestion", - "raw_tab": "Brut", - "interceptor_no_support": "L'intercepteur que vous avez sélectionné ne prend pas en charge les cookies. Sélectionnez un autre intercepteur et réessayez.", - "empty_domains": "La liste des domaines est vide", + "cookie_name": "Nom", + "cookie_path": "Chemin d'accès", + "cookie_string": "Chaîne de caractères de cookie", + "cookie_value": "Valeur", "empty_domain": "Le domaine est vide", - "no_cookies_in_domain": "Aucun cookie n'est défini pour ce domaine" + "empty_domains": "La liste des domaines est vide", + "enter_cookie_string": "Saisir la chaîne de caractères du cookie", + "interceptor_no_support": "L'intercepteur que vous avez sélectionné ne prend pas en charge les cookies. Sélectionnez un autre intercepteur et réessayez.", + "managed_tab": "Gestion", + "new_domain_name": "Nouveau nom de domaine", + "no_cookies_in_domain": "Aucun cookie n'est défini pour ce domaine", + "raw_tab": "Brut", + "set": "Définir un cookie" } }, "count": { @@ -236,13 +268,16 @@ "pending_invites": "Il n'y a pas d'invitation en cours pour cette équipe", "profile": "Connectez-vous pour voir votre profil", "protocols": "Les protocoles sont vides", + "request_variables": "This request does not have any request variables", "schema": "Se connecter à un point de terminaison GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Il n'y a pas de requêtes partagées", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Nom de l'équipe vide", "teams": "Les équipes sont vides", "tests": "Il n'y a pas de tests pour cette requête", + "access_tokens": "Access tokens are empty", "shortcodes": "Les shortcodes sont vides" }, "environment": { @@ -269,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Sélectionnez l'environnement", "set": "Set environment", "set_as_environment": "Set as environment", @@ -277,7 +314,10 @@ "updated": "Mise à jour de l'environnement", "value": "Value", "variable": "Variable", - "variable_list": "Liste des variables" + "variables": "Variables", + "variable_list": "Liste des variables", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -288,6 +328,7 @@ "danger_zone": "Zone de danger", "delete_account": "Votre compte est actuellement propriétaire de ces équipes :", "delete_account_description": "Vous devez vous retirer, transférer la propriété ou supprimer ces équipes avant de pouvoir supprimer votre compte.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nom de la requête vide", "f12_details": "(F12 pour les détails)", "gql_prettify_invalid_query": "Impossible de formater une requête non valide, résolvez les erreurs de syntaxe de la requête et réessayer", @@ -295,6 +336,7 @@ "incorrect_email": "Email incorrect", "invalid_link": "Lien invalide", "invalid_link_description": "Le lien sur lequel vous avez cliqué n'est pas valide ou a expiré.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSON non valide", "json_prettify_invalid_body": "Impossible de formater un corps non valide, résolvez les erreurs de syntaxe json et réessayez", "network_error": "Il semble y avoir une erreur de réseau. Veuillez réessayer.", @@ -306,17 +348,25 @@ "page_not_found": "Cette page n'a pas pu être trouvée", "please_install_extension": "Veuillez installer l'extension et ajouter l'origine à l'extension.", "proxy_error": "Erreur de proxy", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Impossible d'exécuter le script de pré-requête", "something_went_wrong": "Quelque chose s'est mal passé", - "test_script_fail": "Impossible d'exécuter le script post-requête" + "test_script_fail": "Impossible d'exécuter le script post-requête", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exporter au format JSON", "create_secret_gist": "Créer un Gist secret", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist créé", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Connectez-vous avec GitHub pour créer un Gist secret", - "title": "Exportation" + "title": "Exportation", + "success": "Successfully exported", + "gist_created": "Gist créé" }, "filter": { "all": "Tout", @@ -338,7 +388,8 @@ "mutations": "Mutations", "schema": "Schéma", "subscriptions": "Abonnements", - "switch_connection": "Changer de connexion" + "switch_connection": "Changer de connexion", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -404,12 +455,17 @@ "json_description": "Importer des collections depuis un fichier JSON Hoppscotch", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importer" + "title": "Importer", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspecter les erreurs possibles", "environment": { "add_environment": "Ajouter à l'environnement", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "La variable d'environnement “{environment}“ n'a pas été trouvée." }, "header": { @@ -525,8 +581,8 @@ "enter_curl": "Entrer cURL", "generate_code": "Générer le code", "generated_code": "Code généré", - "go_to_body_tab": "Go to Body tab", "go_to_authorization_tab": "Aller à l'autorisation", + "go_to_body_tab": "Go to Body tab", "header_list": "Liste des en-têtes", "invalid_name": "Veuillez fournir un nom pour la requête", "method": "Méthode", @@ -545,6 +601,7 @@ "raw_body": "Corps de requête brut", "rename": "Demande de renommage", "renamed": "Requête renommée", + "request_variables": "Request variables", "run": "Lancer", "save": "Sauvegarder", "save_as": "Enregistrer sous", @@ -556,6 +613,7 @@ "title": "Requête", "type": "Type de requête", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variables", "view_my_links": "Voir mes liens", "copy_link": "Copier le lien" @@ -810,6 +868,13 @@ "new": "Créer une nouvelle équipe", "switch_to_personal": "Passez à votre espace de travail personnel", "title": "Les équipes" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -835,8 +900,8 @@ "disconnected": "Déconnecté", "disconnected_from": "Déconnecté de {name}", "docs_generated": "Documentation générée", - "download_started": "Téléchargement commencé", "download_failed": "Téléchargement échoué", + "download_started": "Téléchargement commencé", "enabled": "Active", "file_imported": "Fichier importé", "finished_in": "Terminé en {duration} ms", @@ -866,9 +931,9 @@ "forum": "Posez des questions et obtenez des réponses", "github": "Follow us on Github", "shortcuts": "Parcourir l'application plus rapidement", - "team": "Entrez en contact avec l'équipe", "title": "Support", - "twitter": "Suivez-nous sur Twitter" + "twitter": "Suivez-nous sur Twitter", + "team": "Entrez en contact avec l'équipe" }, "tab": { "authorization": "Autorisation", @@ -888,6 +953,9 @@ "query": "Requête", "schema": "Schema", "shared_requests": "Requêtes partagées", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "ESS", "tests": "Tests", @@ -904,6 +972,7 @@ "email_do_not_match": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.", "exit": "Quitter l'équipe", "exit_disabled": "Seul le propriétaire ne peut pas quitter l'équipe", + "failed_invites": "Échec des invitations", "invalid_coll_id": "Invalid collection ID", "invalid_email_format": "Le format de l'e-mail n'est pas valide", "invalid_id": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.", @@ -915,7 +984,6 @@ "invite_tooltip": "Invitez des personnes à cet espace de travail", "invited_to_team": "{owner} vous invite à rejoindre {team}", "join": "Invitation accepté", - "join_beta": "Rejoignez le programme bêta pour accéder aux équipes.", "join_team": "Rejoindre {team}", "joined_team": "Vous avez rejoint {team}", "joined_team_description": "Vous êtes maintenant membre de cette équipe", @@ -940,16 +1008,20 @@ "not_found": "Équipe non trouvée. Contactez le propriétaire de votre équipe.", "not_valid_viewer": "Vous n'êtes pas un visionneur valide. Contactez le propriétaire de votre équipe.", "parent_coll_move": "Impossible de déplacer une collection vers une collection enfant", - "success_invites": "Les invitations réussites", "pending_invites": "Invitations en attente", - "failed_invites": "Échec des invitations", "permissions": "Autorisations", "same_target_destination": "Même destinataire et même cible", "saved": "Équipe enregistrée", "select_a_team": "Choisir une équipe", + "success_invites": "Les invitations réussites", "title": "Équipes", "we_sent_invite_link": "Nous avons envoyé un lien d'invitation à tous les invités !", - "we_sent_invite_link_description": "Demandez à tous les invités de vérifier leur boîte de réception. Cliquez sur le lien pour rejoindre l'équipe." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Demandez à tous les invités de vérifier leur boîte de réception. Cliquez sur le lien pour rejoindre l'équipe.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Rejoignez le programme bêta pour accéder aux équipes." }, "team_environment": { "deleted": "Environment supprimé", @@ -976,9 +1048,50 @@ "workspace": { "change": "Changer d'espace de travail", "personal": "Mon espace de travail", + "other_workspaces": "My Workspaces", "team": "Espace de travail de l'équipe", "title": "Espaces de travail" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Créé le", diff --git a/packages/hoppscotch-common/locales/he.json b/packages/hoppscotch-common/locales/he.json index ed4af133c..be8f931fa 100644 --- a/packages/hoppscotch-common/locales/he.json +++ b/packages/hoppscotch-common/locales/he.json @@ -24,8 +24,10 @@ "go_back": "תחזור", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "תווית", "learn_more": "למד עוד", + "download_here": "Download here", "less": "Less", "more": "יותר", "new": "חָדָשׁ", @@ -43,6 +45,7 @@ "search": "לחפש", "send": "לִשְׁלוֹחַ", "share": "Share", + "show_secret": "Show secret", "start": "הַתחָלָה", "starting": "Starting", "stop": "תפסיק", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "הקלד פקודה או חפש ...", "we_use_cookies": "אנו משתמשים בעוגיות", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "מה חדש?", + "see_whats_new": "See what’s new", "wiki": "ויקי" }, "auth": { "account_exists": "החשבון קיים עם אישורים שונים - התחבר לקישור שני החשבונות", "all_sign_in_options": "כל אפשרויות הכניסה", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "המשך עם מייל", "continue_with_github": "המשך עם GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "המשך עם גוגל", "continue_with_microsoft": "Continue with Microsoft", "email": "אימייל", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "סיסמה", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "אֲסִימוֹן", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "ערוך אוסף", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "אנא ספק שם תקף לאוסף", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "בחר אוסף", "select_location": "תבחר מיקום", + "details": "Details", "select_team": "בחר צוות", "team_collections": "אוספי צוות" }, @@ -183,7 +213,8 @@ "remove_telemetry": "האם אתה בטוח שברצונך לבטל את הסכמתך לטלמטריה?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "האם אתה בטוח שברצונך לסנכרן את סביבת העבודה הזו?" + "sync": "האם אתה בטוח שברצונך לסנכרן את סביבת העבודה הזו?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "הפרוטוקולים ריקים", + "request_variables": "This request does not have any request variables", "schema": "התחבר לנקודת קצה של GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "שם הקבוצה ריק", "teams": "הקבוצות ריקות", "tests": "אין בדיקות לבקשה זו", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "בחר סביבה", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "רשימת משתנים" + "variables": "Variables", + "variable_list": "רשימת משתנים", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "שם הבקשה ריק", "f12_details": "(F12 לפרטים)", "gql_prettify_invalid_query": "לא ניתן היה לייפות שאילתה לא חוקית, לפתור שגיאות תחביר שאילתות ולנסות שוב", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "לא ניתן היה לייפות גוף לא חוקי, לפתור שגיאות תחביר של json ולנסות שוב", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "לא ניתן להפעיל סקריפט של בקשה מראש", "something_went_wrong": "משהו השתבש", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "ייצא כ- JSON", "create_secret_gist": "צור גיסט סודי", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "גיסט נוצר", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "התחבר עם GitHub כדי ליצור תמצית סודית", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "גיסט נוצר" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "מוטציות", "schema": "סכֵימָה", "subscriptions": "מנויים", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "יְבוּא" + "title": "יְבוּא", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "גוף בקשה גולמית", "rename": "Rename Request", "renamed": "שם הבקשה שונה", + "request_variables": "Request variables", "run": "לָרוּץ", "save": "להציל", "save_as": "שמור כ", @@ -557,6 +613,7 @@ "title": "בַּקָשָׁה", "type": "סוג בקשה", "url": "כתובת URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "משתנים", "view_my_links": "View my links", "copy_link": "העתק קישור" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "שאל שאלות וקבל תשובות", "github": "Follow us on Github", "shortcuts": "עיון באפליקציה מהר יותר", - "team": "צרו קשר עם הצוות", "title": "תמיכה", - "twitter": "עקבו אחרינו בטוויטר" + "twitter": "עקבו אחרינו בטוויטר", + "team": "צרו קשר עם הצוות" }, "tab": { "authorization": "הרשאה", @@ -889,6 +953,9 @@ "query": "שאילתא", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "בדיקות", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "הצטרף לתוכנית הביטא לגישה לצוותים.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "צוותים", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "הצטרף לתוכנית הביטא לגישה לצוותים." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/hu.json b/packages/hoppscotch-common/locales/hu.json index 60390a384..cf1991896 100644 --- a/packages/hoppscotch-common/locales/hu.json +++ b/packages/hoppscotch-common/locales/hu.json @@ -24,8 +24,10 @@ "go_back": "Vissza", "go_forward": "Előre", "group_by": "Csoportosítás", + "hide_secret": "Hide secret", "label": "Címke", "learn_more": "Tudjon meg többet", + "download_here": "Download here", "less": "Kevesebb", "more": "Több", "new": "Új", @@ -43,6 +45,7 @@ "search": "Keresés", "send": "Küldés", "share": "Megosztás", + "show_secret": "Show secret", "start": "Indítás", "starting": "Indítás", "stop": "Leállítás", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Írjon be parancsot vagy keresést…", "we_use_cookies": "Sütiket használunk", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Mik az újdonságok?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "A fiók különböző hitelesítési adatokkal létezik – jelentkezzen be a két fiók összekapcsolásához", "all_sign_in_options": "Összes bejelentkezési lehetőség", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Folytatás e-mail-címmel", "continue_with_github": "Folytatás GitHub használatával", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Folytatás Google használatával", "continue_with_microsoft": "Folytatás Microsoft használatával", "email": "E-mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "Nincs \"Token Endpoint\"", "something_went_wrong_on_oauth_redirect": "Valami rosszul sikerült az OAuth átirányításakor", "something_went_wrong_on_token_generation": "Valami rosszul sikerült a token generálásakor", - "token_generation_oidc_discovery_failed": "Hiba a token generálásakor: OpenID Connect Discovery hiba" + "token_generation_oidc_discovery_failed": "Hiba a token generálásakor: OpenID Connect Discovery hiba", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Átadta", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Jelszó", "save_to_inherit": "Kérjük, mentse el ezt kérést bármelyik gyűjteménybe, hogy az azonosítás örökölhető lehessen", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Nem lehet átrendezni a különböző szülővel rendelkező gyűjteményt", "edit": "Gyűjtemény szerkesztése", "import_or_create": "Gyűjtemény importálása vagy létrehozása", + "import_collection": "Import Collection", "invalid_name": "Adjon nevet a gyűjteménynek", "invalid_root_move": "A gyűjtemény már a gyökérben van", "moved": "Sikeresen áthelyezve", @@ -165,6 +194,7 @@ "save_to_collection": "Mentés egy gyűjteménybe", "select": "Gyűjtemény kiválasztása", "select_location": "Hely kiválasztása", + "details": "Details", "select_team": "Csapat kiválasztása", "team_collections": "Csapat gyűjteményei" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Biztosan ki szeretné kapcsolni a telemetriát?", "request_change": "Biztosan el szeretné vetni a jelenlegi kérést? Minden mentetlen változtatás el fog veszni.", "save_unsaved_tab": "Szeretné menteni az ezen a lapon elvégzett változtatásokat?", - "sync": "Szeretné visszaállítani a munkaterületét a felhőből? Ez el fogja vetni a helyi folyamatát." + "sync": "Szeretné visszaállítani a munkaterületét a felhőből? Ez el fogja vetni a helyi folyamatát.", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Paraméterek hozzáadása", @@ -237,13 +268,16 @@ "pending_invites": "Nincsenek függőben lévő meghívások ennél a csapatnál", "profile": "Jelentkezzen be a profilja megtekintéséhez", "protocols": "A protokollok üresek", + "request_variables": "This request does not have any request variables", "schema": "Kapcsolódjon egy GraphQL-végponthoz a séma megtekintéséhez", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "A feliratkozások üresek", "team_name": "A csapat neve üres", "teams": "Ön nem tartozik semmilyen csapathoz", "tests": "Nincsenek tesztek ehhez a kéréshez", + "access_tokens": "Access tokens are empty", "shortcodes": "A rövid kódok üresek" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Környezet gyors megnézése", "replace_with_variable": "Cserélje le egy változóra", "scope": "Hatókör", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Környezet kiválasztása", "set": "Környezet beállítása", "set_as_environment": "Környezetként való beállítás", @@ -278,7 +314,10 @@ "updated": "Környezet frissítve", "value": "Érték", "variable": "Változó", - "variable_list": "Változólista" + "variables": "Variables", + "variable_list": "Változólista", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Nem sikerült betölteni az azonosító szolgáltatókat", @@ -289,6 +328,7 @@ "danger_zone": "Veszélyes zóna", "delete_account": "Az Ön fiókja jelenleg tulajdonos ezekben a csapatokban:", "delete_account_description": "El kell távolítani magát, át kell adnia a tulajdonjogot vagy törölnie kell ezeket a csapatokat, mielőtt törölhetné a fiókját.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Üres kérésnév", "f12_details": "(F12 a részletekért)", "gql_prettify_invalid_query": "Nem sikerült csinosítani egy érvénytelen lekérdezést, oldja meg a lekérdezés szintaktikai hibáit, és próbálja újra", @@ -296,6 +336,7 @@ "incorrect_email": "Hibás e-mail", "invalid_link": "Érvénytelen hivatkozás", "invalid_link_description": "A kattintott hivatkozás érvénytelen vagy lejárt.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Érvénytelen JSON", "json_prettify_invalid_body": "Nem sikerült csinosítani egy érvénytelen törzset, oldja meg a JSON szintaktikai hibáit, és próbálja újra", "network_error": "Úgy tűnik, hogy hálózati hiba van. Próbálja újra.", @@ -307,17 +348,25 @@ "page_not_found": "Ez az oldal nem található", "please_install_extension": "Kérjük telepítse a bővítményt és adja hozzá a forráshoz.", "proxy_error": "Proxy hiba", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Nem sikerült végrehajtani a kérés előtti parancsfájlt", "something_went_wrong": "Valami elromlott", - "test_script_fail": "Nem sikerült végrehajtani a kérés utáni parancsfájlt" + "test_script_fail": "Nem sikerült végrehajtani a kérés utáni parancsfájlt", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exportálás JSON formátumban", "create_secret_gist": "Titkos Gist létrehozása", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Valami hiba történt az exportálás közben", - "gist_created": "Gist létrehozva", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Jelentkezzen be GitHub használatával a titkos Gist létrehozásához", - "title": "Exportálás" + "title": "Exportálás", + "success": "Successfully exported", + "gist_created": "Gist létrehozva" }, "filter": { "all": "Összes", @@ -339,7 +388,8 @@ "mutations": "Mutációk", "schema": "Séma", "subscriptions": "Feliratkozások", - "switch_connection": "Kapcsolat váltása" + "switch_connection": "Kapcsolat váltása", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL gyűjtemény" @@ -405,12 +455,17 @@ "json_description": "Gyűjtemények importálása Hoppscotch-gyűjtemények JSON-fájlból", "postman_environment": "Postman környezet", "postman_environment_description": "Postman környezet importálása JSON fájlból", - "title": "Importálás" + "title": "Importálás", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Lehetséges hibák ellenőrzése", "environment": { "add_environment": "Hozzáadás a környezethez", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "\"{environment}\" környezet nem található." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Nyers kéréstörzs", "rename": "Rename Request", "renamed": "Kérés átnevezve", + "request_variables": "Request variables", "run": "Futtatás", "save": "Mentés", "save_as": "Mentés másként", @@ -557,6 +613,7 @@ "title": "Kérés", "type": "Kérés típusa", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Változók", "view_my_links": "Saját hivatkozások megtekintése", "copy_link": "Hivatkozás másolása" @@ -811,6 +868,13 @@ "new": "Új csapat létrehozása", "switch_to_personal": "Váltás a személyes munkaterületére", "title": "Csapatok" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Tegyen fel kérdéseket és kapjon válaszokat", "github": "Kövessen minket GitHubon", "shortcuts": "Az alkalmazás gyorsabb böngészése", - "team": "Vegye fel a kapcsolatot a csapattal", "title": "Támogatás", - "twitter": "Kövessen minket Twitteren" + "twitter": "Kövessen minket Twitteren", + "team": "Vegye fel a kapcsolatot a csapattal" }, "tab": { "authorization": "Azonosítás", @@ -889,6 +953,9 @@ "query": "Lekérdezés", "schema": "Séma", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Tesztek", @@ -917,7 +984,6 @@ "invite_tooltip": "Emberek meghívása erre a munkaterületre", "invited_to_team": "{owner} meghívta Önt, hogy csatlakozzon ehhez a csapathoz: {team}", "join": "Meghívás elfogadva", - "join_beta": "Csatlakozzon a béta programhoz, hogy hozzáférjen a csapatokhoz.", "join_team": "Csatlakozás ehhez: {team}", "joined_team": "Ön csatlakozott ehhez a csapathoz: {team}", "joined_team_description": "Ön mostantól ennek a csapatnak a tagja", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Csapatok", "we_sent_invite_link": "Elküldtünk egy meghívási hivatkozást az összes meghívottnak.", - "we_sent_invite_link_description": "Kérje meg az összes meghívottat, hogy nézzék meg a beérkező leveleiket. Kattintsanak a hivatkozásra a csapathoz való csatlakozáshoz." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Kérje meg az összes meghívottat, hogy nézzék meg a beérkező leveleiket. Kattintsanak a hivatkozásra a csapathoz való csatlakozáshoz.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Csatlakozzon a béta programhoz, hogy hozzáférjen a csapatokhoz." }, "team_environment": { "deleted": "Környezet törölve", @@ -977,9 +1048,50 @@ "workspace": { "change": "Munkaterület váltása", "personal": "Saját munkaterület", + "other_workspaces": "My Workspaces", "team": "Csapat-munkaterület", "title": "Munkaterületek" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Műveletek", "created_on": "Létrehozva", diff --git a/packages/hoppscotch-common/locales/id.json b/packages/hoppscotch-common/locales/id.json index 3803e925e..3579b6746 100644 --- a/packages/hoppscotch-common/locales/id.json +++ b/packages/hoppscotch-common/locales/id.json @@ -24,8 +24,10 @@ "go_back": "Kembali", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Label", "learn_more": "Pelajari lebih lanjut", + "download_here": "Download here", "less": "Lebih sedikit", "more": "Lebih banyak", "new": "Baru", @@ -43,6 +45,7 @@ "search": "Cari", "send": "Kirim", "share": "Share", + "show_secret": "Show secret", "start": "Mulai", "starting": "Memulai", "stop": "Berhenti", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Ketik perintah atau cari…", "we_use_cookies": "Kami menggunakan cookie", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Apa yang baru?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Akun ada dengan kredensial berbeda - Masuk untuk menautkan kedua akun", "all_sign_in_options": "Semua opsi masuk", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Lanjutkan dengan Surel", "continue_with_github": "Lanjutkan dengan GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Lanjutkan dengan Google", "continue_with_microsoft": "Lanjutkan dengan Microsoft", "email": "Surel", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Lewat", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Kata Sandi", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Tidak dapat mengubah urutan koleksi dengan induk yang berbeda", "edit": "Mengubah Koleksi", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Berikan nama untuk Koleksi", "invalid_root_move": "Koleksi sudah berada di akar direktori", "moved": "Berhasil Dipindahkan", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Pilih Koleksi", "select_location": "Pilih lokasi", + "details": "Details", "select_team": "Pilih tim", "team_collections": "Koleksi Tim" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Apakah Anda yakin ingin menyisih dari Telemetri?", "request_change": "Apakah Anda yakin ingin membuang permintaan saat ini, perubahan yang belum disimpan akan hilang.", "save_unsaved_tab": "Apakah Anda ingin menyimpan perubahan yang dibuat di tab ini?", - "sync": "Apakah Anda ingin memulihkan ruang kerja Anda dari cloud? Ini akan membuang kemajuan lokal Anda." + "sync": "Apakah Anda ingin memulihkan ruang kerja Anda dari cloud? Ini akan membuang kemajuan lokal Anda.", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "Tidak ada undangan yang tertunda untuk tim ini", "profile": "Masuk untuk melihat profil Anda", "protocols": "Protokol kosong", + "request_variables": "This request does not have any request variables", "schema": "Hubungkan ke endpoint GraphQL untuk melihat skema", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Langganan kosong", "team_name": "Nama team kosong", "teams": "Kamu bukan di team manapun", "tests": "Tidak ada tes untuk permintaan ini", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes kosong" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Pilih environment", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment diperbarui", "value": "Value", "variable": "Variable", - "variable_list": "Daftar Variable" + "variables": "Variables", + "variable_list": "Daftar Variable", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Akun Anda saat ini merupakan pemilik dalam tim-tim ini:", "delete_account_description": "Anda harus menghapus diri Anda dari tim-tim ini, mentransfer kepemilikan, atau menghapus tim-tim ini sebelum Anda dapat menghapus akun Anda.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nama Permintaan Kosong", "f12_details": "(F12 untuk detailnya)", "gql_prettify_invalid_query": "Tidak dapat prettify kueri yang tidak valid, menyelesaikan kesalahan sintaksis kueri, dan coba lagi", @@ -296,6 +336,7 @@ "incorrect_email": "Surel Salah", "invalid_link": "Tautan tidak valid", "invalid_link_description": "Tautan yang Anda klik tidak valid atau kedaluwarsa.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSON tidak valid", "json_prettify_invalid_body": "Tidak dapat prettify body yang tidak valid, selesaikan kesalahan sintaks json dan coba lagi", "network_error": "Sepertinya ada kesalahan jaringan. Silakan coba lagi.", @@ -307,17 +348,25 @@ "page_not_found": "Halaman ini tidak dapat ditemukan", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Tidak dapat menjalankan pre-request script", "something_went_wrong": "Ada yang salah", - "test_script_fail": "Tidak dapat mengeksekusi post-request script" + "test_script_fail": "Tidak dapat mengeksekusi post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Export sebagai JSON", "create_secret_gist": "Buat secret Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist dibuat", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Masuk dengan GitHub untuk membuat secret gist", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist dibuat" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutations", "schema": "Schema", "subscriptions": "Subscriptions", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Impor Koleksi dari berkas JSON Koleksi Hoppscotch", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Impor" + "title": "Impor", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Raw Request Body", "rename": "Rename Request", "renamed": "Request berganti nama", + "request_variables": "Request variables", "run": "Jalankan", "save": "Menyimpan", "save_as": "Simpan sebagai", @@ -557,6 +613,7 @@ "title": "Request", "type": "Tipe Request", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variables", "view_my_links": "Lihat tautan saya", "copy_link": "Salin tautan" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Ajukan pertanyaan dan dapatkan jawaban", "github": "Ikuti kami di GitHub", "shortcuts": "Jelajahi aplikasi lebih cepat", - "team": "Hubungi tim", "title": "Mendukung", - "twitter": "Ikuti kami di Twitter" + "twitter": "Ikuti kami di Twitter", + "team": "Hubungi tim" }, "tab": { "authorization": "Authorization", @@ -889,6 +953,9 @@ "query": "Query", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Tests", @@ -917,7 +984,6 @@ "invite_tooltip": "Undang orang ke ruang kerja ini", "invited_to_team": "{owner} mengundang Anda untuk bergabung {team}", "join": "Undangan diterima", - "join_beta": "Bergabunglah dengan program beta untuk mengakses tim.", "join_team": "Bergabung {team}", "joined_team": "Anda telah bergabung {team}", "joined_team_description": "Anda sekarang adalah anggota tim ini", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "tim", "we_sent_invite_link": "Kami mengirim tautan undangan ke semua undangan!", - "we_sent_invite_link_description": "Minta semua undangan untuk memeriksa kotak masuk mereka. Klik tautan untuk bergabung dengan tim." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Minta semua undangan untuk memeriksa kotak masuk mereka. Klik tautan untuk bergabung dengan tim.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Bergabunglah dengan program beta untuk mengakses tim." }, "team_environment": { "deleted": "Environment dihapus", @@ -977,9 +1048,50 @@ "workspace": { "change": "Beralih workspace", "personal": "Workspace Saya", + "other_workspaces": "My Workspaces", "team": "Workspace Tim", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Tindakan", "created_on": "Dibuat pada", diff --git a/packages/hoppscotch-common/locales/it.json b/packages/hoppscotch-common/locales/it.json index 752fef811..26b884783 100644 --- a/packages/hoppscotch-common/locales/it.json +++ b/packages/hoppscotch-common/locales/it.json @@ -24,8 +24,10 @@ "go_back": "Torna indietro", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etichetta", "learn_more": "Per saperne di più", + "download_here": "Download here", "less": "Less", "more": "Di più", "new": "Nuovo", @@ -43,6 +45,7 @@ "search": "Cerca", "send": "Invia", "share": "Share", + "show_secret": "Show secret", "start": "Avvia", "starting": "Starting", "stop": "Interrompi", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Digita un comando o cerca...", "we_use_cookies": "Utilizziamo i cookie", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Cosa c'è di nuovo?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "L'account esiste con credenziali diverse - Accedi per collegare entrambi gli account", "all_sign_in_options": "Tutte le opzioni di accesso", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Continua con e-mail", "continue_with_github": "Continua con GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Continua con Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Password", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Modifica raccolta", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Si prega di fornire un nome valido per la raccolta", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Seleziona una raccolta", "select_location": "Seleziona la posizione", + "details": "Details", "select_team": "Seleziona un team", "team_collections": "Raccolte di team" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Sei sicuro di voler disattivare la telemetria?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Vuoi ripristinare il tuo spazio di lavoro con quello del cloud? Questo annullerà le tue modifiche fatte in locale." + "sync": "Vuoi ripristinare il tuo spazio di lavoro con quello del cloud? Questo annullerà le tue modifiche fatte in locale.", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "Non ci sono inviti pendenti per questo team", "profile": "Accedi per mostrare il tuo profilo", "protocols": "I protocolli sono vuoti", + "request_variables": "This request does not have any request variables", "schema": "Connettiti a un endpoint GraphQL per mostrare lo schema", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Il nome del team è vuoto", "teams": "I team sono vuoti", "tests": "Non ci sono test per questa richiesta", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Seleziona ambiente", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Elenco variabili" + "variables": "Variables", + "variable_list": "Elenco variabili", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nome richiesta vuoto", "f12_details": "(F12 per i dettagli)", "gql_prettify_invalid_query": "Impossibile abbellire una query non valida, risolvere gli errori di sintassi della query e riprovare", @@ -296,6 +336,7 @@ "incorrect_email": "Indirizzo email errato", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Impossibile abbellire un corpo non valido, risolvere gli errori di sintassi JSON e riprovare", "network_error": "Sembra ci sia un problema di rete. Per favore prova di nuovo.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Impossibile eseguire lo script di pre-richiesta", "something_went_wrong": "Qualcosa è andato storto", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Esporta come JSON", "create_secret_gist": "Crea un Gist segreto", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist creato", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Accedi con GitHub per creare un Gist segreto", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist creato" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutazioni", "schema": "Schema", "subscriptions": "Sottoscrizioni", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importa" + "title": "Importa", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Corpo della richiesta non formattato", "rename": "Rename Request", "renamed": "Richiesta rinominata", + "request_variables": "Request variables", "run": "Esegui", "save": "Salva", "save_as": "Salva come", @@ -557,6 +613,7 @@ "title": "Richiesta", "type": "Tipo di richiesta", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variabili", "view_my_links": "View my links", "copy_link": "Copia collegamento" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Fai domande e ottieni risposte", "github": "Follow us on Github", "shortcuts": "Naviga nella app più velocemente", - "team": "Mettiti in contatto con il team", "title": "Supporto", - "twitter": "Seguici su Twitter" + "twitter": "Seguici su Twitter", + "team": "Mettiti in contatto con il team" }, "tab": { "authorization": "Autorizzazione", @@ -889,6 +953,9 @@ "query": "Query", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Test", @@ -917,7 +984,6 @@ "invite_tooltip": "Invita persone in questo spazio di lavoro", "invited_to_team": "{owner} ti ha invitato ad unirti al team {team}", "join": "Invito accettato", - "join_beta": "Partecipa al programma beta per accedere ai team.", "join_team": "Unisciti al team {team}", "joined_team": "Sei parte del team {team}", "joined_team_description": "Ora sei un membro di questo team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Team", "we_sent_invite_link": "Abbiamo inviato un link di invito a tutti gli invitati!", - "we_sent_invite_link_description": "Chiedi a tutti gli invitati di controllare la loro casella email. Cliccando sul link possono entrare nel team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Chiedi a tutti gli invitati di controllare la loro casella email. Cliccando sul link possono entrare nel team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Partecipa al programma beta per accedere ai team." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/ja.json b/packages/hoppscotch-common/locales/ja.json index b55859957..627b688fd 100644 --- a/packages/hoppscotch-common/locales/ja.json +++ b/packages/hoppscotch-common/locales/ja.json @@ -24,8 +24,10 @@ "go_back": "戻る", "go_forward": "Go forward", "group_by": "グループ化", + "hide_secret": "Hide secret", "label": "ラベル", "learn_more": "もっと詳しく", + "download_here": "Download here", "less": "表示を減らす", "more": "もっと見る", "new": "新規", @@ -43,6 +45,7 @@ "search": "検索", "send": "送信", "share": "Share", + "show_secret": "Show secret", "start": "はじめる", "starting": "開始中", "stop": "止める", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "コマンドもしくは検索ワードを入力…", "we_use_cookies": "Cookieを使用しています", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "新着情報", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "アカウントがそれぞれの認証情報で存在しています - ログインして両方のアカウントを連携する", "all_sign_in_options": "すべてのサインインオプション", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "メールアドレスで続行", "continue_with_github": "GitHubアカウントで続行", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Googleアカウントで続行", "continue_with_microsoft": "Microsoftアカウントで続行", "email": "メールアドレス", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "認証元: ", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "パスワード", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "トークン", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "コレクションの編集", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "コレクション名を入力してください", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "コレクションを選択", "select_location": "場所を選択", + "details": "Details", "select_team": "チームを選択", "team_collections": "チームコレクション" }, @@ -183,7 +213,8 @@ "remove_telemetry": "テレメトリをオプトアウトしてもよろしいですか?", "request_change": "現在のリクエストを削除してもよろしいですか?保存されていない変更は削除されます。", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "クラウドからワークスペースを復元しますか?この場合、ローカルの進行状況は破棄されます。" + "sync": "クラウドからワークスペースを復元しますか?この場合、ローカルの進行状況は破棄されます。", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "このチームに保留中の招待はありません", "profile": "ログインしてプロフィールを見る", "protocols": "プロトコルがありません", + "request_variables": "This request does not have any request variables", "schema": "GraphQLエンドポイントに接続する", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "サブスクリプションはありません", "team_name": "チーム名がありません", "teams": "チームに参加していません", "tests": "このリクエストのテストはありません", + "access_tokens": "Access tokens are empty", "shortcodes": "ショートコードはありません" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "環境変数を選択", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "環境変数を更新しました", "value": "Value", "variable": "Variable", - "variable_list": "環境変数リスト" + "variables": "Variables", + "variable_list": "環境変数リスト", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "危険", "delete_account": "あなたのアカウントは以下のチームのオーナーとなっています:", "delete_account_description": "アカウントを削除する前にチームを離脱するか、オーナーを委任するか、チームを削除してください。", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "リクエスト名がありません", "f12_details": "(詳細はF12キーを押してください)", "gql_prettify_invalid_query": "クエリを整形できませんでした。クエリの構文エラーを解決して再試行してください。", @@ -296,6 +336,7 @@ "incorrect_email": "メールアドレスが間違っています", "invalid_link": "リンクが無効です", "invalid_link_description": "クリックしたリンクは無効か期限切れです。", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSONが無効です", "json_prettify_invalid_body": "ボディを整形できませんでした。JSONの構文エラーを解決して再試行してください。", "network_error": "ネットワークエラーが発生したようです。もう一度お試しください。", @@ -307,17 +348,25 @@ "page_not_found": "このページは見つかりませんでした", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "リクエスト前のスクリプトを実行できませんでした", "something_went_wrong": "不明なエラーです", - "test_script_fail": "リクエスト後のスクリプトを実行できませんでした" + "test_script_fail": "リクエスト後のスクリプトを実行できませんでした", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "JSONとしてエクスポート", "create_secret_gist": "Secret Gistを作成", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gistが作成されました", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "GitHubにログインしてSecret Gistを作成", - "title": "エクスポート" + "title": "エクスポート", + "success": "Successfully exported", + "gist_created": "Gistが作成されました" }, "filter": { "all": "全て", @@ -339,7 +388,8 @@ "mutations": "ミューテーション", "schema": "スキーマ", "subscriptions": "サブスクリプション", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Hoppscotchのコレクション (JSONファイル) からインポート", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "インポート" + "title": "インポート", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "生のリクエストボディ", "rename": "Rename Request", "renamed": "リクエストの名前を変更", + "request_variables": "Request variables", "run": "実行", "save": "保存", "save_as": "名前を付けて保存", @@ -557,6 +613,7 @@ "title": "リクエスト", "type": "リクエストの種類", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "変数", "view_my_links": "自分のリンクを見る", "copy_link": "リンクをコピー" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "質問をして答えを得る", "github": "GitHubでフォローする", "shortcuts": "アプリをより効率よく使いこなす", - "team": "チームと連絡を取る", "title": "サポート", - "twitter": "私たちのTwitterをフォローする" + "twitter": "私たちのTwitterをフォローする", + "team": "チームと連絡を取る" }, "tab": { "authorization": "認証", @@ -889,6 +953,9 @@ "query": "クエリ", "schema": "スキーマ", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "テスト", @@ -917,7 +984,6 @@ "invite_tooltip": "このワークスペースに招待", "invited_to_team": "{owner}が{team}にあなたを招待しました", "join": "招待を了承しました", - "join_beta": "ベータプログラムに参加して、チームにアクセスする。", "join_team": "{team}に参加", "joined_team": "あなたは{team}に参加しました", "joined_team_description": "あなたはこのチームのメンバーです", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "チーム", "we_sent_invite_link": "招待者の皆様に、招待リンクを送信しました!", - "we_sent_invite_link_description": "招待者全員に受信トレイを確認するよう依頼します。リンクをクリックすると、チームに参加できます。" + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "招待者全員に受信トレイを確認するよう依頼します。リンクをクリックすると、チームに参加できます。", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "ベータプログラムに参加して、チームにアクセスする。" }, "team_environment": { "deleted": "環境変数を削除しました", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "アクション", "created_on": "作成日", diff --git a/packages/hoppscotch-common/locales/ko.json b/packages/hoppscotch-common/locales/ko.json index 1d5876ec5..d31642a3a 100644 --- a/packages/hoppscotch-common/locales/ko.json +++ b/packages/hoppscotch-common/locales/ko.json @@ -24,8 +24,10 @@ "go_back": "돌아가기", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "이름", "learn_more": "더 알아보기", + "download_here": "Download here", "less": "접기", "more": "더보기", "new": "추가", @@ -43,6 +45,7 @@ "search": "검색", "send": "보내기", "share": "Share", + "show_secret": "Show secret", "start": "시작", "starting": "Starting", "stop": "정지", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "명령을 입력하거나 검색...", "we_use_cookies": "우리는 쿠키를 사용중입니다.", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "새로 바뀐 점", + "see_whats_new": "See what’s new", "wiki": "위키" }, "auth": { "account_exists": "계정이 다른 자격 증명으로 존재합니다. 두 계정을 연결하려면 로그인하세요.", "all_sign_in_options": "모든 로그인 옵션", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "이메일로 계속하기", "continue_with_github": "GitHub로 계속하기", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Google로 계속하기", "continue_with_microsoft": "Continue with Microsoft", "email": "이메일", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "전달 방식", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "비밀번호", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "토큰", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "모음집 편집", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "모음집 이름을 바르게 입력하세요.", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "모음집 선택", "select_location": "위치 선택", + "details": "Details", "select_team": "팀 선택", "team_collections": "팀 모음집" }, @@ -183,7 +213,8 @@ "remove_telemetry": "진단 데이터를 보내지 않겠습니까?", "request_change": "현재 요청을 취소하시겠습니까? 저장되지 않은 변경사항은 삭제됩니다.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "이 작업 공간을 동기화하겠습니까?" + "sync": "이 작업 공간을 동기화하겠습니까?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "이 팀에는 대기 중인 초대가 없습니다.", "profile": "로그인하여 프로필을 확인합니다.", "protocols": "프로토콜이 비어 있습니다.", + "request_variables": "This request does not have any request variables", "schema": "스키마를 보려면 GraphQL 엔드포인트에 연결하세요.", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "팀 이름이 비어 있습니다.", "teams": "아무 팀에도 속하지 않았습니다.", "tests": "이 요청에 대한 테스트가 없습니다.", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "환경 선택", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "환경 수정됨", "value": "Value", "variable": "Variable", - "variable_list": "변수 목록" + "variables": "Variables", + "variable_list": "변수 목록", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "빈 요청 이름", "f12_details": "(자세한 내용은 F12)", "gql_prettify_invalid_query": "잘못된 쿼리를 구문 강조할 수 없습니다. 쿼리 구문 오류를 해결하고 다시 시도하세요.", @@ -296,6 +336,7 @@ "incorrect_email": "잘못된 이메일 형식", "invalid_link": "잘못된 링크", "invalid_link_description": "잘못된 링크이거나 만료된 링크입니다.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "잘못된 본문을 구문 강조할 수 없습니다. json 구문 오류를 해결하고 다시 시도하세요.", "network_error": "네트워크 에러인 것 같습니다. 다시 시도하세요.", @@ -307,17 +348,25 @@ "page_not_found": "해당 페이지를 찾을 수 없습니다.", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "사전 요청 스크립트를 실행할 수 없습니다.", "something_went_wrong": "문제가 발생했습니다.", - "test_script_fail": "테스트 스크립트를 실행할 수 없습니다." + "test_script_fail": "테스트 스크립트를 실행할 수 없습니다.", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "JSON으로 내보내기", "create_secret_gist": "Secret Gist 만들기", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist가 생성됨", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "GitHub에 로그인하여 secret gist 만들기", - "title": "내보내기" + "title": "내보내기", + "success": "Successfully exported", + "gist_created": "Gist가 생성됨" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "뮤테이션", "schema": "스키마", "subscriptions": "섭스크립션", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "호프스카치 모음집 JSON 파일을 가져옵니다.", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "가져오기" + "title": "가져오기", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "원시 요청 본문", "rename": "Rename Request", "renamed": "요청 이름이 변경됨", + "request_variables": "Request variables", "run": "실행", "save": "저장", "save_as": "다른 이름으로 저장", @@ -557,6 +613,7 @@ "title": "제목", "type": "요청 유형", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "변수", "view_my_links": "내 링크 보기", "copy_link": "링크 복사" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "질문하고 답변 받기", "github": "Follow us on Github", "shortcuts": "더 빠르게 앱 탐색", - "team": "팀에 연락하기", "title": "지원", - "twitter": "트위터에서 팔로우" + "twitter": "트위터에서 팔로우", + "team": "팀에 연락하기" }, "tab": { "authorization": "인증", @@ -889,6 +953,9 @@ "query": "쿼리", "schema": "스키마", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "테스트", @@ -917,7 +984,6 @@ "invite_tooltip": "사람들을 이 작업 공간에 초대하기", "invited_to_team": "{owner}가 당신을 {team} 팀에 초대했습니다.", "join": "Invitation accepted", - "join_beta": "베타 프로그램에 참여하여 팀에 액세스하세요.", "join_team": "{team}에 합류", "joined_team": "{team}에 합류했습니다.", "joined_team_description": "이 팀의 구성원이 되었습니다.", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "팀", "we_sent_invite_link": "모든 초대 대상에게 초대 링크를 보냈습니다.", - "we_sent_invite_link_description": "초대 대상들이 이메일을 확인하도록 권하세요. 이메일 속 링크를 클릭하면 팀에 합류할 수 있습니다." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "초대 대상들이 이메일을 확인하도록 권하세요. 이메일 속 링크를 클릭하면 팀에 합류할 수 있습니다.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "베타 프로그램에 참여하여 팀에 액세스하세요." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/nl.json b/packages/hoppscotch-common/locales/nl.json index 01ae6f411..00a3ea4bd 100644 --- a/packages/hoppscotch-common/locales/nl.json +++ b/packages/hoppscotch-common/locales/nl.json @@ -24,8 +24,10 @@ "go_back": "Ga terug", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Label", "learn_more": "Leer meer", + "download_here": "Download here", "less": "Less", "more": "Meer", "new": "Nieuw", @@ -43,6 +45,7 @@ "search": "Zoeken", "send": "Versturen", "share": "Share", + "show_secret": "Show secret", "start": "Begin", "starting": "Starting", "stop": "Stop", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Typ een opdracht of zoek...", "we_use_cookies": "Wij gebruiken cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Wat is er nieuw?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Account bestaat met verschillende inloggegevens. Log in om beide accounts te koppelen", "all_sign_in_options": "Alle aanmeldmogelijkheden", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Ga verder met e-mail", "continue_with_github": "Ga verder met GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Ga verder met Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Wachtwoord", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "token", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Verzameling bewerken", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Geef een geldige naam op voor de collectie", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Selecteer een collectie", "select_location": "Selecteer een locatie", + "details": "Details", "select_team": "Selecteer een team", "team_collections": "Teamcollecties" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Weet u zeker dat u zich wilt afmelden voor telemetrie?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Weet u zeker dat u deze werkruimte wilt synchroniseren?" + "sync": "Weet u zeker dat u deze werkruimte wilt synchroniseren?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protocollen zijn leeg", + "request_variables": "This request does not have any request variables", "schema": "Verbinding maken met een GraphQL-eindpunt", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Teamnaam leeg", "teams": "Teams zijn leeg", "tests": "Er zijn geen tests voor dit verzoek", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Selecteer omgeving", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Variabele lijst" + "variables": "Variables", + "variable_list": "Variabele lijst", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Lege aanvraagnaam", "f12_details": "(F12 voor details)", "gql_prettify_invalid_query": "Kon een ongeldige zoekopdracht niet mooier maken, syntaxisfouten in de query oplossen en opnieuw proberen", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Kon een ongeldige hoofdtekst niet mooier maken, json-syntaxisfouten oplossen en opnieuw proberen", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Kon pre-aanvraagscript niet uitvoeren", "something_went_wrong": "Er is iets fout gegaan", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exporteren als JSON", "create_secret_gist": "Maak geheime kern", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Kern gemaakt", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Log in met GitHub om een geheime kern te maken", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Kern gemaakt" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutaties", "schema": "Schema", "subscriptions": "Abonnementen", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importeren" + "title": "Importeren", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Ruwe body", "rename": "Rename Request", "renamed": "Verzoek hernoemd", + "request_variables": "Request variables", "run": "Uitvoeren", "save": "Opslaan", "save_as": "Opslaan als", @@ -557,6 +613,7 @@ "title": "Verzoek", "type": "Aanvraag type", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variabelen", "view_my_links": "View my links", "copy_link": "Kopieer link" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Stel vragen en krijg antwoorden", "github": "Follow us on Github", "shortcuts": "Sneller door de app bladeren", - "team": "Neem contact op met het team", "title": "Steun", - "twitter": "Volg ons op Twitter" + "twitter": "Volg ons op Twitter", + "team": "Neem contact op met het team" }, "tab": { "authorization": "Autorisatie", @@ -889,6 +953,9 @@ "query": "Query", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Testen", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Doe mee aan het bètaprogramma om toegang te krijgen tot teams.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "teams", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Doe mee aan het bètaprogramma om toegang te krijgen tot teams." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/no.json b/packages/hoppscotch-common/locales/no.json index 3d8cec341..1f61a630d 100644 --- a/packages/hoppscotch-common/locales/no.json +++ b/packages/hoppscotch-common/locales/no.json @@ -24,8 +24,10 @@ "go_back": "Gå tilbake", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Merkelapp", "learn_more": "Lær mer", + "download_here": "Download here", "less": "Less", "more": "Mer", "new": "Ny", @@ -43,6 +45,7 @@ "search": "Søk", "send": "Sende", "share": "Share", + "show_secret": "Show secret", "start": "Start", "starting": "Starting", "stop": "Stoppe", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Skriv inn en kommando eller søk ...", "we_use_cookies": "Vi bruker informasjonskapsler", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Hva er nytt?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Kontoen eksisterer med annen legitimasjon - Logg på for å koble begge kontoene", "all_sign_in_options": "Alle påloggingsalternativer", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Fortsett med e-post", "continue_with_github": "Fortsett med GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Fortsett med Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-post", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Passord", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Nøkkel", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Rediger samling", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Oppgi et gyldig navn på samlingen", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Velg en samling", "select_location": "Velg plassering", + "details": "Details", "select_team": "Velg et lag", "team_collections": "Lagsamlinger" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Er du sikker på at du vil velge bort telemetri?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Er du sikker på at du vil synkronisere dette arbeidsområdet?" + "sync": "Er du sikker på at du vil synkronisere dette arbeidsområdet?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protokoller er tomme", + "request_variables": "This request does not have any request variables", "schema": "Koble til et GraphQL-endepunkt", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Lagnavnet er tomt", "teams": "Lagene er tomme", "tests": "Det er ingen tester for denne forespørselen", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Velg miljø", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Variabel liste" + "variables": "Variables", + "variable_list": "Variabel liste", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Tom forespørselsnavn", "f12_details": "(F12 for detaljer)", "gql_prettify_invalid_query": "Kunne ikke forskjønne en ugyldig spørring, løse spørringssyntaksfeil og prøve igjen", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Kunne ikke forskjønne et ugyldig brødtekst, løse json -syntaksfeil og prøve igjen", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Kunne ikke kjøre forhåndsforespørselsskript", "something_went_wrong": "Noe gikk galt", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Eksporter som JSON", "create_secret_gist": "Lag hemmelig Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist opprettet", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Logg på med GitHub for å lage en hemmelig oppgave", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist opprettet" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutasjoner", "schema": "Skjema", "subscriptions": "Abonnementer", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Import" + "title": "Import", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Raw Request Body", "rename": "Rename Request", "renamed": "Forespørsel omdøpt", + "request_variables": "Request variables", "run": "Løpe", "save": "Lagre", "save_as": "Lagre som", @@ -557,6 +613,7 @@ "title": "Be om", "type": "Type forespørsel", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variabler", "view_my_links": "View my links", "copy_link": "Kopier link" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Still spørsmål og få svar", "github": "Follow us on Github", "shortcuts": "Bla gjennom appen raskere", - "team": "Ta kontakt med teamet", "title": "Brukerstøtte", - "twitter": "Følg oss på Twitter" + "twitter": "Følg oss på Twitter", + "team": "Ta kontakt med teamet" }, "tab": { "authorization": "Autorisasjon", @@ -889,6 +953,9 @@ "query": "Spørsmål", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Tester", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Bli med i betaprogrammet for å få tilgang til lag.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Lag", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Bli med i betaprogrammet for å få tilgang til lag." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/pl.json b/packages/hoppscotch-common/locales/pl.json index 16f4bba69..410240489 100644 --- a/packages/hoppscotch-common/locales/pl.json +++ b/packages/hoppscotch-common/locales/pl.json @@ -24,8 +24,10 @@ "go_back": "Wróć", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etykieta", "learn_more": "Dowiedz się więcej", + "download_here": "Download here", "less": "Less", "more": "Więcej", "new": "Nowa", @@ -43,6 +45,7 @@ "search": "Szukaj", "send": "Wyślij", "share": "Share", + "show_secret": "Show secret", "start": "Rozpocznij", "starting": "Starting", "stop": "Zatrzymaj", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Wpisz polecenie lub wyszukaj…", "we_use_cookies": "Używamy plików cookie", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Co nowego?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Konto istnieje z różnymi danymi uwierzytelniającymi - Zaloguj się, aby połączyć oba konta", "all_sign_in_options": "Wszystkie opcje logowania", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Kontynuuj z e-mailem", "continue_with_github": "Kontynuuj z GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Kontynuuj z Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Hasło", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Edytuj kolekcję", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Podaj prawidłową nazwę kolekcji", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Wybierz kolekcję", "select_location": "Wybierz lokalizację", + "details": "Details", "select_team": "Wybierz zespół", "team_collections": "Kolekcje zespołowe" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Czy na pewno chcesz zrezygnować z telemetrii?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Czy na pewno chcesz zsynchronizować ten obszar roboczy?" + "sync": "Czy na pewno chcesz zsynchronizować ten obszar roboczy?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protokoły są puste", + "request_variables": "This request does not have any request variables", "schema": "Połącz się z punktem końcowym GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Nazwa zespołu jest pusta", "teams": "Zespoły są puste", "tests": "Nie ma testów dla tego żądania", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Wybierz środowisko", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Lista zmiennych" + "variables": "Variables", + "variable_list": "Lista zmiennych", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Pusta nazwa żądania", "f12_details": "(F12 po szczegóły)", "gql_prettify_invalid_query": "Nie można poprawić czytelności nieprawidłowego zapytania, napraw błędy składni zapytania i spróbuj ponownie", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Nie można poprawić czytelności nieprawidłowej treści, napraw błędy składni json i spróbuj ponownie", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Nie można wykonać skryptu żądania wstępnego", "something_went_wrong": "Coś poszło nie tak", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Eksportuj jako JSON", "create_secret_gist": "Utwórz tajny Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Utworzono Gist", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Zaloguj się za pomocą GitHub, aby utworzyć tajny Gist", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Utworzono Gist" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutacje", "schema": "Schemat", "subscriptions": "Subskrypcje", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Import" + "title": "Import", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Surowa treść żądania", "rename": "Rename Request", "renamed": "Zmieniono nazwę żądania", + "request_variables": "Request variables", "run": "Uruchom", "save": "Zapisz", "save_as": "Zapisz jako", @@ -557,6 +613,7 @@ "title": "Żądanie", "type": "Typ żądania", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Zmienne", "view_my_links": "View my links", "copy_link": "Skopiuj link" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Zadawaj pytania i otrzymuj odpowiedzi", "github": "Follow us on Github", "shortcuts": "Przeglądaj aplikację szybciej", - "team": "Skontaktuj się z zespołem", "title": "Wsparcie", - "twitter": "Śledź nas na Twitterze" + "twitter": "Śledź nas na Twitterze", + "team": "Skontaktuj się z zespołem" }, "tab": { "authorization": "Autoryzacja", @@ -889,6 +953,9 @@ "query": "Zapytanie", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Testy", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Dołącz do programu beta, aby uzyskać dostęp do zespołów.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Zespoły", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Dołącz do programu beta, aby uzyskać dostęp do zespołów." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/pt-br.json b/packages/hoppscotch-common/locales/pt-br.json index db976b9bc..1c42fa858 100644 --- a/packages/hoppscotch-common/locales/pt-br.json +++ b/packages/hoppscotch-common/locales/pt-br.json @@ -24,8 +24,10 @@ "go_back": "Voltar", "go_forward": "Avançar", "group_by": "Agrupar por", + "hide_secret": "Hide secret", "label": "Etiqueta", "learn_more": "Saber mais", + "download_here": "Download here", "less": "Menos", "more": "Mais", "new": "Novo", @@ -43,6 +45,7 @@ "search": "Buscar", "send": "Enviar", "share": "Compartilhar", + "show_secret": "Show secret", "start": "Começar", "starting": "Iniciando", "stop": "Parar", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Digite um comando ou pesquise...", "we_use_cookies": "Usamos cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "O que há de novo?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "A conta existe com credenciais diferentes - Faça login para vincular as duas contas", "all_sign_in_options": "Todas as opções de login", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Continuar com Email", "continue_with_github": "Continuar com GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Continuar com o Google", "continue_with_microsoft": "Continuar com Microsoft", "email": "E-mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "Nenhum endpoint de token definido", "something_went_wrong_on_oauth_redirect": "Algo deu errado no redirecionamento OAuth", "something_went_wrong_on_token_generation": "Algo deu errado na geração do token", - "token_generation_oidc_discovery_failed": "Falha na geração do token: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Falha na geração do token: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Passar por", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Senha", "save_to_inherit": "Por favor, salve essa requisição em alguma coleção para herdar a autorização", "token": "Símbolo", @@ -149,6 +177,7 @@ "different_parent": "Não é possível reordenar coleções com Coleções Pai diferentes", "edit": "Editar coleção", "import_or_create": "Importar ou criar uma coleção", + "import_collection": "Import Collection", "invalid_name": "Forneça um nome válido para a coleção", "invalid_root_move": "A coleção já está na raiz", "moved": "Movido com sucesso", @@ -165,6 +194,7 @@ "save_to_collection": "Salvar na Coleção", "select": "Selecione uma coleção", "select_location": "Selecione a localização", + "details": "Details", "select_team": "Selecione uma equipe", "team_collections": "Coleções da equipe" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Tem certeza de que deseja cancelar a telemetria?", "request_change": "Tem certeza que deseja descartar a requisição atual? Alterações não salvas serão perdidas.", "save_unsaved_tab": "Deseja salvar as atualizações feitas nesta aba?", - "sync": "Tem certeza de que deseja sincronizar este espaço de trabalho?" + "sync": "Tem certeza de que deseja sincronizar este espaço de trabalho?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Adicionar aos parâmetros", @@ -237,13 +268,16 @@ "pending_invites": "Não há convites pendentes para esta equipe", "profile": "Entre para visualizar seu perfil", "protocols": "Os protocolos estão vazios", + "request_variables": "This request does not have any request variables", "schema": "Conecte-se a um endpoint GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Lista de requisições compartilhadas vazia", "shared_requests_logout": "Faça Login para visualizar suas requisições compartilhadas ou crie uma nova", "subscription": "Lista de inscrições vazia", "team_name": "Nome da equipe vazio", "teams": "Equipes estão vazias", "tests": "Não há testes para esta requisição", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Visualização rápida do ambiente", "replace_with_variable": "Substituir por variável", "scope": "Escopo", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Selecione o ambiente", "set": "Definir ambiente", "set_as_environment": "Definir como ambiente", @@ -278,7 +314,10 @@ "updated": "Atualizacao de ambientes", "value": "Valor", "variable": "Variável", - "variable_list": "Lista de Variáveis" + "variables": "Variables", + "variable_list": "Lista de Variáveis", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Não foi possível carregar os provedores de autenticação", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Atualmente sua conta é proprietária nesses times:", "delete_account_description": "Você precisa sair, transferir para outro proprietário ou remover essas equipes antes de excluir sua conta.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nome de requisição vazio", "f12_details": "(F12 para detalhes)", "gql_prettify_invalid_query": "Não foi possível justificar uma requisição inválida, resolva os erros de sintaxe da requisição e tente novamente", @@ -296,6 +336,7 @@ "incorrect_email": "Email incorreto", "invalid_link": "Link inválido", "invalid_link_description": "O link que você clicou é inválido ou já expirou.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Não foi possível embelezar um corpo inválido, resolver erros de sintaxe json e tentar novamente", "network_error": "Parece que houve um problema de rede. Por favor, tente novamente.", @@ -307,17 +348,25 @@ "page_not_found": "Esta página não foi encontrada", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Erro de Proxy", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Não foi possível executar o script pré-requisição", "something_went_wrong": "Algo deu errado", - "test_script_fail": "Não foi possível executar o script pós-requisição" + "test_script_fail": "Não foi possível executar o script pós-requisição", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exportar como JSON", "create_secret_gist": "Crie um gist secreto", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Algo deu errado ao exportar", - "gist_created": "Gist criado", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Faça login com GitHub para criar um gist secreta", - "title": "Exportar" + "title": "Exportar", + "success": "Successfully exported", + "gist_created": "Gist criado" }, "filter": { "all": "Todos", @@ -339,7 +388,8 @@ "mutations": "Mutações", "schema": "Esquema", "subscriptions": "Assinaturas", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "Coleções GraphQL" @@ -405,12 +455,17 @@ "json_description": "Importa coleções de um arquivo JSON de Coleções Hoppscotch", "postman_environment": "Ambiente Postman", "postman_environment_description": "Importar Ambiente Postman de arquivo JSON/YAML", - "title": "Importar" + "title": "Importar", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspecionar possíveis erros", "environment": { "add_environment": "Adicionar ao Ambiente", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Variável de ambiente “{environment}” não encontrada." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Corpo de Requisição Bruta", "rename": "Renomear Requisição", "renamed": "Requisição renomeada", + "request_variables": "Request variables", "run": "Executar", "save": "Salvar", "save_as": "Salvar como", @@ -557,6 +613,7 @@ "title": "Solicitar", "type": "Tipo de requisição", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variáveis", "view_my_links": "View my links", "copy_link": "Link de cópia" @@ -811,6 +868,13 @@ "new": "Criar nova equipe", "switch_to_personal": "Alternar para seu Workspace pessoal", "title": "Equipes" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Faça perguntas e obtenha respostas", "github": "Siga-nos no Github", "shortcuts": "Navegue pelo aplicativo mais rápido", - "team": "Entre em contato com a equipe", "title": "Apoio, suporte", - "twitter": "Siga-nos no Twitter" + "twitter": "Siga-nos no Twitter", + "team": "Entre em contato com a equipe" }, "tab": { "authorization": "Autorização", @@ -889,6 +953,9 @@ "query": "Consulta", "schema": "Schema", "shared_requests": "Requisições compartilhadas", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Testes", @@ -917,7 +984,6 @@ "invite_tooltip": "Convidar pessoas para esse workspace", "invited_to_team": "{owner} te convidou para se juntar ao {team}", "join": "Convite aceito", - "join_beta": "Junte-se ao programa beta para acessar as equipes.", "join_team": "Junte-se ao {team}", "joined_team": "Você se juntou ao {team}", "joined_team_description": "Você agora é um membro deste time", @@ -950,7 +1016,12 @@ "success_invites": "Convites com sucesso", "title": "Equipes", "we_sent_invite_link": "Link de convite enviado para todos os convidados!", - "we_sent_invite_link_description": "Peça a todos os convidados que verifiquem sua caixa de entrada. Clique no link para se juntar à equipe." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Peça a todos os convidados que verifiquem sua caixa de entrada. Clique no link para se juntar à equipe.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Junte-se ao programa beta para acessar as equipes." }, "team_environment": { "deleted": "Ambiente Excluído", @@ -977,9 +1048,50 @@ "workspace": { "change": "Alterar Workspace", "personal": "Meu Workspace", + "other_workspaces": "My Workspaces", "team": "Workspace da Equipe", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Ações", "created_on": "Criado em", diff --git a/packages/hoppscotch-common/locales/pt.json b/packages/hoppscotch-common/locales/pt.json index 0af076ffb..9f6472cb9 100644 --- a/packages/hoppscotch-common/locales/pt.json +++ b/packages/hoppscotch-common/locales/pt.json @@ -24,8 +24,10 @@ "go_back": "Volte", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etiqueta", "learn_more": "Saber mais", + "download_here": "Download here", "less": "Less", "more": "Mais", "new": "Novo", @@ -43,6 +45,7 @@ "search": "Procurar", "send": "Mandar", "share": "Share", + "show_secret": "Show secret", "start": "Começar", "starting": "Starting", "stop": "Pare", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Digite um comando ou pesquise ...", "we_use_cookies": "Usamos cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "O que há de novo?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "A conta existe com credenciais diferentes - Faça login para vincular as duas contas", "all_sign_in_options": "Todas as opções de login", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Continue com Email", "continue_with_github": "Continue com GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Continue com o Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Senha", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Símbolo", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Editar coleção", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Forneça um nome válido para a coleção", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Selecione uma coleção", "select_location": "Selecione a localização", + "details": "Details", "select_team": "Selecione uma equipe", "team_collections": "Coleções da equipe" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Tem certeza de que deseja cancelar a telemetria?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Tem certeza de que deseja sincronizar este espaço de trabalho?" + "sync": "Tem certeza de que deseja sincronizar este espaço de trabalho?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Os protocolos estão vazios", + "request_variables": "This request does not have any request variables", "schema": "Conecte-se a um endpoint GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Nome do time vazio", "teams": "Times estão vazios", "tests": "Não há testes para esta solicitação", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Selecione o ambiente", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Lista de Variáveis" + "variables": "Variables", + "variable_list": "Lista de Variáveis", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nome do pedido vazio", "f12_details": "(F12 para detalhes)", "gql_prettify_invalid_query": "Não foi possível justificar uma consulta inválida, resolva os erros de sintaxe da consulta e tente novamente", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Não foi possível embelezar um corpo inválido, resolver erros de sintaxe json e tentar novamente", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Não foi possível executar o script de pré-solicitação", "something_went_wrong": "Algo deu errado", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exportar como JSON", "create_secret_gist": "Crie uma essência secreta", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist criado", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Faça login com GitHub para criar uma essência secreta", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist criado" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutações", "schema": "Esquema", "subscriptions": "Assinaturas", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importar" + "title": "Importar", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Corpo de Solicitação Bruta", "rename": "Rename Request", "renamed": "Pedido renomeado", + "request_variables": "Request variables", "run": "Corre", "save": "Salvar", "save_as": "Salvar como", @@ -557,6 +613,7 @@ "title": "Solicitar", "type": "Tipo de solicitação", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variáveis", "view_my_links": "View my links", "copy_link": "Link de cópia" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Faça perguntas e obtenha respostas", "github": "Follow us on Github", "shortcuts": "Navegue pelo aplicativo mais rápido", - "team": "Entre em contato com a equipe", "title": "Apoio, suporte", - "twitter": "Siga-nos no Twitter" + "twitter": "Siga-nos no Twitter", + "team": "Entre em contato com a equipe" }, "tab": { "authorization": "Autorização", @@ -889,6 +953,9 @@ "query": "Consulta", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Testes", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Junte-se ao programa beta para acessar as equipes.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Times", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Junte-se ao programa beta para acessar as equipes." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/ro.json b/packages/hoppscotch-common/locales/ro.json index 366b00db6..a4047d83e 100644 --- a/packages/hoppscotch-common/locales/ro.json +++ b/packages/hoppscotch-common/locales/ro.json @@ -24,8 +24,10 @@ "go_back": "Înapoi", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etichetă", "learn_more": "Află mai multe", + "download_here": "Download here", "less": "Mai puțin", "more": "Mai mult", "new": "Nou", @@ -43,6 +45,7 @@ "search": "Căutare", "send": "Trimite", "share": "Share", + "show_secret": "Show secret", "start": "Start", "starting": "Starting", "stop": "Stop", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Tastați o comandă sau căutați ...", "we_use_cookies": "Folosim cookie-uri", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Ce mai e nou?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Contul există cu credențiale diferite - Conectați-vă pentru a conecta ambele conturi", "all_sign_in_options": "Toate opțiunile de conectare", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Continuați cu e-mailul", "continue_with_github": "Continuați cu GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Continuați cu Google", "continue_with_microsoft": "Continuați cu Microsoft", "email": "E-mail", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Transimteți cheia", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Parolă", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Editați colecția", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Vă rugăm să furnizați un nume valid pentru colecție", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Selectați o colecție", "select_location": "Selectați locația", + "details": "Details", "select_team": "Selectați o echipă", "team_collections": "Colecții de echipă" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Sigur doriți să renunțați la telemetrie?", "request_change": "Sigur doriți să renunțați la cererea curentă? Modificările nesalvate se vor pierde.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Sigur doriți să sincronizați acest spațiu de lucru?" + "sync": "Sigur doriți să sincronizați acest spațiu de lucru?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "Nu sunt invitații în așteptare pentru această echipă", "profile": "Autentificați-vă pentru a putea vizualiza profilul", "protocols": "Protocoalele sunt goale", + "request_variables": "This request does not have any request variables", "schema": "Conectați-vă la un Endpoint GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Numele echipei este gol", "teams": "Echipele sunt goale", "tests": "Nu există teste pentru această solicitare", + "access_tokens": "Access tokens are empty", "shortcodes": "Codurile scurte sunt goale" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Selectați mediul", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Mediu actualizat", "value": "Value", "variable": "Variable", - "variable_list": "Lista variabilelor" + "variables": "Variables", + "variable_list": "Lista variabilelor", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Nume cerere goală", "f12_details": "(F12 pentru detalii)", "gql_prettify_invalid_query": "Nu am putut formata o interogare nevalidă, rezolvați erorile de sintaxă ale interogării și încercați din nou", @@ -296,6 +336,7 @@ "incorrect_email": "Email incorect", "invalid_link": "Link invalid", "invalid_link_description": "Link-ul este invalid sau a expirat.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSON invalid", "json_prettify_invalid_body": "Nu s-a putut formata un corp invalid, rezolvați erorile de sintaxă JSON și încercați din nou", "network_error": "Probleme cu conexiunea. Încercați din nou.", @@ -307,17 +348,25 @@ "page_not_found": "Pagina nu a putut fi găsită", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Nu s-a putut executa scriptul", "something_went_wrong": "Ceva nu a mers bine", - "test_script_fail": "Nu s-a putut executa scriptul" + "test_script_fail": "Nu s-a putut executa scriptul", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exportați ca JSON", "create_secret_gist": "Creați Gist secret", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist creat", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Conectați-vă cu GitHub pentru a crea un Gist secret", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist creat" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutații", "schema": "Schemă", "subscriptions": "Abonamente", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Importați colecții dintr-un fisier de collectii JSON Hoppscotch", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Import" + "title": "Import", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Corpul cererii", "rename": "Rename Request", "renamed": "Cerere redenumită", + "request_variables": "Request variables", "run": "Execută", "save": "Salvează", "save_as": "Salvează ca", @@ -557,6 +613,7 @@ "title": "Cerere", "type": "Tip de cerere", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variabile", "view_my_links": "Vizualizare link-uri", "copy_link": "Copiază legătură" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Puneți întrebări și primiți răspunsuri", "github": "Urmarește-ne pe Github", "shortcuts": "Utilizați aplicația mai rapid", - "team": "Luați legătura cu echipa", "title": "Suport", - "twitter": "Urmăriți-ne pe Twitter" + "twitter": "Urmăriți-ne pe Twitter", + "team": "Luați legătura cu echipa" }, "tab": { "authorization": "Autorizare", @@ -889,6 +953,9 @@ "query": "Interogare", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Teste", @@ -917,7 +984,6 @@ "invite_tooltip": "Invită oameni la acest workspace", "invited_to_team": "{owner} te-a invitat să te alături echipei {team}", "join": "Invitație acceptată", - "join_beta": "Alăturați-vă programului beta pentru a accesa echipe.", "join_team": "Alăturați-vă {team}", "joined_team": "V-ați alăturat echipei {team}", "joined_team_description": "Sunteți acum membru al acestei echipe", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Echipe", "we_sent_invite_link": "Am trimis un link de invitație către toți invitații!", - "we_sent_invite_link_description": "Rugați toate persoanele invitate să iși verifice căsuța. Click pe link pentru a se alătura echipei." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Rugați toate persoanele invitate să iși verifice căsuța. Click pe link pentru a se alătura echipei.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Alăturați-vă programului beta pentru a accesa echipe." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Acțiuni", "created_on": "Creat la", diff --git a/packages/hoppscotch-common/locales/ru.json b/packages/hoppscotch-common/locales/ru.json index 203118610..8a03b5ae5 100644 --- a/packages/hoppscotch-common/locales/ru.json +++ b/packages/hoppscotch-common/locales/ru.json @@ -17,7 +17,6 @@ "dismiss": "Скрыть", "dont_save": "Не сохранять", "download_file": "Скачать файл", - "download_here": "Download here", "drag_to_reorder": "Перетягивайте для сортировки", "duplicate": "Дублировать", "edit": "Редактировать", @@ -28,6 +27,7 @@ "hide_secret": "Hide secret", "label": "Название", "learn_more": "Узнать больше", + "download_here": "Download here", "less": "Меньше", "more": "Больше", "new": "Создать новый", @@ -98,7 +98,9 @@ "twitter": "Twitter", "type_a_command_search": "Введите команду или выполните поиск…", "we_use_cookies": "Мы используем куки", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Что нового?", + "see_whats_new": "See what’s new", "wiki": "Узнать больше" }, "auth": { @@ -161,9 +163,9 @@ "label_auth_code": "Authorization Code", "label_client_credentials": "Client Credentials" }, - "pass_by_headers_label": "Headers", - "pass_by_query_params_label": "Query Parameters", "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Пароль", "save_to_inherit": "Чтобы унаследовать аутентификации, нужно сохранить запрос в коллекции", "token": "Токен", @@ -191,7 +193,8 @@ "save_as": "Сохранить как", "save_to_collection": "Сохранить в коллекцию", "select": "Выбрать коллекцию", - "select_location": "Выберите местоположение" + "select_location": "Выберите местоположение", + "details": "Details" }, "confirm": { "close_unsaved_tab": "Вы уверены, что хотите закрыть эту вкладку?", @@ -208,7 +211,8 @@ "remove_telemetry": "Вы действительно хотите отказаться от телеметрии?", "request_change": "Вы уверены, что хотите сбросить текущий запрос, все не сохранённые данные будт утеряны?", "save_unsaved_tab": "Вы хотите сохранить изменения в этой вкладке?", - "sync": "Вы уверены, что хотите синхронизировать это рабочее пространство?" + "sync": "Вы уверены, что хотите синхронизировать это рабочее пространство?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Добавить в список параметров", @@ -263,14 +267,15 @@ "profile": "Войдите, чтобы просмотреть свой профиль", "protocols": "Протоколы пустые", "request_variables": "Этот запрос не содержит никаких переменных", - "secret_environments": "Секреты хранятся только на этом устройстве и не синхронизируются с сервером", "schema": "Подключиться к конечной точке GraphQL", + "secret_environments": "Секреты хранятся только на этом устройстве и не синхронизируются с сервером", "shared_requests": "Вы еще не делились запросами с другими", "shared_requests_logout": "Нужно войти, чтобы делиться запросами и управлять ими", "subscription": "Нет подписок", "team_name": "Название команды пусто", "teams": "Команды пустые", - "tests": "Для этого запроса нет тестов" + "tests": "Для этого запроса нет тестов", + "access_tokens": "Access tokens are empty" }, "environment": { "add_to_global": "Добавить в глобальное окружение", @@ -307,7 +312,9 @@ "value": "Значение", "variable": "Переменная", "variables": "Переменные", - "variable_list": "Список переменных" + "variable_list": "Список переменных", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -338,11 +345,14 @@ "page_not_found": "Эта страница не найдена", "please_install_extension": "Ничего страшного. Просто нужно установить специальное расширение в браузере.", "proxy_error": "Proxy error", - "reading_files": "Произошла ошибка при чтении файла или нескольких файлов", "same_profile_name": "Задано имя пользователя такое же как и было", "script_fail": "Не удалось выполнить сценарий предварительного запроса", "something_went_wrong": "Что-то пошло не так", - "test_script_fail": "Не удалось выполнить тестирование запроса" + "test_script_fail": "Не удалось выполнить тестирование запроса", + "reading_files": "Произошла ошибка при чтении файла или нескольких файлов", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Экспорт как JSON", @@ -374,7 +384,8 @@ "mutations": "Мутации", "schema": "Схема", "subscriptions": "Подписки", - "switch_connection": "Изменить соединение" + "switch_connection": "Изменить соединение", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "Коллекции GraphQL" @@ -414,8 +425,6 @@ "environments_from_gist": "Импортировать из Gist", "environments_from_gist_description": "Импортировать переменные окружения Hoppscotch из Gist", "failed": "Ошибка импорта", - "file_size_limit_exceeded_warning_multiple_files": "Выбранные файлы превышают рекомендованный лимит в 10MB. Были импортированы только первые {files}", - "file_size_limit_exceeded_warning_single_file": "Размер выбранного в данный момент файла превышает рекомендуемый лимит в 10 МБ. Пожалуйста, выберите другой файл.", "from_file": "Импортировать из одного или нескольких файлов", "from_gist": "Импорт из Gist", "from_gist_description": "Импортировать через Gist URL", @@ -442,8 +451,10 @@ "json_description": "Импортировать из коллекции Hoppscotch", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "success": "Успешно импортировано", - "title": "Импортировать" + "title": "Импортировать", + "file_size_limit_exceeded_warning_multiple_files": "Выбранные файлы превышают рекомендованный лимит в 10MB. Были импортированы только первые {files}", + "file_size_limit_exceeded_warning_single_file": "Размер выбранного в данный момент файла превышает рекомендуемый лимит в 10 МБ. Пожалуйста, выберите другой файл.", + "success": "Успешно импортировано" }, "inspections": { "description": "Показать возможные ошибки", @@ -598,6 +609,7 @@ "title": "Запрос", "type": "Тип запроса", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Переменные", "view_my_links": "Посмотреть мои ссылки" }, @@ -765,11 +777,6 @@ "more": "Показать больше", "sidebar": "Показать боковую панель" }, - "site_protection": { - "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status", - "login_to_continue": "Login to continue", - "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance." - }, "socketio": { "communication": "Коммуникация", "connection_not_authorized": "Это SocketIO соединение не использует какую-либо авторизацию.", @@ -807,13 +814,6 @@ "invite": "Пригласить друзей в Hoppscotch", "title": "Другое" }, - "phrases": { - "create_environment": "Создать окружение", - "create_workspace": "Создать пространство", - "import_collections": "Импортировать коллекцию", - "share_request": "Поделиться запросом", - "try": "Попробовать" - }, "request": { "save_as_new": "Сохранить как новый запрос", "select_method": "Выбрать метод", @@ -862,6 +862,13 @@ "new": "Создать новую команду", "switch_to_personal": "Переключить на персональное пространство", "title": "Команды" + }, + "phrases": { + "try": "Попробовать", + "import_collections": "Импортировать коллекцию", + "create_environment": "Создать окружение", + "create_workspace": "Создать пространство", + "share_request": "Поделиться запросом" } }, "sse": { @@ -939,6 +946,8 @@ "query": "Запрос", "schema": "Схема", "shared_requests": "Запросы в общем доступе", + "codegen": "Generate Code", + "code_snippet": "Code snippet", "share_tab_request": "Поделиться запросом", "socketio": "Socket.IO", "sse": "SSE", @@ -996,12 +1005,15 @@ "permissions": "Разрешения", "same_target_destination": "Таже цель и конечная точка", "saved": "Команда сохранена", - "search_title": "Team Requests", "select_a_team": "Выбрать команду", "success_invites": "Принятые приглашения", "title": "Команды", "we_sent_invite_link": "Мы отправили все приглашения!", - "we_sent_invite_link_description": "Попросите тех, кого Вы пригласили, проверить их почтовые ящики. Им нужно перейди по ссылке, чтобы подтвердить вступление в эту команду." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Попросите тех, кого Вы пригласили, проверить их почтовые ящики. Им нужно перейди по ссылке, чтобы подтвердить вступление в эту команду.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests" }, "team_environment": { "deleted": "Окружение удалено", @@ -1031,5 +1043,45 @@ "other_workspaces": "Пространства", "team": "Пространство команды", "title": "Рабочие пространства" + }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" } } diff --git a/packages/hoppscotch-common/locales/sr.json b/packages/hoppscotch-common/locales/sr.json index 3ef60ed9d..26deb0df4 100644 --- a/packages/hoppscotch-common/locales/sr.json +++ b/packages/hoppscotch-common/locales/sr.json @@ -24,8 +24,10 @@ "go_back": "Вратити се", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Ознака", "learn_more": "Сазнајте више", + "download_here": "Download here", "less": "Less", "more": "Више", "new": "Нова", @@ -43,6 +45,7 @@ "search": "Претрага", "send": "Пошаљи", "share": "Share", + "show_secret": "Show secret", "start": "Почетак", "starting": "Starting", "stop": "Зауставити", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Унесите команду или претражите…", "we_use_cookies": "Користимо колачиће", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Шта је ново?", + "see_whats_new": "See what’s new", "wiki": "Вики" }, "auth": { "account_exists": "Налог постоји са различитим акредитивима - Пријавите се да бисте повезали оба налога", "all_sign_in_options": "Све опције пријављивања", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Наставите са е -поштом", "continue_with_github": "Наставите са ГитХуб -ом", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Наставите са Гоогле -ом", "continue_with_microsoft": "Continue with Microsoft", "email": "Емаил", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Лозинка", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Токен", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Измени збирку", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Наведите важећи назив збирке", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Изаберите колекцију", "select_location": "Изаберите локацију", + "details": "Details", "select_team": "Изаберите тим", "team_collections": "Збирке тима" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Јесте ли сигурни да желите да искључите Телеметрију?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Јесте ли сигурни да желите да синхронизујете овај радни простор?" + "sync": "Јесте ли сигурни да желите да синхронизујете овај радни простор?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Протоколи су празни", + "request_variables": "This request does not have any request variables", "schema": "Повежите се са ГрапхКЛ крајњом тачком", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Назив тима празан", "teams": "Тимови су празни", "tests": "Нема тестова за овај захтев", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Изаберите окружење", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Листа променљивих" + "variables": "Variables", + "variable_list": "Листа променљивих", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Празан назив захтева", "f12_details": "(Ф12 за детаље)", "gql_prettify_invalid_query": "Није могуће унапредити неважећи упит, решити грешке у синтакси упита и покушати поново", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Није могуће унапредити неважеће тело, решити грешке у синтакси јсон -а и покушати поново", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Није могуће извршити скрипту пре захтева", "something_went_wrong": "Нешто није у реду", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Извези као ЈСОН", "create_secret_gist": "Направите тајну суштину", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Суштина створена", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Пријавите се са ГитХуб -ом да бисте креирали тајну суштину", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Суштина створена" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Мутације", "schema": "Схема", "subscriptions": "Претплате", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Увоз" + "title": "Увоз", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Сирово тело захтева", "rename": "Rename Request", "renamed": "Захтев је преименован", + "request_variables": "Request variables", "run": "Трцати", "save": "сачувати", "save_as": "Сачувај као", @@ -557,6 +613,7 @@ "title": "Захтев", "type": "Врста Захтева", "url": "УРЛ", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Променљиве", "view_my_links": "View my links", "copy_link": "Копирај везу" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Постављајте питања и добијте одговоре", "github": "Follow us on Github", "shortcuts": "Брже прегледајте апликацију", - "team": "Ступите у контакт са тимом", "title": "Подршка", - "twitter": "Пратите нас на Твиттер -у" + "twitter": "Пратите нас на Твиттер -у", + "team": "Ступите у контакт са тимом" }, "tab": { "authorization": "Овлашћење", @@ -889,6 +953,9 @@ "query": "Упит", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Соцкет.ИО", "sse": "ССЕ", "tests": "Тестови", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Придружите се бета програму да бисте приступили тимовима.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Тимови", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Придружите се бета програму да бисте приступили тимовима." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/sv.json b/packages/hoppscotch-common/locales/sv.json index 676c530d9..779a71769 100644 --- a/packages/hoppscotch-common/locales/sv.json +++ b/packages/hoppscotch-common/locales/sv.json @@ -24,8 +24,10 @@ "go_back": "Gå tillbaka", "go_forward": "Go forward", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Märka", "learn_more": "Läs mer", + "download_here": "Download here", "less": "Less", "more": "Mer", "new": "Ny", @@ -43,6 +45,7 @@ "search": "Sök", "send": "Skicka", "share": "Share", + "show_secret": "Show secret", "start": "Start", "starting": "Starting", "stop": "Sluta", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Skriv ett kommando eller sök ...", "we_use_cookies": "Vi använder cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Vad är nytt?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Kontot finns med olika uppgifter - Logga in för att länka båda kontona", "all_sign_in_options": "Alla inloggningsalternativ", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Fortsätt med e -post", "continue_with_github": "Fortsätt med GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Fortsätt med Google", "continue_with_microsoft": "Continue with Microsoft", "email": "E-post", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Pass by", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Lösenord", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Tecken", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Redigera samling", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Ange ett giltigt namn för samlingen", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Välj en samling", "select_location": "Välj plats", + "details": "Details", "select_team": "Välj ett lag", "team_collections": "Lagsamlingar" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Är du säker på att du vill välja bort telemetri?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Är du säker på att du vill synkronisera den här arbetsytan?" + "sync": "Är du säker på att du vill synkronisera den här arbetsytan?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "There are no pending invites for this team", "profile": "Login to view your profile", "protocols": "Protokoll är tomma", + "request_variables": "This request does not have any request variables", "schema": "Anslut till en GraphQL -slutpunkt", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Lagets namn är tomt", "teams": "Lag är tomma", "tests": "Det finns inga tester för denna begäran", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes are empty" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Välj miljö", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Environment updation", "value": "Value", "variable": "Variable", - "variable_list": "Variabel lista" + "variables": "Variables", + "variable_list": "Variabel lista", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Tom förfrågningsnamn", "f12_details": "(F12 för detaljer)", "gql_prettify_invalid_query": "Det gick inte att pryda en ogiltig fråga, lösa frågesyntaxfel och försök igen", @@ -296,6 +336,7 @@ "incorrect_email": "Incorrect email", "invalid_link": "Invalid link", "invalid_link_description": "The link you clicked is invalid or expired.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Invalid JSON", "json_prettify_invalid_body": "Det gick inte att pryda en ogiltig kropp, lösa json -syntaxfel och försök igen", "network_error": "There seems to be a network error. Please try again.", @@ -307,17 +348,25 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Det gick inte att köra skriptet för förhandsbegäran", "something_went_wrong": "Något gick fel", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Exportera som JSON", "create_secret_gist": "Skapa hemlig Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist skapad", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Logga in med GitHub för att skapa hemlig information", - "title": "Export" + "title": "Export", + "success": "Successfully exported", + "gist_created": "Gist skapad" }, "filter": { "all": "All", @@ -339,7 +388,8 @@ "mutations": "Mutationer", "schema": "Schema", "subscriptions": "Prenumerationer", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Importera" + "title": "Importera", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Raw Request Body", "rename": "Rename Request", "renamed": "Begäran bytt namn", + "request_variables": "Request variables", "run": "Springa", "save": "Spara", "save_as": "Spara som", @@ -557,6 +613,7 @@ "title": "Begäran", "type": "Typ av förfrågan", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Variabler", "view_my_links": "View my links", "copy_link": "Kopiera länk" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Ställ frågor och få svar", "github": "Follow us on Github", "shortcuts": "Bläddra snabbare i appen", - "team": "Ta kontakt med laget", "title": "Stöd", - "twitter": "Följ oss på Twitter" + "twitter": "Följ oss på Twitter", + "team": "Ta kontakt med laget" }, "tab": { "authorization": "Tillstånd", @@ -889,6 +953,9 @@ "query": "Fråga", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Tester", @@ -917,7 +984,6 @@ "invite_tooltip": "Invite people to this workspace", "invited_to_team": "{owner} invited you to join {team}", "join": "Invitation accepted", - "join_beta": "Gå med i betaprogrammet för att komma åt team.", "join_team": "Join {team}", "joined_team": "You have joined {team}", "joined_team_description": "You are now a member of this team", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Lag", "we_sent_invite_link": "We sent an invite link to all invitees!", - "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Gå med i betaprogrammet för att komma åt team." }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/tr.json b/packages/hoppscotch-common/locales/tr.json index e61da251b..ecf79e3b2 100644 --- a/packages/hoppscotch-common/locales/tr.json +++ b/packages/hoppscotch-common/locales/tr.json @@ -17,15 +17,17 @@ "dismiss": "Boşver", "dont_save": "Don't save", "download_file": "Dosyayı Indir", - "drag_to_reorder": "Drag to reorder", + "drag_to_reorder": "Yeniden sıralamak için sürükleyin", "duplicate": "Klonla", "edit": "Düzenle", - "filter": "Filter", + "filter": "Filtre", "go_back": "Geri git", - "go_forward": "Go forward", + "go_forward": "İleri git", "group_by": "Group by", + "hide_secret": "Hide secret", "label": "Etiket", "learn_more": "Daha fazla bilgi edin", + "download_here": "Download here", "less": "Daha az", "more": "Daha fazla", "new": "Yeni", @@ -42,7 +44,8 @@ "scroll_to_top": "Scroll to top", "search": "Arama", "send": "Gönder", - "share": "Share", + "share": "Paylaş", + "show_secret": "Show secret", "start": "Başla", "starting": "Starting", "stop": "Dur", @@ -95,22 +98,26 @@ "twitter": "Twitter", "type_a_command_search": "Bir komut yazın veya arayın…", "we_use_cookies": "Çerezleri kullanıyoruz", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Ne var ne yok?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Farklı kimlik bilgilerine sahip hesap var - Her iki hesabı birbirine bağlamak için giriş yapın", "all_sign_in_options": "Tüm oturum açma seçenekleri", + "continue_with_auth_provider": "{provider} ile devam et", "continue_with_email": "E-posta ile devam et", - "continue_with_github": "GitHub hesabı ile devam et", - "continue_with_google": "Google hesabı ile devam et", - "continue_with_microsoft": "Microsoft hesabı ile devam et", + "continue_with_github": "GitHub ile devam et", + "continue_with_github_enterprise": "GitHub Enterprise ile devam et", + "continue_with_google": "Google ile devam et", + "continue_with_microsoft": "Microsoft ile devam et", "email": "E-posta", "logged_out": "Çıkış yapıldı", - "login": "Giriş yap", + "login": "Giriş", "login_success": "Başarıyla giriş yapıldı", "login_to_hoppscotch": "Hoppscotch'a giriş yapın", - "logout": "Çıkış yap", + "logout": "Çıkış", "re_enter_email": "E-mail adresinizi yeniden girin", "send_magic_link": "Sihirli bir bağlantı gönder", "sync": "Senkronizasyon", @@ -135,20 +142,42 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Şunla anahtar ekleyin", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Parola", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Jeton", - "type": "Yetki türü", + "type": "Yetki Türü", "username": "Kullanıcı adı" }, "collection": { "created": "Koleksiyon oluşturuldu", "different_parent": "Cannot reorder collection with different parent", "edit": "Koleksiyonu düzenle", - "import_or_create": "Import or create a collection", + "import_or_create": "Koleksiyon oluşturun veya içe aktarın", + "import_collection": "Koleksiyonu İçe Aktar", "invalid_name": "Lütfen koleksiyon için geçerli bir ad girin", "invalid_root_move": "Collection already in the root", "moved": "Başarıyla taşındı", @@ -158,15 +187,14 @@ "new": "Yeni koleksiyon", "order_changed": "Collection Order Updated", "properties": "Collection Properties", - "properties_updated": "Collection Properties Updated", + "properties_updated": "Koleksiyon Özellikleri Güncellendi", "renamed": "Koleksiyon yeniden adlandırıldı", "request_in_use": "Kullanımda istek", "save_as": "Farklı kaydet", "save_to_collection": "Save to Collection", "select": "Bir koleksiyon Seçin", "select_location": "Konum seçin", - "select_team": "Bir takım seçin", - "team_collections": "Takım koleksiyonları" + "details": "Details" }, "confirm": { "close_unsaved_tab": "Are you sure you want to close this tab?", @@ -183,7 +211,8 @@ "remove_telemetry": "Telemetriden çıkmak istediğinizden emin misiniz?", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "save_unsaved_tab": "Bu sekmede yapılan değişiklikleri kaydetmek istiyor musunuz?", - "sync": "Bu çalışma alanını senkronize etmek istediğinizden emin misiniz?" + "sync": "Bu çalışma alanını senkronize etmek istediğinizden emin misiniz?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,14 +266,16 @@ "pending_invites": "Bu takım için bekleyen bir istek yok", "profile": "Bu profili görüntülemek için giriş yapın", "protocols": "Protokoller boş", + "request_variables": "This request does not have any request variables", "schema": "Bir GraphQL uç noktasına bağlanma", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Subscriptions are empty", "team_name": "Takım adı boş", "teams": "Takımlar boş", "tests": "Bu istek için test yok", - "shortcodes": "Shortcodes are empty" + "access_tokens": "Access tokens are empty" }, "environment": { "add_to_global": "Globale ekle", @@ -260,25 +291,30 @@ "import_or_create": "Import or create a environment", "invalid_name": "Lütfen ortam için geçerli bir ad girin", "list": "Environment variables", - "my_environments": "My Environments", + "my_environments": "Ortamlarım", "name": "Name", "nested_overflow": "İç içe ortam değişkenleri 10 düzeyle sınırlıdır", "new": "Yeni ortam", - "no_active_environment": "No active environment", + "no_active_environment": "Aktif ortam yok", "no_environment": "Ortam yok", "no_environment_description": "Hiçbir ortam seçilmedi. Aşağıdaki değişkenlerle ne yapacağınızı seçin.", "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Ortam seçin", "set": "Set environment", "set_as_environment": "Set as environment", "team_environments": "Team Environments", "title": "Ortamlar", "updated": "Ortam güncellendi", - "value": "Value", - "variable": "Variable", - "variable_list": "Değişken listesi" + "value": "Değer", + "variable": "Değişken", + "variables": "Değişkenler", + "variable_list": "Değişken listesi", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +325,7 @@ "danger_zone": "Danger zone", "delete_account": "Your account is currently an owner in these teams:", "delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Boş İstek Adı", "f12_details": "(Ayrıntılar için F12)", "gql_prettify_invalid_query": "Geçersiz bir sorgu güzelleştirilemedi, sorgu sözdizimi hatalarını çözüp tekrar deneyin", @@ -296,7 +333,8 @@ "incorrect_email": "Geçersiz e-posta", "invalid_link": "Geçersiz bağlantı", "invalid_link_description": "Tıkladığınız linkin süresi geçmiş veya geçersiz", - "json_parsing_failed": "Invalid JSON", + "invalid_embed_link": "The embed does not exist or is invalid.", + "json_parsing_failed": "Geçersiz JSON", "json_prettify_invalid_body": "Geçersiz bir gövde güzelleştirilemedi, JSON sözdizimi hatalarını çözüp tekrar deneyin", "network_error": "Görünene göre bir ağ hatası var. Lütfen tekrar deneyin.", "network_fail": "İstek gönderilemedi", @@ -307,22 +345,29 @@ "page_not_found": "This page could not be found", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Ön istek komut dosyası çalıştırılamadı", "something_went_wrong": "Bir şeyler yanlış gitti", - "test_script_fail": "Could not execute post-request script" + "test_script_fail": "Could not execute post-request script", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "JSON olarak dışa aktar", "create_secret_gist": "Gizli Gist oluştur", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist oluşturuldu", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Gizli Gist oluşturmak için GitHub ile giriş yapın", - "title": "Dışarı Aktar" + "title": "Dışarı Aktar", + "success": "Successfully exported" }, "filter": { - "all": "All", - "none": "None", - "starred": "Starred" + "all": "Tümü", + "none": "Hiçbiri", + "starred": "Yıldızlı" }, "folder": { "created": "Klasör oluşturuldu", @@ -339,7 +384,8 @@ "mutations": "Mutasyonlar", "schema": "Şema", "subscriptions": "Abonelikler", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +451,17 @@ "json_description": "Import collections from a Hoppscotch Collections JSON file", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "İçe aktar" + "title": "İçe aktar", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +597,7 @@ "raw_body": "Ham istek gövdesi", "rename": "Rename Request", "renamed": "Yeniden adlandırılmış istek", + "request_variables": "Request variables", "run": "Çalıştır", "save": "Kaydet", "save_as": "Farklı kaydet", @@ -557,9 +609,9 @@ "title": "İstek", "type": "İstek türü", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Değişkenler", - "view_my_links": "View my links", - "copy_link": "Bağlantıyı kopyala" + "view_my_links": "View my links" }, "response": { "audio": "Audio", @@ -611,7 +663,7 @@ "profile_description": "Profil detaylarını güncelle", "profile_email": "E-posta adresi", "profile_name": "Profil ismi", - "proxy": "vekil", + "proxy": "Proxy", "proxy_url": "Proxy URL'si", "proxy_use_toggle": "İstek göndermek için proxy ara yazılımını kullanın", "read_the": "Oku", @@ -704,8 +756,7 @@ "send_request": "İstek gönder", "share_request": "Share Request", "show_code": "Generate code snippet", - "title": "İstek", - "copy_request_link": "İstek bağlantısını kopyala" + "title": "İstek" }, "response": { "copy": "Copy response to clipboard", @@ -811,6 +862,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,7 +925,6 @@ "forum": "Sorular sorun ve cevaplar alın", "github": "Bizi Github'da takip edin", "shortcuts": "Uygulamaya daha hızlı göz atın", - "team": "Takımla iletişim kurun", "title": "Destek", "twitter": "Bizi Twitter'da takip edin" }, @@ -889,6 +946,9 @@ "query": "Sorgu", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Testler", @@ -917,7 +977,6 @@ "invite_tooltip": "İnsanları bu çalışma alanına davet edin", "invited_to_team": "{owner} seni {team} takımına davet etti.", "join": "Davet kabul edildi", - "join_beta": "Takımlara erişmek için beta programına katılın.", "join_team": "{team}'e katıl", "joined_team": "{team} takımına katıldın", "joined_team_description": "Artık bu takımın bir üyesisin", @@ -950,7 +1009,11 @@ "success_invites": "Success invites", "title": "Başlık", "we_sent_invite_link": "Tüm davetlilere bir davet bağlantısı gönderdik!", - "we_sent_invite_link_description": "Tüm davetlilerden gelen kutularını kontrol etmelerini isteyin. Ekibe katılmak için bağlantıya tıklayın." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Tüm davetlilerden gelen kutularını kontrol etmelerini isteyin. Ekibe katılmak için bağlantıya tıklayın.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests" }, "team_environment": { "deleted": "Environment Deleted", @@ -977,9 +1040,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Actions", "created_on": "Created on", diff --git a/packages/hoppscotch-common/locales/tw.json b/packages/hoppscotch-common/locales/tw.json index 4b75843ae..dceafbb66 100644 --- a/packages/hoppscotch-common/locales/tw.json +++ b/packages/hoppscotch-common/locales/tw.json @@ -24,8 +24,10 @@ "go_back": "返回", "go_forward": "向前", "group_by": "分組方式", + "hide_secret": "Hide secret", "label": "標籤", "learn_more": "瞭解更多", + "download_here": "Download here", "less": "更少", "more": "更多", "new": "新增", @@ -43,6 +45,7 @@ "search": "搜尋", "send": "傳送", "share": "Share", + "show_secret": "Show secret", "start": "開始", "starting": "正在開始", "stop": "停止", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "輸入命令或搜尋內容……", "we_use_cookies": "我們使用 cookies", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "新功能", + "see_whats_new": "See what’s new", "wiki": "維基" }, "auth": { "account_exists": "帳號存在不同的憑證 - 登入後可連結兩個帳號", "all_sign_in_options": "所有登入選項", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "使用電子信箱登入", "continue_with_github": "使用 GitHub 登入", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "使用 Google 登入", "continue_with_microsoft": "使用 Microsoft 登入", "email": "電子信箱地址", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "傳遞方式", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "密碼", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "權杖", @@ -149,6 +177,7 @@ "different_parent": "無法為父集合不同的集合重新排序", "edit": "編輯集合", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "請提供有效的集合名稱", "invalid_root_move": "集合已在根目錄", "moved": "移動成功", @@ -165,6 +194,7 @@ "save_to_collection": "儲存到集合", "select": "選擇一個集合", "select_location": "選擇位置", + "details": "Details", "select_team": "選擇一個團隊", "team_collections": "團隊集合" }, @@ -183,7 +213,8 @@ "remove_telemetry": "您確定要退出遙測服務嗎?", "request_change": "您確定要捨棄目前的請求嗎?未儲存的變更將遺失。", "save_unsaved_tab": "您要儲存在此分頁做出的改動嗎?", - "sync": "您想從雲端恢復您的工作區嗎?這將丟棄您的本地進度。" + "sync": "您想從雲端恢復您的工作區嗎?這將丟棄您的本地進度。", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "新增至參數", @@ -237,13 +268,16 @@ "pending_invites": "這個團隊沒有待定的邀請", "profile": "登入以檢視您的設定檔", "protocols": "協定為空", + "request_variables": "This request does not have any request variables", "schema": "連線至 GraphQL 端點", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "訂閱為空", "team_name": "團隊名稱為空", "teams": "團隊為空", "tests": "沒有針對該請求的測試", + "access_tokens": "Access tokens are empty", "shortcodes": "Shortcodes 為空" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "快速預覽環境", "replace_with_variable": "以變數替代", "scope": "範圍", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "選擇環境", "set": "設定環境", "set_as_environment": "設為環境", @@ -278,7 +314,10 @@ "updated": "更新環境", "value": "數值", "variable": "變數", - "variable_list": "變數列表" + "variables": "Variables", + "variable_list": "變數列表", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "危險地帶", "delete_account": "您的帳號目前為這些團隊的擁有者:", "delete_account_description": "您在刪除帳號前必須先將您自己從團隊中移除、轉移擁有權,或是刪除團隊。", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "空請求名稱", "f12_details": "(按下 F12 以獲悉詳情)", "gql_prettify_invalid_query": "無法美化無效的查詢,處理查詢語法錯誤並重試", @@ -296,6 +336,7 @@ "incorrect_email": "錯誤的電子信箱", "invalid_link": "連結無效", "invalid_link_description": "您點擊的連結無效或已過期。", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSON 無效", "json_prettify_invalid_body": "無法美化無效的請求主體,處理 JSON 語法錯誤並重試", "network_error": "似乎有網路錯誤。請再試一次。", @@ -307,17 +348,25 @@ "page_not_found": "找不到此頁面", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy 錯誤", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "無法執行預請求指令碼", "something_went_wrong": "發生了一些錯誤", - "test_script_fail": "無法執行測試指令碼" + "test_script_fail": "無法執行測試指令碼", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "匯出為 JSON", "create_secret_gist": "建立私密 Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "已建立 Gist", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "使用 GitHub 登入以建立私密 Gist", - "title": "匯出" + "title": "匯出", + "success": "Successfully exported", + "gist_created": "已建立 Gist" }, "filter": { "all": "全部", @@ -339,7 +388,8 @@ "mutations": "變體", "schema": "綱要", "subscriptions": "訂閱", - "switch_connection": "切換連線" + "switch_connection": "切換連線", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "從 Hoppscotch 集合 JSON 檔匯入集合", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "匯入" + "title": "匯入", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "檢查潛在錯誤", "environment": { "add_environment": "新增至環境", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "找不到環境變數 “{environment}”。" }, "header": { @@ -546,6 +601,7 @@ "raw_body": "原始請求本體", "rename": "重新命名請求", "renamed": "請求已重新命名", + "request_variables": "Request variables", "run": "執行", "save": "儲存", "save_as": "另存為", @@ -557,6 +613,7 @@ "title": "請求", "type": "請求類型", "url": "網址", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "變數", "view_my_links": "檢視我的連結", "copy_link": "複製連結" @@ -811,6 +868,13 @@ "new": "建立新團隊", "switch_to_personal": "切換至您的個人工作區", "title": "團隊" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "答疑解惑", "github": "在 GitHub 關注我們", "shortcuts": "更快瀏覽應用程式", - "team": "與團隊保持聯絡", "title": "支援", - "twitter": "在 Twitter 關注我們" + "twitter": "在 Twitter 關注我們", + "team": "與團隊保持聯絡" }, "tab": { "authorization": "授權", @@ -889,6 +953,9 @@ "query": "查詢", "schema": "綱要", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "測試", @@ -917,7 +984,6 @@ "invite_tooltip": "邀請他人到這個工作區", "invited_to_team": "{owner} 邀請您加入 {team}", "join": "已接受邀請", - "join_beta": "加入 Beta 計畫以存取團隊。", "join_team": "加入 {team}", "joined_team": "您已加入 {team}", "joined_team_description": "您現在是這個團隊的成員", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "團隊", "we_sent_invite_link": "我們向所有受邀者傳送了邀請連結!", - "we_sent_invite_link_description": "請所有受邀者檢查他們的收件匣。點擊連結加入團隊。" + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "請所有受邀者檢查他們的收件匣。點擊連結加入團隊。", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "加入 Beta 計畫以存取團隊。" }, "team_environment": { "deleted": "已刪除環境", @@ -977,9 +1048,50 @@ "workspace": { "change": "切換工作區", "personal": "我的工作區", + "other_workspaces": "My Workspaces", "team": "團隊工作區", "title": "工作區" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "操作", "created_on": "建立於", diff --git a/packages/hoppscotch-common/locales/uk.json b/packages/hoppscotch-common/locales/uk.json index e56d6f911..d00054415 100644 --- a/packages/hoppscotch-common/locales/uk.json +++ b/packages/hoppscotch-common/locales/uk.json @@ -24,8 +24,10 @@ "go_back": "Повернутись", "go_forward": "Go forward", "group_by": "Групувати за", + "hide_secret": "Hide secret", "label": "Мітка", "learn_more": "Дізнатись більше", + "download_here": "Download here", "less": "Менше", "more": "Більше", "new": "Новий", @@ -43,6 +45,7 @@ "search": "Пошук", "send": "Надіслати", "share": "Share", + "show_secret": "Show secret", "start": "Почати", "starting": "Розпочинається", "stop": "Зупити", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Введіть команду або виконайте пошук…", "we_use_cookies": "Ми використовуємо файли cookie", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Що нового?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Обліковий запис існує з різними обліковими даними - увійдіть, щоб зв'язати обидва облікові записи", "all_sign_in_options": "Усі параметри входу", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Продовжити з електронною поштою", "continue_with_github": "Продовжити з GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Продовжити з Google", "continue_with_microsoft": "Продовжити з Microsoft", "email": "Електронна пошта", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Пропустити", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Пароль", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Токен", @@ -149,6 +177,7 @@ "different_parent": "Cannot reorder collection with different parent", "edit": "Редагувати колекцію", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Укажіть дійсну назву колекції", "invalid_root_move": "Collection already in the root", "moved": "Moved Successfully", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Виберіть колекцію", "select_location": "Виберіть місце розташування", + "details": "Details", "select_team": "Виберіть команду", "team_collections": "Колекції команд" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Ви впевнені, що хочете відмовитися від телеметрії?", "request_change": "Ви дійсно бажаєте скасувати поточний запит? Незбережені зміни будуть втрачені.", "save_unsaved_tab": "Do you want to save changes made in this tab?", - "sync": "Ви впевнені, що хочете синхронізувати цю робочу область?" + "sync": "Ви впевнені, що хочете синхронізувати цю робочу область?", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "Немає запрошень в очікуванні для цієї команди", "profile": "Увійдіть для перегляду вашого профілю", "protocols": "Протоколи порожні", + "request_variables": "This request does not have any request variables", "schema": "Підключіться до кінцевої точки GraphQL", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Підписки порожні", "team_name": "Назва команди порожня", "teams": "Команди порожні", "tests": "Для цього запиту немає тестів", + "access_tokens": "Access tokens are empty", "shortcodes": "Короткі коди порожні" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Виберіть середовище", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Оновлення середовища", "value": "Value", "variable": "Variable", - "variable_list": "Список змінних" + "variables": "Variables", + "variable_list": "Список змінних", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Небезпечна зона", "delete_account": "Ваш обліковий запис на разі володіє цими командами:", "delete_account_description": "Ви повинні або видалити себе, або передати право власності, або видалити ці команди, перш ніж ви зможете видалити свій обліковий запис.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Пуста назва запиту", "f12_details": "(F12 для деталей)", "gql_prettify_invalid_query": "Не вдалося попередньо визначити недійсний запит, вирішити синтаксичні помилки запиту та повторити спробу", @@ -296,6 +336,7 @@ "incorrect_email": "Невірна електронна адреса", "invalid_link": "Невірне посилання", "invalid_link_description": "Посилання, яке ви натиснули, є недійсним або застарілим.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "Неправильний JSON", "json_prettify_invalid_body": "Не вдалося заздалегідь визначити недійсне тіло, вирішити синтаксичні помилки json і повторити спробу", "network_error": "Здається, виникла мережева помилка. Будь ласка, спробуйте ще раз.", @@ -307,17 +348,25 @@ "page_not_found": "Ця сторінка не знайдена", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Не вдалося виконати сценарій попереднього запиту", "something_went_wrong": "Щось пішло не так", - "test_script_fail": "Не вдалося виконати скрипт після запиту" + "test_script_fail": "Не вдалося виконати скрипт після запиту", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Експортувати як JSON", "create_secret_gist": "Створити секретний GitHub Gist", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist створений", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Увійдіть за допомогою GitHub, щоб створити секретний Gist", - "title": "Експортувати" + "title": "Експортувати", + "success": "Successfully exported", + "gist_created": "Gist створений" }, "filter": { "all": "Всі", @@ -339,7 +388,8 @@ "mutations": "Мутації", "schema": "Схема", "subscriptions": "Підписки", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Імпортувати колекції з колекцій Hoppscotch JSON файлу", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Імпортувати" + "title": "Імпортувати", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Сировина запиту", "rename": "Rename Request", "renamed": "Запит перейменовано", + "request_variables": "Request variables", "run": "Біжи", "save": "Зберегти", "save_as": "Зберегти як", @@ -557,6 +613,7 @@ "title": "Запит", "type": "Тип запиту", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Змінні", "view_my_links": "Переглянути мої посилання", "copy_link": "Скопіювати посилання" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Задавайте питання і отримуйте відповіді", "github": "Слідкуйте за нами на Github", "shortcuts": "Швидше переглядайте програми", - "team": "Зв'яжіться з командою", "title": "Підтримка", - "twitter": "Слідкуйте за нами у Twitter" + "twitter": "Слідкуйте за нами у Twitter", + "team": "Зв'яжіться з командою" }, "tab": { "authorization": "Авторизація", @@ -889,6 +953,9 @@ "query": "Запит", "schema": "Схема", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Тести", @@ -917,7 +984,6 @@ "invite_tooltip": "Запросити людей в це робоче середовище", "invited_to_team": "{owner} запросив вас приєднатися до {team}", "join": "Запрошення прийнято", - "join_beta": "Приєднуйтесь до бета -програми, щоб отримати доступ до команд.", "join_team": "Приєднатися до {team}", "joined_team": "Ви приєдналися до {team}", "joined_team_description": "Ви тепер учасник цієї команди", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Команди", "we_sent_invite_link": "Ми надіслали запрошення всім запрошеним!", - "we_sent_invite_link_description": "Попросіть всіх запрошених перевірити свої поштові скриньки. Перейдіть за посиланням, щоб приєднатися до команди." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Попросіть всіх запрошених перевірити свої поштові скриньки. Перейдіть за посиланням, щоб приєднатися до команди.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Приєднуйтесь до бета -програми, щоб отримати доступ до команд." }, "team_environment": { "deleted": "Середовище видалено", @@ -977,9 +1048,50 @@ "workspace": { "change": "Change workspace", "personal": "My Workspace", + "other_workspaces": "My Workspaces", "team": "Team Workspace", "title": "Workspaces" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Дії", "created_on": "Дата створення", diff --git a/packages/hoppscotch-common/locales/vi.json b/packages/hoppscotch-common/locales/vi.json index 9fc5229c0..84597272f 100644 --- a/packages/hoppscotch-common/locales/vi.json +++ b/packages/hoppscotch-common/locales/vi.json @@ -24,8 +24,10 @@ "go_back": "Quay lại", "go_forward": "Tiến về phía trước", "group_by": "Nhóm theo", + "hide_secret": "Hide secret", "label": "Nhãn", "learn_more": "Tìm hiểu thêm", + "download_here": "Download here", "less": "Ít hơn", "more": "Thêm", "new": "Mới", @@ -43,6 +45,7 @@ "search": "Tìm kiếm", "send": "Gửi", "share": "Share", + "show_secret": "Show secret", "start": "Bắt đầu", "starting": "Đang bắt đầu", "stop": "Dừng", @@ -95,14 +98,18 @@ "twitter": "Twitter", "type_a_command_search": "Nhập một lệnh hoặc tìm kiếm...", "we_use_cookies": "Chúng tôi sử dụng cookie", + "updated_text": "Hoppscotch has been updated to v{version} 🎉", "whats_new": "Có gì mới?", + "see_whats_new": "See what’s new", "wiki": "Wiki" }, "auth": { "account_exists": "Tài khoản đã tồn tại với thông tin đăng nhập khác - Đăng nhập để liên kết hai tài khoản", "all_sign_in_options": "Tất cả các tùy chọn đăng nhập", + "continue_with_auth_provider": "Continue with {provider}", "continue_with_email": "Tiếp tục bằng Email", "continue_with_github": "Tiếp tục bằng GitHub", + "continue_with_github_enterprise": "Continue with GitHub Enterprise", "continue_with_google": "Tiếp tục bằng Google", "continue_with_microsoft": "Tiếp tục bằng Microsoft", "email": "Email", @@ -135,9 +142,30 @@ "redirect_no_token_endpoint": "No Token Endpoint Defined", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "something_went_wrong_on_token_generation": "Something went wrong on token generation", - "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" + "token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed", + "grant_type": "Grant Type", + "grant_type_auth_code": "Authorization Code", + "token_fetched_successfully": "Token fetched successfully", + "token_fetch_failed": "Failed to fetch token", + "validation_failed": "Validation Failed, please check the form fields", + "label_authorization_endpoint": "Authorization Endpoint", + "label_client_id": "Client ID", + "label_client_secret": "Client Secret", + "label_code_challenge": "Code Challenge", + "label_code_challenge_method": "Code Challenge Method", + "label_code_verifier": "Code Verifier", + "label_scopes": "Scopes", + "label_token_endpoint": "Token Endpoint", + "label_use_pkce": "Use PKCE", + "label_implicit": "Implicit", + "label_password": "Password", + "label_username": "Username", + "label_auth_code": "Authorization Code", + "label_client_credentials": "Client Credentials" }, "pass_key_by": "Truyền qua", + "pass_by_query_params_label": "Query Parameters", + "pass_by_headers_label": "Headers", "password": "Mật khẩu", "save_to_inherit": "Please save this request in any collection to inherit the authorization", "token": "Token", @@ -149,6 +177,7 @@ "different_parent": "Không thể sắp xếp lại bộ sưu tập với cha khác", "edit": "Chỉnh sửa bộ sưu tập", "import_or_create": "Import or create a collection", + "import_collection": "Import Collection", "invalid_name": "Vui lòng cung cấp tên cho bộ sưu tập", "invalid_root_move": "Bộ sưu tập đã nằm trong thư mục gốc", "moved": "Di chuyển thành công", @@ -165,6 +194,7 @@ "save_to_collection": "Save to Collection", "select": "Chọn một bộ sưu tập", "select_location": "Chọn vị trí", + "details": "Details", "select_team": "Chọn một nhóm", "team_collections": "Bộ sưu tập của nhóm" }, @@ -183,7 +213,8 @@ "remove_telemetry": "Bạn có chắc chắn muốn tắt Telemetry?", "request_change": "Bạn có chắc chắn muốn hủy request hiện tại? Những thay đổi chưa được lưu sẽ bị mất.", "save_unsaved_tab": "Bạn có muốn lưu các thay đổi đã được thực hiện trong tab này?", - "sync": "Bạn có muốn khôi phục không gian làm việc từ đám mây? Điều này sẽ xóa bỏ tiến trình địa phương của bạn." + "sync": "Bạn có muốn khôi phục không gian làm việc từ đám mây? Điều này sẽ xóa bỏ tiến trình địa phương của bạn.", + "delete_access_token": "Are you sure you want to delete the access token {tokenLabel}?" }, "context_menu": { "add_parameters": "Add to parameters", @@ -237,13 +268,16 @@ "pending_invites": "Không có lời mời đang chờ cho nhóm này", "profile": "Đăng nhập để xem hồ sơ của bạn", "protocols": "Danh sách giao thức trống rỗng", + "request_variables": "This request does not have any request variables", "schema": "Kết nối với điểm cuối GraphQL để xem lược đồ", + "secret_environments": "Secrets are not synced to Hoppscotch", "shared_requests": "Shared requests are empty", "shared_requests_logout": "Login to view your shared requests or create a new one", "subscription": "Danh sách đăng ký trống rỗng", "team_name": "Tên nhóm trống rỗng", "teams": "Bạn không thuộc bất kỳ nhóm nào", "tests": "Không có bài kiểm tra cho request này", + "access_tokens": "Access tokens are empty", "shortcodes": "Danh sách mã ngắn trống rỗng" }, "environment": { @@ -270,6 +304,8 @@ "quick_peek": "Environment Quick Peek", "replace_with_variable": "Replace with variable", "scope": "Scope", + "secrets": "Secrets", + "secret_value": "Secret value", "select": "Chọn môi trường", "set": "Set environment", "set_as_environment": "Set as environment", @@ -278,7 +314,10 @@ "updated": "Cập nhật môi trường thành công", "value": "Value", "variable": "Variable", - "variable_list": "Danh sách biến" + "variables": "Variables", + "variable_list": "Danh sách biến", + "properties": "Environment Properties", + "details": "Details" }, "error": { "authproviders_load_error": "Unable to load auth providers", @@ -289,6 +328,7 @@ "danger_zone": "Vùng nguy hiểm", "delete_account": "Tài khoản của bạn hiện là chủ sở hữu trong các nhóm sau:", "delete_account_description": "Bạn phải xóa bản thân, chuyển quyền sở hữu hoặc xóa các nhóm này trước khi bạn có thể xóa tài khoản của mình.", + "empty_profile_name": "Profile name cannot be empty", "empty_req_name": "Tên request trống", "f12_details": "(Nhấn F12 để xem chi tiết)", "gql_prettify_invalid_query": "Không thể định dạng đẹp một truy vấn không hợp lệ, giải quyết lỗi cú pháp truy vấn và thử lại", @@ -296,6 +336,7 @@ "incorrect_email": "Email không chính xác", "invalid_link": "Liên kết không hợp lệ", "invalid_link_description": "Liên kết bạn đã nhấp vào không hợp lệ hoặc đã hết hạn.", + "invalid_embed_link": "The embed does not exist or is invalid.", "json_parsing_failed": "JSON không hợp lệ", "json_prettify_invalid_body": "Không thể định dạng đẹp một phần thân không hợp lệ, giải quyết lỗi cú pháp JSON và thử lại", "network_error": "Có vẻ như có lỗi mạng. Vui lòng thử lại.", @@ -307,17 +348,25 @@ "page_not_found": "Không tìm thấy trang này", "please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error", + "same_profile_name": "Updated profile name is same as the current profile name", "script_fail": "Không thể thực thi kịch bản trước request", "something_went_wrong": "Đã xảy ra lỗi", - "test_script_fail": "Không thể thực thi kịch bản sau request" + "test_script_fail": "Không thể thực thi kịch bản sau request", + "reading_files": "Error while reading one or more files.", + "fetching_access_tokens_list": "Something went wrong while fetching the list of tokens", + "generate_access_token": "Something went wrong while generating the access token", + "delete_access_token": "Something went wrong while deleting the access token" }, "export": { "as_json": "Xuất dưới dạng JSON", "create_secret_gist": "Tạo Gist bí mật", + "create_secret_gist_tooltip_text": "Export as secret Gist", "failed": "Something went wrong while exporting", - "gist_created": "Gist đã được tạo", + "secret_gist_success": "Successfully exported as secret Gist", "require_github": "Đăng nhập bằng GitHub để tạo Gist bí mật", - "title": "Xuất" + "title": "Xuất", + "success": "Successfully exported", + "gist_created": "Gist đã được tạo" }, "filter": { "all": "Tất cả", @@ -339,7 +388,8 @@ "mutations": "Mutations", "schema": "Schema", "subscriptions": "Subscriptions", - "switch_connection": "Switch connection" + "switch_connection": "Switch connection", + "url_placeholder": "Enter a GraphQL endpoint URL" }, "graphql_collections": { "title": "GraphQL Collections" @@ -405,12 +455,17 @@ "json_description": "Nhập bộ sưu tập từ tệp JSON Hoppscotch", "postman_environment": "Postman Environment", "postman_environment_description": "Import Postman Environment from a JSON file", - "title": "Nhập" + "title": "Nhập", + "file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", + "file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", + "success": "Successfully imported" }, "inspections": { "description": "Inspect possible errors", "environment": { "add_environment": "Add to Environment", + "add_environment_value": "Add value", + "empty_value": "Environment value is empty for the variable '{variable}' ", "not_found": "Environment variable “{environment}” not found." }, "header": { @@ -546,6 +601,7 @@ "raw_body": "Nội dung thô", "rename": "Rename Request", "renamed": "Đã đổi tên", + "request_variables": "Request variables", "run": "Chạy", "save": "Lưu", "save_as": "Lưu như", @@ -557,6 +613,7 @@ "title": "Request", "type": "Loại request", "url": "URL", + "url_placeholder": "Enter a URL or paste a cURL command", "variables": "Biến", "view_my_links": "Xem các liên kết của tôi", "copy_link": "Sao chép liên kết" @@ -811,6 +868,13 @@ "new": "Create new team", "switch_to_personal": "Switch to your personal workspace", "title": "Teams" + }, + "phrases": { + "try": "Try", + "import_collections": "Import collections", + "create_environment": "Create environment", + "create_workspace": "Create workspace", + "share_request": "Share request" } }, "sse": { @@ -867,9 +931,9 @@ "forum": "Hỏi câu hỏi và nhận câu trả lời", "github": "Theo dõi chúng tôi trên Github", "shortcuts": "Duyệt ứng dụng nhanh hơn", - "team": "Liên hệ với nhóm", "title": "Hỗ trợ", - "twitter": "Theo dõi chúng tôi trên Twitter" + "twitter": "Theo dõi chúng tôi trên Twitter", + "team": "Liên hệ với nhóm" }, "tab": { "authorization": "Xác thực", @@ -889,6 +953,9 @@ "query": "Truy vấn", "schema": "Schema", "shared_requests": "Shared Requests", + "codegen": "Generate Code", + "code_snippet": "Code snippet", + "share_tab_request": "Share tab request", "socketio": "Socket.IO", "sse": "SSE", "tests": "Kiểm tra", @@ -917,7 +984,6 @@ "invite_tooltip": "Mời người khác vào không gian làm việc này", "invited_to_team": "{owner} đã mời bạn tham gia {team}", "join": "Đã chấp nhận lời mời", - "join_beta": "Tham gia chương trình beta để truy cập vào nhóm.", "join_team": "Tham gia {team}", "joined_team": "Bạn đã tham gia {team}", "joined_team_description": "Bạn hiện là thành viên của nhóm này", @@ -950,7 +1016,12 @@ "success_invites": "Success invites", "title": "Nhóm", "we_sent_invite_link": "Chúng tôi đã gửi một liên kết mời đến tất cả người được mời!", - "we_sent_invite_link_description": "Yêu cầu tất cả người được mời kiểm tra hộp thư đến của họ. Nhấp vào liên kết để tham gia nhóm." + "invite_sent_smtp_disabled": "Invite links generated", + "we_sent_invite_link_description": "Yêu cầu tất cả người được mời kiểm tra hộp thư đến của họ. Nhấp vào liên kết để tham gia nhóm.", + "invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.", + "copy_invite_link": "Copy Invite Link", + "search_title": "Team Requests", + "join_beta": "Tham gia chương trình beta để truy cập vào nhóm." }, "team_environment": { "deleted": "Môi trường đã bị xóa", @@ -977,9 +1048,50 @@ "workspace": { "change": "Thay đổi không gian làm việc", "personal": "Không gian làm việc của tôi", + "other_workspaces": "My Workspaces", "team": "Không gian làm việc nhóm", "title": "Không gian làm việc" }, + "site_protection": { + "login_to_continue": "Login to continue", + "login_to_continue_description": "You need to be logged in to access this Hoppscotch Enterprise Instance.", + "error_fetching_site_protection_status": "Something Went Wrong While Fetching Site Protection Status" + }, + "access_tokens": { + "tab_title": "Tokens", + "section_title": "Personal Access Tokens", + "section_description": "Personal access tokens currently helps you connect the CLI to your Hoppscotch account", + "last_used_on": "Last used on", + "expires_on": "Expires on", + "no_expiration": "No expiration", + "expired": "Expired", + "copy_token_warning": "Make sure to copy your personal access token now. You won't be able to see it again!", + "token_purpose": "What's this token for?", + "expiration_label": "Expiration", + "scope_label": "Scope", + "workspace_read_only_access": "Read-only access to workspace data.", + "personal_workspace_access_limitation": "Personal Access Tokens can't access your personal workspace.", + "generate_token": "Generate Token", + "invalid_label": "Please provide a label for the token", + "no_expiration_verbose": "This token will never expire!", + "token_expires_on": "This token will expire on", + "generate_new_token": "Generate new token", + "generate_modal_title": "New Personal Access Token", + "deletion_success": "The access token {label} has been deleted" + }, + "collection_runner": { + "collection_id": "Collection ID", + "environment_id": "Environment ID", + "cli_collection_id_description": "This collection ID will be used by the CLI collection runner for Hoppscotch.", + "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", + "include_active_environment": "Include active environment:", + "cli": "CLI", + "ui": "Runner (coming soon)", + "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", + "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", + "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", + "run_collection": "Run collection" + }, "shortcodes": { "actions": "Hành động", "created_on": "Được tạo vào", diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index c1a2e8bb7..ba2742093 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -1,7 +1,7 @@ { "name": "@hoppscotch/common", "private": true, - "version": "2024.3.3", + "version": "2024.7.0", "scripts": { "dev": "pnpm exec npm-run-all -p -l dev:*", "test": "vitest --run", @@ -35,8 +35,9 @@ "@codemirror/view": "6.25.1", "@hoppscotch/codemirror-lang-graphql": "workspace:^", "@hoppscotch/data": "workspace:^", + "@hoppscotch/httpsnippet": "3.0.6", "@hoppscotch/js-sandbox": "workspace:^", - "@hoppscotch/ui": "0.1.0", + "@hoppscotch/ui": "0.2.0", "@hoppscotch/vue-toasted": "0.1.0", "@lezer/highlight": "1.2.0", "@unhead/vue": "1.8.8", @@ -50,7 +51,7 @@ "axios": "1.6.2", "buffer": "6.0.3", "cookie-es": "1.0.0", - "dioc": "3.0.1", + "dioc": "3.0.2", "esprima": "4.0.1", "events": "3.3.0", "fp-ts": "2.16.1", @@ -58,7 +59,6 @@ "graphql": "16.8.1", "graphql-language-service-interface": "2.10.2", "graphql-tag": "2.12.6", - "httpsnippet": "3.0.1", "insomnia-importers": "3.6.0", "io-ts": "2.2.20", "js-yaml": "4.1.0", @@ -124,6 +124,7 @@ "@types/nprogress": "0.2.3", "@types/paho-mqtt": "1.0.10", "@types/postman-collection": "3.5.10", + "@types/qs": "6.9.12", "@types/splitpanes": "2.2.6", "@types/uuid": "9.0.7", "@types/yargs-parser": "21.0.3", diff --git a/packages/hoppscotch-common/src/App.vue b/packages/hoppscotch-common/src/App.vue index 47a086935..8d9d7e4d1 100644 --- a/packages/hoppscotch-common/src/App.vue +++ b/packages/hoppscotch-common/src/App.vue @@ -8,6 +8,7 @@ + @@ -19,6 +20,7 @@ import { isLoadingInitialRoute } from "@modules/router" import { useI18n } from "@composables/i18n" import { APP_IS_IN_DEV_MODE } from "@helpers/dev" import { platform } from "./platform" +import { Toaster } from "@hoppscotch/ui" const t = useI18n() diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index b1ab7c333..8ff1054de 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -7,6 +7,10 @@ export {} declare module 'vue' { export interface GlobalComponents { + AccessTokens: typeof import('./components/accessTokens/index.vue')['default'] + AccessTokensGenerateModal: typeof import('./components/accessTokens/GenerateModal.vue')['default'] + AccessTokensList: typeof import('./components/accessTokens/List.vue')['default'] + AccessTokensOverview: typeof import('./components/accessTokens/Overview.vue')['default'] AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default'] AppBanner: typeof import('./components/app/Banner.vue')['default'] AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default'] @@ -19,6 +23,7 @@ declare module 'vue' { AppLogo: typeof import('./components/app/Logo.vue')['default'] AppOptions: typeof import('./components/app/Options.vue')['default'] AppPaneLayout: typeof import('./components/app/PaneLayout.vue')['default'] + AppPWAPrompt: typeof import('./components/app/PWAPrompt.vue')['default'] AppShare: typeof import('./components/app/Share.vue')['default'] AppShortcuts: typeof import('./components/app/Shortcuts.vue')['default'] AppShortcutsEntry: typeof import('./components/app/ShortcutsEntry.vue')['default'] @@ -34,6 +39,7 @@ declare module 'vue' { AppSpotlightEntryRESTTeamRequestEntry: typeof import('./components/app/spotlight/entry/RESTTeamRequestEntry.vue')['default'] AppSpotlightSearch: typeof import('./components/app/SpotlightSearch.vue')['default'] AppSupport: typeof import('./components/app/Support.vue')['default'] + AppWhatsNewDialog: typeof import('./components/app/WhatsNewDialog.vue')['default'] Collections: typeof import('./components/collections/index.vue')['default'] CollectionsAdd: typeof import('./components/collections/Add.vue')['default'] CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default'] @@ -57,17 +63,21 @@ declare module 'vue' { CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default'] CollectionsProperties: typeof import('./components/collections/Properties.vue')['default'] CollectionsRequest: typeof import('./components/collections/Request.vue')['default'] + CollectionsRunner: typeof import('./components/collections/Runner.vue')['default'] CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default'] CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default'] CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default'] CookiesEditCookie: typeof import('./components/cookies/EditCookie.vue')['default'] Embeds: typeof import('./components/embeds/index.vue')['default'] + EmbedsHeader: typeof import('./components/embeds/Header.vue')['default'] + EmbedsRequest: typeof import('./components/embeds/Request.vue')['default'] Environments: typeof import('./components/environments/index.vue')['default'] EnvironmentsAdd: typeof import('./components/environments/Add.vue')['default'] EnvironmentsImportExport: typeof import('./components/environments/ImportExport.vue')['default'] EnvironmentsMy: typeof import('./components/environments/my/index.vue')['default'] EnvironmentsMyDetails: typeof import('./components/environments/my/Details.vue')['default'] EnvironmentsMyEnvironment: typeof import('./components/environments/my/Environment.vue')['default'] + EnvironmentsProperties: typeof import('./components/environments/Properties.vue')['default'] EnvironmentsSelector: typeof import('./components/environments/Selector.vue')['default'] EnvironmentsTeams: typeof import('./components/environments/teams/index.vue')['default'] EnvironmentsTeamsDetails: typeof import('./components/environments/teams/Details.vue')['default'] @@ -122,6 +132,7 @@ declare module 'vue' { HttpAuthorizationBasic: typeof import('./components/http/authorization/Basic.vue')['default'] HttpBody: typeof import('./components/http/Body.vue')['default'] HttpBodyParameters: typeof import('./components/http/BodyParameters.vue')['default'] + HttpCodegen: typeof import('./components/http/Codegen.vue')['default'] HttpCodegenModal: typeof import('./components/http/CodegenModal.vue')['default'] HttpHeaders: typeof import('./components/http/Headers.vue')['default'] HttpImportCurl: typeof import('./components/http/ImportCurl.vue')['default'] @@ -148,7 +159,7 @@ declare module 'vue' { IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] - IconLucideBrush: (typeof import("~icons/lucide/brush"))["default"] + IconLucideBrush: typeof import('~icons/lucide/brush')['default'] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] @@ -158,7 +169,7 @@ declare module 'vue' { IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default'] - IconLucideRss: (typeof import("~icons/lucide/rss"))["default"] + IconLucideRss: typeof import('~icons/lucide/rss')['default'] IconLucideSearch: typeof import('~icons/lucide/search')['default'] IconLucideUsers: typeof import('~icons/lucide/users')['default'] IconLucideX: typeof import('~icons/lucide/x')['default'] @@ -181,6 +192,9 @@ declare module 'vue' { LensesRenderersVideoLensRenderer: typeof import('./components/lenses/renderers/VideoLensRenderer.vue')['default'] LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default'] LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default'] + ModalsNativeCACertificates: typeof import('./../../hoppscotch-selfhost-desktop/src/components/modals/NativeCACertificates.vue')['default'] + ModalsNativeClientCertificates: typeof import('./../../hoppscotch-selfhost-desktop/src/components/modals/NativeClientCertificates.vue')['default'] + ModalsNativeClientCertsAdd: typeof import('./../../hoppscotch-selfhost-desktop/src/components/modals/NativeClientCertsAdd.vue')['default'] ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default'] RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default'] RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default'] @@ -188,6 +202,7 @@ declare module 'vue' { RealtimeLogEntry: typeof import('./components/realtime/LogEntry.vue')['default'] RealtimeSubscription: typeof import('./components/realtime/Subscription.vue')['default'] SettingsExtension: typeof import('./components/settings/Extension.vue')['default'] + SettingsNativeInterceptor: typeof import('./../../hoppscotch-selfhost-desktop/src/components/settings/NativeInterceptor.vue')['default'] SettingsProxy: typeof import('./components/settings/Proxy.vue')['default'] Share: typeof import('./components/share/index.vue')['default'] ShareCreateModal: typeof import('./components/share/CreateModal.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/accessTokens/GenerateModal.vue b/packages/hoppscotch-common/src/components/accessTokens/GenerateModal.vue new file mode 100644 index 000000000..b368d7566 --- /dev/null +++ b/packages/hoppscotch-common/src/components/accessTokens/GenerateModal.vue @@ -0,0 +1,221 @@ + + + diff --git a/packages/hoppscotch-common/src/components/accessTokens/List.vue b/packages/hoppscotch-common/src/components/accessTokens/List.vue new file mode 100644 index 000000000..220738cdc --- /dev/null +++ b/packages/hoppscotch-common/src/components/accessTokens/List.vue @@ -0,0 +1,128 @@ + + + diff --git a/packages/hoppscotch-common/src/components/accessTokens/Overview.vue b/packages/hoppscotch-common/src/components/accessTokens/Overview.vue new file mode 100644 index 000000000..0b60e5dff --- /dev/null +++ b/packages/hoppscotch-common/src/components/accessTokens/Overview.vue @@ -0,0 +1,30 @@ + + + diff --git a/packages/hoppscotch-common/src/components/accessTokens/index.vue b/packages/hoppscotch-common/src/components/accessTokens/index.vue new file mode 100644 index 000000000..ef33b5268 --- /dev/null +++ b/packages/hoppscotch-common/src/components/accessTokens/index.vue @@ -0,0 +1,208 @@ + + + diff --git a/packages/hoppscotch-common/src/components/app/Header.vue b/packages/hoppscotch-common/src/components/app/Header.vue index e43782267..07ae5e73e 100644 --- a/packages/hoppscotch-common/src/components/app/Header.vue +++ b/packages/hoppscotch-common/src/components/app/Header.vue @@ -214,7 +214,7 @@ banner.content.value?.content) -let bannerID: number | null = null +let offlineBannerID: number | null = null const offlineBanner: BannerContent = { type: "warning", text: (t) => t("helpers.offline"), alternateText: (t) => t("helpers.offline_short"), - score: BANNER_PRIORITY_HIGH, + score: BANNER_PRIORITY_LOW, dismissible: true, } +// Show the offline banner if the app is offline const network = reactive(useNetwork()) const isOnline = computed(() => network.isOnline) -// Show the offline banner if the user is offline watch(isOnline, () => { if (!isOnline.value) { - bannerID = banner.showBanner(offlineBanner) + offlineBannerID = banner.showBanner(offlineBanner) return } - if (banner.content && bannerID) { - banner.removeBanner(bannerID) + if (banner.content && offlineBannerID) { + banner.removeBanner(offlineBannerID) } }) -const dismissOfflineBanner = () => banner.removeBanner(bannerID!) +const dismissBanner = () => { + if (banner.content.value) { + banner.removeBanner(banner.content.value.id) + } else if (offlineBannerID) { + banner.removeBanner(offlineBannerID) + offlineBannerID = null + } +} const currentUser = useReadonlyStream( platform.auth.getProbableUserStream(), diff --git a/packages/hoppscotch-common/src/components/app/Interceptor.vue b/packages/hoppscotch-common/src/components/app/Interceptor.vue index 427770310..d66e14087 100644 --- a/packages/hoppscotch-common/src/components/app/Interceptor.vue +++ b/packages/hoppscotch-common/src/components/app/Interceptor.vue @@ -1,6 +1,6 @@ @@ -197,26 +238,29 @@ diff --git a/packages/hoppscotch-common/src/components/collections/Request.vue b/packages/hoppscotch-common/src/components/collections/Request.vue index 01e1ba0f4..7e6c6c8bd 100644 --- a/packages/hoppscotch-common/src/components/collections/Request.vue +++ b/packages/hoppscotch-common/src/components/collections/Request.vue @@ -20,7 +20,7 @@ @dragover="handleDragOver($event)" @dragleave="resetDragState" @dragend="resetDragState" - @contextmenu.prevent="options?.tippy.show()" + @contextmenu.prevent="options?.tippy?.show()" >
@@ -211,7 +210,7 @@ const props = defineProps({ default: "my-collections", required: true, }, - duplicateLoading: { + duplicateRequestLoading: { type: Boolean, default: false, required: false, @@ -259,7 +258,7 @@ const emit = defineEmits<{ (event: "update-last-request-order", payload: DataTransfer): void }>() -const tippyActions = ref(null) +const tippyActions = ref(null) const edit = ref(null) const deleteAction = ref(null) const options = ref(null) @@ -277,10 +276,10 @@ const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, { }) watch( - () => props.duplicateLoading, + () => props.duplicateRequestLoading, (val) => { if (!val) { - options.value!.tippy.hide() + options.value!.tippy?.hide() } } ) diff --git a/packages/hoppscotch-common/src/components/collections/Runner.vue b/packages/hoppscotch-common/src/components/collections/Runner.vue new file mode 100644 index 000000000..5ffcdffc0 --- /dev/null +++ b/packages/hoppscotch-common/src/components/collections/Runner.vue @@ -0,0 +1,149 @@ + + + diff --git a/packages/hoppscotch-common/src/components/collections/TeamCollections.vue b/packages/hoppscotch-common/src/components/collections/TeamCollections.vue index 6ed1d2067..861e94efc 100644 --- a/packages/hoppscotch-common/src/components/collections/TeamCollections.vue +++ b/packages/hoppscotch-common/src/components/collections/TeamCollections.vue @@ -61,6 +61,7 @@ :export-loading="exportLoading" :has-no-team-access="hasNoTeamAccess || isShowingSearchResults" :collection-move-loading="collectionMoveLoading" + :duplicate-collection-loading="duplicateCollectionLoading" :is-last-item="node.data.isLastItem" :is-selected=" isSelected({ @@ -89,6 +90,12 @@ collection: node.data.data.data, }) " + @duplicate-collection=" + node.data.type === 'collections' && + emit('duplicate-collection', { + pathOrID: node.data.data.data.id, + }) + " @edit-properties=" node.data.type === 'collections' && emit('edit-properties', { @@ -129,6 +136,7 @@ }) } " + @run-collection="emit('run-collection', $event)" @click=" () => { handleCollectionClick({ @@ -148,6 +156,7 @@ :export-loading="exportLoading" :has-no-team-access="hasNoTeamAccess || isShowingSearchResults" :collection-move-loading="collectionMoveLoading" + :duplicate-collection-loading="duplicateCollectionLoading" :is-last-item="node.data.isLastItem" :is-selected=" isSelected({ @@ -175,6 +184,12 @@ folder: node.data.data.data, }) " + @duplicate-collection=" + node.data.type === 'folders' && + emit('duplicate-collection', { + pathOrID: node.data.data.data.id, + }) + " @edit-properties=" node.data.type === 'folders' && emit('edit-properties', { @@ -218,6 +233,7 @@ }) } " + @run-collection="emit('run-collection', $event)" @click=" () => { handleCollectionClick({ @@ -234,7 +250,7 @@ :request-i-d="node.data.data.data.id" :parent-i-d="node.data.data.parentIndex" :collections-type="collectionsType.type" - :duplicate-loading="duplicateLoading" + :duplicate-request-loading="duplicateRequestLoading" :is-active="isActiveRequest(node.data.data.data.id)" :has-no-team-access="hasNoTeamAccess || isShowingSearchResults" :request-move-loading="requestMoveLoading" @@ -443,7 +459,12 @@ const props = defineProps({ default: false, required: false, }, - duplicateLoading: { + duplicateRequestLoading: { + type: Boolean, + default: false, + required: false, + }, + duplicateCollectionLoading: { type: Boolean, default: false, required: false, @@ -495,6 +516,13 @@ const emit = defineEmits<{ folder: TeamCollection } ): void + ( + event: "duplicate-collection", + payload: { + pathOrID: string + collectionSyncID?: string + } + ): void ( event: "edit-properties", payload: { @@ -586,6 +614,7 @@ const emit = defineEmits<{ (event: "expand-team-collection", payload: string): void (event: "display-modal-add"): void (event: "display-modal-import-export"): void + (event: "run-collection", collectionID: string): void }>() const getPath = (path: string) => { diff --git a/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue b/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue index 02f62c507..5fa4081ab 100644 --- a/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/graphql/Collection.vue @@ -73,7 +73,13 @@ @keyup.r="requestAction.$el.click()" @keyup.n="folderAction.$el.click()" @keyup.e="edit.$el.click()" + @keyup.d=" + showDuplicateCollectionAction + ? duplicateAction.$el.click() + : null + " @keyup.delete="deleteAction.$el.click()" + @keyup.p="propertiesAction.$el.click()" @keyup.escape="hide()" > + -import { computed, ref } from "vue" -import IconCheckCircle from "~icons/lucide/check-circle" -import IconFolder from "~icons/lucide/folder" -import IconFolderOpen from "~icons/lucide/folder-open" -import IconFilePlus from "~icons/lucide/file-plus" -import IconFolderPlus from "~icons/lucide/folder-plus" -import IconMoreVertical from "~icons/lucide/more-vertical" -import IconEdit from "~icons/lucide/edit" -import IconTrash2 from "~icons/lucide/trash-2" -import IconSettings2 from "~icons/lucide/settings-2" -import { useToast } from "@composables/toast" import { useI18n } from "@composables/i18n" import { useColorMode } from "@composables/theming" -import { removeGraphqlCollection } from "~/newstore/collections" -import { Picked } from "~/helpers/types/HoppPicked" -import { useService } from "dioc/vue" -import { GQLTabService } from "~/services/tab/graphql" +import { useToast } from "@composables/toast" import { HoppCollection } from "@hoppscotch/data" +import { useService } from "dioc/vue" +import { computed, ref } from "vue" +import { useReadonlyStream } from "~/composables/stream" +import { Picked } from "~/helpers/types/HoppPicked" +import { removeGraphqlCollection } from "~/newstore/collections" +import { platform } from "~/platform" +import { GQLTabService } from "~/services/tab/graphql" +import IconCheckCircle from "~icons/lucide/check-circle" +import IconCopy from "~icons/lucide/copy" +import IconEdit from "~icons/lucide/edit" +import IconFilePlus from "~icons/lucide/file-plus" +import IconFolder from "~icons/lucide/folder" +import IconFolderOpen from "~icons/lucide/folder-open" +import IconFolderPlus from "~icons/lucide/folder-plus" +import IconMoreVertical from "~icons/lucide/more-vertical" +import IconSettings2 from "~icons/lucide/settings-2" +import IconTrash2 from "~icons/lucide/trash-2" const props = defineProps<{ picked: Picked | null @@ -271,6 +297,13 @@ const emit = defineEmits<{ (e: "add-request", i: any): void (e: "add-folder", i: any): void (e: "edit-folder", i: any): void + ( + e: "duplicate-collection", + payload: { + path: string + collectionSyncID?: string + } + ): void ( e: "edit-properties", payload: { @@ -296,13 +329,20 @@ const options = ref(null) const requestAction = ref(null) const folderAction = ref(null) const edit = ref(null) +const duplicateAction = ref(null) const deleteAction = ref(null) +const propertiesAction = ref(null) const showChildren = ref(false) const dragging = ref(false) const confirmRemove = ref(false) +const currentUser = useReadonlyStream( + platform.auth.getCurrentUserStream(), + platform.auth.getCurrentUser() +) + const isSelected = computed( () => props.picked?.pickedType === "gql-my-collection" && @@ -315,6 +355,17 @@ const collectionIcon = computed(() => { return IconFolder }) +const showDuplicateCollectionAction = computed(() => { + // Show if the user is not logged in + if (!currentUser.value) { + return true + } + + // Duplicate collection action is disabled on SH until the issue with syncing is resolved + return !platform.platformFeatureFlags + .duplicateCollectionDisabledInPersonalWorkspace +}) + const pick = () => { emit("select", { pickedType: "gql-my-collection", diff --git a/packages/hoppscotch-common/src/components/collections/graphql/EditRequest.vue b/packages/hoppscotch-common/src/components/collections/graphql/EditRequest.vue index 38c087df9..4872cd0a7 100644 --- a/packages/hoppscotch-common/src/components/collections/graphql/EditRequest.vue +++ b/packages/hoppscotch-common/src/components/collections/graphql/EditRequest.vue @@ -6,13 +6,28 @@ @close="hideModal" > diff --git a/packages/hoppscotch-common/src/components/embeds/Request.vue b/packages/hoppscotch-common/src/components/embeds/Request.vue new file mode 100644 index 000000000..3fb78ff07 --- /dev/null +++ b/packages/hoppscotch-common/src/components/embeds/Request.vue @@ -0,0 +1,185 @@ + + + diff --git a/packages/hoppscotch-common/src/components/embeds/index.vue b/packages/hoppscotch-common/src/components/embeds/index.vue index adbcedf78..3aed03f21 100644 --- a/packages/hoppscotch-common/src/components/embeds/index.vue +++ b/packages/hoppscotch-common/src/components/embeds/index.vue @@ -1,105 +1,34 @@ diff --git a/packages/hoppscotch-common/src/components/environments/Properties.vue b/packages/hoppscotch-common/src/components/environments/Properties.vue new file mode 100644 index 000000000..ede8f811c --- /dev/null +++ b/packages/hoppscotch-common/src/components/environments/Properties.vue @@ -0,0 +1,122 @@ + + + diff --git a/packages/hoppscotch-common/src/components/environments/teams/Environment.vue b/packages/hoppscotch-common/src/components/environments/teams/Environment.vue index 2271a997b..9ed169fa6 100644 --- a/packages/hoppscotch-common/src/components/environments/teams/Environment.vue +++ b/packages/hoppscotch-common/src/components/environments/teams/Environment.vue @@ -40,7 +40,8 @@ @keyup.d="duplicate!.$el.click()" @keyup.j="exportAsJsonEl!.$el.click()" @keyup.delete="deleteAction!.$el.click()" - @keyup.escape="options!.tippy().hide()" + @keyup.p="propertiesAction!.$el.click()" + @keyup.escape="options!.tippy?.hide()" > +
@@ -108,26 +121,28 @@ - - diff --git a/packages/hoppscotch-common/src/components/http/Codegen.vue b/packages/hoppscotch-common/src/components/http/Codegen.vue new file mode 100644 index 000000000..9d490640d --- /dev/null +++ b/packages/hoppscotch-common/src/components/http/Codegen.vue @@ -0,0 +1,253 @@ + + + diff --git a/packages/hoppscotch-common/src/components/http/CodegenModal.vue b/packages/hoppscotch-common/src/components/http/CodegenModal.vue index 510e00ae8..b19cb0eb9 100644 --- a/packages/hoppscotch-common/src/components/http/CodegenModal.vue +++ b/packages/hoppscotch-common/src/components/http/CodegenModal.vue @@ -6,110 +6,7 @@ @close="hideModal" > diff --git a/packages/hoppscotch-common/src/components/http/Headers.vue b/packages/hoppscotch-common/src/components/http/Headers.vue index 19e0436b3..10212f6da 100644 --- a/packages/hoppscotch-common/src/components/http/Headers.vue +++ b/packages/hoppscotch-common/src/components/http/Headers.vue @@ -26,7 +26,7 @@ @click="clearContent()" /> -
+
diff --git a/packages/hoppscotch-common/src/components/http/OAuth2Authorization.vue b/packages/hoppscotch-common/src/components/http/OAuth2Authorization.vue index bdeed0e7e..326d2a878 100644 --- a/packages/hoppscotch-common/src/components/http/OAuth2Authorization.vue +++ b/packages/hoppscotch-common/src/components/http/OAuth2Authorization.vue @@ -302,7 +302,7 @@ const supportedGrantTypes = [ auth.value.grantTypeInfo = { ...auth.value.grantTypeInfo, - clientSecret: value, + clientSecret: value ?? "", } } ) diff --git a/packages/hoppscotch-common/src/components/http/Parameters.vue b/packages/hoppscotch-common/src/components/http/Parameters.vue index 9e8463f11..7e85ef8b3 100644 --- a/packages/hoppscotch-common/src/components/http/Parameters.vue +++ b/packages/hoppscotch-common/src/components/http/Parameters.vue @@ -44,7 +44,7 @@ />
-
+
diff --git a/packages/hoppscotch-common/src/components/http/RawBody.vue b/packages/hoppscotch-common/src/components/http/RawBody.vue index a3bffd95c..07c639cb6 100644 --- a/packages/hoppscotch-common/src/components/http/RawBody.vue +++ b/packages/hoppscotch-common/src/components/http/RawBody.vue @@ -59,7 +59,7 @@ />
-
+
@@ -89,6 +89,7 @@ import { readFileAsText } from "~/helpers/functional/files" import xmlFormat from "xml-formatter" import { useNestedSetting } from "~/composables/settings" import { toggleNestedSetting } from "~/newstore/settings" +import * as LJSON from "lossless-json" type PossibleContentTypes = Exclude< ValidContentTypes, @@ -187,8 +188,8 @@ const prettifyRequestBody = () => { let prettifyBody = "" try { if (body.value.contentType.endsWith("json")) { - const jsonObj = JSON.parse(rawParamsBody.value as string) - prettifyBody = JSON.stringify(jsonObj, null, 2) + const jsonObj = LJSON.parse(rawParamsBody.value as string) + prettifyBody = LJSON.stringify(jsonObj, undefined, 2) as string } else if (body.value.contentType === "application/xml") { prettifyBody = prettifyXML(rawParamsBody.value as string) } diff --git a/packages/hoppscotch-common/src/components/http/RequestVariables.vue b/packages/hoppscotch-common/src/components/http/RequestVariables.vue index 68bc8ec39..352e390b5 100644 --- a/packages/hoppscotch-common/src/components/http/RequestVariables.vue +++ b/packages/hoppscotch-common/src/components/http/RequestVariables.vue @@ -21,7 +21,7 @@ @click="clearContent()" /> -
+
diff --git a/packages/hoppscotch-common/src/components/http/ResponseMeta.vue b/packages/hoppscotch-common/src/components/http/ResponseMeta.vue index 3cf5513bc..dcfbe0946 100644 --- a/packages/hoppscotch-common/src/components/http/ResponseMeta.vue +++ b/packages/hoppscotch-common/src/components/http/ResponseMeta.vue @@ -143,7 +143,8 @@ const readableResponseSize = computed(() => { props.response.type === "loading" || props.response.type === "network_fail" || props.response.type === "script_fail" || - props.response.type === "fail" + props.response.type === "fail" || + props.response.type === "extension_error" ) return undefined @@ -162,7 +163,8 @@ const statusCategory = computed(() => { props.response.type === "loading" || props.response.type === "network_fail" || props.response.type === "script_fail" || - props.response.type === "fail" + props.response.type === "fail" || + props.response.type === "extension_error" ) return { name: "error", diff --git a/packages/hoppscotch-common/src/components/http/Sidebar.vue b/packages/hoppscotch-common/src/components/http/Sidebar.vue index a29e8b518..5d5b65835 100644 --- a/packages/hoppscotch-common/src/components/http/Sidebar.vue +++ b/packages/hoppscotch-common/src/components/http/Sidebar.vue @@ -33,6 +33,24 @@ > + +
+ {{ t("request.title") }} + + {{ t("tab.code_snippet") }} +
+ +
@@ -41,12 +59,18 @@ import IconClock from "~icons/lucide/clock" import IconLayers from "~icons/lucide/layers" import IconFolder from "~icons/lucide/folder" import IconShare2 from "~icons/lucide/share-2" +import IconCode from "~icons/lucide/code" import { ref } from "vue" import { useI18n } from "@composables/i18n" const t = useI18n() -type RequestOptionTabs = "history" | "collections" | "env" +type RequestOptionTabs = + | "history" + | "collections" + | "env" + | "share-request" + | "codegen" const selectedNavigationTab = ref("collections") diff --git a/packages/hoppscotch-common/src/components/http/URLEncodedParams.vue b/packages/hoppscotch-common/src/components/http/URLEncodedParams.vue index 5c9139a75..1048b6b54 100644 --- a/packages/hoppscotch-common/src/components/http/URLEncodedParams.vue +++ b/packages/hoppscotch-common/src/components/http/URLEncodedParams.vue @@ -44,7 +44,7 @@ />
-
+
diff --git a/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue b/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue index 3a9ac7f7d..4c74a4ef0 100644 --- a/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue +++ b/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue @@ -8,7 +8,7 @@
${getSpecialKey()}ShiftP`" :icon="!previewEnabled ? IconEye : IconEyeOff" - @click.prevent="togglePreview" + @click.prevent="doTogglePreview" />
-
-
+
+