Compare commits

..

47 Commits

Author SHA1 Message Date
nivedin
fa8c7a9f35 fix: ancestor header not updating 2023-12-13 22:35:44 +05:30
nivedin
56f427740d fix: use remaining gql request items when sync 2023-12-13 22:35:44 +05:30
nivedin
e4eaa2cec9 refactor: update rest-gql import-export sync 2023-12-13 22:35:44 +05:30
nivedin
8bb628354e chore: remove console log 2023-12-13 22:35:44 +05:30
nivedin
b3bef85b87 fix: inherit auth-headers closest to path bug 2023-12-13 22:35:44 +05:30
nivedin
80d54565eb chore: add toast for personal collection 2023-12-13 22:35:44 +05:30
Andrew Bastin
9e7f1b58b9 fix: type checking issues for recursive types 2023-12-13 22:35:44 +05:30
nivedin
b07d0c69cc chore: update opened team coll tab and toast added 2023-12-13 22:35:44 +05:30
nivedin
00ff318194 refactor: change the default req auth type to none 2023-12-13 22:35:44 +05:30
nivedin
b5c1fe4619 chore: update type 2023-12-13 22:35:44 +05:30
nivedin
8e72ab44a6 fix: update hopp-data coll header type 2023-12-13 22:35:44 +05:30
nivedin
987d1a74d1 fix: inherit auth from ancestor bug 2023-12-13 22:35:44 +05:30
nivedin
d5d4e51848 fix: header component bug when auth api key undefined 2023-12-13 22:35:44 +05:30
nivedin
509775878a refactor: import-export team coll in new schema 2023-12-13 22:35:44 +05:30
nivedin
95953557de refactor: update all hoppCollection type to remove generic pattern 2023-12-13 22:35:44 +05:30
nivedin
9d2b7cc03f refactor: update inherited property schema 2023-12-13 22:35:44 +05:30
nivedin
276d48e3f8 refactor: import validate using version 2 2023-12-13 22:35:44 +05:30
nivedin
6526247869 refactor: update localpersistance schema 2023-12-13 22:35:44 +05:30
nivedin
08b6e0b747 refactor: open request with header and auth in search 2023-12-13 22:35:44 +05:30
nivedin
79a8bc669e refactor: update collection type using zod 2023-12-13 22:35:44 +05:30
nivedin
b6f3b24b9e fix: add auth and headers for old collection 2023-12-13 22:35:44 +05:30
nivedin
16bbfec736 fix: minor change 2023-12-13 22:35:44 +05:30
nivedin
ebe680c596 chore: minor padding update 2023-12-13 22:35:44 +05:30
nivedin
f79ab298da chore: update properties banner colour 2023-12-13 22:35:44 +05:30
nivedin
83019733e3 refactor: update gql properties sync 2023-12-13 22:35:44 +05:30
nivedin
c18e801420 refactor: update coll properties when syncing 2023-12-13 22:35:44 +05:30
nivedin
bb8c77fa7a chore: remove console 2023-12-13 22:35:44 +05:30
nivedin
951cff9f30 feat: auth-headers in team collection 2023-12-13 22:35:44 +05:30
nivedin
574d800a12 chore: update properties info banner 2023-12-13 22:35:44 +05:30
nivedin
ed825cf648 chore: add inherited auth and header in req runner 2023-12-13 22:35:44 +05:30
nivedin
569c170fec refactor: update inheritence when moving request 2023-12-13 22:35:44 +05:30
nivedin
056a8a8719 chore: filter header for non active values 2023-12-13 22:35:44 +05:30
nivedin
9bfe195253 chore: use samrt-env for gql headers 2023-12-13 22:35:44 +05:30
nivedin
fce68de282 feat: added inherit auth-header for personal gql 2023-12-13 22:35:44 +05:30
nivedin
ad7b3f05b1 chore: update graphql with ts and setup 2023-12-13 22:35:44 +05:30
nivedin
ea8de655d7 chore: root collection auth issue and ui update 2023-12-13 22:35:44 +05:30
nivedin
369d01a399 chore: add computed header for graphql 2023-12-13 22:35:44 +05:30
nivedin
0a54455fe7 refactor: inherit header from multiple collecions 2023-12-13 22:35:44 +05:30
nivedin
b893607ad1 refactor: inherit properties when saving 2023-12-13 22:35:44 +05:30
nivedin
7c3a84246d refactor: add fallback for new request auth state 2023-12-13 22:35:44 +05:30
nivedin
2687592a56 refactor: add auth and header fields coll in sh 2023-12-13 22:35:44 +05:30
nivedin
a48d7e879f refactor: inherited auth and header for req runner 2023-12-13 22:35:44 +05:30
nivedin
d326063659 chore: update types 2023-12-13 22:35:44 +05:30
nivedin
59735c15e4 refactor: update components to encapsulate 2023-12-13 22:35:44 +05:30
nivedin
ae531f5882 chore: update properties UI flow 2023-12-13 22:35:44 +05:30
nivedin
f8aeb42da5 chore: improve UI flow and add i18n 2023-12-13 22:35:44 +05:30
nivedin
1896e5afe1 feat: added properties option for root collection 2023-12-13 22:35:44 +05:30
464 changed files with 14096 additions and 18226 deletions

5
.gitignore vendored
View File

@@ -81,7 +81,10 @@ web_modules/
# dotenv environment variable files # dotenv environment variable files
.env .env
.env.* .env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache (https://parceljs.org/)
.cache .cache

View File

@@ -239,7 +239,7 @@ Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) f
📦 **Add-ons:** Official add-ons for hoppscotch. 📦 **Add-ons:** Official add-ons for hoppscotch.
- **[Hoppscotch CLI](https://github.com/hoppscotch/hoppscotch/tree/main/packages/hoppscotch-cli)** - Command-line interface for Hoppscotch. - **[Hoppscotch CLI](https://github.com/hoppscotch/hopp-cli)** - Command-line interface for Hoppscotch.
- **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch. - **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch.
- **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that enhance your Hoppscotch experience. - **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that enhance your Hoppscotch experience.

View File

@@ -112,7 +112,7 @@ services:
build: build:
dockerfile: packages/hoppscotch-backend/Dockerfile dockerfile: packages/hoppscotch-backend/Dockerfile
context: . context: .
target: dev target: prod
env_file: env_file:
- ./.env - ./.env
restart: always restart: always
@@ -122,7 +122,7 @@ services:
- PORT=3000 - PORT=3000
volumes: volumes:
# Uncomment the line below when modifying code. Only applicable when using the "dev" target. # Uncomment the line below when modifying code. Only applicable when using the "dev" target.
- ./packages/hoppscotch-backend/:/usr/src/app # - ./packages/hoppscotch-backend/:/usr/src/app
- /usr/src/app/node_modules/ - /usr/src/app/node_modules/
depends_on: depends_on:
hoppscotch-db: hoppscotch-db:

View File

@@ -25,7 +25,6 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^16.2.3", "@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1", "@commitlint/config-conventional": "^16.2.1",
"@hoppscotch/ui": "^0.1.0",
"@types/node": "17.0.27", "@types/node": "17.0.27",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"http-server": "^14.1.1", "http-server": "^14.1.1",

View File

@@ -17,9 +17,9 @@
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"sideEffects": false, "sideEffects": false,
"dependencies": { "dependencies": {
"@codemirror/language": "6.9.3", "@codemirror/language": "6.9.0",
"@lezer/highlight": "1.2.0", "@lezer/highlight": "1.1.4",
"@lezer/lr": "^1.3.14" "@lezer/lr": "^1.3.13"
}, },
"devDependencies": { "devDependencies": {
"@lezer/generator": "^1.5.1", "@lezer/generator": "^1.5.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "hoppscotch-backend", "name": "hoppscotch-backend",
"version": "2023.12.6", "version": "2023.8.4-1",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,
@@ -34,14 +34,12 @@
"@nestjs/jwt": "^10.1.1", "@nestjs/jwt": "^10.1.1",
"@nestjs/passport": "^10.0.2", "@nestjs/passport": "^10.0.2",
"@nestjs/platform-express": "^10.2.6", "@nestjs/platform-express": "^10.2.6",
"@nestjs/schedule": "^4.0.1",
"@nestjs/throttler": "^5.0.0", "@nestjs/throttler": "^5.0.0",
"@prisma/client": "^5.8.0", "@prisma/client": "^4.16.2",
"argon2": "^0.30.3", "argon2": "^0.30.3",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"cookie": "^0.5.0", "cookie": "^0.5.0",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cron": "^3.1.6",
"express": "^4.17.1", "express": "^4.17.1",
"express-session": "^1.17.3", "express-session": "^1.17.3",
"fp-ts": "^2.13.1", "fp-ts": "^2.13.1",
@@ -59,8 +57,7 @@
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"passport-microsoft": "^1.0.0", "passport-microsoft": "^1.0.0",
"posthog-node": "^3.6.3", "prisma": "^4.16.2",
"prisma": "^5.8.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^7.6.0" "rxjs": "^7.6.0"

View File

@@ -1,17 +0,0 @@
-- AlterTable
ALTER TABLE
"TeamCollection"
ADD
titleSearch tsvector GENERATED ALWAYS AS (to_tsvector('english', title)) STORED;
-- AlterTable
ALTER TABLE
"TeamRequest"
ADD
titleSearch tsvector GENERATED ALWAYS AS (to_tsvector('english', title)) STORED;
-- CreateIndex
CREATE INDEX "TeamCollection_textSearch_idx" ON "TeamCollection" USING GIN (titleSearch);
-- CreateIndex
CREATE INDEX "TeamRequest_textSearch_idx" ON "TeamRequest" USING GIN (titleSearch);

View File

@@ -41,31 +41,31 @@ model TeamInvitation {
} }
model TeamCollection { model TeamCollection {
id String @id @default(cuid()) id String @id @default(cuid())
parentID String? parentID String?
data Json? data Json?
parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id]) parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id])
children TeamCollection[] @relation("TeamCollectionChildParent") children TeamCollection[] @relation("TeamCollectionChildParent")
requests TeamRequest[] requests TeamRequest[]
teamID String teamID String
team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) team Team @relation(fields: [teamID], references: [id], onDelete: Cascade)
title String title String
orderIndex Int orderIndex Int
createdOn DateTime @default(now()) @db.Timestamp(3) createdOn DateTime @default(now()) @db.Timestamp(3)
updatedOn DateTime @updatedAt @db.Timestamp(3) updatedOn DateTime @updatedAt @db.Timestamp(3)
} }
model TeamRequest { model TeamRequest {
id String @id @default(cuid()) id String @id @default(cuid())
collectionID String collectionID String
collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade) collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade)
teamID String teamID String
team Team @relation(fields: [teamID], references: [id], onDelete: Cascade) team Team @relation(fields: [teamID], references: [id], onDelete: Cascade)
title String title String
request Json request Json
orderIndex Int orderIndex Int
createdOn DateTime @default(now()) @db.Timestamp(3) createdOn DateTime @default(now()) @db.Timestamp(3)
updatedOn DateTime @updatedAt @db.Timestamp(3) updatedOn DateTime @updatedAt @db.Timestamp(3)
} }
model Shortcode { model Shortcode {

View File

@@ -27,7 +27,9 @@ import {
} from './input-types.args'; } from './input-types.args';
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
import { SkipThrottle } from '@nestjs/throttler'; import { SkipThrottle } from '@nestjs/throttler';
import { UserDeletionResult } from 'src/user/user.model'; import { User } from 'src/user/user.model';
import { PaginationArgs } from 'src/types/input-types.args';
import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
@UseGuards(GqlThrottlerGuard) @UseGuards(GqlThrottlerGuard)
@Resolver(() => Admin) @Resolver(() => Admin)
@@ -47,6 +49,203 @@ export class AdminResolver {
return admin; return admin;
} }
@ResolveField(() => [User], {
description: 'Returns a list of all admin users in infra',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async admins() {
const admins = await this.adminService.fetchAdmins();
return admins;
}
@ResolveField(() => User, {
description: 'Returns a user info by UID',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async userInfo(
@Args({
name: 'userUid',
type: () => ID,
description: 'The user UID',
})
userUid: string,
): Promise<AuthUser> {
const user = await this.adminService.fetchUserInfo(userUid);
if (E.isLeft(user)) throwErr(user.left);
return user.right;
}
@ResolveField(() => [User], {
description: 'Returns a list of all the users in infra',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsers(
@Parent() admin: Admin,
@Args() args: PaginationArgs,
): Promise<AuthUser[]> {
const users = await this.adminService.fetchUsers(args.cursor, args.take);
return users;
}
@ResolveField(() => [InvitedUser], {
description: 'Returns a list of all the invited users',
deprecationReason: 'Use `infra` query instead',
})
async invitedUsers(@Parent() admin: Admin): Promise<InvitedUser[]> {
const users = await this.adminService.fetchInvitedUsers();
return users;
}
@ResolveField(() => [Team], {
description: 'Returns a list of all the teams in the infra',
deprecationReason: 'Use `infra` query instead',
})
async allTeams(
@Parent() admin: Admin,
@Args() args: PaginationArgs,
): Promise<Team[]> {
const teams = await this.adminService.fetchAllTeams(args.cursor, args.take);
return teams;
}
@ResolveField(() => Team, {
description: 'Returns a team info by ID when requested by Admin',
deprecationReason: 'Use `infra` query instead',
})
async teamInfo(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which info to fetch',
})
teamID: string,
): Promise<Team> {
const team = await this.adminService.getTeamInfo(teamID);
if (E.isLeft(team)) throwErr(team.left);
return team.right;
}
@ResolveField(() => Number, {
description: 'Return count of all the members in a team',
deprecationReason: 'Use `infra` query instead',
})
async membersCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
nullable: false,
})
teamID: string,
): Promise<number> {
const teamMembersCount = await this.adminService.membersCountInTeam(teamID);
return teamMembersCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored collections in a team',
deprecationReason: 'Use `infra` query instead',
})
async collectionCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const teamCollCount = await this.adminService.collectionCountInTeam(teamID);
return teamCollCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored requests in a team',
deprecationReason: 'Use `infra` query instead',
})
async requestCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const teamReqCount = await this.adminService.requestCountInTeam(teamID);
return teamReqCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored environments in a team',
deprecationReason: 'Use `infra` query instead',
})
async environmentCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const envsCount = await this.adminService.environmentCountInTeam(teamID);
return envsCount;
}
@ResolveField(() => [TeamInvitation], {
description: 'Return all the pending invitations in a team',
deprecationReason: 'Use `infra` query instead',
})
async pendingInvitationCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
) {
const invitations = await this.adminService.pendingInvitationCountInTeam(
teamID,
);
return invitations;
}
@ResolveField(() => Number, {
description: 'Return total number of Users in organization',
deprecationReason: 'Use `infra` query instead',
})
async usersCount() {
return this.adminService.getUsersCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Teams in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamsCount() {
return this.adminService.getTeamsCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Team Collections in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamCollectionsCount() {
return this.adminService.getTeamCollectionsCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Team Requests in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamRequestsCount() {
return this.adminService.getTeamRequestsCount();
}
/* Mutations */ /* Mutations */
@Mutation(() => InvitedUser, { @Mutation(() => InvitedUser, {
@@ -70,26 +269,8 @@ export class AdminResolver {
return invitedUser.right; return invitedUser.right;
} }
@Mutation(() => Boolean, {
description: 'Revoke a user invites by invitee emails',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async revokeUserInvitationsByAdmin(
@Args({
name: 'inviteeEmails',
description: 'Invitee Emails',
type: () => [String],
})
inviteeEmails: string[],
): Promise<boolean> {
const invite = await this.adminService.revokeUserInvitations(inviteeEmails);
if (E.isLeft(invite)) throwErr(invite.left);
return invite.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Delete an user account from infra', description: 'Delete an user account from infra',
deprecationReason: 'Use removeUsersByAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUserByAdmin( async removeUserByAdmin(
@@ -100,33 +281,12 @@ export class AdminResolver {
}) })
userUID: string, userUID: string,
): Promise<boolean> { ): Promise<boolean> {
const removedUser = await this.adminService.removeUserAccount(userUID); const invitedUser = await this.adminService.removeUserAccount(userUID);
if (E.isLeft(removedUser)) throwErr(removedUser.left); if (E.isLeft(invitedUser)) throwErr(invitedUser.left);
return removedUser.right; return invitedUser.right;
} }
@Mutation(() => [UserDeletionResult], {
description: 'Delete user accounts from infra',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUsersByAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<UserDeletionResult[]> {
const deletionResults = await this.adminService.removeUserAccounts(
userUIDs,
);
if (E.isLeft(deletionResults)) throwErr(deletionResults.left);
return deletionResults.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Make user an admin', description: 'Make user an admin',
deprecationReason: 'Use makeUsersAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async makeUserAdmin( async makeUserAdmin(
@@ -142,51 +302,8 @@ export class AdminResolver {
return admin.right; return admin.right;
} }
@Mutation(() => Boolean, {
description: 'Make users an admin',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async makeUsersAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<boolean> {
const isUpdated = await this.adminService.makeUsersAdmin(userUIDs);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Boolean, {
description: 'Update user display name',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async updateUserDisplayNameByAdmin(
@Args({
name: 'userUID',
description: 'users UID',
type: () => ID,
})
userUID: string,
@Args({
name: 'displayName',
description: 'users display name',
})
displayName: string,
): Promise<boolean> {
const isUpdated = await this.adminService.updateUserDisplayName(
userUID,
displayName,
);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Remove user as admin', description: 'Remove user as admin',
deprecationReason: 'Use demoteUsersByAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUserAsAdmin( async removeUserAsAdmin(
@@ -202,23 +319,6 @@ export class AdminResolver {
return admin.right; return admin.right;
} }
@Mutation(() => Boolean, {
description: 'Remove users as admin',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async demoteUsersByAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<boolean> {
const isUpdated = await this.adminService.demoteUsersByAdmin(userUIDs);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Team, { @Mutation(() => Team, {
description: description:
'Create a new team by providing the user uid to nominate as Team owner', 'Create a new team by providing the user uid to nominate as Team owner',

View File

@@ -1,7 +1,7 @@
import { AdminService } from './admin.service'; import { AdminService } from './admin.service';
import { PubSubService } from '../pubsub/pubsub.service'; import { PubSubService } from '../pubsub/pubsub.service';
import { mockDeep } from 'jest-mock-extended'; import { mockDeep } from 'jest-mock-extended';
import { InvitedUsers, User as DbUser } from '@prisma/client'; import { InvitedUsers } from '@prisma/client';
import { UserService } from '../user/user.service'; import { UserService } from '../user/user.service';
import { TeamService } from '../team/team.service'; import { TeamService } from '../team/team.service';
import { TeamEnvironmentsService } from '../team-environments/team-environments.service'; import { TeamEnvironmentsService } from '../team-environments/team-environments.service';
@@ -13,15 +13,10 @@ import { PrismaService } from 'src/prisma/prisma.service';
import { import {
DUPLICATE_EMAIL, DUPLICATE_EMAIL,
INVALID_EMAIL, INVALID_EMAIL,
ONLY_ONE_ADMIN_ACCOUNT,
USER_ALREADY_INVITED, USER_ALREADY_INVITED,
USER_INVITATION_DELETION_FAILED,
USER_NOT_FOUND,
} from '../errors'; } from '../errors';
import { ShortcodeService } from 'src/shortcode/shortcode.service'; import { ShortcodeService } from 'src/shortcode/shortcode.service';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
import * as E from 'fp-ts/Either';
const mockPrisma = mockDeep<PrismaService>(); const mockPrisma = mockDeep<PrismaService>();
const mockPubSub = mockDeep<PubSubService>(); const mockPubSub = mockDeep<PubSubService>();
@@ -63,87 +58,20 @@ const invitedUsers: InvitedUsers[] = [
invitedOn: new Date(), invitedOn: new Date(),
}, },
]; ];
const dbAdminUsers: DbUser[] = [
{
uid: 'uid 1',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: true,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
createdOn: new Date(),
},
{
uid: 'uid 2',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: true,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
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('AdminService', () => {
describe('fetchInvitedUsers', () => { describe('fetchInvitedUsers', () => {
test('should resolve right and apply pagination correctly', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
mockPrisma.user.findMany.mockResolvedValue([dbAdminUsers[0]]);
// @ts-ignore
mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers);
const paginationArgs: OffsetPaginationArgs = { take: 5, skip: 2 };
const results = await adminService.fetchInvitedUsers(paginationArgs);
expect(mockPrisma.invitedUsers.findMany).toHaveBeenCalledWith({
...paginationArgs,
orderBy: {
invitedOn: 'desc',
},
where: {
NOT: {
inviteeEmail: {
in: [dbAdminUsers[0].email],
},
},
},
});
});
test('should resolve right and return an array of invited users', async () => { test('should resolve right and return an array of invited users', async () => {
const paginationArgs: OffsetPaginationArgs = { take: 10, skip: 0 };
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
mockPrisma.user.findMany.mockResolvedValue([dbAdminUsers[0]]);
// @ts-ignore
mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers); mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers);
const results = await adminService.fetchInvitedUsers(paginationArgs); const results = await adminService.fetchInvitedUsers();
expect(results).toEqual(invitedUsers); expect(results).toEqual(invitedUsers);
}); });
test('should resolve left and return an empty array if invited users not found', async () => { test('should resolve left and return an empty array if invited users not found', async () => {
const paginationArgs: OffsetPaginationArgs = { take: 10, skip: 0 };
mockPrisma.invitedUsers.findMany.mockResolvedValue([]); mockPrisma.invitedUsers.findMany.mockResolvedValue([]);
const results = await adminService.fetchInvitedUsers(paginationArgs); const results = await adminService.fetchInvitedUsers();
expect(results).toEqual([]); expect(results).toEqual([]);
}); });
}); });
@@ -206,58 +134,6 @@ describe('AdminService', () => {
}); });
}); });
describe('revokeUserInvitations', () => {
test('should resolve left and return error if email not invited', async () => {
mockPrisma.invitedUsers.deleteMany.mockRejectedValueOnce(
'RecordNotFound',
);
const result = await adminService.revokeUserInvitations([
'test@gmail.com',
]);
expect(result).toEqualLeft(USER_INVITATION_DELETION_FAILED);
});
test('should resolve right and return deleted invitee email', async () => {
const adminUid = 'adminUid';
mockPrisma.invitedUsers.deleteMany.mockResolvedValueOnce({ count: 1 });
const result = await adminService.revokeUserInvitations([
invitedUsers[0].inviteeEmail,
]);
expect(mockPrisma.invitedUsers.deleteMany).toHaveBeenCalledWith({
where: {
inviteeEmail: { in: [invitedUsers[0].inviteeEmail] },
},
});
expect(result).toEqualRight(true);
});
});
describe('removeUsersAsAdmin', () => {
test('should resolve right and make admins to users', async () => {
mockUserService.fetchAdminUsers.mockResolvedValueOnce(dbAdminUsers);
mockUserService.removeUsersAsAdmin.mockResolvedValueOnce(E.right(true));
return expect(
await adminService.demoteUsersByAdmin([dbAdminUsers[0].uid]),
).toEqualRight(true);
});
test('should resolve left and return error if only one admin in the infra', async () => {
mockUserService.fetchAdminUsers.mockResolvedValueOnce(dbAdminUsers);
mockUserService.removeUsersAsAdmin.mockResolvedValueOnce(E.right(true));
return expect(
await adminService.demoteUsersByAdmin(
dbAdminUsers.map((user) => user.uid),
),
).toEqualLeft(ONLY_ONE_ADMIN_ACCOUNT);
});
});
describe('getUsersCount', () => { describe('getUsersCount', () => {
test('should return count of all users in the organization', async () => { test('should return count of all users in the organization', async () => {
mockUserService.getUsersCount.mockResolvedValueOnce(10); mockUserService.getUsersCount.mockResolvedValueOnce(10);

View File

@@ -6,16 +6,13 @@ import * as E from 'fp-ts/Either';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import { validateEmail } from '../utils'; import { validateEmail } from '../utils';
import { import {
ADMIN_CAN_NOT_BE_DELETED,
DUPLICATE_EMAIL, DUPLICATE_EMAIL,
EMAIL_FAILED, EMAIL_FAILED,
INVALID_EMAIL, INVALID_EMAIL,
ONLY_ONE_ADMIN_ACCOUNT, ONLY_ONE_ADMIN_ACCOUNT,
TEAM_INVITE_ALREADY_MEMBER, TEAM_INVITE_ALREADY_MEMBER,
TEAM_INVITE_NO_INVITE_FOUND, TEAM_INVITE_NO_INVITE_FOUND,
USERS_NOT_FOUND,
USER_ALREADY_INVITED, USER_ALREADY_INVITED,
USER_INVITATION_DELETION_FAILED,
USER_IS_ADMIN, USER_IS_ADMIN,
USER_NOT_FOUND, USER_NOT_FOUND,
} from '../errors'; } from '../errors';
@@ -29,8 +26,6 @@ import { TeamInvitationService } from '../team-invitation/team-invitation.servic
import { TeamMemberRole } from '../team/team.model'; import { TeamMemberRole } from '../team/team.model';
import { ShortcodeService } from 'src/shortcode/shortcode.service'; import { ShortcodeService } from 'src/shortcode/shortcode.service';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
import { UserDeletionResult } from 'src/user/user.model';
@Injectable() @Injectable()
export class AdminService { export class AdminService {
@@ -53,30 +48,12 @@ export class AdminService {
* @param cursorID Users uid * @param cursorID Users uid
* @param take number of users to fetch * @param take number of users to fetch
* @returns an Either of array of user or error * @returns an Either of array of user or error
* @deprecated use fetchUsersV2 instead
*/ */
async fetchUsers(cursorID: string, take: number) { async fetchUsers(cursorID: string, take: number) {
const allUsers = await this.userService.fetchAllUsers(cursorID, take); const allUsers = await this.userService.fetchAllUsers(cursorID, take);
return allUsers; return allUsers;
} }
/**
* Fetch all the users in the infra.
* @param searchString search on users displayName or email
* @param paginationOption pagination options
* @returns an Either of array of user or error
*/
async fetchUsersV2(
searchString: string,
paginationOption: OffsetPaginationArgs,
) {
const allUsers = await this.userService.fetchAllUsersV2(
searchString,
paginationOption,
);
return allUsers;
}
/** /**
* Invite a user to join the infra. * Invite a user to join the infra.
* @param adminUID Admin's UID * @param adminUID Admin's UID
@@ -133,68 +110,14 @@ export class AdminService {
return E.right(invitedUser); return E.right(invitedUser);
} }
/**
* Update the display name of a user
* @param userUid Who's display name is being updated
* @param displayName New display name of the user
* @returns an Either of boolean or error
*/
async updateUserDisplayName(userUid: string, displayName: string) {
const updatedUser = await this.userService.updateUserDisplayName(
userUid,
displayName,
);
if (E.isLeft(updatedUser)) return E.left(updatedUser.left);
return E.right(true);
}
/**
* Revoke infra level user invitations
* @param inviteeEmails Invitee's emails
* @param adminUid Admin Uid
* @returns an Either of boolean or error string
*/
async revokeUserInvitations(inviteeEmails: string[]) {
try {
await this.prisma.invitedUsers.deleteMany({
where: {
inviteeEmail: { in: inviteeEmails },
},
});
return E.right(true);
} catch (error) {
return E.left(USER_INVITATION_DELETION_FAILED);
}
}
/** /**
* Fetch the list of invited users by the admin. * Fetch the list of invited users by the admin.
* @returns an Either of array of `InvitedUser` object or error * @returns an Either of array of `InvitedUser` object or error
*/ */
async fetchInvitedUsers(paginationOption: OffsetPaginationArgs) { async fetchInvitedUsers() {
const userEmailObjs = await this.prisma.user.findMany({ const invitedUsers = await this.prisma.invitedUsers.findMany();
select: {
email: true,
},
});
const pendingInvitedUsers = await this.prisma.invitedUsers.findMany({ const users: InvitedUser[] = invitedUsers.map(
take: paginationOption.take,
skip: paginationOption.skip,
orderBy: {
invitedOn: 'desc',
},
where: {
NOT: {
inviteeEmail: {
in: userEmailObjs.map((user) => user.email),
},
},
},
});
const users: InvitedUser[] = pendingInvitedUsers.map(
(user) => <InvitedUser>{ ...user }, (user) => <InvitedUser>{ ...user },
); );
@@ -414,7 +337,6 @@ export class AdminService {
* Remove a user account by UID * Remove a user account by UID
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use removeUserAccounts instead
*/ */
async removeUserAccount(userUid: string) { async removeUserAccount(userUid: string) {
const user = await this.userService.findUserById(userUid); const user = await this.userService.findUserById(userUid);
@@ -427,73 +349,10 @@ export class AdminService {
return E.right(delUser.right); return E.right(delUser.right);
} }
/**
* Remove user (not Admin) accounts by UIDs
* @param userUIDs User UIDs
* @returns an Either of boolean or error
*/
async removeUserAccounts(userUIDs: string[]) {
const userDeleteResult: UserDeletionResult[] = [];
// step 1: fetch all users
const allUsersList = await this.userService.findUsersByIds(userUIDs);
if (allUsersList.length === 0) return E.left(USERS_NOT_FOUND);
// step 2: admin user can not be deleted without removing admin status/role
allUsersList.forEach((user) => {
if (user.isAdmin) {
userDeleteResult.push({
userUID: user.uid,
isDeleted: false,
errorMessage: ADMIN_CAN_NOT_BE_DELETED,
});
}
});
const nonAdminUsers = allUsersList.filter((user) => !user.isAdmin);
let deletedUserEmails: string[] = [];
// step 3: delete non-admin users
const deletionPromises = nonAdminUsers.map((user) => {
return this.userService
.deleteUserByUID(user)()
.then((res) => {
if (E.isLeft(res)) {
return {
userUID: user.uid,
isDeleted: false,
errorMessage: res.left,
} as UserDeletionResult;
}
deletedUserEmails.push(user.email);
return {
userUID: user.uid,
isDeleted: true,
errorMessage: null,
} as UserDeletionResult;
});
});
const promiseResult = await Promise.allSettled(deletionPromises);
// step 4: revoke all the invites sent to the deleted users
await this.revokeUserInvitations(deletedUserEmails);
// step 5: return the result
promiseResult.forEach((result) => {
if (result.status === 'fulfilled') {
userDeleteResult.push(result.value);
}
});
return E.right(userDeleteResult);
}
/** /**
* Make a user an admin * Make a user an admin
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use makeUsersAdmin instead
*/ */
async makeUserAdmin(userUID: string) { async makeUserAdmin(userUID: string) {
const admin = await this.userService.makeAdmin(userUID); const admin = await this.userService.makeAdmin(userUID);
@@ -501,22 +360,10 @@ export class AdminService {
return E.right(true); return E.right(true);
} }
/**
* Make users to admin
* @param userUid User UIDs
* @returns an Either of boolean or error
*/
async makeUsersAdmin(userUIDs: string[]) {
const isUpdated = await this.userService.makeAdmins(userUIDs);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(true);
}
/** /**
* Remove user as admin * Remove user as admin
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use demoteUsersByAdmin instead
*/ */
async removeUserAsAdmin(userUID: string) { async removeUserAsAdmin(userUID: string) {
const adminUsers = await this.userService.fetchAdminUsers(); const adminUsers = await this.userService.fetchAdminUsers();
@@ -527,26 +374,6 @@ export class AdminService {
return E.right(true); return E.right(true);
} }
/**
* Remove users as admin
* @param userUIDs User UIDs
* @returns an Either of boolean or error
*/
async demoteUsersByAdmin(userUIDs: string[]) {
const adminUsers = await this.userService.fetchAdminUsers();
const remainingAdmins = adminUsers.filter(
(adminUser) => !userUIDs.includes(adminUser.uid),
);
if (remainingAdmins.length < 1) {
return E.left(ONLY_ONE_ADMIN_ACCOUNT);
}
const isUpdated = await this.userService.removeUsersAsAdmin(userUIDs);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(isUpdated.right);
}
/** /**
* Fetch list of all the Users in org * Fetch list of all the Users in org
* @returns number of users in the org * @returns number of users in the org

View File

@@ -1,11 +0,0 @@
import { Injectable, ExecutionContext, CanActivate } from '@nestjs/common';
@Injectable()
export class RESTAdminGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const user = request.user;
return user.isAdmin;
}
}

View File

@@ -17,10 +17,7 @@ import { AuthUser } from 'src/types/AuthUser';
import { throwErr } from 'src/utils'; import { throwErr } from 'src/utils';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { Admin } from './admin.model'; import { Admin } from './admin.model';
import { import { PaginationArgs } from 'src/types/input-types.args';
OffsetPaginationArgs,
PaginationArgs,
} from 'src/types/input-types.args';
import { InvitedUser } from './invited-user.model'; import { InvitedUser } from './invited-user.model';
import { Team } from 'src/team/team.model'; import { Team } from 'src/team/team.model';
import { TeamInvitation } from 'src/team-invitation/team-invitation.model'; import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
@@ -32,8 +29,7 @@ import {
EnableAndDisableSSOArgs, EnableAndDisableSSOArgs,
InfraConfigArgs, InfraConfigArgs,
} from 'src/infra-config/input-args'; } from 'src/infra-config/input-args';
import { InfraConfigEnum } from 'src/types/InfraConfig'; import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
import { ServiceStatus } from 'src/infra-config/helper';
@UseGuards(GqlThrottlerGuard) @UseGuards(GqlThrottlerGuard)
@Resolver(() => Infra) @Resolver(() => Infra)
@@ -80,7 +76,6 @@ export class InfraResolver {
@ResolveField(() => [User], { @ResolveField(() => [User], {
description: 'Returns a list of all the users in infra', description: 'Returns a list of all the users in infra',
deprecationReason: 'Use allUsersV2 instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsers(@Args() args: PaginationArgs): Promise<AuthUser[]> { async allUsers(@Args() args: PaginationArgs): Promise<AuthUser[]> {
@@ -88,33 +83,11 @@ export class InfraResolver {
return users; return users;
} }
@ResolveField(() => [User], {
description: 'Returns a list of all the users in infra',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsersV2(
@Args({
name: 'searchString',
nullable: true,
description: 'Search on users displayName or email',
})
searchString: string,
@Args() paginationOption: OffsetPaginationArgs,
): Promise<AuthUser[]> {
const users = await this.adminService.fetchUsersV2(
searchString,
paginationOption,
);
return users;
}
@ResolveField(() => [InvitedUser], { @ResolveField(() => [InvitedUser], {
description: 'Returns a list of all the invited users', description: 'Returns a list of all the invited users',
}) })
async invitedUsers( async invitedUsers(): Promise<InvitedUser[]> {
@Args() args: OffsetPaginationArgs, const users = await this.adminService.fetchInvitedUsers();
): Promise<InvitedUser[]> {
const users = await this.adminService.fetchInvitedUsers(args);
return users; return users;
} }
@@ -274,10 +247,10 @@ export class InfraResolver {
async infraConfigs( async infraConfigs(
@Args({ @Args({
name: 'configNames', name: 'configNames',
type: () => [InfraConfigEnum], type: () => [InfraConfigEnumForClient],
description: 'Configs to fetch', description: 'Configs to fetch',
}) })
names: InfraConfigEnum[], names: InfraConfigEnumForClient[],
) { ) {
const infraConfigs = await this.infraConfigService.getMany(names); const infraConfigs = await this.infraConfigService.getMany(names);
if (E.isLeft(infraConfigs)) throwErr(infraConfigs.left); if (E.isLeft(infraConfigs)) throwErr(infraConfigs.left);
@@ -311,25 +284,6 @@ export class InfraResolver {
return updatedRes.right; return updatedRes.right;
} }
@Mutation(() => Boolean, {
description: 'Enable or disable analytics collection',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async toggleAnalyticsCollection(
@Args({
name: 'status',
type: () => ServiceStatus,
description: 'Toggle analytics collection',
})
analyticsCollectionStatus: ServiceStatus,
) {
const res = await this.infraConfigService.toggleAnalyticsCollection(
analyticsCollectionStatus,
);
if (E.isLeft(res)) throwErr(res.left);
return res.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Reset Infra Configs with default values (.env)', description: 'Reset Infra Configs with default values (.env)',
}) })
@@ -352,9 +306,7 @@ export class InfraResolver {
}) })
providerInfo: EnableAndDisableSSOArgs[], providerInfo: EnableAndDisableSSOArgs[],
) { ) {
const isUpdated = await this.infraConfigService.enableAndDisableSSO( const isUpdated = await this.infraConfigService.enableAndDisableSSO(providerInfo);
providerInfo,
);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left); if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return true; return true;

View File

@@ -24,8 +24,6 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { InfraConfigModule } from './infra-config/infra-config.module'; import { InfraConfigModule } from './infra-config/infra-config.module';
import { loadInfraConfiguration } from './infra-config/helper'; import { loadInfraConfiguration } from './infra-config/helper';
import { MailerModule } from './mailer/mailer.module'; import { MailerModule } from './mailer/mailer.module';
import { PosthogModule } from './posthog/posthog.module';
import { ScheduleModule } from '@nestjs/schedule';
@Module({ @Module({
imports: [ imports: [
@@ -98,8 +96,6 @@ import { ScheduleModule } from '@nestjs/schedule';
UserCollectionModule, UserCollectionModule,
ShortcodeModule, ShortcodeModule,
InfraConfigModule, InfraConfigModule,
PosthogModule,
ScheduleModule.forRoot(),
], ],
providers: [GQLComplexityPlugin], providers: [GQLComplexityPlugin],
controllers: [AppController], controllers: [AppController],

View File

@@ -18,7 +18,12 @@ import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { GqlUser } from 'src/decorators/gql-user.decorator'; import { GqlUser } from 'src/decorators/gql-user.decorator';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
import { RTCookie } from 'src/decorators/rt-cookie.decorator'; import { RTCookie } from 'src/decorators/rt-cookie.decorator';
import { AuthProvider, authCookieHandler, authProviderCheck } from './helper'; import {
AuthProvider,
authCookieHandler,
authProviderCheck,
throwHTTPErr,
} from './helper';
import { GoogleSSOGuard } from './guards/google-sso.guard'; import { GoogleSSOGuard } from './guards/google-sso.guard';
import { GithubSSOGuard } from './guards/github-sso.guard'; import { GithubSSOGuard } from './guards/github-sso.guard';
import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard'; import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard';
@@ -26,7 +31,6 @@ import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.gua
import { SkipThrottle } from '@nestjs/throttler'; import { SkipThrottle } from '@nestjs/throttler';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@UseGuards(ThrottlerBehindProxyGuard) @UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'auth', version: '1' }) @Controller({ path: 'auth', version: '1' })

View File

@@ -12,10 +12,7 @@ import { GithubStrategy } from './strategies/github.strategy';
import { MicrosoftStrategy } from './strategies/microsoft.strategy'; import { MicrosoftStrategy } from './strategies/microsoft.strategy';
import { AuthProvider, authProviderCheck } from './helper'; import { AuthProvider, authProviderCheck } from './helper';
import { ConfigModule, ConfigService } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { import { loadInfraConfiguration } from 'src/infra-config/helper';
isInfraConfigTablePopulated,
loadInfraConfiguration,
} from 'src/infra-config/helper';
import { InfraConfigModule } from 'src/infra-config/infra-config.module'; import { InfraConfigModule } from 'src/infra-config/infra-config.module';
@Module({ @Module({
@@ -37,11 +34,6 @@ import { InfraConfigModule } from 'src/infra-config/infra-config.module';
}) })
export class AuthModule { export class AuthModule {
static async register() { static async register() {
const isInfraConfigPopulated = await isInfraConfigTablePopulated();
if (!isInfraConfigPopulated) {
return { module: AuthModule };
}
const env = await loadInfraConfiguration(); const env = await loadInfraConfiguration();
const allowedAuthProviders = env.INFRA.VITE_ALLOWED_AUTH_PROVIDERS; const allowedAuthProviders = env.INFRA.VITE_ALLOWED_AUTH_PROVIDERS;

View File

@@ -24,7 +24,7 @@ import {
RefreshTokenPayload, RefreshTokenPayload,
} from 'src/types/AuthTokens'; } from 'src/types/AuthTokens';
import { JwtService } from '@nestjs/jwt'; import { JwtService } from '@nestjs/jwt';
import { RESTError } from 'src/types/RESTError'; import { AuthError } from 'src/types/AuthError';
import { AuthUser, IsAdmin } from 'src/types/AuthUser'; import { AuthUser, IsAdmin } from 'src/types/AuthUser';
import { VerificationToken } from '@prisma/client'; import { VerificationToken } from '@prisma/client';
import { Origin } from './helper'; import { Origin } from './helper';
@@ -117,7 +117,7 @@ export class AuthService {
userUid, userUid,
); );
if (E.isLeft(updatedUser)) if (E.isLeft(updatedUser))
return E.left(<RESTError>{ return E.left(<AuthError>{
message: updatedUser.left, message: updatedUser.left,
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
}); });
@@ -255,7 +255,7 @@ export class AuthService {
*/ */
async verifyMagicLinkTokens( async verifyMagicLinkTokens(
magicLinkIDTokens: VerifyMagicDto, magicLinkIDTokens: VerifyMagicDto,
): Promise<E.Right<AuthTokens> | E.Left<RESTError>> { ): Promise<E.Right<AuthTokens> | E.Left<AuthError>> {
const passwordlessTokens = await this.validatePasswordlessTokens( const passwordlessTokens = await this.validatePasswordlessTokens(
magicLinkIDTokens, magicLinkIDTokens,
); );
@@ -373,7 +373,7 @@ export class AuthService {
if (usersCount === 1) { if (usersCount === 1) {
const elevatedUser = await this.usersService.makeAdmin(user.uid); const elevatedUser = await this.usersService.makeAdmin(user.uid);
if (E.isLeft(elevatedUser)) if (E.isLeft(elevatedUser))
return E.left(<RESTError>{ return E.left(<AuthError>{
message: elevatedUser.left, message: elevatedUser.left,
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
}); });

View File

@@ -1,10 +1,9 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck } from '../helper'; import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@Injectable() @Injectable()
export class GithubSSOGuard extends AuthGuard('github') implements CanActivate { export class GithubSSOGuard extends AuthGuard('github') implements CanActivate {

View File

@@ -1,10 +1,9 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck } from '../helper'; import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@Injectable() @Injectable()
export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate { export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate {

View File

@@ -1,10 +1,9 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck } from '../helper'; import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@Injectable() @Injectable()
export class MicrosoftSSOGuard export class MicrosoftSSOGuard

View File

@@ -1,5 +1,6 @@
import { HttpException, HttpStatus } from '@nestjs/common'; import { HttpException, HttpStatus } from '@nestjs/common';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { AuthError } from 'src/types/AuthError';
import { AuthTokens } from 'src/types/AuthTokens'; import { AuthTokens } from 'src/types/AuthTokens';
import { Response } from 'express'; import { Response } from 'express';
import * as cookie from 'cookie'; import * as cookie from 'cookie';
@@ -24,6 +25,15 @@ export enum AuthProvider {
EMAIL = 'EMAIL', EMAIL = 'EMAIL',
} }
/**
* This function allows throw to be used as an expression
* @param errMessage Message present in the error message
*/
export function throwHTTPErr(errorData: AuthError): never {
const { message, statusCode } = errorData;
throw new HttpException(message, statusCode);
}
/** /**
* Sets and returns the cookies in the response object on successful authentication * Sets and returns the cookies in the response object on successful authentication
* @param res Express Response Object * @param res Express Response Object

View File

@@ -17,8 +17,8 @@ export class GithubStrategy extends PassportStrategy(Strategy) {
super({ super({
clientID: configService.get('INFRA.GITHUB_CLIENT_ID'), clientID: configService.get('INFRA.GITHUB_CLIENT_ID'),
clientSecret: configService.get('INFRA.GITHUB_CLIENT_SECRET'), clientSecret: configService.get('INFRA.GITHUB_CLIENT_SECRET'),
callbackURL: configService.get('INFRA.GITHUB_CALLBACK_URL'), callbackURL: configService.get('GITHUB_CALLBACK_URL'),
scope: [configService.get('INFRA.GITHUB_SCOPE')], scope: [configService.get('GITHUB_SCOPE')],
store: true, store: true,
}); });
} }

View File

@@ -17,8 +17,8 @@ export class GoogleStrategy extends PassportStrategy(Strategy) {
super({ super({
clientID: configService.get('INFRA.GOOGLE_CLIENT_ID'), clientID: configService.get('INFRA.GOOGLE_CLIENT_ID'),
clientSecret: configService.get('INFRA.GOOGLE_CLIENT_SECRET'), clientSecret: configService.get('INFRA.GOOGLE_CLIENT_SECRET'),
callbackURL: configService.get('INFRA.GOOGLE_CALLBACK_URL'), callbackURL: configService.get('GOOGLE_CALLBACK_URL'),
scope: configService.get('INFRA.GOOGLE_SCOPE').split(','), scope: configService.get('GOOGLE_SCOPE').split(','),
passReqToCallback: true, passReqToCallback: true,
store: true, store: true,
}); });

View File

@@ -17,9 +17,9 @@ export class MicrosoftStrategy extends PassportStrategy(Strategy) {
super({ super({
clientID: configService.get('INFRA.MICROSOFT_CLIENT_ID'), clientID: configService.get('INFRA.MICROSOFT_CLIENT_ID'),
clientSecret: configService.get('INFRA.MICROSOFT_CLIENT_SECRET'), clientSecret: configService.get('INFRA.MICROSOFT_CLIENT_SECRET'),
callbackURL: configService.get('INFRA.MICROSOFT_CALLBACK_URL'), callbackURL: configService.get('MICROSOFT_CALLBACK_URL'),
scope: [configService.get('INFRA.MICROSOFT_SCOPE')], scope: [configService.get('MICROSOFT_SCOPE')],
tenant: configService.get('INFRA.MICROSOFT_TENANT'), tenant: configService.get('MICROSOFT_TENANT'),
store: true, store: true,
}); });
} }

View File

@@ -10,14 +10,6 @@ export const DUPLICATE_EMAIL = 'email/both_emails_cannot_be_same' as const;
export const ONLY_ONE_ADMIN_ACCOUNT = export const ONLY_ONE_ADMIN_ACCOUNT =
'admin/only_one_admin_account_found' as const; 'admin/only_one_admin_account_found' as const;
/**
* Admin user can not be deleted
* To delete the admin user, first make the Admin user a normal user
* (AdminService)
*/
export const ADMIN_CAN_NOT_BE_DELETED =
'admin/admin_can_not_be_deleted' as const;
/** /**
* Token Authorization failed (Check 'Authorization' Header) * Token Authorization failed (Check 'Authorization' Header)
* (GqlAuthGuard) * (GqlAuthGuard)
@@ -36,13 +28,6 @@ export const JSON_INVALID = 'json_invalid';
*/ */
export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified'; export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified';
/**
* Auth Provider not specified
* (Auth)
*/
export const AUTH_PROVIDER_NOT_CONFIGURED =
'auth/provider_not_configured_correctly';
/** /**
* Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file * Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file
*/ */
@@ -107,13 +92,6 @@ export const USER_IS_OWNER = 'user/is_owner' as const;
*/ */
export const USER_IS_ADMIN = 'user/is_admin' as const; export const USER_IS_ADMIN = 'user/is_admin' as const;
/**
* User invite deletion failure error due to invitation not found
* (AdminService)
*/
export const USER_INVITATION_DELETION_FAILED =
'user/invitation_deletion_failed' as const;
/** /**
* Teams not found * Teams not found
* (TeamsService) * (TeamsService)
@@ -228,12 +206,6 @@ export const TEAM_COL_NOT_SAME_PARENT =
export const TEAM_COL_SAME_NEXT_COLL = export const TEAM_COL_SAME_NEXT_COLL =
'team_coll/collection_and_next_collection_are_same'; 'team_coll/collection_and_next_collection_are_same';
/**
* Team Collection search failed
* (TeamCollectionService)
*/
export const TEAM_COL_SEARCH_FAILED = 'team_coll/team_collection_search_failed';
/** /**
* Team Collection Re-Ordering Failed * Team Collection Re-Ordering Failed
* (TeamCollectionService) * (TeamCollectionService)
@@ -289,13 +261,6 @@ export const TEAM_NOT_OWNER = 'team_coll/team_not_owner' as const;
export const TEAM_COLL_DATA_INVALID = export const TEAM_COLL_DATA_INVALID =
'team_coll/team_coll_data_invalid' as const; 'team_coll/team_coll_data_invalid' as const;
/**
* Team Collection parent tree generation failed
* (TeamCollectionService)
*/
export const TEAM_COLL_PARENT_TREE_GEN_FAILED =
'team_coll/team_coll_parent_tree_generation_failed';
/** /**
* Tried to perform an action on a request that doesn't accept their member role level * Tried to perform an action on a request that doesn't accept their member role level
* (GqlRequestTeamMemberGuard) * (GqlRequestTeamMemberGuard)
@@ -321,19 +286,6 @@ export const TEAM_REQ_INVALID_TARGET_COLL_ID =
*/ */
export const TEAM_REQ_REORDERING_FAILED = 'team_req/reordering_failed' as const; export const TEAM_REQ_REORDERING_FAILED = 'team_req/reordering_failed' as const;
/**
* Team Request search failed
* (TeamRequestService)
*/
export const TEAM_REQ_SEARCH_FAILED = 'team_req/team_request_search_failed';
/**
* Team Request parent tree generation failed
* (TeamRequestService)
*/
export const TEAM_REQ_PARENT_TREE_GEN_FAILED =
'team_req/team_req_parent_tree_generation_failed';
/** /**
* No Postmark Sender Email defined * No Postmark Sender Email defined
* (AuthService) * (AuthService)
@@ -724,29 +676,9 @@ export const INFRA_CONFIG_RESET_FAILED = 'infra_config/reset_failed' as const;
*/ */
export const INFRA_CONFIG_INVALID_INPUT = 'infra_config/invalid_input' as const; export const INFRA_CONFIG_INVALID_INPUT = 'infra_config/invalid_input' as const;
/**
* Infra Config service (auth provider/mailer/audit logs) not configured
* (InfraConfigService)
*/
export const INFRA_CONFIG_SERVICE_NOT_CONFIGURED =
'infra_config/service_not_configured' as const;
/**
* Infra Config update/fetch operation not allowed
* (InfraConfigService)
*/
export const INFRA_CONFIG_OPERATION_NOT_ALLOWED =
'infra_config/operation_not_allowed';
/** /**
* Error message for when the database table does not exist * Error message for when the database table does not exist
* (InfraConfigService) * (InfraConfigService)
*/ */
export const DATABASE_TABLE_NOT_EXIST = export const DATABASE_TABLE_NOT_EXIST =
'Database migration not found. Please check the documentation for assistance: https://docs.hoppscotch.io/documentation/self-host/community-edition/install-and-build#running-migrations'; 'Database migration not performed. Please check the FAQ for assistance: https://docs.hoppscotch.io/support/faq';
/**
* PostHog client is not initialized
* (InfraConfigService)
*/
export const POSTHOG_CLIENT_NOT_INITIALIZED = 'posthog/client_not_initialized';

View File

@@ -1,44 +1,10 @@
import { AuthProvider } from 'src/auth/helper';
import {
AUTH_PROVIDER_NOT_CONFIGURED,
DATABASE_TABLE_NOT_EXIST,
} from 'src/errors';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { throwErr } from 'src/utils';
import { randomBytes } from 'crypto';
export enum ServiceStatus { export enum ServiceStatus {
ENABLE = 'ENABLE', ENABLE = 'ENABLE',
DISABLE = 'DISABLE', DISABLE = 'DISABLE',
} }
const AuthProviderConfigurations = {
[AuthProvider.GOOGLE]: [
InfraConfigEnum.GOOGLE_CLIENT_ID,
InfraConfigEnum.GOOGLE_CLIENT_SECRET,
InfraConfigEnum.GOOGLE_CALLBACK_URL,
InfraConfigEnum.GOOGLE_SCOPE,
],
[AuthProvider.GITHUB]: [
InfraConfigEnum.GITHUB_CLIENT_ID,
InfraConfigEnum.GITHUB_CLIENT_SECRET,
InfraConfigEnum.GITHUB_CALLBACK_URL,
InfraConfigEnum.GITHUB_SCOPE,
],
[AuthProvider.MICROSOFT]: [
InfraConfigEnum.MICROSOFT_CLIENT_ID,
InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
InfraConfigEnum.MICROSOFT_CALLBACK_URL,
InfraConfigEnum.MICROSOFT_SCOPE,
InfraConfigEnum.MICROSOFT_TENANT,
],
[AuthProvider.EMAIL]: [
InfraConfigEnum.MAILER_SMTP_URL,
InfraConfigEnum.MAILER_ADDRESS_FROM,
],
};
/** /**
* Load environment variables from the database and set them in the process * Load environment variables from the database and set them in the process
* *
@@ -64,125 +30,6 @@ export async function loadInfraConfiguration() {
} }
} }
/**
* Read the default values from .env file and return them as an array
* @returns Array of default infra configs
*/
export async function getDefaultInfraConfigs(): Promise<
{ name: InfraConfigEnum; value: string }[]
> {
const prisma = new PrismaService();
// Prepare rows for 'infra_config' table with default values (from .env) for each 'name'
const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [
{
name: InfraConfigEnum.MAILER_SMTP_URL,
value: process.env.MAILER_SMTP_URL,
},
{
name: InfraConfigEnum.MAILER_ADDRESS_FROM,
value: process.env.MAILER_ADDRESS_FROM,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_ID,
value: process.env.GOOGLE_CLIENT_ID,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_SECRET,
value: process.env.GOOGLE_CLIENT_SECRET,
},
{
name: InfraConfigEnum.GOOGLE_CALLBACK_URL,
value: process.env.GOOGLE_CALLBACK_URL,
},
{
name: InfraConfigEnum.GOOGLE_SCOPE,
value: process.env.GOOGLE_SCOPE,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_ID,
value: process.env.GITHUB_CLIENT_ID,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_SECRET,
value: process.env.GITHUB_CLIENT_SECRET,
},
{
name: InfraConfigEnum.GITHUB_CALLBACK_URL,
value: process.env.GITHUB_CALLBACK_URL,
},
{
name: InfraConfigEnum.GITHUB_SCOPE,
value: process.env.GITHUB_SCOPE,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_ID,
value: process.env.MICROSOFT_CLIENT_ID,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
value: process.env.MICROSOFT_CLIENT_SECRET,
},
{
name: InfraConfigEnum.MICROSOFT_CALLBACK_URL,
value: process.env.MICROSOFT_CALLBACK_URL,
},
{
name: InfraConfigEnum.MICROSOFT_SCOPE,
value: process.env.MICROSOFT_SCOPE,
},
{
name: InfraConfigEnum.MICROSOFT_TENANT,
value: process.env.MICROSOFT_TENANT,
},
{
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: getConfiguredSSOProviders(),
},
{
name: InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION,
value: false.toString(),
},
{
name: InfraConfigEnum.ANALYTICS_USER_ID,
value: generateAnalyticsUserId(),
},
{
name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
value: (await prisma.infraConfig.count()) === 0 ? 'true' : 'false',
},
];
return infraConfigDefaultObjs;
}
/**
* Verify if 'infra_config' table is loaded with all entries
* @returns boolean
*/
export async function isInfraConfigTablePopulated(): Promise<boolean> {
const prisma = new PrismaService();
try {
const dbInfraConfigs = await prisma.infraConfig.findMany();
const infraConfigDefaultObjs = await getDefaultInfraConfigs();
const propsRemainingToInsert = infraConfigDefaultObjs.filter(
(p) => !dbInfraConfigs.find((e) => e.name === p.name),
);
if (propsRemainingToInsert.length > 0) {
console.log(
'Infra Config table is not populated with all entries. Populating now...',
);
return false;
}
return true;
} catch (error) {
return false;
}
}
/** /**
* Stop the app after 5 seconds * Stop the app after 5 seconds
* (Docker will re-start the app) * (Docker will re-start the app)
@@ -195,51 +42,3 @@ export function stopApp() {
process.kill(process.pid, 'SIGTERM'); process.kill(process.pid, 'SIGTERM');
}, 5000); }, 5000);
} }
/**
* Get the configured SSO providers
* @returns Array of configured SSO providers
*/
export function getConfiguredSSOProviders() {
const allowedAuthProviders: string[] =
process.env.VITE_ALLOWED_AUTH_PROVIDERS.split(',');
let configuredAuthProviders: string[] = [];
const addProviderIfConfigured = (provider) => {
const configParameters: string[] = AuthProviderConfigurations[provider];
const isConfigured = configParameters.every((configParameter) => {
return process.env[configParameter];
});
if (isConfigured) configuredAuthProviders.push(provider);
};
allowedAuthProviders.forEach((provider) => addProviderIfConfigured(provider));
if (configuredAuthProviders.length === 0) {
throwErr(AUTH_PROVIDER_NOT_CONFIGURED);
} else if (allowedAuthProviders.length !== configuredAuthProviders.length) {
const unConfiguredAuthProviders = allowedAuthProviders.filter(
(provider) => {
return !configuredAuthProviders.includes(provider);
},
);
console.log(
`${unConfiguredAuthProviders.join(
',',
)} SSO auth provider(s) are not configured properly. Do configure them from Admin Dashboard.`,
);
}
return configuredAuthProviders.join(',');
}
/**
* Generate a hashed valued for analytics
* @returns Generated hashed value
*/
export function generateAnalyticsUserId() {
const hashedUserID = randomBytes(20).toString('hex');
return hashedUserID;
}

View File

@@ -1,47 +0,0 @@
import { Controller, Get, HttpStatus, Put, UseGuards } from '@nestjs/common';
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
import { InfraConfigService } from './infra-config.service';
import * as E from 'fp-ts/Either';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { RESTAdminGuard } from 'src/admin/guards/rest-admin.guard';
import { RESTError } from 'src/types/RESTError';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { throwHTTPErr } from 'src/utils';
@UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'site', version: '1' })
export class SiteController {
constructor(private infraConfigService: InfraConfigService) {}
@Get('setup')
@UseGuards(JwtAuthGuard, RESTAdminGuard)
async fetchSetupInfo() {
const status = await this.infraConfigService.get(
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
);
if (E.isLeft(status))
throwHTTPErr(<RESTError>{
message: status.left,
statusCode: HttpStatus.NOT_FOUND,
});
return status.right;
}
@Put('setup')
@UseGuards(JwtAuthGuard, RESTAdminGuard)
async setSetupAsComplete() {
const res = await this.infraConfigService.update(
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
false.toString(),
false,
);
if (E.isLeft(res))
throwHTTPErr(<RESTError>{
message: res.left,
statusCode: HttpStatus.FORBIDDEN,
});
return res.right;
}
}

View File

@@ -1,6 +1,6 @@
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { AuthProvider } from 'src/auth/helper'; import { AuthProvider } from 'src/auth/helper';
import { InfraConfigEnum } from 'src/types/InfraConfig'; import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
import { ServiceStatus } from './helper'; import { ServiceStatus } from './helper';
@ObjectType() @ObjectType()
@@ -8,7 +8,7 @@ export class InfraConfig {
@Field({ @Field({
description: 'Infra Config Name', description: 'Infra Config Name',
}) })
name: InfraConfigEnum; name: InfraConfigEnumForClient;
@Field({ @Field({
description: 'Infra Config Value', description: 'Infra Config Value',
@@ -16,7 +16,7 @@ export class InfraConfig {
value: string; value: string;
} }
registerEnumType(InfraConfigEnum, { registerEnumType(InfraConfigEnumForClient, {
name: 'InfraConfigEnum', name: 'InfraConfigEnum',
}); });

View File

@@ -1,12 +1,10 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { InfraConfigService } from './infra-config.service'; import { InfraConfigService } from './infra-config.service';
import { PrismaModule } from 'src/prisma/prisma.module'; import { PrismaModule } from 'src/prisma/prisma.module';
import { SiteController } from './infra-config.controller';
@Module({ @Module({
imports: [PrismaModule], imports: [PrismaModule],
providers: [InfraConfigService], providers: [InfraConfigService],
exports: [InfraConfigService], exports: [InfraConfigService],
controllers: [SiteController],
}) })
export class InfraConfigModule {} export class InfraConfigModule {}

View File

@@ -1,16 +1,13 @@
import { mockDeep, mockReset } from 'jest-mock-extended'; import { mockDeep, mockReset } from 'jest-mock-extended';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigService } from './infra-config.service'; import { InfraConfigService } from './infra-config.service';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { import {
INFRA_CONFIG_NOT_FOUND, InfraConfigEnum,
INFRA_CONFIG_OPERATION_NOT_ALLOWED, InfraConfigEnumForClient,
INFRA_CONFIG_UPDATE_FAILED, } from 'src/types/InfraConfig';
} from 'src/errors'; import { INFRA_CONFIG_NOT_FOUND, INFRA_CONFIG_UPDATE_FAILED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import * as helper from './helper'; import * as helper from './helper';
import { InfraConfig as dbInfraConfig } from '@prisma/client';
import { InfraConfig } from './infra-config.model';
const mockPrisma = mockDeep<PrismaService>(); const mockPrisma = mockDeep<PrismaService>();
const mockConfigService = mockDeep<ConfigService>(); const mockConfigService = mockDeep<ConfigService>();
@@ -22,82 +19,12 @@ const infraConfigService = new InfraConfigService(
mockConfigService, mockConfigService,
); );
const INITIALIZED_DATE_CONST = new Date();
const dbInfraConfigs: dbInfraConfig[] = [
{
id: '3',
name: InfraConfigEnum.GOOGLE_CLIENT_ID,
value: 'abcdefghijkl',
active: true,
createdOn: INITIALIZED_DATE_CONST,
updatedOn: INITIALIZED_DATE_CONST,
},
{
id: '4',
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: 'google',
active: true,
createdOn: INITIALIZED_DATE_CONST,
updatedOn: INITIALIZED_DATE_CONST,
},
];
const infraConfigs: InfraConfig[] = [
{
name: dbInfraConfigs[0].name as InfraConfigEnum,
value: dbInfraConfigs[0].value,
},
{
name: dbInfraConfigs[1].name as InfraConfigEnum,
value: dbInfraConfigs[1].value,
},
];
beforeEach(() => { beforeEach(() => {
mockReset(mockPrisma); mockReset(mockPrisma);
}); });
describe('InfraConfigService', () => { describe('InfraConfigService', () => {
describe('update', () => { describe('update', () => {
it('should update the infra config without backend server restart', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true';
mockPrisma.infraConfig.update.mockResolvedValueOnce({
id: '',
name,
value,
active: true,
createdOn: new Date(),
updatedOn: new Date(),
});
jest.spyOn(helper, 'stopApp').mockReturnValueOnce();
const result = await infraConfigService.update(name, value);
expect(helper.stopApp).not.toHaveBeenCalled();
expect(result).toEqualRight({ name, value });
});
it('should update the infra config with backend server restart', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true';
mockPrisma.infraConfig.update.mockResolvedValueOnce({
id: '',
name,
value,
active: true,
createdOn: new Date(),
updatedOn: new Date(),
});
jest.spyOn(helper, 'stopApp').mockReturnValueOnce();
const result = await infraConfigService.update(name, value, true);
expect(helper.stopApp).toHaveBeenCalledTimes(1);
expect(result).toEqualRight({ name, value });
});
it('should update the infra config', async () => { it('should update the infra config', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true'; const value = 'true';
@@ -144,7 +71,7 @@ describe('InfraConfigService', () => {
describe('get', () => { describe('get', () => {
it('should get the infra config', async () => { it('should get the infra config', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID;
const value = 'true'; const value = 'true';
mockPrisma.infraConfig.findUniqueOrThrow.mockResolvedValueOnce({ mockPrisma.infraConfig.findUniqueOrThrow.mockResolvedValueOnce({
@@ -160,7 +87,7 @@ describe('InfraConfigService', () => {
}); });
it('should pass correct params to prisma findUnique', async () => { it('should pass correct params to prisma findUnique', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID;
await infraConfigService.get(name); await infraConfigService.get(name);
@@ -171,7 +98,7 @@ describe('InfraConfigService', () => {
}); });
it('should throw an error if the infra config does not exist', async () => { it('should throw an error if the infra config does not exist', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID;
mockPrisma.infraConfig.findUniqueOrThrow.mockRejectedValueOnce('null'); mockPrisma.infraConfig.findUniqueOrThrow.mockRejectedValueOnce('null');
@@ -179,45 +106,4 @@ describe('InfraConfigService', () => {
expect(result).toEqualLeft(INFRA_CONFIG_NOT_FOUND); expect(result).toEqualLeft(INFRA_CONFIG_NOT_FOUND);
}); });
}); });
describe('getMany', () => {
it('should throw error if any disallowed names are provided', async () => {
const disallowedNames = [InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS];
const result = await infraConfigService.getMany(disallowedNames);
expect(result).toEqualLeft(INFRA_CONFIG_OPERATION_NOT_ALLOWED);
});
it('should resolve right with disallowed names if `checkDisallowed` parameter passed', async () => {
const disallowedNames = [InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS];
const dbInfraConfigResponses = dbInfraConfigs.filter((dbConfig) =>
disallowedNames.includes(dbConfig.name as InfraConfigEnum),
);
mockPrisma.infraConfig.findMany.mockResolvedValueOnce(
dbInfraConfigResponses,
);
const result = await infraConfigService.getMany(disallowedNames, false);
expect(result).toEqualRight(
infraConfigs.filter((i) => disallowedNames.includes(i.name)),
);
});
it('should return right with infraConfigs if Prisma query succeeds', async () => {
const allowedNames = [InfraConfigEnum.GOOGLE_CLIENT_ID];
const dbInfraConfigResponses = dbInfraConfigs.filter((dbConfig) =>
allowedNames.includes(dbConfig.name as InfraConfigEnum),
);
mockPrisma.infraConfig.findMany.mockResolvedValueOnce(
dbInfraConfigResponses,
);
const result = await infraConfigService.getMany(allowedNames);
expect(result).toEqualRight(
infraConfigs.filter((i) => allowedNames.includes(i.name)),
);
});
});
}); });

View File

@@ -3,27 +3,23 @@ import { InfraConfig } from './infra-config.model';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfig as DBInfraConfig } from '@prisma/client'; import { InfraConfig as DBInfraConfig } from '@prisma/client';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { InfraConfigEnum } from 'src/types/InfraConfig'; import {
InfraConfigEnum,
InfraConfigEnumForClient,
} from 'src/types/InfraConfig';
import { import {
AUTH_PROVIDER_NOT_SPECIFIED, AUTH_PROVIDER_NOT_SPECIFIED,
DATABASE_TABLE_NOT_EXIST, DATABASE_TABLE_NOT_EXIST,
INFRA_CONFIG_INVALID_INPUT, INFRA_CONFIG_INVALID_INPUT,
INFRA_CONFIG_NOT_FOUND, INFRA_CONFIG_NOT_FOUND,
INFRA_CONFIG_NOT_LISTED,
INFRA_CONFIG_RESET_FAILED, INFRA_CONFIG_RESET_FAILED,
INFRA_CONFIG_UPDATE_FAILED, INFRA_CONFIG_UPDATE_FAILED,
INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
INFRA_CONFIG_OPERATION_NOT_ALLOWED,
} from 'src/errors'; } from 'src/errors';
import { import { throwErr, validateEmail, validateSMTPUrl } from 'src/utils';
throwErr,
validateSMTPEmail,
validateSMTPUrl,
validateUrl,
} from 'src/utils';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { ServiceStatus, getDefaultInfraConfigs, stopApp } from './helper'; import { ServiceStatus, stopApp } from './helper';
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args'; import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
import { AuthProvider } from 'src/auth/helper';
@Injectable() @Injectable()
export class InfraConfigService implements OnModuleInit { export class InfraConfigService implements OnModuleInit {
@@ -32,32 +28,70 @@ export class InfraConfigService implements OnModuleInit {
private readonly configService: ConfigService, private readonly configService: ConfigService,
) {} ) {}
// Following fields are not updatable by `infraConfigs` Mutation. Use dedicated mutations for these fields instead.
EXCLUDE_FROM_UPDATE_CONFIGS = [
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION,
InfraConfigEnum.ANALYTICS_USER_ID,
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
];
// Following fields can not be fetched by `infraConfigs` Query. Use dedicated queries for these fields instead.
EXCLUDE_FROM_FETCH_CONFIGS = [
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
InfraConfigEnum.ANALYTICS_USER_ID,
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
];
async onModuleInit() { async onModuleInit() {
await this.initializeInfraConfigTable(); await this.initializeInfraConfigTable();
} }
getDefaultInfraConfigs(): { name: InfraConfigEnum; value: string }[] {
// Prepare rows for 'infra_config' table with default values (from .env) for each 'name'
const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [
{
name: InfraConfigEnum.MAILER_SMTP_URL,
value: process.env.MAILER_SMTP_URL,
},
{
name: InfraConfigEnum.MAILER_ADDRESS_FROM,
value: process.env.MAILER_ADDRESS_FROM,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_ID,
value: process.env.GOOGLE_CLIENT_ID,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_SECRET,
value: process.env.GOOGLE_CLIENT_SECRET,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_ID,
value: process.env.GITHUB_CLIENT_ID,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_SECRET,
value: process.env.GITHUB_CLIENT_SECRET,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_ID,
value: process.env.MICROSOFT_CLIENT_ID,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
value: process.env.MICROSOFT_CLIENT_SECRET,
},
{
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: process.env.VITE_ALLOWED_AUTH_PROVIDERS.toLocaleUpperCase(),
},
];
return infraConfigDefaultObjs;
}
/** /**
* Initialize the 'infra_config' table with values from .env * Initialize the 'infra_config' table with values from .env
* @description This function create rows 'infra_config' in very first time (only once) * @description This function create rows 'infra_config' in very first time (only once)
*/ */
async initializeInfraConfigTable() { async initializeInfraConfigTable() {
try { try {
// Get all the 'names' of the properties to be saved in the 'infra_config' table
const enumValues = Object.values(InfraConfigEnum);
// Fetch the default values (value in .env) for configs to be saved in 'infra_config' table // Fetch the default values (value in .env) for configs to be saved in 'infra_config' table
const infraConfigDefaultObjs = await getDefaultInfraConfigs(); const infraConfigDefaultObjs = this.getDefaultInfraConfigs();
// Check if all the 'names' are listed in the default values
if (enumValues.length !== infraConfigDefaultObjs.length) {
throw new Error(INFRA_CONFIG_NOT_LISTED);
}
// Eliminate the rows (from 'infraConfigDefaultObjs') that are already present in the database table // Eliminate the rows (from 'infraConfigDefaultObjs') that are already present in the database table
const dbInfraConfigs = await this.prisma.infraConfig.findMany(); const dbInfraConfigs = await this.prisma.infraConfig.findMany();
@@ -90,31 +124,20 @@ export class InfraConfigService implements OnModuleInit {
cast(dbInfraConfig: DBInfraConfig) { cast(dbInfraConfig: DBInfraConfig) {
return <InfraConfig>{ return <InfraConfig>{
name: dbInfraConfig.name, name: dbInfraConfig.name,
value: dbInfraConfig.value ?? '', value: dbInfraConfig.value,
}; };
} }
/**
* Get all the InfraConfigs as map
* @returns InfraConfig map
*/
async getInfraConfigsMap() {
const infraConfigs = await this.prisma.infraConfig.findMany();
const infraConfigMap: Record<string, string> = {};
infraConfigs.forEach((config) => {
infraConfigMap[config.name] = config.value;
});
return infraConfigMap;
}
/** /**
* Update InfraConfig by name * Update InfraConfig by name
* @param name Name of the InfraConfig * @param name Name of the InfraConfig
* @param value Value of the InfraConfig * @param value Value of the InfraConfig
* @param restartEnabled If true, restart the app after updating the InfraConfig
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async update(name: InfraConfigEnum, value: string, restartEnabled = false) { async update(
name: InfraConfigEnumForClient | InfraConfigEnum,
value: string,
) {
const isValidate = this.validateEnvValues([{ name, value }]); const isValidate = this.validateEnvValues([{ name, value }]);
if (E.isLeft(isValidate)) return E.left(isValidate.left); if (E.isLeft(isValidate)) return E.left(isValidate.left);
@@ -124,7 +147,7 @@ export class InfraConfigService implements OnModuleInit {
data: { value }, data: { value },
}); });
if (restartEnabled) stopApp(); stopApp();
return E.right(this.cast(infraConfig)); return E.right(this.cast(infraConfig));
} catch (e) { } catch (e) {
@@ -138,11 +161,6 @@ export class InfraConfigService implements OnModuleInit {
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async updateMany(infraConfigs: InfraConfigArgs[]) { async updateMany(infraConfigs: InfraConfigArgs[]) {
for (let i = 0; i < infraConfigs.length; i++) {
if (this.EXCLUDE_FROM_UPDATE_CONFIGS.includes(infraConfigs[i].name))
return E.left(INFRA_CONFIG_OPERATION_NOT_ALLOWED);
}
const isValidate = this.validateEnvValues(infraConfigs); const isValidate = this.validateEnvValues(infraConfigs);
if (E.isLeft(isValidate)) return E.left(isValidate.left); if (E.isLeft(isValidate)) return E.left(isValidate.left);
@@ -164,62 +182,6 @@ export class InfraConfigService implements OnModuleInit {
} }
} }
/**
* Check if the service is configured or not
* @param service Service can be Auth Provider, Mailer, Audit Log etc.
* @param configMap Map of all the infra configs
* @returns Either true or false
*/
isServiceConfigured(
service: AuthProvider,
configMap: Record<string, string>,
) {
switch (service) {
case AuthProvider.GOOGLE:
return (
configMap.GOOGLE_CLIENT_ID &&
configMap.GOOGLE_CLIENT_SECRET &&
configMap.GOOGLE_CALLBACK_URL &&
configMap.GOOGLE_SCOPE
);
case AuthProvider.GITHUB:
return (
configMap.GITHUB_CLIENT_ID &&
configMap.GITHUB_CLIENT_SECRET &&
configMap.GITHUB_CALLBACK_URL &&
configMap.GITHUB_SCOPE
);
case AuthProvider.MICROSOFT:
return (
configMap.MICROSOFT_CLIENT_ID &&
configMap.MICROSOFT_CLIENT_SECRET &&
configMap.MICROSOFT_CALLBACK_URL &&
configMap.MICROSOFT_SCOPE &&
configMap.MICROSOFT_TENANT
);
case AuthProvider.EMAIL:
return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM;
default:
return false;
}
}
/**
* Enable or Disable Analytics Collection
*
* @param status Status to enable or disable
* @returns Boolean of status of analytics collection
*/
async toggleAnalyticsCollection(status: ServiceStatus) {
const isUpdated = await this.update(
InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION,
status === ServiceStatus.ENABLE ? 'true' : 'false',
);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(isUpdated.right.value === 'true');
}
/** /**
* Enable or Disable SSO for login/signup * Enable or Disable SSO for login/signup
* @param provider Auth Provider to enable or disable * @param provider Auth Provider to enable or disable
@@ -233,14 +195,8 @@ export class InfraConfigService implements OnModuleInit {
let updatedAuthProviders = allowedAuthProviders; let updatedAuthProviders = allowedAuthProviders;
const infraConfigMap = await this.getInfraConfigsMap();
providerInfo.forEach(({ provider, status }) => { providerInfo.forEach(({ provider, status }) => {
if (status === ServiceStatus.ENABLE) { if (status === ServiceStatus.ENABLE) {
const isConfigured = this.isServiceConfigured(provider, infraConfigMap);
if (!isConfigured) {
throwErr(INFRA_CONFIG_SERVICE_NOT_CONFIGURED);
}
updatedAuthProviders.push(provider); updatedAuthProviders.push(provider);
} else if (status === ServiceStatus.DISABLE) { } else if (status === ServiceStatus.DISABLE) {
updatedAuthProviders = updatedAuthProviders.filter( updatedAuthProviders = updatedAuthProviders.filter(
@@ -258,7 +214,6 @@ export class InfraConfigService implements OnModuleInit {
const isUpdated = await this.update( const isUpdated = await this.update(
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS, InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
updatedAuthProviders.join(','), updatedAuthProviders.join(','),
true,
); );
if (E.isLeft(isUpdated)) return E.left(isUpdated.left); if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
@@ -270,7 +225,7 @@ export class InfraConfigService implements OnModuleInit {
* @param name Name of the InfraConfig * @param name Name of the InfraConfig
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async get(name: InfraConfigEnum) { async get(name: InfraConfigEnumForClient) {
try { try {
const infraConfig = await this.prisma.infraConfig.findUniqueOrThrow({ const infraConfig = await this.prisma.infraConfig.findUniqueOrThrow({
where: { name }, where: { name },
@@ -287,15 +242,7 @@ export class InfraConfigService implements OnModuleInit {
* @param names Names of the InfraConfigs * @param names Names of the InfraConfigs
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async getMany(names: InfraConfigEnum[], checkDisallowedKeys: boolean = true) { async getMany(names: InfraConfigEnumForClient[]) {
if (checkDisallowedKeys) {
// Check if the names are allowed to fetch by client
for (let i = 0; i < names.length; i++) {
if (this.EXCLUDE_FROM_FETCH_CONFIGS.includes(names[i]))
return E.left(INFRA_CONFIG_OPERATION_NOT_ALLOWED);
}
}
try { try {
const infraConfigs = await this.prisma.infraConfig.findMany({ const infraConfigs = await this.prisma.infraConfig.findMany({
where: { name: { in: names } }, where: { name: { in: names } },
@@ -322,24 +269,13 @@ export class InfraConfigService implements OnModuleInit {
*/ */
async reset() { async reset() {
try { try {
const infraConfigDefaultObjs = await getDefaultInfraConfigs(); const infraConfigDefaultObjs = this.getDefaultInfraConfigs();
await this.prisma.infraConfig.deleteMany({ await this.prisma.infraConfig.deleteMany({
where: { name: { in: infraConfigDefaultObjs.map((p) => p.name) } }, where: { name: { in: infraConfigDefaultObjs.map((p) => p.name) } },
}); });
// Hardcode t
const updatedInfraConfigDefaultObjs = infraConfigDefaultObjs.filter(
(obj) => obj.name !== InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
);
await this.prisma.infraConfig.createMany({ await this.prisma.infraConfig.createMany({
data: [ data: infraConfigDefaultObjs,
...updatedInfraConfigDefaultObjs,
{
name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
value: 'true',
},
],
}); });
stopApp(); stopApp();
@@ -350,67 +286,22 @@ export class InfraConfigService implements OnModuleInit {
} }
} }
/**
* Validate the values of the InfraConfigs
*/
validateEnvValues( validateEnvValues(
infraConfigs: { infraConfigs: {
name: InfraConfigEnum; name: InfraConfigEnumForClient | InfraConfigEnum;
value: string; value: string;
}[], }[],
) { ) {
for (let i = 0; i < infraConfigs.length; i++) { for (let i = 0; i < infraConfigs.length; i++) {
switch (infraConfigs[i].name) { switch (infraConfigs[i].name) {
case InfraConfigEnum.MAILER_SMTP_URL: case InfraConfigEnumForClient.MAILER_SMTP_URL:
const isValidUrl = validateSMTPUrl(infraConfigs[i].value); const isValidUrl = validateSMTPUrl(infraConfigs[i].value);
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnum.MAILER_ADDRESS_FROM: case InfraConfigEnumForClient.MAILER_ADDRESS_FROM:
const isValidEmail = validateSMTPEmail(infraConfigs[i].value); const isValidEmail = validateEmail(infraConfigs[i].value);
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnum.GOOGLE_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GOOGLE_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GOOGLE_CALLBACK_URL:
if (!validateUrl(infraConfigs[i].value))
return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GOOGLE_SCOPE:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GITHUB_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GITHUB_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GITHUB_CALLBACK_URL:
if (!validateUrl(infraConfigs[i].value))
return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GITHUB_SCOPE:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_CALLBACK_URL:
if (!validateUrl(infraConfigs[i].value))
return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_SCOPE:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_TENANT:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
default: default:
break; break;
} }

View File

@@ -1,14 +1,14 @@
import { Field, InputType } from '@nestjs/graphql'; import { Field, InputType } from '@nestjs/graphql';
import { InfraConfigEnum } from 'src/types/InfraConfig'; import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
import { ServiceStatus } from './helper'; import { ServiceStatus } from './helper';
import { AuthProvider } from 'src/auth/helper'; import { AuthProvider } from 'src/auth/helper';
@InputType() @InputType()
export class InfraConfigArgs { export class InfraConfigArgs {
@Field(() => InfraConfigEnum, { @Field(() => InfraConfigEnumForClient, {
description: 'Infra Config Name', description: 'Infra Config Name',
}) })
name: InfraConfigEnum; name: InfraConfigEnumForClient;
@Field({ @Field({
description: 'Infra Config Value', description: 'Infra Config Value',

View File

@@ -25,7 +25,7 @@ export class MailerService {
): string { ): string {
switch (mailDesc.template) { switch (mailDesc.template) {
case 'team-invitation': case 'team-invitation':
return `A user has invited you to join a team workspace in Hoppscotch`; return `${mailDesc.variables.invitee} invited you to join ${mailDesc.variables.invite_team_name} in Hoppscotch`;
case 'user-invitation': case 'user-invitation':
return 'Sign in to Hoppscotch'; return 'Sign in to Hoppscotch';

View File

@@ -27,12 +27,6 @@
color: #3869D4; color: #3869D4;
} }
a.nohighlight {
color: inherit !important;
text-decoration: none !important;
cursor: default !important;
}
a img { a img {
border: none; border: none;
} }
@@ -464,7 +458,7 @@
<td class="content-cell"> <td class="content-cell">
<div class="f-fallback"> <div class="f-fallback">
<h1>Hi there,</h1> <h1>Hi there,</h1>
<p><a class="nohighlight" name="invitee" href="#">{{invitee}}</a> with <a class="nohighlight" name="invite_team_name" href="#">{{invite_team_name}}</a> has invited you to use Hoppscotch to collaborate with them. Click the button below to set up your account and get started:</p> <p>{{invitee}} with {{invite_team_name}} has invited you to use Hoppscotch to collaborate with them. Click the button below to set up your account and get started:</p>
<!-- Action --> <!-- Action -->
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0"> <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr> <tr>
@@ -490,7 +484,7 @@
Welcome aboard, <br /> Welcome aboard, <br />
Your friends at Hoppscotch Your friends at Hoppscotch
</p> </p>
<p><strong>P.S.</strong> If you don't associate with <a class="nohighlight" name="invitee" href="#">{{invitee}}</a> or <a class="nohighlight" name="invite_team_name" href="#">{{invite_team_name}}</a>, just ignore this email.</p> <p><strong>P.S.</strong> If you don't associate with {{invitee}} or {{invite_team_name}}, just ignore this email.</p>
<!-- Sub copy --> <!-- Sub copy -->
<table class="body-sub"> <table class="body-sub">
<tr> <tr>

View File

@@ -27,12 +27,6 @@
color: #3869D4; color: #3869D4;
} }
a.nohighlight {
color: inherit !important;
text-decoration: none !important;
cursor: default !important;
}
a img { a img {
border: none; border: none;
} }

View File

@@ -17,8 +17,7 @@ async function bootstrap() {
console.log(`Port: ${configService.get('PORT')}`); console.log(`Port: ${configService.get('PORT')}`);
checkEnvironmentAuthProvider( checkEnvironmentAuthProvider(
configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS') ?? configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
); );
app.use( app.use(

View File

@@ -1,9 +0,0 @@
import { Module } from '@nestjs/common';
import { PosthogService } from './posthog.service';
import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
imports: [PrismaModule],
providers: [PosthogService],
})
export class PosthogModule {}

View File

@@ -1,58 +0,0 @@
import { Injectable } from '@nestjs/common';
import { PostHog } from 'posthog-node';
import { Cron, CronExpression, SchedulerRegistry } from '@nestjs/schedule';
import { ConfigService } from '@nestjs/config';
import { PrismaService } from 'src/prisma/prisma.service';
import { CronJob } from 'cron';
import { POSTHOG_CLIENT_NOT_INITIALIZED } from 'src/errors';
import { throwErr } from 'src/utils';
@Injectable()
export class PosthogService {
private postHogClient: PostHog;
private POSTHOG_API_KEY = 'phc_9CipPajQC22mSkk2wxe2TXsUA0Ysyupe8dt5KQQELqx';
constructor(
private readonly configService: ConfigService,
private readonly prismaService: PrismaService,
private schedulerRegistry: SchedulerRegistry,
) {}
async onModuleInit() {
if (this.configService.get('INFRA.ALLOW_ANALYTICS_COLLECTION') === 'true') {
console.log('Initializing PostHog');
this.postHogClient = new PostHog(this.POSTHOG_API_KEY, {
host: 'https://eu.posthog.com',
});
// Schedule the cron job only if analytics collection is allowed
this.scheduleCronJob();
}
}
private scheduleCronJob() {
const job = new CronJob(CronExpression.EVERY_WEEK, async () => {
await this.capture();
});
this.schedulerRegistry.addCronJob('captureAnalytics', job);
job.start();
}
async capture() {
if (!this.postHogClient) {
throwErr(POSTHOG_CLIENT_NOT_INITIALIZED);
}
this.postHogClient.capture({
distinctId: this.configService.get('INFRA.ANALYTICS_USER_ID'),
event: 'sh_instance',
properties: {
type: 'COMMUNITY',
total_user_count: await this.prismaService.user.count(),
total_workspace_count: await this.prismaService.team.count(),
version: this.configService.get('npm_package_version'),
},
});
console.log('Sent event to PostHog');
}
}

View File

@@ -1,14 +0,0 @@
// Type of data returned from the query to obtain all search results
export type SearchQueryReturnType = {
id: string;
title: string;
type: 'collection' | 'request';
method?: string;
};
// Type of data returned from the query to obtain all parents
export type ParentTreeQueryReturnType = {
id: string;
parentID: string;
title: string;
};

View File

@@ -1,38 +0,0 @@
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
import { TeamCollectionService } from './team-collection.service';
import * as E from 'fp-ts/Either';
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { RequiresTeamRole } from 'src/team/decorators/requires-team-role.decorator';
import { TeamMemberRole } from '@prisma/client';
import { RESTTeamMemberGuard } from 'src/team/guards/rest-team-member.guard';
import { throwHTTPErr } from 'src/utils';
@UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'team-collection', version: '1' })
export class TeamCollectionController {
constructor(private readonly teamCollectionService: TeamCollectionService) {}
@Get('search/:teamID/:searchQuery')
@RequiresTeamRole(
TeamMemberRole.VIEWER,
TeamMemberRole.EDITOR,
TeamMemberRole.OWNER,
)
@UseGuards(JwtAuthGuard, RESTTeamMemberGuard)
async searchByTitle(
@Param('searchQuery') searchQuery: string,
@Param('teamID') teamID: string,
@Query('take') take: string,
@Query('skip') skip: string,
) {
const res = await this.teamCollectionService.searchByTitle(
searchQuery,
teamID,
parseInt(take),
parseInt(skip),
);
if (E.isLeft(res)) throwHTTPErr(res.left);
return res.right;
}
}

View File

@@ -6,7 +6,6 @@ import { GqlCollectionTeamMemberGuard } from './guards/gql-collection-team-membe
import { TeamModule } from '../team/team.module'; import { TeamModule } from '../team/team.module';
import { UserModule } from '../user/user.module'; import { UserModule } from '../user/user.module';
import { PubSubModule } from '../pubsub/pubsub.module'; import { PubSubModule } from '../pubsub/pubsub.module';
import { TeamCollectionController } from './team-collection.controller';
@Module({ @Module({
imports: [PrismaModule, TeamModule, UserModule, PubSubModule], imports: [PrismaModule, TeamModule, UserModule, PubSubModule],
@@ -16,6 +15,5 @@ import { TeamCollectionController } from './team-collection.controller';
GqlCollectionTeamMemberGuard, GqlCollectionTeamMemberGuard,
], ],
exports: [TeamCollectionService, GqlCollectionTeamMemberGuard], exports: [TeamCollectionService, GqlCollectionTeamMemberGuard],
controllers: [TeamCollectionController],
}) })
export class TeamCollectionModule {} export class TeamCollectionModule {}

View File

@@ -1,4 +1,4 @@
import { HttpStatus, Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service'; import { PrismaService } from '../prisma/prisma.service';
import { TeamCollection } from './team-collection.model'; import { TeamCollection } from './team-collection.model';
import { import {
@@ -14,10 +14,6 @@ import {
TEAM_COL_SAME_NEXT_COLL, TEAM_COL_SAME_NEXT_COLL,
TEAM_COL_REORDERING_FAILED, TEAM_COL_REORDERING_FAILED,
TEAM_COLL_DATA_INVALID, TEAM_COLL_DATA_INVALID,
TEAM_REQ_SEARCH_FAILED,
TEAM_COL_SEARCH_FAILED,
TEAM_REQ_PARENT_TREE_GEN_FAILED,
TEAM_COLL_PARENT_TREE_GEN_FAILED,
} from '../errors'; } from '../errors';
import { PubSubService } from '../pubsub/pubsub.service'; import { PubSubService } from '../pubsub/pubsub.service';
import { isValidLength } from 'src/utils'; import { isValidLength } from 'src/utils';
@@ -26,9 +22,6 @@ import * as O from 'fp-ts/Option';
import { Prisma, TeamCollection as DBTeamCollection } from '@prisma/client'; import { Prisma, TeamCollection as DBTeamCollection } from '@prisma/client';
import { CollectionFolder } from 'src/types/CollectionFolder'; import { CollectionFolder } from 'src/types/CollectionFolder';
import { stringToJson } from 'src/utils'; import { stringToJson } from 'src/utils';
import { CollectionSearchNode } from 'src/types/CollectionSearchNode';
import { ParentTreeQueryReturnType, SearchQueryReturnType } from './helper';
import { RESTError } from 'src/types/RESTError';
@Injectable() @Injectable()
export class TeamCollectionService { export class TeamCollectionService {
@@ -1063,266 +1056,4 @@ export class TeamCollectionService {
return E.left(TEAM_COLL_NOT_FOUND); return E.left(TEAM_COLL_NOT_FOUND);
} }
} }
/**
* Search for TeamCollections and TeamRequests by title
*
* @param searchQuery The search query
* @param teamID The Team ID
* @param take Number of items we want returned
* @param skip Number of items we want to skip
* @returns An Either of the search results
*/
async searchByTitle(
searchQuery: string,
teamID: string,
take = 10,
skip = 0,
) {
// Fetch all collections and requests that match the search query
const searchResults: SearchQueryReturnType[] = [];
const matchedCollections = await this.searchCollections(
searchQuery,
teamID,
take,
skip,
);
if (E.isLeft(matchedCollections))
return E.left(<RESTError>{
message: matchedCollections.left,
statusCode: HttpStatus.NOT_FOUND,
});
searchResults.push(...matchedCollections.right);
const matchedRequests = await this.searchRequests(
searchQuery,
teamID,
take,
skip,
);
if (E.isLeft(matchedRequests))
return E.left(<RESTError>{
message: matchedRequests.left,
statusCode: HttpStatus.NOT_FOUND,
});
searchResults.push(...matchedRequests.right);
// Generate the parent tree for searchResults
const searchResultsWithTree: CollectionSearchNode[] = [];
for (let i = 0; i < searchResults.length; i++) {
const fetchedParentTree = await this.fetchParentTree(searchResults[i]);
if (E.isLeft(fetchedParentTree))
return E.left(<RESTError>{
message: fetchedParentTree.left,
statusCode: HttpStatus.NOT_FOUND,
});
searchResultsWithTree.push({
type: searchResults[i].type,
title: searchResults[i].title,
method: searchResults[i].method,
id: searchResults[i].id,
path: !fetchedParentTree
? []
: ([fetchedParentTree.right] as CollectionSearchNode[]),
});
}
return E.right({ data: searchResultsWithTree });
}
/**
* Search for TeamCollections by title
*
* @param searchQuery The search query
* @param teamID The Team ID
* @param take Number of items we want returned
* @param skip Number of items we want to skip
* @returns An Either of the search results
*/
private async searchCollections(
searchQuery: string,
teamID: string,
take: number,
skip: number,
) {
const query = Prisma.sql`
select id,title,'collection' AS type
from "TeamCollection"
where "TeamCollection"."teamID"=${teamID}
and titlesearch @@ to_tsquery(${searchQuery})
order by ts_rank(titlesearch,to_tsquery(${searchQuery}))
limit ${take}
OFFSET ${skip === 0 ? 0 : (skip - 1) * take};
`;
try {
const res = await this.prisma.$queryRaw<SearchQueryReturnType[]>(query);
return E.right(res);
} catch (error) {
return E.left(TEAM_COL_SEARCH_FAILED);
}
}
/**
* Search for TeamRequests by title
*
* @param searchQuery The search query
* @param teamID The Team ID
* @param take Number of items we want returned
* @param skip Number of items we want to skip
* @returns An Either of the search results
*/
private async searchRequests(
searchQuery: string,
teamID: string,
take: number,
skip: number,
) {
const query = Prisma.sql`
select id,title,request->>'method' as method,'request' AS type
from "TeamRequest"
where "TeamRequest"."teamID"=${teamID}
and titlesearch @@ to_tsquery(${searchQuery})
order by ts_rank(titlesearch,to_tsquery(${searchQuery}))
limit ${take}
OFFSET ${skip === 0 ? 0 : (skip - 1) * take};
`;
try {
const res = await this.prisma.$queryRaw<SearchQueryReturnType[]>(query);
return E.right(res);
} catch (error) {
return E.left(TEAM_REQ_SEARCH_FAILED);
}
}
/**
* Generate the parent tree of a search result
*
* @param searchResult The search result for which we want to generate the parent tree
* @returns The parent tree of the search result
*/
private async fetchParentTree(searchResult: SearchQueryReturnType) {
return searchResult.type === 'collection'
? await this.fetchCollectionParentTree(searchResult.id)
: await this.fetchRequestParentTree(searchResult.id);
}
/**
* Generate the parent tree of a collection
*
* @param id The ID of the collection
* @returns The parent tree of the collection
*/
private async fetchCollectionParentTree(id: string) {
try {
const query = Prisma.sql`
WITH RECURSIVE collection_tree AS (
SELECT tc.id, tc."parentID", tc.title
FROM "TeamCollection" AS tc
JOIN "TeamCollection" AS tr ON tc.id = tr."parentID"
WHERE tr.id = ${id}
UNION ALL
SELECT parent.id, parent."parentID", parent.title
FROM "TeamCollection" AS parent
JOIN collection_tree AS ct ON parent.id = ct."parentID"
)
SELECT * FROM collection_tree;
`;
const res = await this.prisma.$queryRaw<ParentTreeQueryReturnType[]>(
query,
);
const collectionParentTree = this.generateParentTree(res);
return E.right(collectionParentTree);
} catch (error) {
E.left(TEAM_COLL_PARENT_TREE_GEN_FAILED);
}
}
/**
* Generate the parent tree from the collections
*
* @param parentCollections The parent collections
* @returns The parent tree of the parent collections
*/
private generateParentTree(parentCollections: ParentTreeQueryReturnType[]) {
function findChildren(id) {
const collection = parentCollections.filter((item) => item.id === id)[0];
if (collection.parentID == null) {
return {
id: collection.id,
title: collection.title,
type: 'collection',
path: [],
};
}
const res = {
id: collection.id,
title: collection.title,
type: 'collection',
path: findChildren(collection.parentID),
};
return res;
}
if (parentCollections.length > 0) {
if (parentCollections[0].parentID == null) {
return {
id: parentCollections[0].id,
title: parentCollections[0].title,
type: 'collection',
path: [],
};
}
return {
id: parentCollections[0].id,
title: parentCollections[0].title,
type: 'collection',
path: findChildren(parentCollections[0].parentID),
};
}
return null;
}
/**
* Generate the parent tree of a request
*
* @param id The ID of the request
* @returns The parent tree of the request
*/
private async fetchRequestParentTree(id: string) {
try {
const query = Prisma.sql`
WITH RECURSIVE request_collection_tree AS (
SELECT tc.id, tc."parentID", tc.title
FROM "TeamCollection" AS tc
JOIN "TeamRequest" AS tr ON tc.id = tr."collectionID"
WHERE tr.id = ${id}
UNION ALL
SELECT parent.id, parent."parentID", parent.title
FROM "TeamCollection" AS parent
JOIN request_collection_tree AS ct ON parent.id = ct."parentID"
)
SELECT * FROM request_collection_tree;
`;
const res = await this.prisma.$queryRaw<ParentTreeQueryReturnType[]>(
query,
);
const requestParentTree = this.generateParentTree(res);
return E.right(requestParentTree);
} catch (error) {
return E.left(TEAM_REQ_PARENT_TREE_GEN_FAILED);
}
}
} }

View File

@@ -1,47 +0,0 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { TeamService } from '../../team/team.service';
import { TeamMemberRole } from '../../team/team.model';
import {
BUG_TEAM_NO_REQUIRE_TEAM_ROLE,
BUG_AUTH_NO_USER_CTX,
BUG_TEAM_NO_TEAM_ID,
TEAM_MEMBER_NOT_FOUND,
TEAM_NOT_REQUIRED_ROLE,
} from 'src/errors';
import { throwHTTPErr } from 'src/utils';
@Injectable()
export class RESTTeamMemberGuard implements CanActivate {
constructor(
private readonly reflector: Reflector,
private readonly teamService: TeamService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const requireRoles = this.reflector.get<TeamMemberRole[]>(
'requiresTeamRole',
context.getHandler(),
);
if (!requireRoles)
throwHTTPErr({ message: BUG_TEAM_NO_REQUIRE_TEAM_ROLE, statusCode: 400 });
const request = context.switchToHttp().getRequest();
const { user } = request;
if (user == undefined)
throwHTTPErr({ message: BUG_AUTH_NO_USER_CTX, statusCode: 400 });
const teamID = request.params.teamID;
if (!teamID)
throwHTTPErr({ message: BUG_TEAM_NO_TEAM_ID, statusCode: 400 });
const teamMember = await this.teamService.getTeamMember(teamID, user.uid);
if (!teamMember)
throwHTTPErr({ message: TEAM_MEMBER_NOT_FOUND, statusCode: 404 });
if (requireRoles.includes(teamMember.role)) return true;
throwHTTPErr({ message: TEAM_NOT_REQUIRED_ROLE, statusCode: 403 });
}
}

View File

@@ -1,10 +1,10 @@
import { HttpStatus } from '@nestjs/common'; import { HttpStatus } from '@nestjs/common';
/** /**
** Custom interface to handle errors for REST modules such as Auth, Admin modules ** Custom interface to handle errors specific to Auth module
** Since its REST we need to return the HTTP status code along with the error message ** Since its REST we need to return the HTTP status code along with the error message
*/ */
export type RESTError = { export type AuthError = {
message: string; message: string;
statusCode: HttpStatus; statusCode: HttpStatus;
}; };

View File

@@ -1,17 +0,0 @@
// Response type of results from the search query
export type CollectionSearchNode = {
/** Encodes the hierarchy of where the node is **/
path: CollectionSearchNode[];
} & (
| {
type: 'request';
title: string;
method: string;
id: string;
}
| {
type: 'collection';
title: string;
id: string;
}
);

View File

@@ -4,23 +4,26 @@ export enum InfraConfigEnum {
GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID', GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID',
GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET', GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET',
GOOGLE_CALLBACK_URL = 'GOOGLE_CALLBACK_URL',
GOOGLE_SCOPE = 'GOOGLE_SCOPE',
GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID', GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID',
GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET', GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET',
GITHUB_CALLBACK_URL = 'GITHUB_CALLBACK_URL',
GITHUB_SCOPE = 'GITHUB_SCOPE',
MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID', MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID',
MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET', MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET',
MICROSOFT_CALLBACK_URL = 'MICROSOFT_CALLBACK_URL',
MICROSOFT_SCOPE = 'MICROSOFT_SCOPE',
MICROSOFT_TENANT = 'MICROSOFT_TENANT',
VITE_ALLOWED_AUTH_PROVIDERS = 'VITE_ALLOWED_AUTH_PROVIDERS', VITE_ALLOWED_AUTH_PROVIDERS = 'VITE_ALLOWED_AUTH_PROVIDERS',
}
ALLOW_ANALYTICS_COLLECTION = 'ALLOW_ANALYTICS_COLLECTION',
ANALYTICS_USER_ID = 'ANALYTICS_USER_ID', export enum InfraConfigEnumForClient {
IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP', MAILER_SMTP_URL = 'MAILER_SMTP_URL',
MAILER_ADDRESS_FROM = 'MAILER_ADDRESS_FROM',
GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID',
GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID',
GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET',
MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID',
MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET',
} }

View File

@@ -17,21 +17,3 @@ export class PaginationArgs {
}) })
take: number; take: number;
} }
@ArgsType()
@InputType()
export class OffsetPaginationArgs {
@Field({
nullable: true,
defaultValue: 0,
description: 'Number of items to skip',
})
skip: number;
@Field({
nullable: true,
defaultValue: 10,
description: 'Number of items to fetch',
})
take: number;
}

View File

@@ -56,22 +56,3 @@ export enum SessionType {
registerEnumType(SessionType, { registerEnumType(SessionType, {
name: 'SessionType', name: 'SessionType',
}); });
@ObjectType()
export class UserDeletionResult {
@Field(() => ID, {
description: 'UID of the user',
})
userUID: string;
@Field(() => Boolean, {
description: 'Flag to determine if user deletion was successful or not',
})
isDeleted: Boolean;
@Field({
nullable: true,
description: 'Error message if user deletion was not successful',
})
errorMessage: String;
}

View File

@@ -1,4 +1,4 @@
import { JSON_INVALID, USERS_NOT_FOUND, USER_NOT_FOUND } from 'src/errors'; import { JSON_INVALID, USER_NOT_FOUND } from 'src/errors';
import { mockDeep, mockReset } from 'jest-mock-extended'; import { mockDeep, mockReset } from 'jest-mock-extended';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
@@ -176,26 +176,6 @@ describe('UserService', () => {
}); });
}); });
describe('findUsersByIds', () => {
test('should successfully return users given valid user UIDs', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.findUsersByIds([
'123344',
'5555',
'6666',
]);
expect(result).toEqual(users);
});
test('should return empty array of users given a invalid user UIDs', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce([]);
const result = await userService.findUsersByIds(['sdcvbdbr']);
expect(result).toEqual([]);
});
});
describe('createUserViaMagicLink', () => { describe('createUserViaMagicLink', () => {
test('should successfully create user and account for magic-link given valid inputs', async () => { test('should successfully create user and account for magic-link given valid inputs', async () => {
mockPrisma.user.create.mockResolvedValueOnce(user); mockPrisma.user.create.mockResolvedValueOnce(user);
@@ -434,54 +414,6 @@ describe('UserService', () => {
}); });
}); });
describe('updateUserDisplayName', () => {
test('should resolve right and update user display name', async () => {
const newDisplayName = 'New Name';
mockPrisma.user.update.mockResolvedValueOnce({
...user,
displayName: newDisplayName,
});
const result = await userService.updateUserDisplayName(
user.uid,
newDisplayName,
);
expect(result).toEqualRight({
...user,
displayName: newDisplayName,
currentGQLSession: JSON.stringify(user.currentGQLSession),
currentRESTSession: JSON.stringify(user.currentRESTSession),
});
});
test('should resolve right and publish user updated subscription', async () => {
const newDisplayName = 'New Name';
mockPrisma.user.update.mockResolvedValueOnce({
...user,
displayName: newDisplayName,
});
await userService.updateUserDisplayName(user.uid, user.displayName);
expect(mockPubSub.publish).toHaveBeenCalledWith(
`user/${user.uid}/updated`,
{
...user,
displayName: newDisplayName,
currentGQLSession: JSON.stringify(user.currentGQLSession),
currentRESTSession: JSON.stringify(user.currentRESTSession),
},
);
});
test('should resolve left and error when invalid user uid is passed', async () => {
mockPrisma.user.update.mockRejectedValueOnce('NotFoundError');
const result = await userService.updateUserDisplayName(
'invalidUserUid',
user.displayName,
);
expect(result).toEqualLeft(USER_NOT_FOUND);
});
});
describe('fetchAllUsers', () => { describe('fetchAllUsers', () => {
test('should resolve right and return 20 users when cursor is null', async () => { test('should resolve right and return 20 users when cursor is null', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users); mockPrisma.user.findMany.mockResolvedValueOnce(users);
@@ -503,36 +435,6 @@ describe('UserService', () => {
}); });
}); });
describe('fetchAllUsersV2', () => {
test('should resolve right and return first 20 users when searchString is null', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.fetchAllUsersV2(null, {
take: 20,
skip: 0,
});
expect(result).toEqual(users);
});
test('should resolve right and return next 20 users when searchString is provided', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.fetchAllUsersV2('.com', {
take: 20,
skip: 0,
});
expect(result).toEqual(users);
});
test('should resolve left and return an empty array when users not found', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce([]);
const result = await userService.fetchAllUsersV2('Unknown entry', {
take: 20,
skip: 0,
});
expect(result).toEqual([]);
});
});
describe('fetchAdminUsers', () => { describe('fetchAdminUsers', () => {
test('should return a list of admin users', async () => { test('should return a list of admin users', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(adminUsers); mockPrisma.user.findMany.mockResolvedValueOnce(adminUsers);
@@ -654,17 +556,4 @@ describe('UserService', () => {
expect(result).toEqual(10); expect(result).toEqual(10);
}); });
}); });
describe('removeUsersAsAdmin', () => {
test('should resolve right and return true for valid user UIDs', async () => {
mockPrisma.user.updateMany.mockResolvedValueOnce({ count: 1 });
const result = await userService.removeUsersAsAdmin(['123344']);
expect(result).toEqualRight(true);
});
test('should resolve right and return false for invalid user UIDs', async () => {
mockPrisma.user.updateMany.mockResolvedValueOnce({ count: 0 });
const result = await userService.removeUsersAsAdmin(['123344']);
expect(result).toEqualLeft(USERS_NOT_FOUND);
});
});
}); });

View File

@@ -8,14 +8,13 @@ import * as T from 'fp-ts/Task';
import * as A from 'fp-ts/Array'; import * as A from 'fp-ts/Array';
import { pipe, constVoid } from 'fp-ts/function'; import { pipe, constVoid } from 'fp-ts/function';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
import { USERS_NOT_FOUND, USER_NOT_FOUND } from 'src/errors'; import { USER_NOT_FOUND } from 'src/errors';
import { SessionType, User } from './user.model'; import { SessionType, User } from './user.model';
import { USER_UPDATE_FAILED } from 'src/errors'; import { USER_UPDATE_FAILED } from 'src/errors';
import { PubSubService } from 'src/pubsub/pubsub.service'; import { PubSubService } from 'src/pubsub/pubsub.service';
import { stringToJson, taskEitherValidateArraySeq } from 'src/utils'; import { stringToJson, taskEitherValidateArraySeq } from 'src/utils';
import { UserDataHandler } from './user.data.handler'; import { UserDataHandler } from './user.data.handler';
import { User as DbUser } from '@prisma/client'; import { User as DbUser } from '@prisma/client';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
@Injectable() @Injectable()
export class UserService { export class UserService {
@@ -89,20 +88,6 @@ export class UserService {
} }
} }
/**
* Find users with given IDs
* @param userUIDs User IDs
* @returns Array of found Users
*/
async findUsersByIds(userUIDs: string[]): Promise<AuthUser[]> {
const users = await this.prisma.user.findMany({
where: {
uid: { in: userUIDs },
},
});
return users;
}
/** /**
* Update User with new generated hashed refresh token * Update User with new generated hashed refresh token
* *
@@ -284,30 +269,6 @@ export class UserService {
} }
} }
/**
* Update a user's data
* @param userUID User UID
* @param displayName User's displayName
* @returns a Either of User or error
*/
async updateUserDisplayName(userUID: string, displayName: string) {
try {
const dbUpdatedUser = await this.prisma.user.update({
where: { uid: userUID },
data: { displayName },
});
const updatedUser = this.convertDbUserToUser(dbUpdatedUser);
// Publish subscription for user updates
await this.pubsub.publish(`user/${updatedUser.uid}/updated`, updatedUser);
return E.right(updatedUser);
} catch (error) {
return E.left(USER_NOT_FOUND);
}
}
/** /**
* Validate and parse currentRESTSession and currentGQLSession * Validate and parse currentRESTSession and currentGQLSession
* @param sessionData string of the session * @param sessionData string of the session
@@ -325,7 +286,6 @@ export class UserService {
* @param cursorID string of userUID or null * @param cursorID string of userUID or null
* @param take number of users to query * @param take number of users to query
* @returns an array of `User` object * @returns an array of `User` object
* @deprecated use fetchAllUsersV2 instead
*/ */
async fetchAllUsers(cursorID: string, take: number) { async fetchAllUsers(cursorID: string, take: number) {
const fetchedUsers = await this.prisma.user.findMany({ const fetchedUsers = await this.prisma.user.findMany({
@@ -336,43 +296,6 @@ export class UserService {
return fetchedUsers; return fetchedUsers;
} }
/**
* Fetch all the users in the `User` table based on cursor
* @param searchString search on user's displayName or email
* @param paginationOption pagination options
* @returns an array of `User` object
*/
async fetchAllUsersV2(
searchString: string,
paginationOption: OffsetPaginationArgs,
) {
const fetchedUsers = await this.prisma.user.findMany({
skip: paginationOption.skip,
take: paginationOption.take,
where: searchString
? {
OR: [
{
displayName: {
contains: searchString,
mode: 'insensitive',
},
},
{
email: {
contains: searchString,
mode: 'insensitive',
},
},
],
}
: undefined,
orderBy: [{ isAdmin: 'desc' }, { displayName: 'asc' }],
});
return fetchedUsers;
}
/** /**
* Fetch the number of users in db * Fetch the number of users in db
* @returns a count (Int) of user records in DB * @returns a count (Int) of user records in DB
@@ -403,23 +326,6 @@ export class UserService {
} }
} }
/**
* Change users to admins by toggling isAdmin param to true
* @param userUID user UIDs
* @returns a Either of true or error
*/
async makeAdmins(userUIDs: string[]) {
try {
await this.prisma.user.updateMany({
where: { uid: { in: userUIDs } },
data: { isAdmin: true },
});
return E.right(true);
} catch (error) {
return E.left(USER_UPDATE_FAILED);
}
}
/** /**
* Fetch all the admin users * Fetch all the admin users
* @returns an array of admin users * @returns an array of admin users
@@ -538,22 +444,4 @@ export class UserService {
return E.left(USER_NOT_FOUND); return E.left(USER_NOT_FOUND);
} }
} }
/**
* Change users from an admin by toggling isAdmin param to false
* @param userUIDs user UIDs
* @returns a Either of true or error
*/
async removeUsersAsAdmin(userUIDs: string[]) {
const data = await this.prisma.user.updateMany({
where: { uid: { in: userUIDs } },
data: { isAdmin: false },
});
if (data.count === 0) {
return E.left(USERS_NOT_FOUND);
}
return E.right(true);
}
} }

View File

@@ -1,4 +1,4 @@
import { ExecutionContext, HttpException } from '@nestjs/common'; import { ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core'; import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql'; import { GqlExecutionContext } from '@nestjs/graphql';
import { pipe } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/function';
@@ -16,7 +16,6 @@ import {
JSON_INVALID, JSON_INVALID,
} from './errors'; } from './errors';
import { AuthProvider } from './auth/helper'; import { AuthProvider } from './auth/helper';
import { RESTError } from './types/RESTError';
/** /**
* A workaround to throw an exception in an expression. * A workaround to throw an exception in an expression.
@@ -28,15 +27,6 @@ export function throwErr(errMessage: string): never {
throw new Error(errMessage); throw new Error(errMessage);
} }
/**
* This function allows throw to be used as an expression
* @param errMessage Message present in the error message
*/
export function throwHTTPErr(errorData: RESTError): never {
const { message, statusCode } = errorData;
throw new HttpException(message, statusCode);
}
/** /**
* Prints the given value to log and returns the same value. * Prints the given value to log and returns the same value.
* Used for debugging functional pipelines. * Used for debugging functional pipelines.
@@ -141,28 +131,6 @@ export const validateEmail = (email: string) => {
).test(email); ).test(email);
}; };
// Regular expressions for supported address object formats by nodemailer
// check out for more info https://nodemailer.com/message/addresses
const emailRegex1 = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const emailRegex2 =
/^[\w\s]* <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
const emailRegex3 =
/^"[\w\s]+" <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
/**
* Checks to see if the SMTP email is valid or not
* @param email
* @returns A Boolean depending on the format of the email
*/
export const validateSMTPEmail = (email: string) => {
// Check if the input matches any of the formats
return (
emailRegex1.test(email) ||
emailRegex2.test(email) ||
emailRegex3.test(email)
);
};
/** /**
* Checks to see if the URL is valid or not * Checks to see if the URL is valid or not
* @param url The URL to validate * @param url The URL to validate
@@ -183,16 +151,6 @@ export const validateSMTPUrl = (url: string) => {
return false; return false;
}; };
/**
* Checks to see if the URL is valid or not
* @param url The URL to validate
* @returns boolean
*/
export const validateUrl = (url: string) => {
const urlRegex = /^(http|https):\/\/[^ "]+$/;
return urlRegex.test(url);
};
/** /**
* String to JSON parser * String to JSON parser
* @param {str} str The string to parse * @param {str} str The string to parse

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
// * The entry point of the CLI
require("../dist").cli(process.argv);

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env node
// * The entry point of the CLI
import { cli } from "../dist/index.js";
cli(process.argv);

View File

@@ -1,12 +1,11 @@
{ {
"name": "@hoppscotch/cli", "name": "@hoppscotch/cli",
"version": "0.6.0", "version": "0.4.0",
"description": "A CLI to run Hoppscotch test scripts in CI environments.", "description": "A CLI to run Hoppscotch test scripts in CI environments.",
"homepage": "https://hoppscotch.io", "homepage": "https://hoppscotch.io",
"type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
"hopp": "bin/hopp.js" "hopp": "bin/hopp"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@@ -40,31 +39,27 @@
}, },
"license": "MIT", "license": "MIT",
"private": false, "private": false,
"dependencies": {
"axios": "^1.6.6",
"chalk": "^5.3.0",
"commander": "^11.1.0",
"lodash-es": "^4.17.21",
"qs": "^6.11.2",
"zod": "^3.22.4"
},
"devDependencies": { "devDependencies": {
"@hoppscotch/data": "workspace:^", "@hoppscotch/data": "workspace:^",
"@hoppscotch/js-sandbox": "workspace:^", "@hoppscotch/js-sandbox": "workspace:^",
"@relmify/jest-fp-ts": "^2.1.1", "@relmify/jest-fp-ts": "^2.1.1",
"@swc/core": "^1.3.105", "@swc/core": "^1.3.92",
"@types/jest": "^29.5.11", "@types/jest": "^29.5.5",
"@types/lodash-es": "^4.17.12", "@types/lodash": "^4.14.199",
"@types/qs": "^6.9.11", "@types/qs": "^6.9.8",
"fp-ts": "^2.16.2", "axios": "^0.21.4",
"chalk": "^4.1.2",
"commander": "^11.0.0",
"esm": "^3.2.25",
"fp-ts": "^2.16.1",
"io-ts": "^2.2.20",
"jest": "^29.7.0", "jest": "^29.7.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"prettier": "^3.2.4", "prettier": "^3.0.3",
"qs": "^6.11.2", "qs": "^6.11.2",
"ts-jest": "^29.1.2", "ts-jest": "^29.1.1",
"tsup": "^8.0.1", "tsup": "^7.2.0",
"typescript": "^5.3.3", "typescript": "^5.2.2",
"verzod": "^0.2.2",
"zod": "^3.22.4" "zod": "^3.22.4"
} }
} }

View File

@@ -1,274 +1,140 @@
import { ExecException } from "child_process"; import { ExecException } from "child_process";
import { HoppErrorCode } from "../../types/errors"; import { HoppErrorCode } from "../../types/errors";
import { runCLI, getErrorCode, getTestJsonFilePath } from "../utils"; import { execAsync, getErrorCode, getTestJsonFilePath } from "../utils";
describe("Test `hopp test <file>` command:", () => { describe("Test 'hopp test <file>' command:", () => {
describe("Argument parsing", () => { test("No collection file path provided.", async () => {
test("Errors with the code `INVALID_ARGUMENT` for not supplying enough arguments", async () => { const cmd = `node ./bin/hopp test`;
const args = "test"; const { stderr } = await execAsync(cmd);
const { stderr } = await runCLI(args); const out = getErrorCode(stderr);
const out = getErrorCode(stderr); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
expect(out).toBe<HoppErrorCode>("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<HoppErrorCode>("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<HoppErrorCode>("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<HoppErrorCode>("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<HoppErrorCode>("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<HoppErrorCode>("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(<ExecException>{
code: 1,
});
});
}); });
test("Successfully processes a supplied collection export file of the expected format", async () => { test("Collection file not found.", async () => {
const args = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; const cmd = `node ./bin/hopp test notfound.json`;
const { error } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(error).toBeNull(); expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
}); });
test("Successfully inherits headers and authorization set at the root collection", async () => { test("Collection file is invalid JSON.", async () => {
const args = `test ${getTestJsonFilePath( const cmd = `node ./bin/hopp test ${getTestJsonFilePath(
"collection-level-headers-auth-coll.json", "collection" "malformed-collection.json"
)}`; )}`;
const { error } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(error).toBeNull(); expect(out).toBe<HoppErrorCode>("UNKNOWN_ERROR");
}); });
test("Persists environment variables set in the pre-request script for consumption in the test script", async () => { test("Malformed collection file.", async () => {
const args = `test ${getTestJsonFilePath( const cmd = `node ./bin/hopp test ${getTestJsonFilePath(
"pre-req-script-env-var-persistence-coll.json", "collection" "malformed-collection2.json"
)}`; )}`;
const { error } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION");
});
test("Invalid arguement.", async () => {
const cmd = `node ./bin/hopp invalid-arg`;
const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
});
test("Collection file not JSON type.", async () => {
const cmd = `node ./bin/hopp test ${getTestJsonFilePath("notjson.txt")}`;
const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
});
test("Some errors occured (exit code 1).", async () => {
const cmd = `node ./bin/hopp test ${getTestJsonFilePath("fails.json")}`;
const { error } = await execAsync(cmd);
expect(error).not.toBeNull();
expect(error).toMatchObject(<ExecException>{
code: 1,
});
});
test("No errors occured (exit code 0).", async () => {
const cmd = `node ./bin/hopp test ${getTestJsonFilePath("passes.json")}`;
const { error } = await execAsync(cmd);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
}); });
describe("Test `hopp test <file> --env <file>` command:", () => { describe("Test 'hopp test <file> --env <file>' command:", () => {
describe("Supplied environment export file validations", () => { const VALID_TEST_CMD = `node ./bin/hopp test ${getTestJsonFilePath(
const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; "passes.json"
)}`;
test("Errors with the code `INVALID_ARGUMENT` if no file is supplied", async () => { test("No env file path provided.", async () => {
const args = `${VALID_TEST_ARGS} --env`; const cmd = `${VALID_TEST_CMD} --env`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
const out = getErrorCode(stderr); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
expect(out).toBe<HoppErrorCode>("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<HoppErrorCode>("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<HoppErrorCode>("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<HoppErrorCode>("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<HoppErrorCode>("BULK_ENV_FILE");
});
}); });
test("Successfully resolves values from the supplied environment export file", async () => { test("ENV file not JSON type.", async () => {
const TESTS_PATH = getTestJsonFilePath("env-flag-tests-coll.json", "collection"); const cmd = `${VALID_TEST_CMD} --env ${getTestJsonFilePath("notjson.txt")}`;
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment"); const { stderr } = await execAsync(cmd);
const args = `test ${TESTS_PATH} --env ${ENV_PATH}`; const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
});
test("ENV file not found.", async () => {
const cmd = `${VALID_TEST_CMD} --env notfound.json`;
const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
});
test("No errors occured (exit code 0).", async () => {
const TESTS_PATH = getTestJsonFilePath("env-flag-tests.json");
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json");
const cmd = `node ./bin/hopp test ${TESTS_PATH} --env ${ENV_PATH}`;
const { error, stdout } = await execAsync(cmd);
const { error } = await runCLI(args);
expect(error).toBeNull(); 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(10000);
// 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 <file> --delay <delay_in_ms>` command:", () => { describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {
const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`; const VALID_TEST_CMD = `node ./bin/hopp test ${getTestJsonFilePath(
"passes.json"
)}`;
test("Errors with the code `INVALID_ARGUMENT` on not supplying a delay value", async () => { test("No value passed to delay flag.", async () => {
const args = `${VALID_TEST_ARGS} --delay`; const cmd = `${VALID_TEST_CMD} --delay`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
});
test("Invalid value passed to delay flag.", async () => {
const cmd = `${VALID_TEST_CMD} --delay 'NaN'`;
const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Errors with the code `INVALID_ARGUMENT` on supplying an invalid delay value", async () => { test("Valid value passed to delay flag.", async () => {
const args = `${VALID_TEST_ARGS} --delay 'NaN'`; const cmd = `${VALID_TEST_CMD} --delay 1`;
const { stderr } = await runCLI(args); const { error } = await execAsync(cmd);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("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(); expect(error).toBeNull();
}); });

View File

@@ -1,227 +0,0 @@
[
{
"v": 1,
"name": "CollectionA",
"folders": [
{
"v": 1,
"name": "FolderA",
"folders": [
{
"v": 1,
"name": "FolderB",
"folders": [
{
"v": 1,
"name": "FolderC",
"folders": [],
"requests": [
{
"v": "2",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestD",
"params": [],
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Overriden at RequestD"
}
],
"method": "GET",
"auth": {
"authType": "basic",
"authActive": true,
"username": "username",
"password": "password"
},
"preRequestScript": "",
"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});",
"body": {
"contentType": null,
"body": null
},
"requestVariables": []
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [
{
"v": "2",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestC",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"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});",
"body": {
"contentType": null,
"body": null
},
"requestVariables": []
}
],
"auth": {
"authType": "api-key",
"authActive": true,
"addTo": "Headers",
"key": "key",
"value": "test-key"
},
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Overriden at FolderB"
}
]
}
],
"requests": [
{
"v": "2",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestB",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"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});",
"body": {
"contentType": null,
"body": null
},
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv"
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [
{
"v": "2",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestA",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"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});",
"body": {
"contentType": null,
"body": null
},
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv"
}
],
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Set at root collection"
}
],
"auth": {
"authType": "bearer",
"authActive": true,
"token": "BearerToken"
}
},
{
"v": 1,
"name": "CollectionB",
"folders": [
{
"v": 1,
"name": "FolderA",
"folders": [],
"requests": [
{
"v": "2",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestB",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"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});",
"body": {
"contentType": null,
"body": null
},
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv"
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [
{
"v": "2",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestA",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"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});",
"body": {
"contentType": null,
"body": null
},
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv"
}
],
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Set at root collection"
}
],
"auth": {
"authType": "bearer",
"authActive": true,
"token": "BearerToken"
}
}
]

View File

@@ -1,22 +0,0 @@
{
"v": 2,
"name": "pre-req-script-env-var-persistence-coll",
"folders": [],
"requests": [
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "sample-req",
"method": "GET",
"params": [],
"headers": [],
"endpoint": "https://echo.hoppscotch.io",
"testScript": "pw.expect(pw.env.get(\"variable\")).toBe(\"value\")",
"preRequestScript": "pw.env.set(\"variable\", \"value\");",
"requestVariables": []
}
],
"auth": { "authType": "inherit", "authActive": true },
"headers": []
}

View File

@@ -1,31 +0,0 @@
{
"v": 2,
"name": "Test environment variables in request body",
"folders": [],
"requests": [
{
"v": "2",
"name": "test-request",
"endpoint": "https://echo.hoppscotch.io",
"method": "POST",
"headers": [],
"params": [],
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"contentType": "application/json",
"body": "{\n \"firstName\": \"<<firstName>>\",\n \"lastName\": \"<<lastName>>\",\n \"greetText\": \"<<salutation>>, <<fullName>>\",\n \"fullName\": \"<<fullName>>\",\n \"id\": \"<<id>>\"\n}"
},
"preRequestScript": "",
"testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully resolves environments recursively\", ()=> {\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n});\n\npw.test(\"Successfully resolves environments referenced in the request body\", () => {\n const expectedId = \"7\"\n const expectedFirstName = \"John\"\n const expectedLastName = \"Doe\"\n const expectedFullName = `${expectedFirstName} ${expectedLastName}`\n const expectedGreetText = `Hello, ${expectedFullName}`\n\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n\n const { id, firstName, lastName, fullName, greetText } = JSON.parse(pw.response.body.data)\n\n pw.expect(id).toBe(expectedId)\n pw.expect(expectedFirstName).toBe(firstName)\n pw.expect(expectedLastName).toBe(lastName)\n pw.expect(fullName).toBe(expectedFullName)\n pw.expect(greetText).toBe(expectedGreetText)\n});",
"requestVariables": []
}
],
"auth": {
"authType": "none",
"authActive": true
},
"headers": []
}

View File

@@ -1,113 +0,0 @@
{
"v": 2,
"name": "secret-envs-coll",
"folders": [],
"requests": [
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "test-secret-headers",
"method": "GET",
"params": [],
"headers": [
{
"key": "Secret-Header-Key",
"value": "<<secretHeaderValue>>",
"active": true
}
],
"requestVariables": [],
"endpoint": "<<baseURL>>/headers",
"testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.get(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.get(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})",
"preRequestScript": "const secretHeaderValueFromPreReqScript = pw.env.get(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
},
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": {
"body": "{\n \"secretBodyKey\": \"<<secretBodyValue>>\"\n}",
"contentType": "application/json"
},
"name": "test-secret-body",
"method": "POST",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/post",
"testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})",
"preRequestScript": "const secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)"
},
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "test-secret-query-params",
"method": "GET",
"params": [
{
"key": "secretQueryParamKey",
"value": "<<secretQueryParamValue>>",
"active": true
}
],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/get",
"testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})",
"preRequestScript": "const secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "basic",
"password": "<<secretBasicAuthPassword>>",
"username": "<<secretBasicAuthUsername>>",
"authActive": true
},
"body": { "body": null, "contentType": null },
"name": "test-secret-basic-auth",
"method": "GET",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>",
"testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});",
"preRequestScript": ""
},
{
"v": "2",
"auth": {
"token": "<<secretBearerToken>>",
"authType": "bearer",
"password": "testpassword",
"username": "testuser",
"authActive": true
},
"body": { "body": null, "contentType": null },
"name": "test-secret-bearer-auth",
"method": "GET",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/bearer",
"testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.get(\"secretBearerToken\")\n const preReqSecretBearerToken = pw.env.get(\"preReqSecretBearerToken\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});",
"preRequestScript": "const secretBearerToken = pw.env.get(\"secretBearerToken\")\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)"
},
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "test-secret-fallback",
"method": "GET",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>",
"testScript": "pw.test(\"Returns an empty string if the value for a secret environment variable is not found in the system environment\", () => {\n pw.expect(pw.env.get(\"nonExistentValueInSystemEnv\")).toBe(\"\")\n})",
"preRequestScript": ""
}
],
"auth": { "authType": "inherit", "authActive": false },
"headers": []
}

View File

@@ -1,149 +0,0 @@
{
"v": 2,
"name": "secret-envs-setters-coll",
"folders": [],
"requests": [
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-headers",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [
{
"key": "Secret-Header-Key",
"value": "<<secretHeaderValue>>",
"active": true
}
],
"endpoint": "<<baseURL>>/headers",
"testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})",
"preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-headers-overrides",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [
{
"key": "Secret-Header-Key",
"value": "<<secretHeaderValue>>",
"active": true
}
],
"endpoint": "<<baseURL>>/headers",
"testScript": "pw.test(\"Value set at the pre-request script takes precedence\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value-overriden\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value-overriden\")\n})",
"preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value-overriden\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": "{\n \"secretBodyKey\": \"<<secretBodyValue>>\"\n}",
"contentType": "application/json"
},
"name": "test-secret-body",
"method": "POST",
"params": [],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/post",
"testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})",
"preRequestScript": "const secretBodyValue = pw.env.get(\"secretBodyValue\")\n\nif (!secretBodyValue) { \n pw.env.set(\"secretBodyValue\", \"secret-body-value\")\n}\n\nconst secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-query-params",
"method": "GET",
"params": [
{
"key": "secretQueryParamKey",
"value": "<<secretQueryParamValue>>",
"active": true
}
],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/get",
"testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})",
"preRequestScript": "const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n\nif (!secretQueryParamValue) {\n pw.env.set(\"secretQueryParamValue\", \"secret-query-param-value\")\n}\n\nconst secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "basic",
"password": "<<secretBasicAuthPassword>>",
"username": "<<secretBasicAuthUsername>>",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-basic-auth",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>",
"testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});",
"preRequestScript": "let secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n\nlet secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\nif (!secretBasicAuthUsername) {\n pw.env.set(\"secretBasicAuthUsername\", \"test-user\")\n}\n\nif (!secretBasicAuthPassword) {\n pw.env.set(\"secretBasicAuthPassword\", \"test-pass\")\n}"
},
{
"v": "2",
"auth": {
"token": "<<secretBearerToken>>",
"authType": "bearer",
"password": "testpassword",
"username": "testuser",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-bearer-auth",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/bearer",
"testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n const preReqSecretBearerToken = pw.env.resolve(\"<<preReqSecretBearerToken>>\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});",
"preRequestScript": "let secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n\nif (!secretBearerToken) {\n pw.env.set(\"secretBearerToken\", \"test-token\")\n secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n}\n\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)"
}
],
"auth": {
"authType": "inherit",
"authActive": false
},
"headers": []
}

View File

@@ -1,31 +0,0 @@
{
"v": 2,
"name": "secret-envs-persistence-scripting-req",
"folders": [],
"requests": [
{
"v": "2",
"endpoint": "https://httpbin.org/post",
"name": "req",
"params": [],
"headers": [
{
"active": true,
"key": "Custom-Header",
"value": "<<customHeaderValueFromSecretVar>>"
}
],
"method": "POST",
"auth": { "authType": "none", "authActive": true },
"preRequestScript": "pw.env.set(\"preReqVarOne\", \"pre-req-value-one\")\n\npw.env.set(\"preReqVarTwo\", \"pre-req-value-two\")\n\npw.env.set(\"customHeaderValueFromSecretVar\", \"custom-header-secret-value\")\n\npw.env.set(\"customBodyValue\", \"custom-body-value\")",
"testScript": "pw.test(\"Secret environment value set from the pre-request script takes precedence\", () => {\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(\"pre-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the pre-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request headers that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"custom-header-secret-value\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request body that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.json.key).toBe(\"custom-body-value\")\n})\n\npw.test(\"Secret environment variable set from the post-request script takes precedence\", () => {\n pw.env.set(\"postReqVarOne\", \"post-req-value-one\")\n pw.expect(pw.env.get(\"postReqVarOne\")).toBe(\"post-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the post-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully removes environment variables via the pw.env.unset method\", () => {\n pw.env.unset(\"preReqVarOne\")\n pw.env.unset(\"postReqVarTwo\")\n\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(undefined)\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(undefined)\n})",
"body": {
"contentType": "application/json",
"body": "{\n \"key\": \"<<customBodyValue>>\"\n}"
},
"requestVariables": []
}
],
"auth": { "authType": "inherit", "authActive": false },
"headers": []
}

View File

@@ -4,7 +4,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "2", "v": "1",
"endpoint": "<<URL>>", "endpoint": "<<URL>>",
"name": "test1", "name": "test1",
"params": [], "params": [],
@@ -16,8 +16,7 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n \"<<BODY_KEY>>\":\"<<BODY_VALUE>>\"\n}" "body": "{\n \"<<BODY_KEY>>\":\"<<BODY_VALUE>>\"\n}"
}, }
"requestVariables": []
} }
] ]
} }

View File

@@ -1,32 +0,0 @@
[
{
"v": 0,
"name": "Env-I",
"variables": [
{
"key": "firstName",
"value": "John"
},
{
"key": "lastName",
"value": "Doe"
}
]
},
{
"v": 1,
"id": "2",
"name": "Env-II",
"variables": [
{
"key": "baseUrl",
"value": "https://echo.hoppscotch.io",
"secret": false
},
{
"key": "secretVar",
"secret": true
}
]
}
]

View File

@@ -1,16 +0,0 @@
{
"id": 123,
"v": "1",
"name": "secret-envs",
"values": [
{
"key": "secretVar",
"secret": true
},
{
"key": "regularVar",
"secret": false,
"value": "regular-variable"
}
]
}

View File

@@ -1,38 +0,0 @@
{
"v": 0,
"name": "Response body sample",
"variables": [
{
"key": "firstName",
"value": "John"
},
{
"key": "lastName",
"value": "Doe"
},
{
"key": "id",
"value": "7"
},
{
"key": "fullName",
"value": "<<firstName>> <<lastName>>"
},
{
"key": "recursiveVarX",
"value": "<<recursiveVarY>>"
},
{
"key": "recursiveVarY",
"value": "<<salutation>>"
},
{
"key": "salutation",
"value": "Hello"
},
{
"key": "greetText",
"value": "<<salutation>> <<fullName>>"
}
]
}

View File

@@ -1,27 +0,0 @@
{
"v": 1,
"id": "2",
"name": "secret-envs-persistence-scripting-envs",
"variables": [
{
"key": "preReqVarOne",
"secret": true
},
{
"key": "preReqVarTwo",
"secret": true
},
{
"key": "postReqVarOne",
"secret": true
},
{
"key": "preReqVarTwo",
"secret": true
},
{
"key": "customHeaderValueFromSecretVar",
"secret": true
}
]
}

View File

@@ -1,40 +0,0 @@
{
"id": "2",
"v": 1,
"name": "secret-envs",
"variables": [
{
"key": "secretBearerToken",
"secret": true
},
{
"key": "secretBasicAuthUsername",
"secret": true
},
{
"key": "secretBasicAuthPassword",
"secret": true
},
{
"key": "secretQueryParamValue",
"secret": true
},
{
"key": "secretBodyValue",
"secret": true
},
{
"key": "secretHeaderValue",
"secret": true
},
{
"key": "nonExistentValueInSystemEnv",
"secret": true
},
{
"key": "baseURL",
"value": "https://httpbin.org",
"secret": false
}
]
}

View File

@@ -1,46 +0,0 @@
{
"v": 1,
"id": "2",
"name": "secret-values-envs",
"variables": [
{
"key": "secretBearerToken",
"value": "test-token",
"secret": true
},
{
"key": "secretBasicAuthUsername",
"value": "test-user",
"secret": true
},
{
"key": "secretBasicAuthPassword",
"value": "test-pass",
"secret": true
},
{
"key": "secretQueryParamValue",
"value": "secret-query-param-value",
"secret": true
},
{
"key": "secretBodyValue",
"value": "secret-body-value",
"secret": true
},
{
"key": "secretHeaderValue",
"value": "secret-header-value",
"secret": true
},
{
"key": "nonExistentValueInSystemEnv",
"secret": true
},
{
"key": "baseURL",
"value": "https://httpbin.org",
"secret": false
}
]
}

View File

@@ -5,7 +5,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "2", "v": "1",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "", "name": "",
"params": [], "params": [],
@@ -23,11 +23,10 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
}, }
"requestVariables": [],
}, },
{ {
"v": "2", "v": "1",
"endpoint": "https://echo.hoppscotch.dio/<<HEADERS_TYPE2>>", "endpoint": "https://echo.hoppscotch.dio/<<HEADERS_TYPE2>>",
"name": "success", "name": "success",
"params": [], "params": [],
@@ -45,8 +44,7 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
}, }
"requestVariables": []
} }
] ]
} }

View File

@@ -4,7 +4,7 @@
"folders": [], "folders": [],
"requests": "requests":
{ {
"v": "2", "v": "1",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "fail", "name": "fail",
"params": [], "params": [],
@@ -22,11 +22,10 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
}, }
"requestVariables": [],
}, },
{ {
"v": "2", "v": "1",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>",
"name": "success", "name": "success",
"params": [], "params": [],
@@ -44,8 +43,7 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
}, }
"requestVariables": []
} }
] ]
} }

View File

@@ -4,7 +4,7 @@
"folders": [], "folders": [],
"requests": "requests":
{ {
"v": "2", "v": "1",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "fail", "name": "fail",
"params": [], "params": [],
@@ -22,8 +22,7 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
}, }
"requestVariables": []
} }
] ]
} }

View File

@@ -5,7 +5,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "2", "v": "1",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "", "name": "",
"params": [], "params": [],
@@ -23,11 +23,10 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
}, }
"requestVariables": []
}, },
{ {
"v": "2", "v": "1",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>",
"name": "success", "name": "success",
"params": [], "params": [],
@@ -45,8 +44,7 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
}, }
"requestVariables": []
} }
] ]
} }

View File

@@ -1,17 +1,10 @@
import { exec } from "child_process"; import { exec } from "child_process";
import { resolve } from "path";
import { ExecResponse } from "./types"; import { ExecResponse } from "./types";
export const runCLI = (args: string, options = {}): Promise<ExecResponse> => export const execAsync = (command: string): Promise<ExecResponse> =>
{ new Promise((resolve) =>
const CLI_PATH = resolve(__dirname, "../../bin/hopp"); exec(command, (error, stdout, stderr) => resolve({ error, stdout, stderr }))
const command = `node ${CLI_PATH} ${args}` );
return new Promise((resolve) =>
exec(command, options, (error, stdout, stderr) => resolve({ error, stdout, stderr }))
);
}
export const trimAnsi = (target: string) => { export const trimAnsi = (target: string) => {
const ansiRegex = const ansiRegex =
@@ -22,15 +15,12 @@ export const trimAnsi = (target: string) => {
export const getErrorCode = (out: string) => { export const getErrorCode = (out: string) => {
const ansiTrimmedStr = trimAnsi(out); const ansiTrimmedStr = trimAnsi(out);
return ansiTrimmedStr.split(" ")[0]; return ansiTrimmedStr.split(" ")[0];
}; };
export const getTestJsonFilePath = (file: string, kind: "collection" | "environment") => { export const getTestJsonFilePath = (file: string) => {
const kindDir = { const filePath = `${process.cwd()}/src/__tests__/samples/${file}`;
collection: "collections",
environment: "environments",
}[kind];
const filePath = resolve(__dirname, `../../src/__tests__/samples/${kindDir}/${file}`);
return filePath; return filePath;
}; };

View File

@@ -1,5 +1,5 @@
import chalk from "chalk"; import chalk from "chalk";
import { Command } from "commander"; import { program } from "commander";
import * as E from "fp-ts/Either"; import * as E from "fp-ts/Either";
import { version } from "../package.json"; import { version } from "../package.json";
import { test } from "./commands/test"; import { test } from "./commands/test";
@@ -20,8 +20,6 @@ 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"
)}`; )}`;
const program = new Command()
program program
.name("hopp") .name("hopp")
.version(version, "-v, --ver", "see the current version of hopp-cli") .version(version, "-v, --ver", "see the current version of hopp-cli")

View File

@@ -21,7 +21,6 @@ export interface RequestStack {
*/ */
export interface RequestConfig extends AxiosRequestConfig { export interface RequestConfig extends AxiosRequestConfig {
supported: boolean; supported: boolean;
displayUrl?: string
} }
export interface EffectiveHoppRESTRequest extends HoppRESTRequest { export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
@@ -31,7 +30,6 @@ export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
* This contains path, params and environment variables all applied to it * This contains path, params and environment variables all applied to it
*/ */
effectiveFinalURL: string; effectiveFinalURL: string;
effectiveFinalDisplayURL?: string;
effectiveFinalHeaders: { key: string; value: string; active: boolean }[]; effectiveFinalHeaders: { key: string; value: string; active: boolean }[];
effectiveFinalParams: { key: string; value: string; active: boolean }[]; effectiveFinalParams: { key: string; value: string; active: boolean }[];
effectiveFinalBody: FormData | string | null; effectiveFinalBody: FormData | string | null;

View File

@@ -1,42 +1,34 @@
import { Environment } from "@hoppscotch/data";
import { entityReference } from "verzod";
import { z } from "zod";
import { error } from "../../types/errors"; import { error } from "../../types/errors";
import { import {
HoppEnvKeyPairObject, HoppEnvs,
HoppEnvPair, HoppEnvPair,
HoppEnvs HoppEnvKeyPairObject,
HoppEnvExportObject,
HoppBulkEnvExportObject,
} from "../../types/request"; } from "../../types/request";
import { readJsonFile } from "../../utils/mutators"; import { readJsonFile } from "../../utils/mutators";
/** /**
* Parses env json file for given path and validates the parsed env json object * Parses env json file for given path and validates the parsed env json object.
* @param path Path of env.json file to be parsed * @param path Path of env.json file to be parsed.
* @returns For successful parsing we get HoppEnvs object * @returns For successful parsing we get HoppEnvs object.
*/ */
export async function parseEnvsData(path: string) { export async function parseEnvsData(path: string) {
const contents = await readJsonFile(path); const contents = await readJsonFile(path);
const envPairs: Array<Environment["variables"][number] | HoppEnvPair> = []; const envPairs: Array<HoppEnvPair> = [];
// The legacy key-value pair format that is still supported
const HoppEnvKeyPairResult = HoppEnvKeyPairObject.safeParse(contents); const HoppEnvKeyPairResult = HoppEnvKeyPairObject.safeParse(contents);
const HoppEnvExportObjectResult = HoppEnvExportObject.safeParse(contents);
const HoppBulkEnvExportObjectResult =
HoppBulkEnvExportObject.safeParse(contents);
// Shape of the single environment export object that is exported from the app // CLI doesnt support bulk environments export.
const HoppEnvExportObjectResult = Environment.safeParse(contents); // Hence we check for this case and throw an error if it matches the format.
// Shape of the bulk environment export object that is exported from the app
const HoppBulkEnvExportObjectResult = z.array(entityReference(Environment)).safeParse(contents)
// CLI doesnt support bulk environments export
// Hence we check for this case and throw an error if it matches the format
if (HoppBulkEnvExportObjectResult.success) { if (HoppBulkEnvExportObjectResult.success) {
throw error({ code: "BULK_ENV_FILE", path, data: error }); throw error({ code: "BULK_ENV_FILE", path, data: error });
} }
// Checks if the environment file is of the correct format // Checks if the environment file is of the correct format.
// If it doesnt match either of them, we throw an error // If it doesnt match either of them, we throw an error.
if (!HoppEnvKeyPairResult.success && HoppEnvExportObjectResult.type === "err") { if (!(HoppEnvKeyPairResult.success || HoppEnvExportObjectResult.success)) {
throw error({ code: "MALFORMED_ENV_FILE", path, data: error }); throw error({ code: "MALFORMED_ENV_FILE", path, data: error });
} }
@@ -44,8 +36,9 @@ export async function parseEnvsData(path: string) {
for (const [key, value] of Object.entries(HoppEnvKeyPairResult.data)) { for (const [key, value] of Object.entries(HoppEnvKeyPairResult.data)) {
envPairs.push({ key, value }); envPairs.push({ key, value });
} }
} else if (HoppEnvExportObjectResult.type === "ok") { } else if (HoppEnvExportObjectResult.success) {
envPairs.push(...HoppEnvExportObjectResult.value.variables); const { key, value } = HoppEnvExportObjectResult.data.variables[0];
envPairs.push({ key, value });
} }
return <HoppEnvs>{ global: [], selected: envPairs }; return <HoppEnvs>{ global: [], selected: envPairs };

View File

@@ -1,18 +1,31 @@
import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import { z } from "zod";
import { TestReport } from "../interfaces/response"; import { TestReport } from "../interfaces/response";
import { HoppCLIError } from "./errors"; import { HoppCLIError } from "./errors";
import { z } from "zod";
export type FormDataEntry = { export type FormDataEntry = {
key: string; key: string;
value: string | Blob; value: string | Blob;
}; };
export type HoppEnvPair = Environment["variables"][number]; export type HoppEnvPair = { key: string; value: string };
export const HoppEnvKeyPairObject = z.record(z.string(), z.string()); export const HoppEnvKeyPairObject = z.record(z.string(), z.string());
// Shape of the single environment export object that is exported from the app.
export const HoppEnvExportObject = z.object({
name: z.string(),
variables: z.array(
z.object({
key: z.string(),
value: z.string(),
})
),
});
// Shape of the bulk environment export object that is exported from the app.
export const HoppBulkEnvExportObject = z.array(HoppEnvExportObject);
export type HoppEnvs = { export type HoppEnvs = {
global: HoppEnvPair[]; global: HoppEnvPair[];
selected: HoppEnvPair[]; selected: HoppEnvPair[];

View File

@@ -1,23 +1,21 @@
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import chalk from "chalk";
import { log } from "console";
import * as A from "fp-ts/Array"; import * as A from "fp-ts/Array";
import { pipe } from "fp-ts/function"; import { pipe } from "fp-ts/function";
import { round } from "lodash-es"; import { bold } from "chalk";
import { log } from "console";
import { CollectionRunnerParam } from "../types/collections"; import round from "lodash/round";
import { HoppCollection } from "@hoppscotch/data";
import { import {
CollectionStack,
HoppEnvs, HoppEnvs,
ProcessRequestParams, CollectionStack,
RequestReport, RequestReport,
ProcessRequestParams,
} from "../types/request"; } from "../types/request";
import { import {
PreRequestMetrics, getRequestMetrics,
RequestMetrics, preProcessRequest,
TestMetrics, processRequest,
} from "../types/response"; } from "./request";
import { DEFAULT_DURATION_PRECISION } from "./constants"; import { exceptionColors } from "./getters";
import { import {
printErrorsReport, printErrorsReport,
printFailedTestsReport, printFailedTestsReport,
@@ -25,14 +23,15 @@ import {
printRequestsMetrics, printRequestsMetrics,
printTestsMetrics, printTestsMetrics,
} from "./display"; } from "./display";
import { exceptionColors } from "./getters";
import { getPreRequestMetrics } from "./pre-request";
import { import {
getRequestMetrics, PreRequestMetrics,
preProcessRequest, RequestMetrics,
processRequest, TestMetrics,
} from "./request"; } from "../types/response";
import { getTestMetrics } from "./test"; import { getTestMetrics } from "./test";
import { DEFAULT_DURATION_PRECISION } from "./constants";
import { getPreRequestMetrics } from "./pre-request";
import { CollectionRunnerParam } from "../types/collections";
const { WARN, FAIL } = exceptionColors; const { WARN, FAIL } = exceptionColors;
@@ -56,19 +55,19 @@ export const collectionsRunner = async (
// Pop out top-most collection from stack to be processed. // Pop out top-most collection from stack to be processed.
const { collection, path } = <CollectionStack>collectionStack.pop(); const { collection, path } = <CollectionStack>collectionStack.pop();
// Processing each request in collection // Processing each request in collection
for (const request of collection.requests) { for (const request of collection.requests) {
const _request = preProcessRequest(request as HoppRESTRequest, collection); const _request = preProcessRequest(request);
const requestPath = `${path}/${_request.name}`; const requestPath = `${path}/${_request.name}`;
const processRequestParams: ProcessRequestParams = { const processRequestParams: ProcessRequestParams = {
path: requestPath, path: requestPath,
request: _request, request: _request,
envs, envs,
delay, delay,
}; };
// Request processing initiated message. // Request processing initiated message.
log(WARN(`\nRunning: ${chalk.bold(requestPath)}`)); log(WARN(`\nRunning: ${bold(requestPath)}`));
// Processing current request. // Processing current request.
const result = await processRequest(processRequestParams)(); const result = await processRequest(processRequestParams)();
@@ -78,35 +77,20 @@ export const collectionsRunner = async (
envs.global = global; envs.global = global;
envs.selected = selected; envs.selected = selected;
// Storing current request's report. // Storing current request's report.
const requestReport = result.report; const requestReport = result.report;
requestsReport.push(requestReport); 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,
});
}
} }
// Pushing remaining folders realted collection to stack.
for (const folder of collection.folders) {
collectionStack.push({
path: `${path}/${folder.name}`,
collection: folder,
});
}
}
return requestsReport; return requestsReport;
}; };

View File

@@ -1,4 +1,4 @@
import chalk from "chalk"; import { bold } from "chalk";
import { groupEnd, group, log } from "console"; import { groupEnd, group, log } from "console";
import { handleError } from "../handlers/error"; import { handleError } from "../handlers/error";
import { RequestConfig } from "../interfaces/request"; import { RequestConfig } from "../interfaces/request";
@@ -120,7 +120,7 @@ export const printErrorsReport = (
errorsReport: HoppCLIError[] errorsReport: HoppCLIError[]
) => { ) => {
if (errorsReport.length > 0) { if (errorsReport.length > 0) {
const REPORTED_ERRORS_TITLE = FAIL(`\n${chalk.bold(path)} reported errors:`); const REPORTED_ERRORS_TITLE = FAIL(`\n${bold(path)} reported errors:`);
group(REPORTED_ERRORS_TITLE); group(REPORTED_ERRORS_TITLE);
for (const errorReport of errorsReport) { for (const errorReport of errorsReport) {
@@ -143,7 +143,7 @@ export const printFailedTestsReport = (
// Only printing test-reports with failed test-cases. // Only printing test-reports with failed test-cases.
if (failedTestsReport.length > 0) { if (failedTestsReport.length > 0) {
const FAILED_TESTS_PATH = FAIL(`\n${chalk.bold(path)} failed tests:`); const FAILED_TESTS_PATH = FAIL(`\n${bold(path)} failed tests:`);
group(FAILED_TESTS_PATH); group(FAILED_TESTS_PATH);
for (const failedTestReport of failedTestsReport) { for (const failedTestReport of failedTestsReport) {
@@ -176,7 +176,7 @@ export const printRequestRunner = {
*/ */
start: (requestConfig: RequestConfig) => { start: (requestConfig: RequestConfig) => {
const METHOD = BG_INFO(` ${requestConfig.method} `); const METHOD = BG_INFO(` ${requestConfig.method} `);
const ENDPOINT = requestConfig.displayUrl || requestConfig.url; const ENDPOINT = requestConfig.url;
process.stdout.write(`${METHOD} ${ENDPOINT}`); process.stdout.write(`${METHOD} ${ENDPOINT}`);
}, },

View File

@@ -1,4 +1,4 @@
import { clone } from "lodash-es"; import { clone } from "lodash";
/** /**
* Sorts the array based on the sort func. * Sorts the array based on the sort func.

View File

@@ -11,7 +11,7 @@ import * as E from "fp-ts/Either";
import * as S from "fp-ts/string"; import * as S from "fp-ts/string";
import * as O from "fp-ts/Option"; import * as O from "fp-ts/Option";
import { error } from "../types/errors"; import { error } from "../types/errors";
import { round } from "lodash-es"; import round from "lodash/round";
import { DEFAULT_DURATION_PRECISION } from "./constants"; import { DEFAULT_DURATION_PRECISION } from "./constants";
/** /**

View File

@@ -36,10 +36,7 @@ import { toFormData } from "./mutators";
export const preRequestScriptRunner = ( export const preRequestScriptRunner = (
request: HoppRESTRequest, request: HoppRESTRequest,
envs: HoppEnvs envs: HoppEnvs
): TE.TaskEither< ): TE.TaskEither<HoppCLIError, EffectiveHoppRESTRequest> =>
HoppCLIError,
{ effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs }
> =>
pipe( pipe(
TE.of(request), TE.of(request),
TE.chain(({ preRequestScript }) => TE.chain(({ preRequestScript }) =>
@@ -71,10 +68,7 @@ export const preRequestScriptRunner = (
export function getEffectiveRESTRequest( export function getEffectiveRESTRequest(
request: HoppRESTRequest, request: HoppRESTRequest,
environment: Environment environment: Environment
): E.Either< ): E.Either<HoppCLIError, EffectiveHoppRESTRequest> {
HoppCLIError,
{ effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs }
> {
const envVariables = environment.variables; const envVariables = environment.variables;
// Parsing final headers with applied ENVs. // Parsing final headers with applied ENVs.
@@ -168,30 +162,12 @@ export function getEffectiveRESTRequest(
} }
const effectiveFinalURL = _effectiveFinalURL.right; const effectiveFinalURL = _effectiveFinalURL.right;
// Secret environment variables referenced in the request endpoint should be masked
let effectiveFinalDisplayURL;
if (envVariables.some(({ secret }) => secret)) {
const _effectiveFinalDisplayURL = parseTemplateStringE(
request.endpoint,
envVariables,
true
);
if (E.isRight(_effectiveFinalDisplayURL)) {
effectiveFinalDisplayURL = _effectiveFinalDisplayURL.right;
}
}
return E.right({ return E.right({
effectiveRequest: { ...request,
...request, effectiveFinalURL,
effectiveFinalURL, effectiveFinalHeaders,
effectiveFinalDisplayURL, effectiveFinalParams,
effectiveFinalHeaders, effectiveFinalBody,
effectiveFinalParams,
effectiveFinalBody,
},
updatedEnvs: { global: [], selected: envVariables },
}); });
} }

View File

@@ -1,66 +1,34 @@
import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import axios, { Method } from "axios"; import axios, { Method } from "axios";
import * as A from "fp-ts/Array";
import * as E from "fp-ts/Either";
import * as T from "fp-ts/Task";
import * as TE from "fp-ts/TaskEither";
import { pipe } from "fp-ts/function";
import * as S from "fp-ts/string";
import { hrtime } from "process";
import { URL } from "url"; import { URL } from "url";
import { EffectiveHoppRESTRequest, RequestConfig } from "../interfaces/request"; import * as S from "fp-ts/string";
import * as A from "fp-ts/Array";
import * as T from "fp-ts/Task";
import * as E from "fp-ts/Either";
import * as TE from "fp-ts/TaskEither";
import { HoppRESTRequest } from "@hoppscotch/data";
import { responseErrors } from "./constants";
import { getDurationInSeconds, getMetaDataPairs } from "./getters";
import { testRunner, getTestScriptParams, hasFailedTestCases } from "./test";
import { RequestConfig, EffectiveHoppRESTRequest } from "../interfaces/request";
import { RequestRunnerResponse } from "../interfaces/response"; import { RequestRunnerResponse } from "../interfaces/response";
import { HoppCLIError, error } from "../types/errors"; import { preRequestScriptRunner } from "./pre-request";
import { import {
HoppEnvs, HoppEnvs,
ProcessRequestParams, ProcessRequestParams,
RequestReport, RequestReport,
} from "../types/request"; } from "../types/request";
import { RequestMetrics } from "../types/response";
import { responseErrors } from "./constants";
import { import {
printPreRequestRunner, printPreRequestRunner,
printRequestRunner, printRequestRunner,
printTestRunner, printTestRunner,
} from "./display"; } from "./display";
import { getDurationInSeconds, getMetaDataPairs } from "./getters"; import { error, HoppCLIError } from "../types/errors";
import { preRequestScriptRunner } from "./pre-request"; import { hrtime } from "process";
import { getTestScriptParams, hasFailedTestCases, testRunner } from "./test"; import { RequestMetrics } from "../types/response";
import { pipe } from "fp-ts/function";
// !NOTE: The `config.supported` checks are temporary until OAuth2 and Multipart Forms are supported // !NOTE: The `config.supported` checks are temporary until OAuth2 and Multipart Forms are supported
/**
* Processes given variable, which includes checking for secret variables
* and getting value from system environment
* @param variable Variable to be processed
* @returns Updated variable with value from system environment
*/
const processVariables = (variable: Environment["variables"][number]) => {
if (variable.secret) {
return {
...variable,
value:
"value" in variable ? variable.value : process.env[variable.key] || "",
}
}
return variable
}
/**
* Processes given envs, which includes processing each variable in global
* and selected envs
* @param envs Global + selected envs used by requests with in collection
* @returns Processed envs with each variable processed
*/
const processEnvs = (envs: HoppEnvs) => {
const processedEnvs = {
global: envs.global.map(processVariables),
selected: envs.selected.map(processVariables),
}
return processedEnvs
}
/** /**
* Transforms given request data to request-config used by request-runner to * Transforms given request data to request-config used by request-runner to
* perform HTTP request. * perform HTTP request.
@@ -70,7 +38,6 @@ const processEnvs = (envs: HoppEnvs) => {
export const createRequest = (req: EffectiveHoppRESTRequest): RequestConfig => { export const createRequest = (req: EffectiveHoppRESTRequest): RequestConfig => {
const config: RequestConfig = { const config: RequestConfig = {
supported: true, supported: true,
displayUrl: req.effectiveFinalDisplayURL
}; };
const { finalBody, finalEndpoint, finalHeaders, finalParams } = getRequest; const { finalBody, finalEndpoint, finalHeaders, finalParams } = getRequest;
const reqParams = finalParams(req); const reqParams = finalParams(req);
@@ -254,13 +221,9 @@ export const processRequest =
effectiveFinalParams: [], effectiveFinalParams: [],
effectiveFinalURL: "", effectiveFinalURL: "",
}; };
let updatedEnvs = <HoppEnvs>{};
// Fetch values for secret environment variables from system environment
const processedEnvs = processEnvs(envs)
// Executing pre-request-script // Executing pre-request-script
const preRequestRes = await preRequestScriptRunner(request, processedEnvs)(); const preRequestRes = await preRequestScriptRunner(request, envs)();
if (E.isLeft(preRequestRes)) { if (E.isLeft(preRequestRes)) {
printPreRequestRunner.fail(); printPreRequestRunner.fail();
@@ -268,8 +231,8 @@ export const processRequest =
report.errors.push(preRequestRes.left); report.errors.push(preRequestRes.left);
report.result = report.result && false; report.result = report.result && false;
} else { } else {
// Updating effective-request and consuming updated envs after pre-request script execution // Updating effective-request
({ effectiveRequest, updatedEnvs } = preRequestRes.right); effectiveRequest = preRequestRes.right;
} }
// Creating request-config for request-runner. // Creating request-config for request-runner.
@@ -307,7 +270,7 @@ export const processRequest =
const testScriptParams = getTestScriptParams( const testScriptParams = getTestScriptParams(
_requestRunnerRes, _requestRunnerRes,
request, request,
updatedEnvs envs
); );
// Executing test-runner. // Executing test-runner.
@@ -346,12 +309,9 @@ export const processRequest =
* @returns Updated request object free of invalid/missing data. * @returns Updated request object free of invalid/missing data.
*/ */
export const preProcessRequest = ( export const preProcessRequest = (
request: HoppRESTRequest, request: HoppRESTRequest
collection: HoppCollection,
): HoppRESTRequest => { ): HoppRESTRequest => {
const tempRequest = Object.assign({}, request); const tempRequest = Object.assign({}, request);
const { headers: parentHeaders, auth: parentAuth } = collection;
if (!tempRequest.v) { if (!tempRequest.v) {
tempRequest.v = "1"; tempRequest.v = "1";
} }
@@ -367,31 +327,18 @@ export const preProcessRequest = (
if (!tempRequest.params) { if (!tempRequest.params) {
tempRequest.params = []; tempRequest.params = [];
} }
if (!tempRequest.headers) {
if (parentHeaders?.length) {
// Filter out header entries present in the parent (folder/collection) under the same name
// This ensures the child headers take precedence over the parent headers
const filteredEntries = parentHeaders.filter((parentHeaderEntries) => {
return !tempRequest.headers.some((reqHeaderEntries) => reqHeaderEntries.key === parentHeaderEntries.key)
})
tempRequest.headers.push(...filteredEntries);
} else if (!tempRequest.headers) {
tempRequest.headers = []; tempRequest.headers = [];
} }
if (!tempRequest.preRequestScript) { if (!tempRequest.preRequestScript) {
tempRequest.preRequestScript = ""; tempRequest.preRequestScript = "";
} }
if (!tempRequest.testScript) { if (!tempRequest.testScript) {
tempRequest.testScript = ""; tempRequest.testScript = "";
} }
if (!tempRequest.auth) {
if (tempRequest.auth?.authType === "inherit") {
tempRequest.auth = parentAuth;
} else if (!tempRequest.auth) {
tempRequest.auth = { authActive: false, authType: "none" }; tempRequest.auth = { authActive: false, authType: "none" };
} }
if (!tempRequest.body) { if (!tempRequest.body) {
tempRequest.body = { contentType: null, body: null }; tempRequest.body = { contentType: null, body: null };
} }

View File

@@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ES6",
"module": "ESNext", "module": "commonjs",
"outDir": ".", "outDir": ".",
"rootDir": ".", "rootDir": ".",
"strict": true, "strict": true,

View File

@@ -3,14 +3,17 @@ import { defineConfig } from "tsup";
export default defineConfig({ export default defineConfig({
entry: [ "./src/index.ts" ], entry: [ "./src/index.ts" ],
outDir: "./dist/", outDir: "./dist/",
format: ["esm"], format: ["cjs"],
platform: "node", platform: "node",
sourcemap: true, sourcemap: true,
bundle: true, bundle: true,
target: "esnext", target: "node12",
skipNodeModulesBundle: false, skipNodeModulesBundle: false,
esbuildOptions(options) { esbuildOptions(options) {
options.bundle = true options.bundle = true
}, },
noExternal: [
/\w+/
],
clean: true, clean: true,
}); });

View File

@@ -429,11 +429,6 @@ pre.ace_editor {
} }
} }
.splitpanes__pane {
@apply will-change-auto;
transform: translateZ(0);
}
.smart-splitter .splitpanes__splitter { .smart-splitter .splitpanes__splitter {
@apply relative; @apply relative;
@apply before:absolute; @apply before:absolute;
@@ -563,22 +558,12 @@ details[open] summary .indicator {
.env-highlight { .env-highlight {
@apply text-accentContrast; @apply text-accentContrast;
&.request-variable-highlight { &.env-found {
@apply bg-amber-500; @apply bg-accentDark;
@apply hover:bg-amber-600; @apply hover:bg-accent;
} }
&.environment-variable-highlight { &.env-not-found {
@apply bg-green-500;
@apply hover:bg-green-600;
}
&.global-variable-highlight {
@apply bg-blue-500;
@apply hover:bg-blue-600;
}
&.environment-not-found-highlight {
@apply bg-red-500; @apply bg-red-500;
@apply hover:bg-red-600; @apply hover:bg-red-600;
} }

View File

@@ -17,7 +17,7 @@
--lower-tertiary-sticky-fold: 7.125rem; --lower-tertiary-sticky-fold: 7.125rem;
--lower-fourth-sticky-fold: 9.188rem; --lower-fourth-sticky-fold: 9.188rem;
--sidebar-primary-sticky-fold: 2rem; --sidebar-primary-sticky-fold: 2rem;
--properties-primary-sticky-fold: 2.063rem; --properties-primary-sticky-fold: 2.05rem;
} }
@mixin light-theme { @mixin light-theme {

View File

@@ -78,6 +78,12 @@
"iso": "he-HE", "iso": "he-HE",
"name": "עִברִית" "name": "עִברִית"
}, },
{
"code": "hi",
"file": "hi.json",
"iso": "hi-HI",
"name": "हिन्दी"
},
{ {
"code": "hu", "code": "hu",
"file": "hu.json", "file": "hu.json",

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Kanselleer", "cancel": "Kanselleer",
"choose_file": "Kies 'n lêer", "choose_file": "Kies 'n lêer",
@@ -11,7 +10,6 @@
"connect": "Koppel", "connect": "Koppel",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Kopieer", "copy": "Kopieer",
"create": "Create",
"delete": "Vee uit", "delete": "Vee uit",
"disconnect": "Ontkoppel", "disconnect": "Ontkoppel",
"dismiss": "Weier", "dismiss": "Weier",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties",
"remove": "Verwyder", "remove": "Verwyder",
"rename": "Rename", "rename": "Rename",
"restore": "Herstel", "restore": "Herstel",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Soek", "search": "Soek",
"send": "Stuur", "send": "Stuur",
"share": "Share",
"start": "Begin", "start": "Begin",
"starting": "Starting", "starting": "Starting",
"stop": "Stop", "stop": "Stop",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Gesels met ons", "chat_with_us": "Gesels met ons",
"contact_us": "Kontak Ons", "contact_us": "Kontak Ons",
"cookies": "Cookies",
"copy": "Kopieer", "copy": "Kopieer",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Sleutelbord kortpaaie", "keyboard_shortcuts": "Sleutelbord kortpaaie",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Nuwe weergawe gevind. Herlaai om op te dateer.", "new_version_found": "Nuwe weergawe gevind. Herlaai om op te dateer.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Volmag privaatheidsbeleid", "proxy_privacy_policy": "Volmag privaatheidsbeleid",
"reload": "Herlaai", "reload": "Herlaai",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Genereer teken", "generate_token": "Genereer teken",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Sluit in by URL", "include_in_url": "Sluit in by URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Leer hoe", "learn": "Leer hoe",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Wagwoord", "password": "Wagwoord",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Teken", "token": "Teken",
"type": "Magtigingstipe", "type": "Magtigingstipe",
"username": "Gebruikersnaam" "username": "Gebruikersnaam"
@@ -148,7 +124,6 @@
"created": "Versameling geskep", "created": "Versameling geskep",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Wysig versameling", "edit": "Wysig versameling",
"import_or_create": "Import or create a collection",
"invalid_name": "Gee 'n geldige naam vir die versameling", "invalid_name": "Gee 'n geldige naam vir die versameling",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Nuwe versameling", "new": "Nuwe versameling",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Versameling hernoem", "renamed": "Versameling hernoem",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Stoor as", "save_as": "Stoor as",
@@ -178,7 +151,6 @@
"remove_folder": "Weet u seker dat u hierdie vouer permanent wil uitvee?", "remove_folder": "Weet u seker dat u hierdie vouer permanent wil uitvee?",
"remove_history": "Is u seker dat u alle geskiedenis permanent wil uitvee?", "remove_history": "Is u seker dat u alle geskiedenis permanent wil uitvee?",
"remove_request": "Is u seker dat u hierdie versoek permanent wil uitvee?", "remove_request": "Is u seker dat u hierdie versoek permanent wil uitvee?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Weet u seker dat u hierdie span wil uitvee?", "remove_team": "Weet u seker dat u hierdie span wil uitvee?",
"remove_telemetry": "Weet u seker dat u van Telemetry wil afskakel?", "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.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Koptekst {count}", "header": "Koptekst {count}",
"message": "Boodskap {count}", "message": "Boodskap {count}",
@@ -238,13 +192,11 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protokolle is leeg", "protocols": "Protokolle is leeg",
"schema": "Koppel aan 'n GraphQL -eindpunt", "schema": "Koppel aan 'n GraphQL -eindpunt",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Spannaam leeg", "team_name": "Spannaam leeg",
"teams": "Spanne is leeg", "teams": "Spanne is leeg",
"tests": "Daar is geen toetse vir hierdie versoek nie", "tests": "Daar is geen toetse vir hierdie versoek nie"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Gee 'n geldige naam vir die omgewing", "invalid_name": "Gee 'n geldige naam vir die omgewing",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Veranderlike lys" "variable_list": "Veranderlike lys"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Dit lyk nie asof hierdie blaaier ondersteuning vir bedieners gestuurde geleenthede het nie.", "browser_support_sse": "Dit lyk nie asof hierdie blaaier ondersteuning vir bedieners gestuurde geleenthede het nie.",
"check_console_details": "Kyk na die konsole -log vir meer inligting.", "check_console_details": "Kyk na die konsole -log vir meer inligting.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL is nie behoorlik geformateer nie", "curl_invalid_format": "cURL is nie behoorlik geformateer nie",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Kon nie 'n ongeldige liggaam mooi maak nie, los json -sintaksisfoute op en probeer weer", "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.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "Kon nie versoek stuur nie", "network_fail": "Kon nie versoek stuur nie",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Geen duur nie", "no_duration": "Geen duur nie",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "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", "proxy_error": "Proxy error",
"script_fail": "Kon nie voorafversoekskrip uitvoer nie", "script_fail": "Kon nie voorafversoekskrip uitvoer nie",
"something_went_wrong": "Iets het verkeerd geloop", "something_went_wrong": "Iets het verkeerd geloop",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Uitvoer as JSON", "as_json": "Uitvoer as JSON",
"create_secret_gist": "Skep geheime Gist", "create_secret_gist": "Skep geheime Gist",
"failed": "Something went wrong while exporting",
"gist_created": "Gis geskep", "gist_created": "Gis geskep",
"require_github": "Teken in met GitHub om 'n geheime idee te skep", "require_github": "Teken in met GitHub om 'n geheime idee te skep",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Inskrywings", "subscriptions": "Inskrywings",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Die magtigingskop sal outomaties gegenereer word wanneer u die versoek stuur.", "authorization": "Die magtigingskop sal outomaties gegenereer word wanneer u die versoek stuur.",
"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.",
"generate_documentation_first": "Genereer eers dokumentasie", "generate_documentation_first": "Genereer eers dokumentasie",
"network_fail": "Kon nie die API -eindpunt bereik nie. Kontroleer u netwerkverbinding en probeer weer.", "network_fail": "Kon nie die API -eindpunt bereik nie. Kontroleer u netwerkverbinding en probeer weer.",
"offline": "Dit lyk asof u vanlyn is. Data in hierdie werkruimte is moontlik nie op datum nie.", "offline": "Dit lyk asof u vanlyn is. Data in hierdie werkruimte is moontlik nie op datum nie.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Voer versamelings in", "collections": "Voer versamelings in",
"curl": "Voer cURL in", "curl": "Voer cURL in",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Invoer misluk", "failed": "Invoer misluk",
"from_file": "Import from File",
"from_gist": "Invoer vanaf Gist", "from_gist": "Invoer vanaf Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Voer Gist URL in", "gist_url": "Voer Gist URL in",
"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_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "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_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "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"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Versamelings", "collections": "Versamelings",
"confirm": "Bevestig", "confirm": "Bevestig",
"customize_request": "Customize Request",
"edit_request": "Wysig versoek", "edit_request": "Wysig versoek",
"import_export": "Invoer uitvoer", "import_export": "Invoer uitvoer"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Kopieer skakel",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Duur", "duration": "Duur",
"enter_curl": "Voer cURL in", "enter_curl": "Voer cURL in",
"generate_code": "Genereer kode", "generate_code": "Genereer kode",
"generated_code": "Kode gegenereer", "generated_code": "Kode gegenereer",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Koplys", "header_list": "Koplys",
"invalid_name": "Gee 'n naam vir die versoek", "invalid_name": "Gee 'n naam vir die versoek",
"method": "Metode", "method": "Metode",
@@ -552,14 +480,12 @@
"saved": "Versoek gestoor", "saved": "Versoek gestoor",
"share": "Deel", "share": "Deel",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Versoek", "title": "Versoek",
"type": "Soort versoek", "type": "Soort versoek",
"url": "URL", "url": "URL",
"variables": "Veranderlikes", "variables": "Veranderlikes",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Kopieer skakel"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Pas u rekeninginstellings aan.", "account_description": "Pas u rekeninginstellings aan.",
"account_email_description": "Jou primêre e -posadres.", "account_email_description": "Jou primêre e -posadres.",
"account_name_description": "Dit is u vertoonnaam.", "account_name_description": "Dit is u vertoonnaam.",
"additional": "Additional Settings",
"background": "Agtergrond", "background": "Agtergrond",
"black_mode": "Swart", "black_mode": "Swart",
"choose_language": "Kies taal", "choose_language": "Kies taal",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Kopieer versoekskakel",
"delete_method": "Kies DELETE metode", "delete_method": "Kies DELETE metode",
"get_method": "Kies GET -metode", "get_method": "Kies GET -metode",
"head_method": "Kies HOOF metode", "head_method": "Kies HOOF metode",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Stoor in versamelings", "save_to_collections": "Stoor in versamelings",
"send_request": "Stuur versoek", "send_request": "Stuur versoek",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Versoek", "title": "Versoek"
"copy_request_link": "Kopieer versoekskakel"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Na knipbord gekopieer", "copied_to_clipboard": "Na knipbord gekopieer",
"deleted": "Uitgevee", "deleted": "Uitgevee",
"deprecated": "GEDRAGTEER", "deprecated": "GEDRAGTEER",
@@ -836,12 +742,10 @@
"disconnected": "Ontkoppel", "disconnected": "Ontkoppel",
"disconnected_from": "Ontkoppel van {name}", "disconnected_from": "Ontkoppel van {name}",
"docs_generated": "Dokumentasie gegenereer", "docs_generated": "Dokumentasie gegenereer",
"download_failed": "Download failed",
"download_started": "Aflaai begin", "download_started": "Aflaai begin",
"enabled": "Geaktiveer", "enabled": "Geaktiveer",
"file_imported": "Lêer ingevoer", "file_imported": "Lêer ingevoer",
"finished_in": "Klaar in {duration} ms", "finished_in": "Klaar in {duration} ms",
"hide": "Hide",
"history_deleted": "Geskiedenis uitgevee", "history_deleted": "Geskiedenis uitgevee",
"linewrap": "Draai lyne toe", "linewrap": "Draai lyne toe",
"loading": "Laai tans ...", "loading": "Laai tans ...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Navrae", "queries": "Navrae",
"query": "Navraag", "query": "Navraag",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Toetse", "tests": "Toetse",
@@ -905,7 +807,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Verlaat span", "exit": "Verlaat span",
"exit_disabled": "Slegs eienaar kan nie die span verlaat nie", "exit_disabled": "Slegs eienaar kan nie die span verlaat nie",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Die e -posformaat is ongeldig", "invalid_email_format": "Die e -posformaat is ongeldig",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Span gered", "saved": "Span gered",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Spanne", "title": "Spanne",
"we_sent_invite_link": "We sent an invite link to all invitees!", "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." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "الغاء", "cancel": "الغاء",
"choose_file": "اختيار ملف", "choose_file": "اختيار ملف",
@@ -11,7 +10,6 @@
"connect": "الاتصال", "connect": "الاتصال",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "نسخ", "copy": "نسخ",
"create": "Create",
"delete": "حذف", "delete": "حذف",
"disconnect": "قطع الاتصال", "disconnect": "قطع الاتصال",
"dismiss": "رفض", "dismiss": "رفض",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "لصق", "paste": "لصق",
"prettify": "جمال", "prettify": "جمال",
"properties": "Properties",
"remove": "ازالة", "remove": "ازالة",
"rename": "Rename", "rename": "Rename",
"restore": "اعادة", "restore": "اعادة",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "بحث", "search": "بحث",
"send": "ارسل", "send": "ارسل",
"share": "Share",
"start": "ابدأ", "start": "ابدأ",
"starting": "Starting", "starting": "Starting",
"stop": "قف", "stop": "قف",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "دردش معنا", "chat_with_us": "دردش معنا",
"contact_us": "اتصل بنا", "contact_us": "اتصل بنا",
"cookies": "Cookies",
"copy": "انسخ", "copy": "انسخ",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "اختصارات لوحة المفاتيح", "keyboard_shortcuts": "اختصارات لوحة المفاتيح",
"name": "هوبسكوتش", "name": "هوبسكوتش",
"new_version_found": "تم العثور على نسخة جديدة. قم بالتحديث للتحديث.", "new_version_found": "تم العثور على نسخة جديدة. قم بالتحديث للتحديث.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "سياسة خصوصية الوكيل", "proxy_privacy_policy": "سياسة خصوصية الوكيل",
"reload": "إعادة تحميل", "reload": "إعادة تحميل",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "توليد رمز", "generate_token": "توليد رمز",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "تضمين في URL", "include_in_url": "تضمين في URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "تعلم كيف", "learn": "تعلم كيف",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "كلمة المرور", "password": "كلمة المرور",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "رمز", "token": "رمز",
"type": "نوع التفويض", "type": "نوع التفويض",
"username": "اسم المستخدم" "username": "اسم المستخدم"
@@ -148,7 +124,6 @@
"created": "تم إنشاء المجموعة", "created": "تم إنشاء المجموعة",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "تحرير المجموعة", "edit": "تحرير المجموعة",
"import_or_create": "Import or create a collection",
"invalid_name": "الرجاء تقديم اسم صالح للمجموعة", "invalid_name": "الرجاء تقديم اسم صالح للمجموعة",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "اسم المجموعة يجب ان لايقل على 3 رموز", "name_length_insufficient": "اسم المجموعة يجب ان لايقل على 3 رموز",
"new": "مجموعة جديدة", "new": "مجموعة جديدة",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "تمت إعادة تسمية المجموعة", "renamed": "تمت إعادة تسمية المجموعة",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "حفظ باسم", "save_as": "حفظ باسم",
@@ -178,7 +151,6 @@
"remove_folder": "هل أنت متأكد أنك تريد حذف هذا المجلد نهائيًا؟", "remove_folder": "هل أنت متأكد أنك تريد حذف هذا المجلد نهائيًا؟",
"remove_history": "هل أنت متأكد أنك تريد حذف كل المحفوظات بشكل دائم؟", "remove_history": "هل أنت متأكد أنك تريد حذف كل المحفوظات بشكل دائم؟",
"remove_request": "هل أنت متأكد أنك تريد حذف هذا الطلب نهائيًا؟", "remove_request": "هل أنت متأكد أنك تريد حذف هذا الطلب نهائيًا؟",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "هل أنت متأكد أنك تريد حذف هذا الفريق؟", "remove_team": "هل أنت متأكد أنك تريد حذف هذا الفريق؟",
"remove_telemetry": "هل أنت متأكد أنك تريد الانسحاب من القياس عن بعد؟", "remove_telemetry": "هل أنت متأكد أنك تريد الانسحاب من القياس عن بعد؟",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "رأس {count}", "header": "رأس {count}",
"message": "الرسالة {count}", "message": "الرسالة {count}",
@@ -238,13 +192,11 @@
"profile": "سجل الدخول لرؤية فريقك", "profile": "سجل الدخول لرؤية فريقك",
"protocols": "البروتوكولات فارغة", "protocols": "البروتوكولات فارغة",
"schema": "اتصل بنقطة نهاية GraphQL", "schema": "اتصل بنقطة نهاية GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "اسم الفريق فارغ", "team_name": "اسم الفريق فارغ",
"teams": "الفرق فارغة", "teams": "الفرق فارغة",
"tests": "لا توجد اختبارات لهذا الطلب", "tests": "لا توجد اختبارات لهذا الطلب"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "الرجاء تقديم اسم صالح للبيئة", "invalid_name": "الرجاء تقديم اسم صالح للبيئة",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "قائمة متغيرة" "variable_list": "قائمة متغيرة"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "يبدو أن هذا المستعرض لا يدعم أحداث إرسال الخادم.", "browser_support_sse": "يبدو أن هذا المستعرض لا يدعم أحداث إرسال الخادم.",
"check_console_details": "تحقق من سجل وحدة التحكم للحصول على التفاصيل.", "check_console_details": "تحقق من سجل وحدة التحكم للحصول على التفاصيل.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "لم يتم تنسيق cURL بشكل صحيح", "curl_invalid_format": "لم يتم تنسيق cURL بشكل صحيح",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "تعذر تجميل جسم غير صالح وحل أخطاء بناء جملة json وحاول مرة أخرى", "json_prettify_invalid_body": "تعذر تجميل جسم غير صالح وحل أخطاء بناء جملة json وحاول مرة أخرى",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "تعذر إرسال الطلب", "network_fail": "تعذر إرسال الطلب",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "لا مدة", "no_duration": "لا مدة",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "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", "proxy_error": "Proxy error",
"script_fail": "تعذر تنفيذ نص الطلب المسبق", "script_fail": "تعذر تنفيذ نص الطلب المسبق",
"something_went_wrong": "هناك خطأ ما", "something_went_wrong": "هناك خطأ ما",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "تصدير بتنسيق JSON", "as_json": "تصدير بتنسيق JSON",
"create_secret_gist": "إنشاء جوهر سري", "create_secret_gist": "إنشاء جوهر سري",
"failed": "Something went wrong while exporting",
"gist_created": "خلقت الجست", "gist_created": "خلقت الجست",
"require_github": "تسجيل الدخول باستخدام GitHub لإنشاء جوهر سري", "require_github": "تسجيل الدخول باستخدام GitHub لإنشاء جوهر سري",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "الاشتراكات", "subscriptions": "الاشتراكات",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "سيتم إنشاء رأس التفويض تلقائيًا عند إرسال الطلب.", "authorization": "سيتم إنشاء رأس التفويض تلقائيًا عند إرسال الطلب.",
"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.",
"generate_documentation_first": "قم بإنشاء الوثائق أولاً", "generate_documentation_first": "قم بإنشاء الوثائق أولاً",
"network_fail": "تعذر الوصول إلى نقطة نهاية API. تحقق من اتصالك بالشبكة وحاول مرة أخرى.", "network_fail": "تعذر الوصول إلى نقطة نهاية API. تحقق من اتصالك بالشبكة وحاول مرة أخرى.",
"offline": "يبدو أنك غير متصل بالإنترنت. قد لا تكون البيانات الموجودة في مساحة العمل هذه محدثة.", "offline": "يبدو أنك غير متصل بالإنترنت. قد لا تكون البيانات الموجودة في مساحة العمل هذه محدثة.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "مجموعات الاستيراد", "collections": "مجموعات الاستيراد",
"curl": "استيراد cURL", "curl": "استيراد cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "فشل الاستيراد", "failed": "فشل الاستيراد",
"from_file": "Import from File",
"from_gist": "الاستيراد من Gist", "from_gist": "الاستيراد من Gist",
"from_gist_description": "استيراد من Gist URL", "from_gist_description": "استيراد من Gist URL",
"from_insomnia": "استيراد من Insomnia", "from_insomnia": "استيراد من Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "استيراد من مجموعة Postman", "from_postman_description": "استيراد من مجموعة Postman",
"from_url": "استيراد من رابط", "from_url": "استيراد من رابط",
"gist_url": "أدخل عنوان URL لـ Gist", "gist_url": "أدخل عنوان URL لـ Gist",
"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_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "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_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "استيراد مجموعة من ملفHoppscotch Collections JSON file", "json_description": "استيراد مجموعة من ملفHoppscotch Collections JSON file",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "يستورد" "title": "يستورد"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "المجموعات", "collections": "المجموعات",
"confirm": "يتأكد", "confirm": "يتأكد",
"customize_request": "Customize Request",
"edit_request": "تحرير الطلب", "edit_request": "تحرير الطلب",
"import_export": "استيراد و تصدير", "import_export": "استيراد و تصدير"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "نسخ الوصلة",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "مدة", "duration": "مدة",
"enter_curl": "أدخل cURL", "enter_curl": "أدخل cURL",
"generate_code": "إنشاء التعليمات البرمجية", "generate_code": "إنشاء التعليمات البرمجية",
"generated_code": "رمز تم إنشاؤه", "generated_code": "رمز تم إنشاؤه",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "قائمة الرأس", "header_list": "قائمة الرأس",
"invalid_name": "يرجى تقديم اسم للطلب", "invalid_name": "يرجى تقديم اسم للطلب",
"method": "طريقة", "method": "طريقة",
@@ -552,14 +480,12 @@
"saved": "تم حفظ الطلب", "saved": "تم حفظ الطلب",
"share": "يشارك", "share": "يشارك",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "طلب", "title": "طلب",
"type": "نوع الطلب", "type": "نوع الطلب",
"url": "URL", "url": "URL",
"variables": "المتغيرات", "variables": "المتغيرات",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "نسخ الوصلة"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "تخصيص إعدادات حسابك.", "account_description": "تخصيص إعدادات حسابك.",
"account_email_description": "عنوان بريدك الإلكتروني الأساسي.", "account_email_description": "عنوان بريدك الإلكتروني الأساسي.",
"account_name_description": "هذا هو اسم العرض الخاص بك.", "account_name_description": "هذا هو اسم العرض الخاص بك.",
"additional": "Additional Settings",
"background": "خلفية", "background": "خلفية",
"black_mode": "أسود", "black_mode": "أسود",
"choose_language": "اختر اللغة", "choose_language": "اختر اللغة",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "تأكيد البريد الإلكتروني" "verify_email": "تأكيد البريد الإلكتروني"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "نسخ ارتباط الطلب",
"delete_method": "حدد طريقة الحذف", "delete_method": "حدد طريقة الحذف",
"get_method": "حدد طريقة GET", "get_method": "حدد طريقة GET",
"head_method": "حدد طريقة HEAD", "head_method": "حدد طريقة HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "حفظ في المجموعات", "save_to_collections": "حفظ في المجموعات",
"send_request": "ارسل طلب", "send_request": "ارسل طلب",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "طلب", "title": "طلب"
"copy_request_link": "نسخ ارتباط الطلب"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "نسخ إلى الحافظة", "copied_to_clipboard": "نسخ إلى الحافظة",
"deleted": "تم الحذف", "deleted": "تم الحذف",
"deprecated": "إهمال", "deprecated": "إهمال",
@@ -836,12 +742,10 @@
"disconnected": "انقطع الاتصال", "disconnected": "انقطع الاتصال",
"disconnected_from": "انقطع الاتصال بـ {name}", "disconnected_from": "انقطع الاتصال بـ {name}",
"docs_generated": "تم إنشاء الوثائق", "docs_generated": "تم إنشاء الوثائق",
"download_failed": "Download failed",
"download_started": "بدأ التنزيل", "download_started": "بدأ التنزيل",
"enabled": "ممكن", "enabled": "ممكن",
"file_imported": "تم استيراد الملف", "file_imported": "تم استيراد الملف",
"finished_in": "انتهى في {duration} مللي ثانية", "finished_in": "انتهى في {duration} مللي ثانية",
"hide": "Hide",
"history_deleted": "تم حذف السجل", "history_deleted": "تم حذف السجل",
"linewrap": "خطوط الالتفاف", "linewrap": "خطوط الالتفاف",
"loading": "تحميل...", "loading": "تحميل...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "استفسارات", "queries": "استفسارات",
"query": "استفسار", "query": "استفسار",
"schema": "مخطط", "schema": "مخطط",
"shared_requests": "Shared Requests",
"socketio": "مقبس", "socketio": "مقبس",
"sse": "SSE", "sse": "SSE",
"tests": "الاختبارات", "tests": "الاختبارات",
@@ -905,7 +807,6 @@
"email_do_not_match": "البريد الإلكتروني لا يتوافق مع معلومات حسابك. اتصل بمدير الفريق.", "email_do_not_match": "البريد الإلكتروني لا يتوافق مع معلومات حسابك. اتصل بمدير الفريق.",
"exit": "فريق الخروج", "exit": "فريق الخروج",
"exit_disabled": "فقط المالك لا يمكنه الخروج من الفريق", "exit_disabled": "فقط المالك لا يمكنه الخروج من الفريق",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "تنسيق البريد الإلكتروني غير صالح", "invalid_email_format": "تنسيق البريد الإلكتروني غير صالح",
"invalid_id": "معرف الفريق غير صالح. اتصل بمدير الفريق.", "invalid_id": "معرف الفريق غير صالح. اتصل بمدير الفريق.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "فريق حفظ", "saved": "فريق حفظ",
"select_a_team": "اختر فريق", "select_a_team": "اختر فريق",
"success_invites": "Success invites",
"title": "فرق", "title": "فرق",
"we_sent_invite_link": "لقد أرسلنا رابط دعوة لجميع المدعوين!", "we_sent_invite_link": "لقد أرسلنا رابط دعوة لجميع المدعوين!",
"we_sent_invite_link_description": "اطلب من جميع المدعوين التحقق من صندوق الوارد الخاص بهم. انقر على الرابط للانضمام إلى الفريق." "we_sent_invite_link_description": "اطلب من جميع المدعوين التحقق من صندوق الوارد الخاص بهم. انقر على الرابط للانضمام إلى الفريق."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Cancel·lar", "cancel": "Cancel·lar",
"choose_file": "Triar un fitxer", "choose_file": "Triar un fitxer",
@@ -11,7 +10,6 @@
"connect": "Connectar", "connect": "Connectar",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Copiar", "copy": "Copiar",
"create": "Create",
"delete": "Eliminar", "delete": "Eliminar",
"disconnect": "Desconnectar", "disconnect": "Desconnectar",
"dismiss": "Tancar", "dismiss": "Tancar",
@@ -33,7 +31,6 @@
"open_workspace": "Obrir espai de treball", "open_workspace": "Obrir espai de treball",
"paste": "Enganxar", "paste": "Enganxar",
"prettify": "Fes-ho bonic", "prettify": "Fes-ho bonic",
"properties": "Properties",
"remove": "Eliminar", "remove": "Eliminar",
"rename": "Rename", "rename": "Rename",
"restore": "Restaurar", "restore": "Restaurar",
@@ -42,7 +39,6 @@
"scroll_to_top": "Desplaceu-vos cap a dalt", "scroll_to_top": "Desplaceu-vos cap a dalt",
"search": "Cercar", "search": "Cercar",
"send": "Enviar", "send": "Enviar",
"share": "Share",
"start": "Començar", "start": "Començar",
"starting": "Starting", "starting": "Starting",
"stop": "Aturar", "stop": "Aturar",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Xateja amb nosaltres", "chat_with_us": "Xateja amb nosaltres",
"contact_us": "Contacta amb nosaltres", "contact_us": "Contacta amb nosaltres",
"cookies": "Cookies",
"copy": "Copiar", "copy": "Copiar",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copiar User Auth Token", "copy_user_id": "Copiar User Auth Token",
"developer_option": "Opcions de desenvolupador", "developer_option": "Opcions de desenvolupador",
"developer_option_description": "Eines de desenvolupament que ajuden en el desenvolupament i manteniment de Hoppscotch.", "developer_option_description": "Eines de desenvolupament que ajuden en el desenvolupament i manteniment de Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Dreceres de teclat", "keyboard_shortcuts": "Dreceres de teclat",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "S'ha trobat una nova versió. Refresca per actualitzar.", "new_version_found": "S'ha trobat una nova versió. Refresca per actualitzar.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Opcions", "options": "Opcions",
"proxy_privacy_policy": "Política de privadesa del servidor intermediari (proxy)", "proxy_privacy_policy": "Política de privadesa del servidor intermediari (proxy)",
"reload": "Recarregar", "reload": "Recarregar",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generar Token", "generate_token": "Generar Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Inclou a l'URL", "include_in_url": "Inclou a l'URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Aprèn com", "learn": "Aprèn com",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Passar per", "pass_key_by": "Passar per",
"password": "Contrasenya", "password": "Contrasenya",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
"type": "Tipus d'autorització", "type": "Tipus d'autorització",
"username": "Nom d'usuari" "username": "Nom d'usuari"
@@ -148,7 +124,6 @@
"created": "Col·lecció creada", "created": "Col·lecció creada",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Editar la col·lecció", "edit": "Editar la col·lecció",
"import_or_create": "Import or create a collection",
"invalid_name": "Proporcioneu un nom vàlid per a la col·lecció", "invalid_name": "Proporcioneu un nom vàlid per a la col·lecció",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "El nom de la col·lecció ha de tenir almenys 3 caràcters", "name_length_insufficient": "El nom de la col·lecció ha de tenir almenys 3 caràcters",
"new": "Nova col · lecció", "new": "Nova col · lecció",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "S'ha canviat el nom de la col·lecció", "renamed": "S'ha canviat el nom de la col·lecció",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Guardar com", "save_as": "Guardar com",
@@ -178,7 +151,6 @@
"remove_folder": "Està segur que vol suprimir definitivament aquesta carpeta?", "remove_folder": "Està segur que vol suprimir definitivament aquesta carpeta?",
"remove_history": "Està segur que vol suprimir definitivament tot l'historial?", "remove_history": "Està segur que vol suprimir definitivament tot l'historial?",
"remove_request": "Està segur que vol suprimir definitivament aquesta sol·licitud?", "remove_request": "Està segur que vol suprimir definitivament aquesta sol·licitud?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Està segur que vol suprimir aquest equip?", "remove_team": "Està segur que vol suprimir aquest equip?",
"remove_telemetry": "Està segur que vol desactivar Telemetry?", "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.", "request_change": "Està segur que vol descartar la sol·licitud actual, els canvis no desats es perdran.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Capçalera {count}", "header": "Capçalera {count}",
"message": "Missatges {count}", "message": "Missatges {count}",
@@ -238,13 +192,11 @@
"profile": "Inicia sessió per veure el vostre perfil", "profile": "Inicia sessió per veure el vostre perfil",
"protocols": "Els protocols estan buits", "protocols": "Els protocols estan buits",
"schema": "Connecta't a un endpoint GraphQL", "schema": "Connecta't a un endpoint GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Els shortcodes estan buits",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "El nom de l'equip és buit", "team_name": "El nom de l'equip és buit",
"teams": "Els equips estan buits", "teams": "Els equips estan buits",
"tests": "No hi ha proves per a aquesta sol·licitud", "tests": "No hi ha proves per a aquesta sol·licitud"
"shortcodes": "Els shortcodes estan buits"
}, },
"environment": { "environment": {
"add_to_global": "Afegir-ho a Global", "add_to_global": "Afegir-ho a Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Proporcioneu un nom vàlid per a l'entorn", "invalid_name": "Proporcioneu un nom vàlid per a l'entorn",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Llista de variables" "variable_list": "Llista de variables"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Sembla que aquest navegador no és compatible amb els Esdeveniments Enviats pel Servidor (Server Sent Events).", "browser_support_sse": "Sembla que aquest navegador no és compatible amb els Esdeveniments Enviats pel Servidor (Server Sent Events).",
"check_console_details": "Consulta el registre de la consola per obtenir més informació.", "check_console_details": "Consulta el registre de la consola per obtenir més informació.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL no està formatat correctament", "curl_invalid_format": "cURL no està formatat correctament",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"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", "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.", "network_error": "Sembla que hi ha un error de xarxa. Si us plau torna-ho a provar.",
"network_fail": "No s'ha pogut enviar la sol·licitud", "network_fail": "No s'ha pogut enviar la sol·licitud",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Sense durada", "no_duration": "Sense durada",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No s'ha trobat cap coincidència", "no_results_found": "No s'ha trobat cap coincidència",
"page_not_found": "This page could not be found", "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", "proxy_error": "Proxy error",
"script_fail": "No s'ha pogut executar l'script de sol·licitud prèvia", "script_fail": "No s'ha pogut executar l'script de sol·licitud prèvia",
"something_went_wrong": "Alguna cosa ha anat malament", "something_went_wrong": "Alguna cosa ha anat malament",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Exporta com a JSON", "as_json": "Exporta com a JSON",
"create_secret_gist": "Crear un Gist secret", "create_secret_gist": "Crear un Gist secret",
"failed": "Something went wrong while exporting",
"gist_created": "Gist creat", "gist_created": "Gist creat",
"require_github": "Inicieu la sessió amb GitHub per crear un Gisst secret", "require_github": "Inicieu la sessió amb GitHub per crear un Gisst secret",
"title": "Exportar" "title": "Exportar"
@@ -341,9 +286,6 @@
"subscriptions": "Subscripcions", "subscriptions": "Subscripcions",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "La capçalera de l'autorització es generarà automàticament quan envieu la sol·licitud.", "authorization": "La capçalera de l'autorització es generarà automàticament quan envieu la sol·licitud.",
"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.",
"generate_documentation_first": "Genereu documentació primer", "generate_documentation_first": "Genereu documentació primer",
"network_fail": "No es pot arribar al punt final de l'API. Comproveu la connexió de xarxa i torneu-ho a provar.", "network_fail": "No es pot arribar al punt final de l'API. Comproveu la connexió de xarxa i torneu-ho a provar.",
"offline": "Sembla que estàs fora de línia. És possible que les dades d'aquest espai de treball no estiguin actualitzades.", "offline": "Sembla que estàs fora de línia. És possible que les dades d'aquest espai de treball no estiguin actualitzades.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Importar col·leccions", "collections": "Importar col·leccions",
"curl": "Importar cURL", "curl": "Importar cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "La importació ha fallat", "failed": "La importació ha fallat",
"from_file": "Import from File",
"from_gist": "Importar des de Gist", "from_gist": "Importar des de Gist",
"from_gist_description": "Importar des de l'URL de Gist", "from_gist_description": "Importar des de l'URL de Gist",
"from_insomnia": "Importar des d'Insomnia", "from_insomnia": "Importar des d'Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Importar des de la col·lecció de Postman", "from_postman_description": "Importar des de la col·lecció de Postman",
"from_url": "Importar des de l'URL", "from_url": "Importar des de l'URL",
"gist_url": "Introduïu l'URL del Gist", "gist_url": "Introduïu l'URL del Gist",
"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": "No s'han pogut obtenir dades de l'URL", "import_from_url_invalid_fetch": "No s'han pogut obtenir dades de l'URL",
"import_from_url_invalid_file_format": "S'ha produït un error en importar les col·leccions", "import_from_url_invalid_file_format": "S'ha produït un error en importar les col·leccions",
"import_from_url_invalid_type": "Tipus no compatible. Els valors acceptats són 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Tipus no compatible. Els valors acceptats són 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Col·leccions importades", "import_from_url_success": "Col·leccions importades",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Importar col·leccions des d'un fitxer JSON de col·leccions Hoppscotch", "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ó"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Col·leccions", "collections": "Col·leccions",
"confirm": "Confirmar", "confirm": "Confirmar",
"customize_request": "Customize Request",
"edit_request": "Sol·licitud d'edició", "edit_request": "Sol·licitud d'edició",
"import_export": "Importar / Exportar", "import_export": "Importar / Exportar"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Estructurat", "structured": "Estructurat",
"text": "Text" "text": "Text"
}, },
"copy_link": "Copia l'enllaç",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Durada", "duration": "Durada",
"enter_curl": "Introduïu cURL", "enter_curl": "Introduïu cURL",
"generate_code": "Generar codi", "generate_code": "Generar codi",
"generated_code": "Codi generat", "generated_code": "Codi generat",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Llista de capçaleres", "header_list": "Llista de capçaleres",
"invalid_name": "Proporcioneu un nom per a la sol·licitud", "invalid_name": "Proporcioneu un nom per a la sol·licitud",
"method": "Mètode", "method": "Mètode",
@@ -552,14 +480,12 @@
"saved": "S'ha desat la sol·licitud", "saved": "S'ha desat la sol·licitud",
"share": "Compartir", "share": "Compartir",
"share_description": "Comparteix Hoppscotch amb els teus amics", "share_description": "Comparteix Hoppscotch amb els teus amics",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Sol·licitud", "title": "Sol·licitud",
"type": "Tipus de sol·licitud", "type": "Tipus de sol·licitud",
"url": "URL", "url": "URL",
"variables": "Variables", "variables": "Variables",
"view_my_links": "Visualitzar els meus enllaços", "view_my_links": "Visualitzar els meus enllaços"
"copy_link": "Copia l'enllaç"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Personalitzeu la configuració del compte.", "account_description": "Personalitzeu la configuració del compte.",
"account_email_description": "La vostra adreça de correu electrònic principal.", "account_email_description": "La vostra adreça de correu electrònic principal.",
"account_name_description": "Aquest és el vostre nom d'exposició", "account_name_description": "Aquest és el vostre nom d'exposició",
"additional": "Additional Settings",
"background": "Fons", "background": "Fons",
"black_mode": "Negre", "black_mode": "Negre",
"choose_language": "Tria l'idioma", "choose_language": "Tria l'idioma",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verificar correu electronic" "verify_email": "Verificar correu electronic"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Accions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Creat el",
"copy_html": "Copy HTML", "deleted": "S'ha suprimit el shortcode",
"copy_link": "Copy Link", "method": "Mètode",
"copy_markdown": "Copy Markdown", "not_found": "No s'ha trobat el shortcode",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Copiar l'enllaç de la sol·licitud",
"delete_method": "Seleccionar el mètode DELETE", "delete_method": "Seleccionar el mètode DELETE",
"get_method": "Seleccionar el mètode GET", "get_method": "Seleccionar el mètode GET",
"head_method": "Seleccionar el mètode HEAD", "head_method": "Seleccionar el mètode HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Guardar a les col·leccions", "save_to_collections": "Guardar a les col·leccions",
"send_request": "Enviar sol.licitud", "send_request": "Enviar sol.licitud",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Sol·licitud", "title": "Sol·licitud"
"copy_request_link": "Copiar l'enllaç de la sol·licitud"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "No s'ha pogut connectar", "connection_error": "No s'ha pogut connectar",
"connection_failed": "Connexió fallida", "connection_failed": "Connexió fallida",
"connection_lost": "Connexió perduda", "connection_lost": "Connexió perduda",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Copiat al porta-retalls", "copied_to_clipboard": "Copiat al porta-retalls",
"deleted": "Eliminat", "deleted": "Eliminat",
"deprecated": "Obsolet", "deprecated": "Obsolet",
@@ -836,12 +742,10 @@
"disconnected": "Desconnectat", "disconnected": "Desconnectat",
"disconnected_from": "Desconnectat de {name}", "disconnected_from": "Desconnectat de {name}",
"docs_generated": "Documentació generada", "docs_generated": "Documentació generada",
"download_failed": "Download failed",
"download_started": "S'ha iniciat la baixada", "download_started": "S'ha iniciat la baixada",
"enabled": "Activat", "enabled": "Activat",
"file_imported": "Fitxer importat", "file_imported": "Fitxer importat",
"finished_in": "Acabat en {duration} ms", "finished_in": "Acabat en {duration} ms",
"hide": "Hide",
"history_deleted": "S'ha suprimit l'historial", "history_deleted": "S'ha suprimit l'historial",
"linewrap": "Embolcar línies", "linewrap": "Embolcar línies",
"loading": "S'està carregant...", "loading": "S'està carregant...",
@@ -852,7 +756,6 @@
"published_error": "S'ha produït un error en publicar el missatge: {topic} al tema: {message}", "published_error": "S'ha produït un error en publicar el missatge: {topic} al tema: {message}",
"published_message": "Missatge publicat: {missatge} al tema: {tema}", "published_message": "Missatge publicat: {missatge} al tema: {tema}",
"reconnection_error": "No s'ha pogut tornar a connectar", "reconnection_error": "No s'ha pogut tornar a connectar",
"show": "Show",
"subscribed_failed": "No s'ha pogut subscriure al tema: {topic}", "subscribed_failed": "No s'ha pogut subscriure al tema: {topic}",
"subscribed_success": "S'ha subscrit correctament al tema: {topic}", "subscribed_success": "S'ha subscrit correctament al tema: {topic}",
"unsubscribed_failed": "No s'ha pogut cancel·lar la subscripció al tema: {topic}", "unsubscribed_failed": "No s'ha pogut cancel·lar la subscripció al tema: {topic}",
@@ -888,7 +791,6 @@
"queries": "Consultes", "queries": "Consultes",
"query": "Consulta", "query": "Consulta",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Proves", "tests": "Proves",
@@ -905,7 +807,6 @@
"email_do_not_match": "El correu electrònic no coincideix amb les dades del vostre compte. Contacta amb el propietari del teu equip.", "email_do_not_match": "El correu electrònic no coincideix amb les dades del vostre compte. Contacta amb el propietari del teu equip.",
"exit": "Sortir de l'equip", "exit": "Sortir de l'equip",
"exit_disabled": "L'únic propietari no pot sortir de l'equip", "exit_disabled": "L'únic propietari no pot sortir de l'equip",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "El format del correu electrònic no és vàlid", "invalid_email_format": "El format del correu electrònic no és vàlid",
"invalid_id": "Identificador d'equip no vàlid. Contacta amb el propietari del teu equip.", "invalid_id": "Identificador d'equip no vàlid. Contacta amb el propietari del teu equip.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "S'ha guardat l'equip", "saved": "S'ha guardat l'equip",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Equips", "title": "Equips",
"we_sent_invite_link": "Hem enviat un enllaç d'invitació a tots els convidats!", "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." "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."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Accions",
"created_on": "Creat el",
"deleted": "S'ha suprimit el shortcode",
"method": "Mètode",
"not_found": "No s'ha trobat el shortcode",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,17 +1,15 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "自动滚动", "autoscroll": "自动滚动",
"cancel": "取消", "cancel": "取消",
"choose_file": "选择文件", "choose_file": "选择文件",
"clear": "清除", "clear": "清除",
"clear_all": "全部清除", "clear_all": "全部清除",
"clear_history": "清除全部历史记录", "clear_history": "Clear all History",
"close": "关闭", "close": "关闭",
"connect": "连接", "connect": "连接",
"connecting": "连接中", "connecting": "连接中",
"copy": "复制", "copy": "复制",
"create": "Create",
"delete": "删除", "delete": "删除",
"disconnect": "断开连接", "disconnect": "断开连接",
"dismiss": "忽略", "dismiss": "忽略",
@@ -33,16 +31,14 @@
"open_workspace": "打开工作区", "open_workspace": "打开工作区",
"paste": "粘贴", "paste": "粘贴",
"prettify": "美化", "prettify": "美化",
"properties": "Properties",
"remove": "移除", "remove": "移除",
"rename": "重命名", "rename": "Rename",
"restore": "恢复", "restore": "恢复",
"save": "保存", "save": "保存",
"scroll_to_bottom": "滚动至底部", "scroll_to_bottom": "滚动至底部",
"scroll_to_top": "滚动至顶部", "scroll_to_top": "滚动至顶部",
"search": "搜索", "search": "搜索",
"send": "发送", "send": "发送",
"share": "Share",
"start": "开始", "start": "开始",
"starting": "正在开始", "starting": "正在开始",
"stop": "停止", "stop": "停止",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "与我们交谈", "chat_with_us": "与我们交谈",
"contact_us": "联系我们", "contact_us": "联系我们",
"cookies": "Cookies",
"copy": "复制", "copy": "复制",
"copy_interface_type": "Copy interface type",
"copy_user_id": "复制认证 Token", "copy_user_id": "复制认证 Token",
"developer_option": "开发者选项", "developer_option": "开发者选项",
"developer_option_description": "开发者工具,有助于开发和维护 Hoppscotch。", "developer_option_description": "开发者工具,有助于开发和维护 Hoppscotch。",
@@ -79,15 +73,14 @@
"keyboard_shortcuts": "键盘快捷键", "keyboard_shortcuts": "键盘快捷键",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "已发现新版本。刷新页面以更新。", "new_version_found": "已发现新版本。刷新页面以更新。",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "选项", "options": "选项",
"proxy_privacy_policy": "代理隐私政策", "proxy_privacy_policy": "代理隐私政策",
"reload": "重新加载", "reload": "重新加载",
"search": "搜索", "search": "搜索",
"share": "分享", "share": "分享",
"shortcuts": "快捷方式", "shortcuts": "快捷方式",
"social_description": "在社交媒体上关注我们,了解最新新闻、更新和发布。", "social_description": "Follow us on social media to stay updated with the latest news, updates and releases.",
"social_links": "社交媒体链接", "social_links": "Social links",
"spotlight": "聚光灯", "spotlight": "聚光灯",
"status": "状态", "status": "状态",
"status_description": "检查网站状态", "status_description": "检查网站状态",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "生成令牌", "generate_token": "生成令牌",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "包含在 URL 内", "include_in_url": "包含在 URL 内",
"inherited_from": "Inherited {auth} from parent collection {collection} ",
"learn": "了解更多", "learn": "了解更多",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "传递方式", "pass_key_by": "传递方式",
"password": "密码", "password": "密码",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "令牌", "token": "令牌",
"type": "授权类型", "type": "授权类型",
"username": "用户名" "username": "用户名"
@@ -148,7 +124,6 @@
"created": "集合已创建", "created": "集合已创建",
"different_parent": "不能用不同的父类来重新排序集合", "different_parent": "不能用不同的父类来重新排序集合",
"edit": "编辑集合", "edit": "编辑集合",
"import_or_create": "Import or create a collection",
"invalid_name": "请提供有效的集合名称", "invalid_name": "请提供有效的集合名称",
"invalid_root_move": "该集合已经在根级了", "invalid_root_move": "该集合已经在根级了",
"moved": "移动完成", "moved": "移动完成",
@@ -157,20 +132,18 @@
"name_length_insufficient": "集合名字至少需要 3 个字符", "name_length_insufficient": "集合名字至少需要 3 个字符",
"new": "新建集合", "new": "新建集合",
"order_changed": "集合顺序已更新", "order_changed": "集合顺序已更新",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "集合已更名", "renamed": "集合已更名",
"request_in_use": "请求正在使用中", "request_in_use": "请求正在使用中",
"save_as": "另存为", "save_as": "另存为",
"save_to_collection": "保存至集合", "save_to_collection": "Save to Collection",
"select": "选择一个集合", "select": "选择一个集合",
"select_location": "选择位置", "select_location": "选择位置",
"select_team": "选择一个团队", "select_team": "选择一个团队",
"team_collections": "团队集合" "team_collections": "团队集合"
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "你确定要关闭此标签页吗?", "close_unsaved_tab": "Are you sure you want to close this tab?",
"close_unsaved_tabs": "你确定要关闭所有标签页吗? {count} 个未保存的标签页将被丢失。", "close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.",
"exit_team": "你确定要离开此团队吗?", "exit_team": "你确定要离开此团队吗?",
"logout": "你确定要登出吗?", "logout": "你确定要登出吗?",
"remove_collection": "你确定要永久删除该集合吗?", "remove_collection": "你确定要永久删除该集合吗?",
@@ -178,7 +151,6 @@
"remove_folder": "你确定要永久删除该文件夹吗?", "remove_folder": "你确定要永久删除该文件夹吗?",
"remove_history": "你确定要永久删除全部历史记录吗?", "remove_history": "你确定要永久删除全部历史记录吗?",
"remove_request": "你确定要永久删除该请求吗?", "remove_request": "你确定要永久删除该请求吗?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "你确定要删除该团队吗?", "remove_team": "你确定要删除该团队吗?",
"remove_telemetry": "你确定要退出遥测服务吗?", "remove_telemetry": "你确定要退出遥测服务吗?",
"request_change": "你确定你要放弃当前的请求,未保存的修改将被丢失。", "request_change": "你确定你要放弃当前的请求,未保存的修改将被丢失。",
@@ -186,27 +158,9 @@
"sync": "您确定要同步该工作区吗?" "sync": "您确定要同步该工作区吗?"
}, },
"context_menu": { "context_menu": {
"add_parameters": "添加至参数", "add_parameters": "Add to parameters",
"open_request_in_new_tab": "在新标签页中打开请求", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "设置为变量" "set_environment_variable": "Set as variable"
},
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
}, },
"count": { "count": {
"header": "请求头 {count}", "header": "请求头 {count}",
@@ -238,13 +192,11 @@
"profile": "登录以查看你的个人资料", "profile": "登录以查看你的个人资料",
"protocols": "协议为空", "protocols": "协议为空",
"schema": "连接至 GraphQL 端点", "schema": "连接至 GraphQL 端点",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes 为空",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "订阅为空", "subscription": "订阅为空",
"team_name": "团队名称为空", "team_name": "团队名称为空",
"teams": "团队为空", "teams": "团队为空",
"tests": "没有针对该请求的测试", "tests": "没有针对该请求的测试"
"shortcodes": "短链接为空"
}, },
"environment": { "environment": {
"add_to_global": "添加到全局环境", "add_to_global": "添加到全局环境",
@@ -252,39 +204,36 @@
"create_new": "创建新环境", "create_new": "创建新环境",
"created": "环境已创建", "created": "环境已创建",
"deleted": "环境已删除", "deleted": "环境已删除",
"duplicated": "环境已复制", "duplicated": "Environment duplicated",
"edit": "编辑环境", "edit": "编辑环境",
"empty_variables": "没有变量", "empty_variables": "No variables",
"global": "全局", "global": "Global",
"global_variables": "全局变量", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "请提供有效的环境名称", "invalid_name": "请提供有效的环境名称",
"list": "环境变量", "list": "Environment variables",
"my_environments": "我的环境", "my_environments": "我的环境",
"name": "名称", "name": "Name",
"nested_overflow": "环境嵌套深度超过限制10层", "nested_overflow": "环境嵌套深度超过限制10层",
"new": "新建环境", "new": "新建环境",
"no_active_environment": "没有激活的环境", "no_active_environment": "No active environment",
"no_environment": "无环境", "no_environment": "无环境",
"no_environment_description": "没有选择环境。选择如何处理以下变量。", "no_environment_description": "没有选择环境。选择如何处理以下变量。",
"quick_peek": "快速浏览环境", "quick_peek": "Environment Quick Peek",
"replace_with_variable": "替换为变量", "replace_with_variable": "Replace with variable",
"scope": "范围", "scope": "Scope",
"select": "选择环境", "select": "选择环境",
"set": "设置环境", "set": "Set environment",
"set_as_environment": "设置为环境", "set_as_environment": "Set as environment",
"team_environments": "团队环境", "team_environments": "团队环境",
"title": "环境", "title": "环境",
"updated": "环境已更新", "updated": "环境已更新",
"value": "", "value": "Value",
"variable": "变量", "variable": "Variable",
"variable_list": "变量列表" "variable_list": "变量列表"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "该浏览器似乎不支持 SSE。", "browser_support_sse": "该浏览器似乎不支持 SSE。",
"check_console_details": "检查控制台日志以获悉详情", "check_console_details": "检查控制台日志以获悉详情",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL 格式不正确", "curl_invalid_format": "cURL 格式不正确",
"danger_zone": "危险区域", "danger_zone": "危险区域",
"delete_account": "您的帐号目前为这些团队的拥有者:", "delete_account": "您的帐号目前为这些团队的拥有者:",
@@ -296,18 +245,14 @@
"incorrect_email": "电子邮箱错误", "incorrect_email": "电子邮箱错误",
"invalid_link": "无效链接", "invalid_link": "无效链接",
"invalid_link_description": "你点击的链接无效或已过期。", "invalid_link_description": "你点击的链接无效或已过期。",
"invalid_embed_link": "The embed does not exist or is invalid.",
"json_parsing_failed": "不合法的 JSON", "json_parsing_failed": "不合法的 JSON",
"json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试", "json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试",
"network_error": "好像发生了网络错误,请重试。", "network_error": "好像发生了网络错误,请重试。",
"network_fail": "无法发送请求", "network_fail": "无法发送请求",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "无持续时间", "no_duration": "无持续时间",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "找不到结果", "no_results_found": "找不到结果",
"page_not_found": "找不到此頁面", "page_not_found": "找不到此頁面",
"please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error",
"proxy_error": "代理错误",
"script_fail": "无法执行预请求脚本", "script_fail": "无法执行预请求脚本",
"something_went_wrong": "发生了一些错误", "something_went_wrong": "发生了一些错误",
"test_script_fail": "无法执行请求脚本" "test_script_fail": "无法执行请求脚本"
@@ -315,13 +260,9 @@
"export": { "export": {
"as_json": "导出为 JSON", "as_json": "导出为 JSON",
"create_secret_gist": "创建私密 Gist", "create_secret_gist": "创建私密 Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist", "gist_created": "已创建 Gist",
"failed": "Something went wrong while exporting",
"secret_gist_success": "Successfully exported as secret Gist",
"require_github": "使用 GitHub 登录以创建私密 Gist", "require_github": "使用 GitHub 登录以创建私密 Gist",
"title": "导出", "title": "导出"
"success": "Successfully exported",
"gist_created": "已创建 Gist"
}, },
"filter": { "filter": {
"all": "全部", "all": "全部",
@@ -337,16 +278,13 @@
"renamed": "文件夹已更名" "renamed": "文件夹已更名"
}, },
"graphql": { "graphql": {
"connection_switch_confirm": "您想连接最新的 GraphQL 端点吗?", "connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?",
"connection_switch_new_url": "切换到标签页将使您与活动的 GraphQL 连接断开。新的连接 URL ", "connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is",
"connection_switch_url": "您已连接到 GraphQL 端点,连接 URL ", "connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is",
"mutations": "变更", "mutations": "变更",
"schema": "模式", "schema": "模式",
"subscriptions": "订阅", "subscriptions": "订阅",
"switch_connection": "切换连接" "switch_connection": "Switch connection"
},
"graphql_collections": {
"title": "GraphQL Collections"
}, },
"group": { "group": {
"time": "时间", "time": "时间",
@@ -359,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "授权头将会在你发送请求时自动生成。", "authorization": "授权头将会在你发送请求时自动生成。",
"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.",
"generate_documentation_first": "请先生成文档", "generate_documentation_first": "请先生成文档",
"network_fail": "无法到达 API 端点。请检查网络连接并重试。", "network_fail": "无法到达 API 端点。请检查网络连接并重试。",
"offline": "你似乎处于离线状态,该工作区中的数据可能不是最新。", "offline": "你似乎处于离线状态,该工作区中的数据可能不是最新。",
@@ -380,10 +316,7 @@
"import": { "import": {
"collections": "导入集合", "collections": "导入集合",
"curl": "导入 cURL", "curl": "导入 cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "导入失败", "failed": "导入失败",
"from_file": "Import from File",
"from_gist": "从 Gist 导入", "from_gist": "从 Gist 导入",
"from_gist_description": "从 Gist URL 导入", "from_gist_description": "从 Gist URL 导入",
"from_insomnia": "从 Insomnia 导入", "from_insomnia": "从 Insomnia 导入",
@@ -398,41 +331,35 @@
"from_postman_description": "从 Postman 集合中导入", "from_postman_description": "从 Postman 集合中导入",
"from_url": "从 URL 导入", "from_url": "从 URL 导入",
"gist_url": "输入 Gist URL", "gist_url": "输入 Gist URL",
"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": "无法从网址取得资料", "import_from_url_invalid_fetch": "无法从网址取得资料",
"import_from_url_invalid_file_format": "导入组合时发生错误", "import_from_url_invalid_file_format": "导入组合时发生错误",
"import_from_url_invalid_type": "不支持此类型。可接受的值为 'hoppscotch'、'openapi'、'postman'、'insomnia'", "import_from_url_invalid_type": "不支持此类型。可接受的值为 'hoppscotch'、'openapi'、'postman'、'insomnia'",
"import_from_url_success": "已导入组合", "import_from_url_success": "已导入组合",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "从 Hoppscotch 的集合文件导入JSON", "json_description": "从 Hoppscotch 的集合文件导入JSON",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "导入" "title": "导入"
}, },
"inspections": { "inspections": {
"description": "查可能的错误", "description": "Inspect possible errors",
"environment": { "environment": {
"add_environment": "添加到环境", "add_environment": "Add to Environment",
"not_found": "环境变量“{environment}”未找到。" "not_found": "Environment variable “{environment}” not found."
}, },
"header": { "header": {
"cookie": "浏览器不允许 Hoppscotch 设置 Cookie 标头。当前我们正在开发 Hoppscotch 桌面应用程序(即将推出),与此同时请改用授权标头。" "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."
}, },
"response": { "response": {
"401_error": "请检查您的身份验证凭据。", "401_error": "Please check your authentication credentials.",
"404_error": "请检查您的请求 URL 和方法类型。", "404_error": "Please check your request URL and method type.",
"cors_error": "请检查您的跨源资源共享配置。", "cors_error": "Please check your Cross-Origin Resource Sharing configuration.",
"default_error": "请检查您的请求。", "default_error": "Please check your request.",
"network_error": "请检查您的网络连接。" "network_error": "Please check your network connection."
}, },
"title": "Inspector", "title": "Inspector",
"url": { "url": {
"extension_not_installed": "未安装扩展。", "extension_not_installed": "Extension not installed.",
"extension_unknown_origin": "确保您已将 API 端点的源添加到 Hoppscotch 浏览器扩展列表中。", "extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.",
"extention_enable_action": "启用浏览器扩展", "extention_enable_action": "Enable Browser Extension",
"extention_not_enabled": "扩展未启用。" "extention_not_enabled": "Extension not enabled."
} }
}, },
"layout": { "layout": {
@@ -446,10 +373,8 @@
"close_unsaved_tab": "有未保存的变更", "close_unsaved_tab": "有未保存的变更",
"collections": "集合", "collections": "集合",
"confirm": "确认", "confirm": "确认",
"customize_request": "Customize Request",
"edit_request": "编辑请求", "edit_request": "编辑请求",
"import_export": "导入/导出", "import_export": "导入/导出"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "您已经订阅了此主题。", "already_subscribed": "您已经订阅了此主题。",
@@ -464,10 +389,10 @@
"invalid_topic": "请提供该订阅的主题", "invalid_topic": "请提供该订阅的主题",
"keep_alive": "Keep Alive", "keep_alive": "Keep Alive",
"log": "日志", "log": "日志",
"lw_message": "遗嘱消息", "lw_message": "Last-Will Message",
"lw_qos": "遗嘱消息QoS", "lw_qos": "Last-Will QoS",
"lw_retain": "遗嘱消息保留", "lw_retain": "Last-Will Retain",
"lw_topic": "遗嘱消息主题", "lw_topic": "Last-Will Topic",
"message": "消息", "message": "消息",
"new": "新订阅", "new": "新订阅",
"not_connected": "请先启动MQTT连接。", "not_connected": "请先启动MQTT连接。",
@@ -524,14 +449,13 @@
"structured": "结构", "structured": "结构",
"text": "文字" "text": "文字"
}, },
"copy_link": "复制链接",
"different_collection": "不能对来自不同集合的请求进行重新排序", "different_collection": "不能对来自不同集合的请求进行重新排序",
"duplicated": "重复的请求", "duplicated": "重复的请求",
"duration": "持续时间", "duration": "持续时间",
"enter_curl": "输入 cURL", "enter_curl": "输入 cURL",
"generate_code": "生成代码", "generate_code": "生成代码",
"generated_code": "已生成代码", "generated_code": "已生成代码",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "请求头列表", "header_list": "请求头列表",
"invalid_name": "请提供请求名称", "invalid_name": "请提供请求名称",
"method": "方法", "method": "方法",
@@ -548,7 +472,7 @@
"payload": "负载", "payload": "负载",
"query": "查询", "query": "查询",
"raw_body": "原始请求体", "raw_body": "原始请求体",
"rename": "重命名请求", "rename": "Rename Request",
"renamed": "请求重命名", "renamed": "请求重命名",
"run": "运行", "run": "运行",
"save": "保存", "save": "保存",
@@ -556,14 +480,12 @@
"saved": "请求已保存", "saved": "请求已保存",
"share": "分享", "share": "分享",
"share_description": "分享 Hoppscotch 给你的朋友", "share_description": "分享 Hoppscotch 给你的朋友",
"share_request": "Share Request", "stop": "Stop",
"stop": "停止",
"title": "请求", "title": "请求",
"type": "请求类型", "type": "请求类型",
"url": "URL", "url": "URL",
"variables": "变量", "variables": "变量",
"view_my_links": "查看我的链接", "view_my_links": "查看我的链接"
"copy_link": "复制链接"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -591,7 +513,6 @@
"account_description": "自定义您的帐户设置。", "account_description": "自定义您的帐户设置。",
"account_email_description": "您的主要电子邮箱地址。", "account_email_description": "您的主要电子邮箱地址。",
"account_name_description": "这是您的显示名称。", "account_name_description": "这是您的显示名称。",
"additional": "Additional Settings",
"background": "背景", "background": "背景",
"black_mode": "黑色", "black_mode": "黑色",
"choose_language": "选择语言", "choose_language": "选择语言",
@@ -638,31 +559,14 @@
"verified_email": "已验证电子邮件地址", "verified_email": "已验证电子邮件地址",
"verify_email": "验证电子邮箱" "verify_email": "验证电子邮箱"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "操作",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "创建于",
"copy_html": "Copy HTML", "deleted": "已刪除快捷键",
"copy_link": "Copy Link", "method": "方法",
"copy_markdown": "Copy Markdown", "not_found": "找不到快捷键",
"creating_widget": "Creating widget", "short_code": "快捷键",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,28 +592,27 @@
"title": "导航" "title": "导航"
}, },
"others": { "others": {
"prettify": "美化内容", "prettify": "Prettify Editor's Content",
"title": "其他" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "复制请求链接",
"delete_method": "选择 DELETE 方法", "delete_method": "选择 DELETE 方法",
"get_method": "选择 GET 方法", "get_method": "选择 GET 方法",
"head_method": "选择 HEAD 方法", "head_method": "选择 HEAD 方法",
"import_curl": "导入cURL", "import_curl": "Import cURL",
"method": "方法", "method": "方法",
"next_method": "选择下一个方法", "next_method": "选择下一个方法",
"post_method": "选择 POST 方法", "post_method": "选择 POST 方法",
"previous_method": "选择上一个方法", "previous_method": "选择上一个方法",
"put_method": "选择 PUT 方法", "put_method": "选择 PUT 方法",
"rename": "重命名请求", "rename": "Rename Request",
"reset_request": "重置请求", "reset_request": "重置请求",
"save_request": "保存请求", "save_request": "Save Request",
"save_to_collections": "保存到集合", "save_to_collections": "保存到集合",
"send_request": "发送请求", "send_request": "发送请求",
"share_request": "Share Request", "show_code": "Generate code snippet",
"show_code": "生成代码片段", "title": "请求"
"title": "请求",
"copy_request_link": "复制请求链接"
}, },
"response": { "response": {
"copy": "复制响应至剪贴板", "copy": "复制响应至剪贴板",
@@ -739,82 +642,82 @@
"url": "URL" "url": "URL"
}, },
"spotlight": { "spotlight": {
"change_language": "更改语言", "change_language": "Change Language",
"environments": { "environments": {
"delete": "删除当前环境", "delete": "Delete current environment",
"duplicate": "复制当前环境", "duplicate": "Duplicate current environment",
"duplicate_global": "复制全局环境", "duplicate_global": "Duplicate global environment",
"edit": "编辑当前环境", "edit": "Edit current environment",
"edit_global": "编辑全局环境", "edit_global": "Edit global environment",
"new": "创建新环境", "new": "Create new environment",
"new_variable": "创建新的环境变量", "new_variable": "Create a new environment variable",
"title": "环境" "title": "Environments"
}, },
"general": { "general": {
"chat": "与支持人员聊天", "chat": "Chat with support",
"help_menu": "帮助和支持", "help_menu": "Help and support",
"open_docs": "阅读文档", "open_docs": "Read Documentation",
"open_github": "打开 GitHub 存储库", "open_github": "Open GitHub repository",
"open_keybindings": "键盘快捷键", "open_keybindings": "Keyboard shortcuts",
"social": "社交媒体", "social": "Social",
"title": "一般" "title": "General"
}, },
"graphql": { "graphql": {
"connect": "连接到服务器", "connect": "Connect to server",
"disconnect": "与服务器断开连接" "disconnect": "Disconnect from server"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "邀请你的朋友来 Hoppscotch", "invite": "Invite your friends to Hoppscotch",
"title": "杂项" "title": "Miscellaneous"
}, },
"request": { "request": {
"save_as_new": "另存为新请求", "save_as_new": "Save as new request",
"select_method": "选择方法", "select_method": "Select method",
"switch_to": "切换到", "switch_to": "Switch to",
"tab_authorization": "授权标签页", "tab_authorization": "Authorization tab",
"tab_body": "请求体标签页", "tab_body": "Body tab",
"tab_headers": "请求头标签页", "tab_headers": "Headers tab",
"tab_parameters": "参数标签页", "tab_parameters": "Parameters tab",
"tab_pre_request_script": "预请求脚本标签页", "tab_pre_request_script": "Pre-request script tab",
"tab_query": "查询标签页", "tab_query": "Query tab",
"tab_tests": "测试标签页b", "tab_tests": "Tests tab",
"tab_variables": "变量标签页" "tab_variables": "Variables tab"
}, },
"response": { "response": {
"copy": "复制响应", "copy": "Copy response",
"download": "将响应下载为文件", "download": "Download response as file",
"title": "响应" "title": "Response"
}, },
"section": { "section": {
"interceptor": "拦截器", "interceptor": "Interceptor",
"interface": "界面", "interface": "Interface",
"theme": "主题", "theme": "Theme",
"user": "用户" "user": "User"
}, },
"settings": { "settings": {
"change_interceptor": "更改拦截器", "change_interceptor": "Change Interceptor",
"change_language": "更改语言", "change_language": "Change Language",
"theme": { "theme": {
"black": "黑色", "black": "Black",
"dark": "暗色", "dark": "Dark",
"light": "亮色", "light": "Light",
"system": "系统" "system": "System preference"
} }
}, },
"tab": { "tab": {
"close_current": "关闭当前标签页", "close_current": "Close current tab",
"close_others": "关闭所有其他标签页", "close_others": "Close all other tabs",
"duplicate": "复制当前标签页", "duplicate": "Duplicate current tab",
"new_tab": "打开新的标签页", "new_tab": "Open a new tab",
"title": "标签页" "title": "Tabs"
}, },
"workspace": { "workspace": {
"delete": "删除当前团队", "delete": "Delete current team",
"edit": "编辑当前团队", "edit": "Edit current team",
"invite": "邀请人员加入团队", "invite": "Invite people to team",
"new": "创建新团队", "new": "Create new team",
"switch_to_personal": "切换到您的个人工作空间", "switch_to_personal": "Switch to your personal workspace",
"title": "团队" "title": "Teams"
} }
}, },
"sse": { "sse": {
@@ -832,7 +735,6 @@
"connection_error": "连接错误", "connection_error": "连接错误",
"connection_failed": "连接失败", "connection_failed": "连接失败",
"connection_lost": "连接丢失", "connection_lost": "连接丢失",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "已复制到剪贴板", "copied_to_clipboard": "已复制到剪贴板",
"deleted": "已删除", "deleted": "已删除",
"deprecated": "已弃用", "deprecated": "已弃用",
@@ -840,12 +742,10 @@
"disconnected": "断开连接", "disconnected": "断开连接",
"disconnected_from": "与 {name} 断开连接", "disconnected_from": "与 {name} 断开连接",
"docs_generated": "已生成文档", "docs_generated": "已生成文档",
"download_failed": "Download failed",
"download_started": "开始下载", "download_started": "开始下载",
"enabled": "启用", "enabled": "启用",
"file_imported": "文件已导入", "file_imported": "文件已导入",
"finished_in": "在 {duration} 毫秒内完成", "finished_in": "在 {duration} 毫秒内完成",
"hide": "Hide",
"history_deleted": "历史记录已删除", "history_deleted": "历史记录已删除",
"linewrap": "换行", "linewrap": "换行",
"loading": "正在加载……", "loading": "正在加载……",
@@ -856,7 +756,6 @@
"published_error": "将信息:{topic}发布至主题:{message}时发生错误", "published_error": "将信息:{topic}发布至主题:{message}时发生错误",
"published_message": "已将此信息:{message} 发布至主题:{topic}", "published_message": "已将此信息:{message} 发布至主题:{topic}",
"reconnection_error": "重连失败", "reconnection_error": "重连失败",
"show": "Show",
"subscribed_failed": "无法订阅此主题:{topic}", "subscribed_failed": "无法订阅此主题:{topic}",
"subscribed_success": "成功订阅此主题:{topic}", "subscribed_success": "成功订阅此主题:{topic}",
"unsubscribed_failed": "无法取消订阅此主题:{topic}", "unsubscribed_failed": "无法取消订阅此主题:{topic}",
@@ -892,7 +791,6 @@
"queries": "查询", "queries": "查询",
"query": "查询", "query": "查询",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "测试", "tests": "测试",
@@ -909,7 +807,6 @@
"email_do_not_match": "邮箱无法与你的帐户信息匹配。请联系你的团队者。", "email_do_not_match": "邮箱无法与你的帐户信息匹配。请联系你的团队者。",
"exit": "退出团队", "exit": "退出团队",
"exit_disabled": "团队所有者无法退出团队", "exit_disabled": "团队所有者无法退出团队",
"failed_invites": "Failed invites",
"invalid_coll_id": "无效的集合 ID", "invalid_coll_id": "无效的集合 ID",
"invalid_email_format": "电子邮箱格式无效", "invalid_email_format": "电子邮箱格式无效",
"invalid_id": "无效的团队 ID请联系你的团队者。", "invalid_id": "无效的团队 ID请联系你的团队者。",
@@ -951,7 +848,6 @@
"same_target_destination": "目标相同", "same_target_destination": "目标相同",
"saved": "团队已保存", "saved": "团队已保存",
"select_a_team": "选择团队", "select_a_team": "选择团队",
"success_invites": "Success invites",
"title": "团队", "title": "团队",
"we_sent_invite_link": "我们向所有受邀者发送了邀请链接!", "we_sent_invite_link": "我们向所有受邀者发送了邀请链接!",
"we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。" "we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。"
@@ -983,14 +879,5 @@
"personal": "我的工作空间", "personal": "我的工作空间",
"team": "团队工作空间", "team": "团队工作空间",
"title": "工作空间" "title": "工作空间"
},
"shortcodes": {
"actions": "操作",
"created_on": "创建于",
"deleted": "已刪除短链接",
"method": "方法",
"not_found": "找不到短链接",
"short_code": "短链接",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "zrušení", "cancel": "zrušení",
"choose_file": "Vyberte soubor", "choose_file": "Vyberte soubor",
@@ -11,7 +10,6 @@
"connect": "Připojit", "connect": "Připojit",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "kopírovat", "copy": "kopírovat",
"create": "Create",
"delete": "Vymazat", "delete": "Vymazat",
"disconnect": "Odpojit", "disconnect": "Odpojit",
"dismiss": "Zavrhnout", "dismiss": "Zavrhnout",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties",
"remove": "Odstranit", "remove": "Odstranit",
"rename": "Rename", "rename": "Rename",
"restore": "Obnovit", "restore": "Obnovit",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Vyhledávání", "search": "Vyhledávání",
"send": "Poslat", "send": "Poslat",
"share": "Share",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Stop", "stop": "Stop",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "piš si s námi", "chat_with_us": "piš si s námi",
"contact_us": "Kontaktujte nás", "contact_us": "Kontaktujte nás",
"cookies": "Cookies",
"copy": "kopírovat", "copy": "kopírovat",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Klávesové zkratky", "keyboard_shortcuts": "Klávesové zkratky",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Nalezena nová verze. Aktualizujte aktualizací.", "new_version_found": "Nalezena nová verze. Aktualizujte aktualizací.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Zásady ochrany osobních údajů proxy", "proxy_privacy_policy": "Zásady ochrany osobních údajů proxy",
"reload": "Znovu načíst", "reload": "Znovu načíst",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generovat token", "generate_token": "Generovat token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Zahrnout do adresy URL", "include_in_url": "Zahrnout do adresy URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Zjistěte jak", "learn": "Zjistěte jak",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Heslo", "password": "Heslo",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Žeton", "token": "Žeton",
"type": "Typ autorizace", "type": "Typ autorizace",
"username": "Uživatelské jméno" "username": "Uživatelské jméno"
@@ -148,7 +124,6 @@
"created": "Kolekce vytvořena", "created": "Kolekce vytvořena",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Upravit sbírku", "edit": "Upravit sbírku",
"import_or_create": "Import or create a collection",
"invalid_name": "Uveďte prosím platný název kolekce", "invalid_name": "Uveďte prosím platný název kolekce",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Nová kolekce", "new": "Nová kolekce",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Sbírka přejmenována", "renamed": "Sbírka přejmenována",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Uložit jako", "save_as": "Uložit jako",
@@ -178,7 +151,6 @@
"remove_folder": "Opravdu chcete tuto složku trvale smazat?", "remove_folder": "Opravdu chcete tuto složku trvale smazat?",
"remove_history": "Opravdu chcete trvale smazat celou historii?", "remove_history": "Opravdu chcete trvale smazat celou historii?",
"remove_request": "Opravdu chcete tento požadavek trvale smazat?", "remove_request": "Opravdu chcete tento požadavek trvale smazat?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Opravdu chcete tento tým smazat?", "remove_team": "Opravdu chcete tento tým smazat?",
"remove_telemetry": "Opravdu se chcete odhlásit z telemetrie?", "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.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Záhlaví {count}", "header": "Záhlaví {count}",
"message": "Zpráva {count}", "message": "Zpráva {count}",
@@ -238,13 +192,11 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protokoly jsou prázdné", "protocols": "Protokoly jsou prázdné",
"schema": "Připojte se ke koncovému bodu GraphQL", "schema": "Připojte se ke koncovému bodu GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Název týmu prázdný", "team_name": "Název týmu prázdný",
"teams": "Týmy jsou prázdné", "teams": "Týmy jsou prázdné",
"tests": "Pro tento požadavek neexistují žádné testy", "tests": "Pro tento požadavek neexistují žádné testy"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Zadejte platný název prostředí", "invalid_name": "Zadejte platný název prostředí",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Seznam proměnných" "variable_list": "Seznam proměnných"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Zdá se, že tento prohlížeč nemá podporu událostí odeslaných serverem.", "browser_support_sse": "Zdá se, že tento prohlížeč nemá podporu událostí odeslaných serverem.",
"check_console_details": "Podrobnosti najdete v protokolu konzoly.", "check_console_details": "Podrobnosti najdete v protokolu konzoly.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL nemá správný formát", "curl_invalid_format": "cURL nemá správný formát",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Nelze předtifikovat neplatné tělo, vyřešit chyby syntaxe json a zkusit to znovu", "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.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "Žádost nelze odeslat", "network_fail": "Žádost nelze odeslat",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Žádné trvání", "no_duration": "Žádné trvání",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "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", "proxy_error": "Proxy error",
"script_fail": "Skript předběžného požadavku nelze spustit", "script_fail": "Skript předběžného požadavku nelze spustit",
"something_went_wrong": "Něco se pokazilo", "something_went_wrong": "Něco se pokazilo",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Exportovat jako JSON", "as_json": "Exportovat jako JSON",
"create_secret_gist": "Vytvořte tajnou podstatu", "create_secret_gist": "Vytvořte tajnou podstatu",
"failed": "Something went wrong while exporting",
"gist_created": "Podstata vytvořena", "gist_created": "Podstata vytvořena",
"require_github": "Přihlaste se pomocí GitHub a vytvořte tajný seznam", "require_github": "Přihlaste se pomocí GitHub a vytvořte tajný seznam",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Předplatné", "subscriptions": "Předplatné",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Autorizační hlavička se automaticky vygeneruje při odeslání požadavku.", "authorization": "Autorizační hlavička se automaticky vygeneruje při odeslání požadavku.",
"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.",
"generate_documentation_first": "Nejprve vytvořte dokumentaci", "generate_documentation_first": "Nejprve vytvořte dokumentaci",
"network_fail": "Nelze dosáhnout koncového bodu API. Zkontrolujte připojení k síti a zkuste to znovu.", "network_fail": "Nelze dosáhnout koncového bodu API. Zkontrolujte připojení k síti a zkuste to znovu.",
"offline": "Zdá se, že jste offline. Data v tomto pracovním prostoru nemusí být aktuální.", "offline": "Zdá se, že jste offline. Data v tomto pracovním prostoru nemusí být aktuální.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Import sbírek", "collections": "Import sbírek",
"curl": "Importovat cURL", "curl": "Importovat cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Import se nezdařil", "failed": "Import se nezdařil",
"from_file": "Import from File",
"from_gist": "Import z Gist", "from_gist": "Import z Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Zadejte URL adresy", "gist_url": "Zadejte URL adresy",
"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_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "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_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "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"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Sbírky", "collections": "Sbírky",
"confirm": "Potvrdit", "confirm": "Potvrdit",
"customize_request": "Customize Request",
"edit_request": "Upravit požadavek", "edit_request": "Upravit požadavek",
"import_export": "Import Export", "import_export": "Import Export"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Kopírovat odkaz",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Doba trvání", "duration": "Doba trvání",
"enter_curl": "Zadejte cURL", "enter_curl": "Zadejte cURL",
"generate_code": "Vygenerujte kód", "generate_code": "Vygenerujte kód",
"generated_code": "Generovaný kód", "generated_code": "Generovaný kód",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Seznam záhlaví", "header_list": "Seznam záhlaví",
"invalid_name": "Uveďte prosím název žádosti", "invalid_name": "Uveďte prosím název žádosti",
"method": "Metoda", "method": "Metoda",
@@ -552,14 +480,12 @@
"saved": "Žádost uložena", "saved": "Žádost uložena",
"share": "Podíl", "share": "Podíl",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Žádost", "title": "Žádost",
"type": "Typ požadavku", "type": "Typ požadavku",
"url": "URL", "url": "URL",
"variables": "Proměnné", "variables": "Proměnné",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Kopírovat odkaz"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Přizpůsobte si nastavení účtu.", "account_description": "Přizpůsobte si nastavení účtu.",
"account_email_description": "Vaše primární e -mailová adresa.", "account_email_description": "Vaše primární e -mailová adresa.",
"account_name_description": "Toto je vaše zobrazované jméno.", "account_name_description": "Toto je vaše zobrazované jméno.",
"additional": "Additional Settings",
"background": "Pozadí", "background": "Pozadí",
"black_mode": "Černá", "black_mode": "Černá",
"choose_language": "Vyber jazyk", "choose_language": "Vyber jazyk",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Kopírovat požadavek na odkaz",
"delete_method": "Vyberte metodu ODSTRANIT", "delete_method": "Vyberte metodu ODSTRANIT",
"get_method": "Vyberte metodu ZÍSKAT", "get_method": "Vyberte metodu ZÍSKAT",
"head_method": "Vyberte metodu HEAD", "head_method": "Vyberte metodu HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Uložit do sbírek", "save_to_collections": "Uložit do sbírek",
"send_request": "Poslat žádost", "send_request": "Poslat žádost",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Žádost", "title": "Žádost"
"copy_request_link": "Kopírovat požadavek na odkaz"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Zkopírováno do schránky", "copied_to_clipboard": "Zkopírováno do schránky",
"deleted": "Smazáno", "deleted": "Smazáno",
"deprecated": "ZASTARALÉ", "deprecated": "ZASTARALÉ",
@@ -836,12 +742,10 @@
"disconnected": "Odpojeno", "disconnected": "Odpojeno",
"disconnected_from": "Odpojeno od {name}", "disconnected_from": "Odpojeno od {name}",
"docs_generated": "Vygenerovaná dokumentace", "docs_generated": "Vygenerovaná dokumentace",
"download_failed": "Download failed",
"download_started": "Stahování zahájeno", "download_started": "Stahování zahájeno",
"enabled": "Povoleno", "enabled": "Povoleno",
"file_imported": "Soubor importován", "file_imported": "Soubor importován",
"finished_in": "Hotovo za {duration} ms", "finished_in": "Hotovo za {duration} ms",
"hide": "Hide",
"history_deleted": "Historie odstraněna", "history_deleted": "Historie odstraněna",
"linewrap": "Zabalit linky", "linewrap": "Zabalit linky",
"loading": "Načítání...", "loading": "Načítání...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Dotazy", "queries": "Dotazy",
"query": "Dotaz", "query": "Dotaz",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Testy", "tests": "Testy",
@@ -905,7 +807,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Ukončete tým", "exit": "Ukončete tým",
"exit_disabled": "Pouze vlastník nemůže opustit tým", "exit_disabled": "Pouze vlastník nemůže opustit tým",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Formát e -mailu je neplatný", "invalid_email_format": "Formát e -mailu je neplatný",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Tým uložen", "saved": "Tým uložen",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Týmy", "title": "Týmy",
"we_sent_invite_link": "We sent an invite link to all invitees!", "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." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Afbestille", "cancel": "Afbestille",
"choose_file": "Vælg en fil", "choose_file": "Vælg en fil",
@@ -11,7 +10,6 @@
"connect": "Opret forbindelse", "connect": "Opret forbindelse",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Kopi", "copy": "Kopi",
"create": "Create",
"delete": "Slet", "delete": "Slet",
"disconnect": "Koble fra", "disconnect": "Koble fra",
"dismiss": "Afskedige", "dismiss": "Afskedige",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties",
"remove": "Fjerne", "remove": "Fjerne",
"rename": "Rename", "rename": "Rename",
"restore": "Gendan", "restore": "Gendan",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Søg", "search": "Søg",
"send": "Sende", "send": "Sende",
"share": "Share",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Hold op", "stop": "Hold op",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Chat med os", "chat_with_us": "Chat med os",
"contact_us": "Kontakt os", "contact_us": "Kontakt os",
"cookies": "Cookies",
"copy": "Kopi", "copy": "Kopi",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Tastaturgenveje", "keyboard_shortcuts": "Tastaturgenveje",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Ny version fundet. Opdater for at opdatere.", "new_version_found": "Ny version fundet. Opdater for at opdatere.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Politik til beskyttelse af personlige oplysninger i proxy", "proxy_privacy_policy": "Politik til beskyttelse af personlige oplysninger i proxy",
"reload": "Genindlæs", "reload": "Genindlæs",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generer Token", "generate_token": "Generer Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Inkluder i URL", "include_in_url": "Inkluder i URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Lær hvordan", "learn": "Lær hvordan",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Adgangskode", "password": "Adgangskode",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Polet", "token": "Polet",
"type": "Godkendelse Type", "type": "Godkendelse Type",
"username": "Brugernavn" "username": "Brugernavn"
@@ -148,7 +124,6 @@
"created": "Samlingen er oprettet", "created": "Samlingen er oprettet",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Rediger samling", "edit": "Rediger samling",
"import_or_create": "Import or create a collection",
"invalid_name": "Angiv et gyldigt navn til samlingen", "invalid_name": "Angiv et gyldigt navn til samlingen",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Ny kollektion", "new": "Ny kollektion",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Samling omdøbt", "renamed": "Samling omdøbt",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Gem som", "save_as": "Gem som",
@@ -178,7 +151,6 @@
"remove_folder": "Er du sikker på, at du vil slette denne mappe permanent?", "remove_folder": "Er du sikker på, at du vil slette denne mappe permanent?",
"remove_history": "Er du sikker på, at du vil slette hele historikken permanent?", "remove_history": "Er du sikker på, at du vil slette hele historikken permanent?",
"remove_request": "Er du sikker på, at du vil slette denne anmodning permanent?", "remove_request": "Er du sikker på, at du vil slette denne anmodning permanent?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Er du sikker på, at du vil slette dette hold?", "remove_team": "Er du sikker på, at du vil slette dette hold?",
"remove_telemetry": "Er du sikker på, at du vil fravælge telemetri?", "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.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Overskrift {count}", "header": "Overskrift {count}",
"message": "Besked {count}", "message": "Besked {count}",
@@ -238,13 +192,11 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protokoller er tomme", "protocols": "Protokoller er tomme",
"schema": "Opret forbindelse til et GraphQL -slutpunkt", "schema": "Opret forbindelse til et GraphQL -slutpunkt",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Teamnavn er tomt", "team_name": "Teamnavn er tomt",
"teams": "Hold er tomme", "teams": "Hold er tomme",
"tests": "Der er ingen test for denne anmodning", "tests": "Der er ingen test for denne anmodning"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Angiv et gyldigt navn på miljøet", "invalid_name": "Angiv et gyldigt navn på miljøet",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Variabel liste" "variable_list": "Variabel liste"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Det ser ikke ud til, at denne browser understøtter Server Sent Events.", "browser_support_sse": "Det ser ikke ud til, at denne browser understøtter Server Sent Events.",
"check_console_details": "Tjek konsollog for at få flere oplysninger.", "check_console_details": "Tjek konsollog for at få flere oplysninger.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL er ikke formateret korrekt", "curl_invalid_format": "cURL er ikke formateret korrekt",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Kunne ikke pryde et ugyldigt brødtekst, løse json -syntaksfejl og prøve igen", "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.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "Anmodningen kunne ikke sendes", "network_fail": "Anmodningen kunne ikke sendes",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Ingen varighed", "no_duration": "Ingen varighed",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "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", "proxy_error": "Proxy error",
"script_fail": "Kunne ikke udføre pre-request script", "script_fail": "Kunne ikke udføre pre-request script",
"something_went_wrong": "Noget gik galt", "something_went_wrong": "Noget gik galt",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Eksporter som JSON", "as_json": "Eksporter som JSON",
"create_secret_gist": "Opret hemmelig Gist", "create_secret_gist": "Opret hemmelig Gist",
"failed": "Something went wrong while exporting",
"gist_created": "Gist skabt", "gist_created": "Gist skabt",
"require_github": "Log ind med GitHub for at skabe hemmelig kerne", "require_github": "Log ind med GitHub for at skabe hemmelig kerne",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Abonnementer", "subscriptions": "Abonnementer",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Autorisationsoverskriften genereres automatisk, når du sender anmodningen.", "authorization": "Autorisationsoverskriften genereres automatisk, når du sender anmodningen.",
"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.",
"generate_documentation_first": "Generer først dokumentation", "generate_documentation_first": "Generer først dokumentation",
"network_fail": "Kunne ikke nå API -slutpunktet. Kontroller din netværksforbindelse, og prøv igen.", "network_fail": "Kunne ikke nå API -slutpunktet. Kontroller din netværksforbindelse, og prøv igen.",
"offline": "Du ser ud til at være offline. Data i dette arbejdsområde er muligvis ikke opdaterede.", "offline": "Du ser ud til at være offline. Data i dette arbejdsområde er muligvis ikke opdaterede.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Importer samlinger", "collections": "Importer samlinger",
"curl": "Importer cURL", "curl": "Importer cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Import mislykkedes", "failed": "Import mislykkedes",
"from_file": "Import from File",
"from_gist": "Import fra Gist", "from_gist": "Import fra Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Indtast Gist URL", "gist_url": "Indtast Gist URL",
"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_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "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_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "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"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Samlinger", "collections": "Samlinger",
"confirm": "Bekræfte", "confirm": "Bekræfte",
"customize_request": "Customize Request",
"edit_request": "Rediger anmodning", "edit_request": "Rediger anmodning",
"import_export": "Import Eksport", "import_export": "Import Eksport"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Kopier link",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Varighed", "duration": "Varighed",
"enter_curl": "Indtast cURL", "enter_curl": "Indtast cURL",
"generate_code": "Generer kode", "generate_code": "Generer kode",
"generated_code": "Genereret kode", "generated_code": "Genereret kode",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Overskriftsliste", "header_list": "Overskriftsliste",
"invalid_name": "Angiv et navn på anmodningen", "invalid_name": "Angiv et navn på anmodningen",
"method": "Metode", "method": "Metode",
@@ -552,14 +480,12 @@
"saved": "Anmodning gemt", "saved": "Anmodning gemt",
"share": "Del", "share": "Del",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Anmodning", "title": "Anmodning",
"type": "Anmodningstype", "type": "Anmodningstype",
"url": "URL", "url": "URL",
"variables": "Variabler", "variables": "Variabler",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Kopier link"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Tilpas dine kontoindstillinger.", "account_description": "Tilpas dine kontoindstillinger.",
"account_email_description": "Din primære e -mail -adresse.", "account_email_description": "Din primære e -mail -adresse.",
"account_name_description": "Dette er dit visningsnavn.", "account_name_description": "Dette er dit visningsnavn.",
"additional": "Additional Settings",
"background": "Baggrund", "background": "Baggrund",
"black_mode": "Sort", "black_mode": "Sort",
"choose_language": "Vælg sprog", "choose_language": "Vælg sprog",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Kopiér anmodningslink",
"delete_method": "Vælg SLET metode", "delete_method": "Vælg SLET metode",
"get_method": "Vælg GET -metode", "get_method": "Vælg GET -metode",
"head_method": "Vælg HEAD -metode", "head_method": "Vælg HEAD -metode",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Gem i samlinger", "save_to_collections": "Gem i samlinger",
"send_request": "Send anmodning", "send_request": "Send anmodning",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Anmodning", "title": "Anmodning"
"copy_request_link": "Kopiér anmodningslink"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Kopieret til udklipsholder", "copied_to_clipboard": "Kopieret til udklipsholder",
"deleted": "Slettet", "deleted": "Slettet",
"deprecated": "DEPRECATED", "deprecated": "DEPRECATED",
@@ -836,12 +742,10 @@
"disconnected": "Afbrudt", "disconnected": "Afbrudt",
"disconnected_from": "Koblet fra {name}", "disconnected_from": "Koblet fra {name}",
"docs_generated": "Dokumentation genereret", "docs_generated": "Dokumentation genereret",
"download_failed": "Download failed",
"download_started": "Downloaden er startet", "download_started": "Downloaden er startet",
"enabled": "Aktiveret", "enabled": "Aktiveret",
"file_imported": "Fil importeret", "file_imported": "Fil importeret",
"finished_in": "Færdig om {duration} ms", "finished_in": "Færdig om {duration} ms",
"hide": "Hide",
"history_deleted": "Historik slettet", "history_deleted": "Historik slettet",
"linewrap": "Wrap linjer", "linewrap": "Wrap linjer",
"loading": "Indlæser...", "loading": "Indlæser...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Forespørgsler", "queries": "Forespørgsler",
"query": "Forespørgsel", "query": "Forespørgsel",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Test", "tests": "Test",
@@ -905,7 +807,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Afslut Team", "exit": "Afslut Team",
"exit_disabled": "Kun ejeren kan ikke forlade teamet", "exit_disabled": "Kun ejeren kan ikke forlade teamet",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "E -mailformatet er ugyldigt", "invalid_email_format": "E -mailformatet er ugyldigt",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Hold reddet", "saved": "Hold reddet",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Hold", "title": "Hold",
"we_sent_invite_link": "We sent an invite link to all invitees!", "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." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"choose_file": "Datei auswählen", "choose_file": "Datei auswählen",
@@ -11,7 +10,6 @@
"connect": "Verbinden", "connect": "Verbinden",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Kopieren", "copy": "Kopieren",
"create": "Create",
"delete": "Löschen", "delete": "Löschen",
"disconnect": "Trennen", "disconnect": "Trennen",
"dismiss": "Verwerfen", "dismiss": "Verwerfen",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Einfügen", "paste": "Einfügen",
"prettify": "Verschönern", "prettify": "Verschönern",
"properties": "Properties",
"remove": "Entfernen", "remove": "Entfernen",
"rename": "Rename", "rename": "Rename",
"restore": "Wiederherstellen", "restore": "Wiederherstellen",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Suchen", "search": "Suchen",
"send": "Senden", "send": "Senden",
"share": "Share",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Stopp", "stop": "Stopp",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Chatte mit uns", "chat_with_us": "Chatte mit uns",
"contact_us": "Kontaktiere uns", "contact_us": "Kontaktiere uns",
"cookies": "Cookies",
"copy": "Kopieren", "copy": "Kopieren",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Kopiere Nutzer-Authentisierungstoken", "copy_user_id": "Kopiere Nutzer-Authentisierungstoken",
"developer_option": "Entwickleroptionen", "developer_option": "Entwickleroptionen",
"developer_option_description": "Entwicklungswerkzeuge, die helfen Hoppscotch weiter zu entwickeln und zu warten.", "developer_option_description": "Entwicklungswerkzeuge, die helfen Hoppscotch weiter zu entwickeln und zu warten.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Tastaturkürzel", "keyboard_shortcuts": "Tastaturkürzel",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Neue Version gefunden. Zum Aktualisieren Seite neu laden.", "new_version_found": "Neue Version gefunden. Zum Aktualisieren Seite neu laden.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Proxy-Datenschutzrichtlinie", "proxy_privacy_policy": "Proxy-Datenschutzrichtlinie",
"reload": "Neu laden", "reload": "Neu laden",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Token generieren", "generate_token": "Token generieren",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "In URL einbinden", "include_in_url": "In URL einbinden",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Dokumentation", "learn": "Dokumentation",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Übertragungsart", "pass_key_by": "Übertragungsart",
"password": "Passwort", "password": "Passwort",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
"type": "Autorisierungsverfahren", "type": "Autorisierungsverfahren",
"username": "Nutzername" "username": "Nutzername"
@@ -148,7 +124,6 @@
"created": "Sammlung erstellt", "created": "Sammlung erstellt",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Sammlung bearbeiten", "edit": "Sammlung bearbeiten",
"import_or_create": "Import or create a collection",
"invalid_name": "Bitte gib einen gültigen Namen für die Sammlung an", "invalid_name": "Bitte gib einen gültigen Namen für die Sammlung an",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Sammlungsname soll mindestens 3 Zeichen lang sein", "name_length_insufficient": "Sammlungsname soll mindestens 3 Zeichen lang sein",
"new": "Neue Sammlung", "new": "Neue Sammlung",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Sammlung umbenannt", "renamed": "Sammlung umbenannt",
"request_in_use": "Anfrage wird ausgeführt", "request_in_use": "Anfrage wird ausgeführt",
"save_as": "Speichern als", "save_as": "Speichern als",
@@ -178,7 +151,6 @@
"remove_folder": "Möchtest Du diesen Ordner wirklich dauerhaft löschen?", "remove_folder": "Möchtest Du diesen Ordner wirklich dauerhaft löschen?",
"remove_history": "Möchtest Du wirklich den gesamten Verlauf dauerhaft löschen?", "remove_history": "Möchtest Du wirklich den gesamten Verlauf dauerhaft löschen?",
"remove_request": "Möchtest Du diese Anfrage wirklich dauerhaft löschen?", "remove_request": "Möchtest Du diese Anfrage wirklich dauerhaft löschen?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Möchtest Du dieses Team wirklich löschen?", "remove_team": "Möchtest Du dieses Team wirklich löschen?",
"remove_telemetry": "Möchtest Du die Telemetrie wirklich deaktivieren?", "remove_telemetry": "Möchtest Du die Telemetrie wirklich deaktivieren?",
"request_change": "Möchtest Du diese Anfrage verwerfen? Ungespeicherte Änderungen gehen verloren.", "request_change": "Möchtest Du diese Anfrage verwerfen? Ungespeicherte Änderungen gehen verloren.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Header {count}", "header": "Header {count}",
"message": "Nachricht {count}", "message": "Nachricht {count}",
@@ -238,13 +192,11 @@
"profile": "Einloggen um das Profil anzusehen", "profile": "Einloggen um das Profil anzusehen",
"protocols": "Protokolle sind leer", "protocols": "Protokolle sind leer",
"schema": "Verbinden mit einem GraphQL-Endpunkt", "schema": "Verbinden mit einem GraphQL-Endpunkt",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Teamname leer", "team_name": "Teamname leer",
"teams": "Teams sind leer", "teams": "Teams sind leer",
"tests": "Es gibt keine Tests für diese Anfrage", "tests": "Es gibt keine Tests für diese Anfrage"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Zur Globalen Umgebung hinzufügen", "add_to_global": "Zur Globalen Umgebung hinzufügen",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Bitte gib einen gültigen Namen für die Umgebung an", "invalid_name": "Bitte gib einen gültigen Namen für die Umgebung an",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Variablenliste" "variable_list": "Variablenliste"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Dieser Browser scheint keine Unterstützung für die vom Server gesendete Ereignisse zu haben.", "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_console_details": "Einzelheiten findest Du in der Browser-Konsole.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL ist nicht richtig formatiert", "curl_invalid_format": "cURL ist nicht richtig formatiert",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Ein ungültiger Text konnte nicht verschönert werden, JSON-Syntaxfehler beheben und erneut versuchen", "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_error": "Netzwerkfehler. Bitte versuche es erneut.",
"network_fail": "Anfrage konnte nicht gesendet werden", "network_fail": "Anfrage konnte nicht gesendet werden",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Keine Dauer", "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", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "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", "proxy_error": "Proxy error",
"script_fail": "Pre-Request-Skripte konnte nicht ausgeführt werden", "script_fail": "Pre-Request-Skripte konnte nicht ausgeführt werden",
"something_went_wrong": "Etwas ist schief gelaufen", "something_went_wrong": "Etwas ist schief gelaufen",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Als JSON exportieren", "as_json": "Als JSON exportieren",
"create_secret_gist": "Geheimen Github Gist erstellen", "create_secret_gist": "Geheimen Github Gist erstellen",
"failed": "Something went wrong while exporting",
"gist_created": "Gist erstellt", "gist_created": "Gist erstellt",
"require_github": "Melde Dich bei GitHub an, um einen geheimen Gist zu erstellen", "require_github": "Melde Dich bei GitHub an, um einen geheimen Gist zu erstellen",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Abonnements", "subscriptions": "Abonnements",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Der Autorisierungsheader wird automatisch generiert, wenn Du die Anfrage sendest.", "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.",
"generate_documentation_first": "Zuerst Dokumentation erstellen", "generate_documentation_first": "Zuerst Dokumentation erstellen",
"network_fail": "Der API-Endpunkt kann nicht erreicht werden. Überprüfe Deine Netzwerkverbindung und versuche es erneut.", "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.", "offline": "Du scheinst offline zu sein. Die Daten in diesem Arbeitsbereich sind möglicherweise nicht aktuell.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Sammlungen importieren", "collections": "Sammlungen importieren",
"curl": "cURL importieren", "curl": "cURL importieren",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Importieren fehlgeschlagen", "failed": "Importieren fehlgeschlagen",
"from_file": "Import from File",
"from_gist": "Von Github Gist importieren", "from_gist": "Von Github Gist importieren",
"from_gist_description": "Von Github Gist URL importieren", "from_gist_description": "Von Github Gist URL importieren",
"from_insomnia": "Von Insomnia importieren", "from_insomnia": "Von Insomnia importieren",
@@ -394,17 +331,11 @@
"from_postman_description": "Postman-Sammlung importieren", "from_postman_description": "Postman-Sammlung importieren",
"from_url": "Von URL importieren", "from_url": "Von URL importieren",
"gist_url": "Gist-URL eingeben", "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_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "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_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Hoppscotch Sammlungsdatei (JSON) importieren", "json_description": "Hoppscotch Sammlungsdatei (JSON) importieren",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Importieren" "title": "Importieren"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Sammlungen", "collections": "Sammlungen",
"confirm": "Aktion bestätigen", "confirm": "Aktion bestätigen",
"customize_request": "Customize Request",
"edit_request": "Anfrage bearbeiten", "edit_request": "Anfrage bearbeiten",
"import_export": "Import / Export", "import_export": "Import / Export"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Link kopieren",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Dauer", "duration": "Dauer",
"enter_curl": "cURL eingeben", "enter_curl": "cURL eingeben",
"generate_code": "Code generieren", "generate_code": "Code generieren",
"generated_code": "Generierter Code", "generated_code": "Generierter Code",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Header-Liste", "header_list": "Header-Liste",
"invalid_name": "Bitte gib einen Namen für die Anfrage an", "invalid_name": "Bitte gib einen Namen für die Anfrage an",
"method": "Methode", "method": "Methode",
@@ -552,14 +480,12 @@
"saved": "Anfrage gespeichert", "saved": "Anfrage gespeichert",
"share": "Teilen", "share": "Teilen",
"share_description": "Teile Hoppscotch mit Deinen Freunden", "share_description": "Teile Hoppscotch mit Deinen Freunden",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Anfrage", "title": "Anfrage",
"type": "Anfragetyp", "type": "Anfragetyp",
"url": "URL", "url": "URL",
"variables": "Variablen", "variables": "Variablen",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Link kopieren"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Passe Deine Kontoeinstellungen an.", "account_description": "Passe Deine Kontoeinstellungen an.",
"account_email_description": "Deine primäre E-Mail-Adresse.", "account_email_description": "Deine primäre E-Mail-Adresse.",
"account_name_description": "Dies ist Dein Anzeigename.", "account_name_description": "Dies ist Dein Anzeigename.",
"additional": "Additional Settings",
"background": "Hintergrund", "background": "Hintergrund",
"black_mode": "Schwarz", "black_mode": "Schwarz",
"choose_language": "Sprache wählen", "choose_language": "Sprache wählen",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "E-Mail-Adresse bestätigen" "verify_email": "E-Mail-Adresse bestätigen"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Anfragelink kopieren",
"delete_method": "DELETE-Methode auswählen", "delete_method": "DELETE-Methode auswählen",
"get_method": "GET-Methode auswählen", "get_method": "GET-Methode auswählen",
"head_method": "HEAD-Methode auswählen", "head_method": "HEAD-Methode auswählen",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "In Sammlungen speichern", "save_to_collections": "In Sammlungen speichern",
"send_request": "Anfrage senden", "send_request": "Anfrage senden",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Anfrage", "title": "Anfrage"
"copy_request_link": "Anfragelink kopieren"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "In die Zwischenablage kopiert", "copied_to_clipboard": "In die Zwischenablage kopiert",
"deleted": "Gelöscht", "deleted": "Gelöscht",
"deprecated": "VERALTET", "deprecated": "VERALTET",
@@ -836,12 +742,10 @@
"disconnected": "Getrennt", "disconnected": "Getrennt",
"disconnected_from": "Verbindung zu {name} getrennt", "disconnected_from": "Verbindung zu {name} getrennt",
"docs_generated": "Dokumentation erstellt", "docs_generated": "Dokumentation erstellt",
"download_failed": "Download failed",
"download_started": "Download gestartet", "download_started": "Download gestartet",
"enabled": "Aktiviert", "enabled": "Aktiviert",
"file_imported": "Datei importiert", "file_imported": "Datei importiert",
"finished_in": "Fertig in {duration} ms", "finished_in": "Fertig in {duration} ms",
"hide": "Hide",
"history_deleted": "Verlauf gelöscht", "history_deleted": "Verlauf gelöscht",
"linewrap": "Zeilen umbrechen", "linewrap": "Zeilen umbrechen",
"loading": "Wird geladen...", "loading": "Wird geladen...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Anfragen", "queries": "Anfragen",
"query": "Anfrage", "query": "Anfrage",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Tests", "tests": "Tests",
@@ -905,7 +807,6 @@
"email_do_not_match": "E-Mail-Adresse stimmt nicht mit Deinen Kontodaten überein, bitte kontaktiere den Teameigentümer.", "email_do_not_match": "E-Mail-Adresse stimmt nicht mit Deinen Kontodaten überein, bitte kontaktiere den Teameigentümer.",
"exit": "Team verlassen", "exit": "Team verlassen",
"exit_disabled": "Eigentümer können das Team nicht verlassen", "exit_disabled": "Eigentümer können das Team nicht verlassen",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "E-Mail-Format ist ungültig", "invalid_email_format": "E-Mail-Format ist ungültig",
"invalid_id": "Ungültige Team-ID, bitte kontaktiere den Teameigentümer.", "invalid_id": "Ungültige Team-ID, bitte kontaktiere den Teameigentümer.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Team gespeichert", "saved": "Team gespeichert",
"select_a_team": "Team auswählen", "select_a_team": "Team auswählen",
"success_invites": "Success invites",
"title": "Team", "title": "Team",
"we_sent_invite_link": "Einladungen wurden an alle E-Mails verschickt!", "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." "we_sent_invite_link_description": "Bitte alle eingeladenen Personen, ihren Posteingang zu überprüfen. Klicke auf den Link, um dem Team beizutreten."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Ματαίωση", "cancel": "Ματαίωση",
"choose_file": "Επιλέξτε ένα αρχείο", "choose_file": "Επιλέξτε ένα αρχείο",
@@ -11,7 +10,6 @@
"connect": "Συνδέω-συωδεομαι", "connect": "Συνδέω-συωδεομαι",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "αντίγραφο", "copy": "αντίγραφο",
"create": "Create",
"delete": "Διαγράφω", "delete": "Διαγράφω",
"disconnect": "Αποσυνδέω", "disconnect": "Αποσυνδέω",
"dismiss": "Απολύω", "dismiss": "Απολύω",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Ωραιοποιώ", "prettify": "Ωραιοποιώ",
"properties": "Properties",
"remove": "Αφαιρώ", "remove": "Αφαιρώ",
"rename": "Rename", "rename": "Rename",
"restore": "Επαναφέρω", "restore": "Επαναφέρω",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Αναζήτηση", "search": "Αναζήτηση",
"send": "Στείλετε", "send": "Στείλετε",
"share": "Share",
"start": "Αρχή", "start": "Αρχή",
"starting": "Starting", "starting": "Starting",
"stop": "Να σταματήσει", "stop": "Να σταματήσει",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "μίλα μαζί μας", "chat_with_us": "μίλα μαζί μας",
"contact_us": "Επικοινωνήστε μαζί μας", "contact_us": "Επικοινωνήστε μαζί μας",
"cookies": "Cookies",
"copy": "αντίγραφο", "copy": "αντίγραφο",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Εργαλεία για προγραμματιστές που βοηθάνε στην ανάπτυξη και συντήρηση του Hoppscotch.", "developer_option_description": "Εργαλεία για προγραμματιστές που βοηθάνε στην ανάπτυξη και συντήρηση του Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου", "keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Βρέθηκε νέα έκδοση. Ανανέωση για ενημέρωση.", "new_version_found": "Βρέθηκε νέα έκδοση. Ανανέωση για ενημέρωση.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Πολιτική απορρήτου μεσολάβησης", "proxy_privacy_policy": "Πολιτική απορρήτου μεσολάβησης",
"reload": "Φορτώνω πάλι", "reload": "Φορτώνω πάλι",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Δημιουργήστε το διακριτικό", "generate_token": "Δημιουργήστε το διακριτικό",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Συμπερίληψη στη διεύθυνση URL", "include_in_url": "Συμπερίληψη στη διεύθυνση URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Μάθε πως", "learn": "Μάθε πως",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Κωδικός πρόσβασης", "password": "Κωδικός πρόσβασης",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Ενδειξη", "token": "Ενδειξη",
"type": "Τύπος εξουσιοδότησης", "type": "Τύπος εξουσιοδότησης",
"username": "Όνομα χρήστη" "username": "Όνομα χρήστη"
@@ -148,7 +124,6 @@
"created": "Η συλλογή δημιουργήθηκε", "created": "Η συλλογή δημιουργήθηκε",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Επεξεργασία Συλλογής", "edit": "Επεξεργασία Συλλογής",
"import_or_create": "Import or create a collection",
"invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για τη συλλογή", "invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για τη συλλογή",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Νέα συλλογή", "new": "Νέα συλλογή",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Η συλλογή μετονομάστηκε", "renamed": "Η συλλογή μετονομάστηκε",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Αποθήκευση ως", "save_as": "Αποθήκευση ως",
@@ -178,7 +151,6 @@
"remove_folder": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτόν τον φάκελο;", "remove_folder": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτόν τον φάκελο;",
"remove_history": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά όλο το ιστορικό;", "remove_history": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά όλο το ιστορικό;",
"remove_request": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτό το αίτημα;", "remove_request": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτό το αίτημα;",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την ομάδα;", "remove_team": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την ομάδα;",
"remove_telemetry": "Είστε βέβαιοι ότι θέλετε να εξαιρεθείτε από την τηλεμετρία;", "remove_telemetry": "Είστε βέβαιοι ότι θέλετε να εξαιρεθείτε από την τηλεμετρία;",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Κεφαλίδα {count}", "header": "Κεφαλίδα {count}",
"message": "Μήνυμα {count}", "message": "Μήνυμα {count}",
@@ -238,13 +192,11 @@
"profile": "Συνδεθείτε για προβολή του προφίλ σας", "profile": "Συνδεθείτε για προβολή του προφίλ σας",
"protocols": "Τα πρωτόκολλα είναι κενά", "protocols": "Τα πρωτόκολλα είναι κενά",
"schema": "Συνδεθείτε σε ένα τελικό σημείο GraphQL", "schema": "Συνδεθείτε σε ένα τελικό σημείο GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Τα Shortcodes είναι κενά",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Το όνομα της ομάδας είναι κενό", "team_name": "Το όνομα της ομάδας είναι κενό",
"teams": "Οι ομάδες είναι άδειες", "teams": "Οι ομάδες είναι άδειες",
"tests": "Δεν υπάρχουν δοκιμές για αυτό το αίτημα", "tests": "Δεν υπάρχουν δοκιμές για αυτό το αίτημα"
"shortcodes": "Τα Shortcodes είναι κενά"
}, },
"environment": { "environment": {
"add_to_global": "Προσθήκη στο Global", "add_to_global": "Προσθήκη στο Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για το περιβάλλον", "invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για το περιβάλλον",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "Τα Περιβάλλοντα μου", "my_environments": "Τα Περιβάλλοντα μου",
@@ -281,10 +232,8 @@
"variable_list": "Λίστα μεταβλητών" "variable_list": "Λίστα μεταβλητών"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Αυτό το πρόγραμμα περιήγησης δεν φαίνεται να υποστηρίζει διακομιστές που έχουν σταλεί συμβάντα.", "browser_support_sse": "Αυτό το πρόγραμμα περιήγησης δεν φαίνεται να υποστηρίζει διακομιστές που έχουν σταλεί συμβάντα.",
"check_console_details": "Ελέγξτε το αρχείο καταγραφής της κονσόλας για λεπτομέρειες.", "check_console_details": "Ελέγξτε το αρχείο καταγραφής της κονσόλας για λεπτομέρειες.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "Το cURL δεν έχει μορφοποιηθεί σωστά", "curl_invalid_format": "Το cURL δεν έχει μορφοποιηθεί σωστά",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Δεν ήταν δυνατή η ομορφιά ενός μη έγκυρου σώματος, η επίλυση σφαλμάτων σύνταξης json και η προσπάθεια ξανά", "json_prettify_invalid_body": "Δεν ήταν δυνατή η ομορφιά ενός μη έγκυρου σώματος, η επίλυση σφαλμάτων σύνταξης json και η προσπάθεια ξανά",
"network_error": "Από ότι φαίνεται υπάρχει ένα σφάλμα δικτύου. Παρακαλούμε προσπαθήστε ξανά.", "network_error": "Από ότι φαίνεται υπάρχει ένα σφάλμα δικτύου. Παρακαλούμε προσπαθήστε ξανά.",
"network_fail": "Δεν ήταν δυνατή η αποστολή του αιτήματος", "network_fail": "Δεν ήταν δυνατή η αποστολή του αιτήματος",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Χωρίς διάρκεια", "no_duration": "Χωρίς διάρκεια",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "Δεν βρέθηκαν αντιστοιχίες", "no_results_found": "Δεν βρέθηκαν αντιστοιχίες",
"page_not_found": "Αυτή η σελίδα δεν βρέθηκε", "page_not_found": "Αυτή η σελίδα δεν βρέθηκε",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Δεν ήταν δυνατή η εκτέλεση του σεναρίου πριν από το αίτημα", "script_fail": "Δεν ήταν δυνατή η εκτέλεση του σεναρίου πριν από το αίτημα",
"something_went_wrong": "Κάτι πήγε στραβά", "something_went_wrong": "Κάτι πήγε στραβά",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Εξαγωγή ως JSON", "as_json": "Εξαγωγή ως JSON",
"create_secret_gist": "Δημιουργήστε μυστική ουσία", "create_secret_gist": "Δημιουργήστε μυστική ουσία",
"failed": "Something went wrong while exporting",
"gist_created": "Η ουσία δημιουργήθηκε", "gist_created": "Η ουσία δημιουργήθηκε",
"require_github": "Συνδεθείτε με το GitHub για να δημιουργήσετε μυστική ουσία", "require_github": "Συνδεθείτε με το GitHub για να δημιουργήσετε μυστική ουσία",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Συνδρομές", "subscriptions": "Συνδρομές",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Η κεφαλίδα εξουσιοδότησης θα δημιουργηθεί αυτόματα κατά την αποστολή του αιτήματος.", "authorization": "Η κεφαλίδα εξουσιοδότησης θα δημιουργηθεί αυτόματα κατά την αποστολή του αιτήματος.",
"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.",
"generate_documentation_first": "Δημιουργήστε πρώτα έγγραφα", "generate_documentation_first": "Δημιουργήστε πρώτα έγγραφα",
"network_fail": "Δεν είναι δυνατή η πρόσβαση στο τελικό σημείο API. Ελέγξτε τη σύνδεση δικτύου και δοκιμάστε ξανά.", "network_fail": "Δεν είναι δυνατή η πρόσβαση στο τελικό σημείο API. Ελέγξτε τη σύνδεση δικτύου και δοκιμάστε ξανά.",
"offline": "Φαίνεται ότι είστε εκτός σύνδεσης. Τα δεδομένα σε αυτόν τον χώρο εργασίας ενδέχεται να μην είναι ενημερωμένα.", "offline": "Φαίνεται ότι είστε εκτός σύνδεσης. Τα δεδομένα σε αυτόν τον χώρο εργασίας ενδέχεται να μην είναι ενημερωμένα.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Εισαγωγή συλλογών", "collections": "Εισαγωγή συλλογών",
"curl": "Εισαγωγή cURL", "curl": "Εισαγωγή cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Η εισαγωγή απέτυχε", "failed": "Η εισαγωγή απέτυχε",
"from_file": "Import from File",
"from_gist": "Εισαγωγή από το Gist", "from_gist": "Εισαγωγή από το Gist",
"from_gist_description": "Εισαγωγή από Gist URL", "from_gist_description": "Εισαγωγή από Gist URL",
"from_insomnia": "Εισαγωγή από Insomnia", "from_insomnia": "Εισαγωγή από Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Εισαγωγή Συλλογής από Postman", "from_postman_description": "Εισαγωγή Συλλογής από Postman",
"from_url": "Εισαγωγή από URL", "from_url": "Εισαγωγή από URL",
"gist_url": "Εισαγάγετε Gist URL", "gist_url": "Εισαγάγετε Gist URL",
"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": "Δεν μπορέσαμε να πάρουμε δεδομένα από το url", "import_from_url_invalid_fetch": "Δεν μπορέσαμε να πάρουμε δεδομένα από το url",
"import_from_url_invalid_file_format": "Σφάλμα κατά την εισαγωγή των Συλλογών", "import_from_url_invalid_file_format": "Σφάλμα κατά την εισαγωγή των Συλλογών",
"import_from_url_invalid_type": "Μη υποστηριζόμενος τύπος. Αποδεκτές τιμές είναι 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Μη υποστηριζόμενος τύπος. Αποδεκτές τιμές είναι 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Η εισαγωγή των Συλλογών ήταν επιτυχής", "import_from_url_success": "Η εισαγωγή των Συλλογών ήταν επιτυχής",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Εισαγωγή συλλογών αρχείο JSON Hoppscotch Collections", "json_description": "Εισαγωγή συλλογών αρχείο JSON Hoppscotch Collections",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Εισαγωγή" "title": "Εισαγωγή"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Συλλογές", "collections": "Συλλογές",
"confirm": "Επιβεβαιώνω", "confirm": "Επιβεβαιώνω",
"customize_request": "Customize Request",
"edit_request": "Αίτημα Επεξεργασίας", "edit_request": "Αίτημα Επεξεργασίας",
"import_export": "Εισαγωγή εξαγωγή", "import_export": "Εισαγωγή εξαγωγή"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Αντιγραφή συνδέσμου",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Διάρκεια", "duration": "Διάρκεια",
"enter_curl": "Εισαγάγετε cURL", "enter_curl": "Εισαγάγετε cURL",
"generate_code": "Δημιουργία κώδικα", "generate_code": "Δημιουργία κώδικα",
"generated_code": "Παραγόμενος κώδικας", "generated_code": "Παραγόμενος κώδικας",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Λίστα κεφαλίδων", "header_list": "Λίστα κεφαλίδων",
"invalid_name": "Καταχωρίστε ένα όνομα για το αίτημα", "invalid_name": "Καταχωρίστε ένα όνομα για το αίτημα",
"method": "Μέθοδος", "method": "Μέθοδος",
@@ -552,14 +480,12 @@
"saved": "Το αίτημα αποθηκεύτηκε", "saved": "Το αίτημα αποθηκεύτηκε",
"share": "Μερίδιο", "share": "Μερίδιο",
"share_description": "Κοινοποίηση Hoppscotch στους φίλους σου", "share_description": "Κοινοποίηση Hoppscotch στους φίλους σου",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Αίτηση", "title": "Αίτηση",
"type": "Τύπος αιτήματος", "type": "Τύπος αιτήματος",
"url": "URL", "url": "URL",
"variables": "Μεταβλητές", "variables": "Μεταβλητές",
"view_my_links": "Προβολή των links μου", "view_my_links": "Προβολή των links μου"
"copy_link": "Αντιγραφή συνδέσμου"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Προσαρμόστε τις ρυθμίσεις του λογαριασμού σας.", "account_description": "Προσαρμόστε τις ρυθμίσεις του λογαριασμού σας.",
"account_email_description": "Η κύρια διεύθυνση email σας.", "account_email_description": "Η κύρια διεύθυνση email σας.",
"account_name_description": "Αυτό είναι το εμφανιζόμενο όνομά σας.", "account_name_description": "Αυτό είναι το εμφανιζόμενο όνομά σας.",
"additional": "Additional Settings",
"background": "Ιστορικό", "background": "Ιστορικό",
"black_mode": "Μαύρος", "black_mode": "Μαύρος",
"choose_language": "Διάλεξε γλώσσα", "choose_language": "Διάλεξε γλώσσα",
@@ -634,31 +559,14 @@
"verified_email": "Επαληθευμένο email", "verified_email": "Επαληθευμένο email",
"verify_email": "Επαλήθευση email" "verify_email": "Επαλήθευση email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Δράσεις",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Το Shortcode διαγράφηκε",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Το Shortcode δεν βρέθηκε",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Αντιγραφή συνδέσμου αιτήματος",
"delete_method": "Επιλέξτε ΔΙΑΓΡΑΦΗ μεθόδου", "delete_method": "Επιλέξτε ΔΙΑΓΡΑΦΗ μεθόδου",
"get_method": "Επιλέξτε μέθοδο GET", "get_method": "Επιλέξτε μέθοδο GET",
"head_method": "Επιλέξτε μέθοδο HEAD", "head_method": "Επιλέξτε μέθοδο HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Αποθήκευση στις Συλλογές", "save_to_collections": "Αποθήκευση στις Συλλογές",
"send_request": "Στείλε αίτημα", "send_request": "Στείλε αίτημα",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Αίτηση", "title": "Αίτηση"
"copy_request_link": "Αντιγραφή συνδέσμου αιτήματος"
}, },
"response": { "response": {
"copy": "Αντιγραφή response στο πρόχειρο", "copy": "Αντιγραφή response στο πρόχειρο",
@@ -828,7 +735,6 @@
"connection_error": "Η Σύνδεση απέτυχε", "connection_error": "Η Σύνδεση απέτυχε",
"connection_failed": "Αποτυχής Σύνδεση", "connection_failed": "Αποτυχής Σύνδεση",
"connection_lost": "Η Σύνδεση χάθηκε", "connection_lost": "Η Σύνδεση χάθηκε",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Αντιγράφηκε στο πρόχειρο", "copied_to_clipboard": "Αντιγράφηκε στο πρόχειρο",
"deleted": "Διαγράφηκε", "deleted": "Διαγράφηκε",
"deprecated": "ΚΑΤΑΡΓΗΘΗΚΕ", "deprecated": "ΚΑΤΑΡΓΗΘΗΚΕ",
@@ -836,12 +742,10 @@
"disconnected": "Ασύνδετος", "disconnected": "Ασύνδετος",
"disconnected_from": "Αποσυνδέθηκε από το {name}", "disconnected_from": "Αποσυνδέθηκε από το {name}",
"docs_generated": "Δημιουργήθηκε τεκμηρίωση", "docs_generated": "Δημιουργήθηκε τεκμηρίωση",
"download_failed": "Download failed",
"download_started": "Η λήψη ξεκίνησε", "download_started": "Η λήψη ξεκίνησε",
"enabled": "Ενεργοποιημένο", "enabled": "Ενεργοποιημένο",
"file_imported": "Το αρχείο εισήχθη", "file_imported": "Το αρχείο εισήχθη",
"finished_in": "Ολοκληρώθηκε σε {duration} ms", "finished_in": "Ολοκληρώθηκε σε {duration} ms",
"hide": "Hide",
"history_deleted": "Το ιστορικό διαγράφηκε", "history_deleted": "Το ιστορικό διαγράφηκε",
"linewrap": "Τυλίξτε γραμμές", "linewrap": "Τυλίξτε γραμμές",
"loading": "Φόρτωση...", "loading": "Φόρτωση...",
@@ -852,7 +756,6 @@
"published_error": "Κάτι πήγε στραβά κατα την αποστολή του μηνύματος: {topic} με θέμα: {message}", "published_error": "Κάτι πήγε στραβά κατα την αποστολή του μηνύματος: {topic} με θέμα: {message}",
"published_message": "Δημοσιευμένο Μηνυμα: {message} με θέμα: {topic}", "published_message": "Δημοσιευμένο Μηνυμα: {message} με θέμα: {topic}",
"reconnection_error": "Αποτυχία επανασύνδεσης", "reconnection_error": "Αποτυχία επανασύνδεσης",
"show": "Show",
"subscribed_failed": "Αποτυχία εγγραφής στο Θέμα: {topic}", "subscribed_failed": "Αποτυχία εγγραφής στο Θέμα: {topic}",
"subscribed_success": "Επιτυχία εγγραφής στο Θέμα: {topic}", "subscribed_success": "Επιτυχία εγγραφής στο Θέμα: {topic}",
"unsubscribed_failed": "Αποτυχία απεγγραφής στο Θέμα: {topic}", "unsubscribed_failed": "Αποτυχία απεγγραφής στο Θέμα: {topic}",
@@ -888,7 +791,6 @@
"queries": "Ερωτήματα", "queries": "Ερωτήματα",
"query": "Ερώτηση", "query": "Ερώτηση",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Υποδοχή.IO", "socketio": "Υποδοχή.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Δοκιμές", "tests": "Δοκιμές",
@@ -905,7 +807,6 @@
"email_do_not_match": "Το Email δεν ταιριάζει με τις λεπτομέριες του προφιλ σας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.", "email_do_not_match": "Το Email δεν ταιριάζει με τις λεπτομέριες του προφιλ σας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.",
"exit": "Έξοδος από την ομάδα", "exit": "Έξοδος από την ομάδα",
"exit_disabled": "Μόνο ο ιδιοκτήτης δεν μπορεί να αποχωρήσει από την ομάδα", "exit_disabled": "Μόνο ο ιδιοκτήτης δεν μπορεί να αποχωρήσει από την ομάδα",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Η μορφή ηλεκτρονικού ταχυδρομείου δεν είναι έγκυρη", "invalid_email_format": "Η μορφή ηλεκτρονικού ταχυδρομείου δεν είναι έγκυρη",
"invalid_id": "Μή εγκυρο αναγνωριστικό ομάδας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.", "invalid_id": "Μή εγκυρο αναγνωριστικό ομάδας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Η ομάδα σώθηκε", "saved": "Η ομάδα σώθηκε",
"select_a_team": "Επιλογή ομάδας", "select_a_team": "Επιλογή ομάδας",
"success_invites": "Success invites",
"title": "Της ομάδας", "title": "Της ομάδας",
"we_sent_invite_link": "Στείλαμε έναν σύνδεσμο πρόσκλησης σε όλους!", "we_sent_invite_link": "Στείλαμε έναν σύνδεσμο πρόσκλησης σε όλους!",
"we_sent_invite_link_description": "Ζητήστε από όλους όσους στείλατε πρόσκληση να ελέγξουν τα email τους. Click στον σύνδεσμο για εισαγωγή στην ομάδα." "we_sent_invite_link_description": "Ζητήστε από όλους όσους στείλατε πρόσκληση να ελέγξουν τα email τους. Click στον σύνδεσμο για εισαγωγή στην ομάδα."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Δράσεις",
"created_on": "Created on",
"deleted": "Το Shortcode διαγράφηκε",
"method": "Method",
"not_found": "Το Shortcode δεν βρέθηκε",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -24,7 +24,6 @@
"go_back": "Go back", "go_back": "Go back",
"go_forward": "Go forward", "go_forward": "Go forward",
"group_by": "Group by", "group_by": "Group by",
"hide_secret": "Hide secret",
"label": "Label", "label": "Label",
"learn_more": "Learn more", "learn_more": "Learn more",
"less": "Less", "less": "Less",
@@ -34,7 +33,7 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties", "properties":"Properties",
"remove": "Remove", "remove": "Remove",
"rename": "Rename", "rename": "Rename",
"restore": "Restore", "restore": "Restore",
@@ -44,7 +43,6 @@
"search": "Search", "search": "Search",
"send": "Send", "send": "Send",
"share": "Share", "share": "Share",
"show_secret": "Show secret",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Stop", "stop": "Stop",
@@ -60,6 +58,24 @@
"new": "Add new", "new": "Add new",
"star": "Add star" "star": "Add star"
}, },
"cookies": {
"modal": {
"new_domain_name": "New domain name",
"set": "Set a cookie",
"cookie_string": "Cookie string",
"enter_cookie_string": "Enter cookie string",
"cookie_name": "Name",
"cookie_value": "Value",
"cookie_path": "Path",
"cookie_expires": "Expires",
"managed_tab": "Managed",
"raw_tab": "Raw",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"empty_domains": "Domain list is empty",
"empty_domain": "Domain is empty",
"no_cookies_in_domain": "No cookies set for this domain"
}
},
"app": { "app": {
"chat_with_us": "Chat with us", "chat_with_us": "Chat with us",
"contact_us": "Contact us", "contact_us": "Contact us",
@@ -123,28 +139,26 @@
"generate_token": "Generate Token", "generate_token": "Generate Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Include in URL", "include_in_url": "Include in URL",
"inherited_from": "Inherited {auth} from parent collection {collection} ",
"learn": "Learn how", "learn": "Learn how",
"pass_key_by": "Pass by",
"password": "Password",
"token": "Token",
"type": "Authorization Type",
"username": "Username",
"oauth": { "oauth": {
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect", "redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"redirect_no_client_id": "No Client ID defined", "redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined", "redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined", "redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined", "redirect_auth_token_request_failed": "Request to get the auth token failed",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"something_went_wrong_on_token_generation": "Something went wrong on token generation", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect"
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" }
},
"pass_key_by": "Pass by",
"password": "Password",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token",
"type": "Authorization Type",
"username": "Username"
}, },
"collection": { "collection": {
"created": "Collection created", "created": "Collection created",
@@ -159,7 +173,7 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "New Collection", "new": "New Collection",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties", "properties":"Colection Properties",
"properties_updated": "Collection Properties Updated", "properties_updated": "Collection Properties Updated",
"renamed": "Collection renamed", "renamed": "Collection renamed",
"request_in_use": "Request in use", "request_in_use": "Request in use",
@@ -192,24 +206,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Header {count}", "header": "Header {count}",
"message": "Message {count}", "message": "Message {count}",
@@ -239,11 +235,9 @@
"pending_invites": "There are no pending invites for this team", "pending_invites": "There are no pending invites for this team",
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protocols are empty", "protocols": "Protocols are empty",
"request_variables": "This request does not have any request variables",
"schema": "Connect to a GraphQL endpoint to view schema", "schema": "Connect to a GraphQL endpoint to view 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", "shared_requests_logout": "Login to view your shared requests or create a new one",
"shared_requests": "Shared requests are empty",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Team name empty", "team_name": "Team name empty",
"teams": "You don't belong to any teams", "teams": "You don't belong to any teams",
@@ -273,8 +267,6 @@
"quick_peek": "Environment Quick Peek", "quick_peek": "Environment Quick Peek",
"replace_with_variable": "Replace with variable", "replace_with_variable": "Replace with variable",
"scope": "Scope", "scope": "Scope",
"secrets": "Secrets",
"secret_value": "Secret value",
"select": "Select environment", "select": "Select environment",
"set": "Set environment", "set": "Set environment",
"set_as_environment": "Set as environment", "set_as_environment": "Set as environment",
@@ -283,11 +275,12 @@
"updated": "Environment updated", "updated": "Environment updated",
"value": "Value", "value": "Value",
"variable": "Variable", "variable": "Variable",
"variables":"Variables",
"variable_list": "Variable List" "variable_list": "Variable List"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "This browser doesn't seems to have Server Sent Events support.", "browser_support_sse": "This browser doesn't seems to have Server Sent Events support.",
"check_console_details": "Check console log for details.", "check_console_details": "Check console log for details.",
"check_how_to_add_origin": "Check how you can add an origin", "check_how_to_add_origin": "Check how you can add an origin",
@@ -302,7 +295,6 @@
"incorrect_email": "Incorrect email", "incorrect_email": "Incorrect email",
"invalid_link": "Invalid link", "invalid_link": "Invalid link",
"invalid_link_description": "The link you clicked is invalid or expired.", "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_parsing_failed": "Invalid JSON",
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again", "json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
@@ -316,18 +308,15 @@
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Could not execute pre-request script", "script_fail": "Could not execute pre-request script",
"something_went_wrong": "Something went wrong", "something_went_wrong": "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."
}, },
"export": { "export": {
"as_json": "Export as JSON", "as_json": "Export as JSON",
"create_secret_gist": "Create secret Gist", "create_secret_gist": "Create secret Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist", "gist_created": "Gist created",
"failed": "Something went wrong while exporting",
"secret_gist_success": "Successfully exported as secret Gist",
"require_github": "Login with GitHub to create secret gist", "require_github": "Login with GitHub to create secret gist",
"title": "Export", "title": "Export",
"success": "Successfully exported" "failed": "Something went wrong while exporting"
}, },
"filter": { "filter": {
"all": "All", "all": "All",
@@ -351,9 +340,6 @@
"subscriptions": "Subscriptions", "subscriptions": "Subscriptions",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -365,14 +351,14 @@
}, },
"helpers": { "helpers": {
"authorization": "The authorization header will be automatically generated when you send the request.", "authorization": "The authorization header will be automatically generated when you send the request.",
"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.",
"generate_documentation_first": "Generate documentation first", "generate_documentation_first": "Generate documentation first",
"network_fail": "Unable to reach the API endpoint. Check your network connection or select a different Interceptor and try again.", "network_fail": "Unable to reach the API endpoint. Check your network connection or select a different Interceptor and try again.",
"offline": "You're using Hoppscotch offline. Updates will sync when you're online, based on workspace settings.", "offline": "You're using Hoppscotch offline. Updates will sync when you're online, based on workspace settings.",
"offline_short": "You're using Hoppscotch offline.", "offline_short": "You're using Hoppscotch offline.",
"post_request_tests": "Test scripts are written in JavaScript, and are run after the response is received.", "post_request_tests": "Test scripts are written in JavaScript, and are run after the response is received.",
"pre_request_script": "Pre-request scripts are written in JavaScript, and are run before the request is sent.", "pre_request_script": "Pre-request scripts are written in JavaScript, and are run before the request is sent.",
"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.",
"script_fail": "It seems there is a glitch in the pre-request script. Check the error below and fix the script accordingly.", "script_fail": "It seems there is a glitch in the pre-request script. Check the error below and fix the script accordingly.",
"test_script_fail": "There seems to be an error with test script. Please fix the errors and run tests again", "test_script_fail": "There seems to be an error with test script. Please fix the errors and run tests again",
"tests": "Write a test script to automate debugging." "tests": "Write a test script to automate debugging."
@@ -386,10 +372,7 @@
"import": { "import": {
"collections": "Import collections", "collections": "Import collections",
"curl": "Import cURL", "curl": "Import cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Error while importing: format not recognized", "failed": "Error while importing: format not recognized",
"from_file": "Import from File",
"from_gist": "Import from Gist", "from_gist": "Import from Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -402,30 +385,28 @@
"from_openapi_description": "Import from OpenAPI specification file (YML/JSON)", "from_openapi_description": "Import from OpenAPI specification file (YML/JSON)",
"from_postman": "Import from Postman", "from_postman": "Import from Postman",
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_file": "Import from File",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Enter Gist URL", "gist_url": "Enter Gist URL",
"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_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "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_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "json_description": "Import collections from a Hoppscotch Collections JSON file",
"title": "Import",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"postman_environment": "Postman Environment", "postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file", "postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Import", "insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported", "environments_from_gist": "Import From Gist",
"file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.", "environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"success": "Successfully imported" "gql_collections_from_gist_description": "Import GraphQL Collections From Gist"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "Inspect possible errors",
"environment": { "environment": {
"add_environment": "Add to 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." "not_found": "Environment variable “{environment}” not found."
}, },
"header": { "header": {
@@ -459,8 +440,8 @@
"confirm": "Confirm", "confirm": "Confirm",
"customize_request": "Customize Request", "customize_request": "Customize Request",
"edit_request": "Edit Request", "edit_request": "Edit Request",
"import_export": "Import / Export", "share_request": "Share Request",
"share_request": "Share Request" "import_export": "Import / Export"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -561,7 +542,6 @@
"raw_body": "Raw Request Body", "raw_body": "Raw Request Body",
"rename": "Rename Request", "rename": "Rename Request",
"renamed": "Request renamed", "renamed": "Request renamed",
"request_variables": "Request variables",
"run": "Run", "run": "Run",
"save": "Save", "save": "Save",
"save_as": "Save as", "save_as": "Save as",
@@ -602,7 +582,6 @@
"account_description": "Customize your account settings.", "account_description": "Customize your account settings.",
"account_email_description": "Your primary email address.", "account_email_description": "Your primary email address.",
"account_name_description": "This is your display name.", "account_name_description": "This is your display name.",
"additional": "Additional Settings",
"background": "Background", "background": "Background",
"black_mode": "Black", "black_mode": "Black",
"choose_language": "Choose language", "choose_language": "Choose language",
@@ -647,16 +626,17 @@
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting", "use_experimental_url_bar": "Use experimental URL bar with environment highlighting",
"user": "User", "user": "User",
"verified_email": "Verified email", "verified_email": "Verified email",
"additional": "Additional Settings",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shared_requests": {
"button": "Button", "button": "Button",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.",
"customize": "Customize",
"creating_widget": "Creating widget",
"copy_html": "Copy HTML", "copy_html": "Copy HTML",
"copy_link": "Copy Link", "copy_link": "Copy Link",
"copy_markdown": "Copy Markdown", "copy_markdown": "Copy Markdown",
"creating_widget": "Creating widget",
"customize": "Customize",
"deleted": "Shared request deleted", "deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later", "description": "Select a widget, you can change and customize this later",
"embed": "Embed", "embed": "Embed",
@@ -717,8 +697,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Save to Collections", "save_to_collections": "Save to Collections",
"send_request": "Send Request", "send_request": "Send Request",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"share_request": "Share Request",
"title": "Request" "title": "Request"
}, },
"response": { "response": {
@@ -842,16 +822,16 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Copied to clipboard", "copied_to_clipboard": "Copied to clipboard",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"deleted": "Deleted", "deleted": "Deleted",
"deprecated": "DEPRECATED", "deprecated": "DEPRECATED",
"disabled": "Disabled", "disabled": "Disabled",
"disconnected": "Disconnected", "disconnected": "Disconnected",
"disconnected_from": "Disconnected from {name}", "disconnected_from": "Disconnected from {name}",
"docs_generated": "Documentation generated", "docs_generated": "Documentation generated",
"download_failed": "Download failed",
"download_started": "Download started", "download_started": "Download started",
"download_failed": "Download failed",
"enabled": "Enabled", "enabled": "Enabled",
"file_imported": "File imported", "file_imported": "File imported",
"finished_in": "Finished in {duration} ms", "finished_in": "Finished in {duration} ms",
@@ -903,7 +883,6 @@
"query": "Query", "query": "Query",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests", "shared_requests": "Shared Requests",
"share_tab_request": "Share tab request",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Tests", "tests": "Tests",
@@ -920,7 +899,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Exit Team", "exit": "Exit Team",
"exit_disabled": "Only owner cannot exit the team", "exit_disabled": "Only owner cannot exit the team",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Email format is invalid", "invalid_email_format": "Email format is invalid",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -957,12 +935,13 @@
"not_found": "Team not found. Contact your team owner.", "not_found": "Team not found. Contact your team owner.",
"not_valid_viewer": "You are not a valid viewer. Contact your team owner.", "not_valid_viewer": "You are not a valid viewer. Contact your team owner.",
"parent_coll_move": "Cannot move collection to a child collection", "parent_coll_move": "Cannot move collection to a child collection",
"success_invites": "Success invites",
"failed_invites": "Failed invites",
"pending_invites": "Pending invites", "pending_invites": "Pending invites",
"permissions": "Permissions", "permissions": "Permissions",
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Team saved", "saved": "Team saved",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Teams", "title": "Teams",
"we_sent_invite_link": "We sent an invite link to all invitees!", "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." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Desplazamiento automático", "autoscroll": "Desplazamiento automático",
"cancel": "Cancelar", "cancel": "Cancelar",
"choose_file": "Seleccionar archivo", "choose_file": "Seleccionar archivo",
@@ -11,7 +10,6 @@
"connect": "Conectar", "connect": "Conectar",
"connecting": "Conectando", "connecting": "Conectando",
"copy": "Copiar", "copy": "Copiar",
"create": "Create",
"delete": "Borrar", "delete": "Borrar",
"disconnect": "Desconectar", "disconnect": "Desconectar",
"dismiss": "Descartar", "dismiss": "Descartar",
@@ -33,7 +31,6 @@
"open_workspace": "Abrir espacio de trabajo", "open_workspace": "Abrir espacio de trabajo",
"paste": "Pegar", "paste": "Pegar",
"prettify": "Embellecer", "prettify": "Embellecer",
"properties": "Properties",
"remove": "Eliminar", "remove": "Eliminar",
"rename": "Rename", "rename": "Rename",
"restore": "Restaurar", "restore": "Restaurar",
@@ -42,7 +39,6 @@
"scroll_to_top": "Desplazar hacia arriba", "scroll_to_top": "Desplazar hacia arriba",
"search": "Buscar", "search": "Buscar",
"send": "Enviar", "send": "Enviar",
"share": "Share",
"start": "Comenzar", "start": "Comenzar",
"starting": "Iniciando", "starting": "Iniciando",
"stop": "Detener", "stop": "Detener",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Habla con nosotros", "chat_with_us": "Habla con nosotros",
"contact_us": "Contáctanos", "contact_us": "Contáctanos",
"cookies": "Cookies",
"copy": "Copiar", "copy": "Copiar",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copiar token de autenticación de usuario", "copy_user_id": "Copiar token de autenticación de usuario",
"developer_option": "Opciones para desarrolladores", "developer_option": "Opciones para desarrolladores",
"developer_option_description": "Herramientas para desarrolladores que ayudan en el desarrollo y mantenimiento de Hoppscotch.", "developer_option_description": "Herramientas para desarrolladores que ayudan en el desarrollo y mantenimiento de Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Atajos de teclado", "keyboard_shortcuts": "Atajos de teclado",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Se ha encontrado una nueva versión. Recarga la página para usarla.", "new_version_found": "Se ha encontrado una nueva versión. Recarga la página para usarla.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Política de privacidad de proxy", "proxy_privacy_policy": "Política de privacidad de proxy",
"reload": "Recargar", "reload": "Recargar",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generar token", "generate_token": "Generar token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Incluir en la URL", "include_in_url": "Incluir en la URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Aprender", "learn": "Aprender",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"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"
},
"pass_key_by": "Pasar por", "pass_key_by": "Pasar por",
"password": "Contraseña", "password": "Contraseña",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
"type": "Tipo de autorización", "type": "Tipo de autorización",
"username": "Nombre de usuario" "username": "Nombre de usuario"
@@ -148,7 +124,6 @@
"created": "Colección creada", "created": "Colección creada",
"different_parent": "No se puede reordenar la colección con un padre diferente", "different_parent": "No se puede reordenar la colección con un padre diferente",
"edit": "Editar colección", "edit": "Editar colección",
"import_or_create": "Import or create a collection",
"invalid_name": "Proporciona un nombre válido para la colección.", "invalid_name": "Proporciona un nombre válido para la colección.",
"invalid_root_move": "La colección ya está en la raíz", "invalid_root_move": "La colección ya está en la raíz",
"moved": "Movido con éxito", "moved": "Movido con éxito",
@@ -157,8 +132,6 @@
"name_length_insufficient": "El nombre de la colección debe tener al menos 3 caracteres", "name_length_insufficient": "El nombre de la colección debe tener al menos 3 caracteres",
"new": "Nueva colección", "new": "Nueva colección",
"order_changed": "Orden de colección actualizada", "order_changed": "Orden de colección actualizada",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Colección renombrada", "renamed": "Colección renombrada",
"request_in_use": "Solicitud en uso", "request_in_use": "Solicitud en uso",
"save_as": "Guardar como", "save_as": "Guardar como",
@@ -178,7 +151,6 @@
"remove_folder": "¿Estás seguro de que deseas eliminar esta carpeta de forma permanente?", "remove_folder": "¿Estás seguro de que deseas eliminar esta carpeta de forma permanente?",
"remove_history": "¿Estás seguro de que deseas eliminar todo el historial de forma permanente?", "remove_history": "¿Estás seguro de que deseas eliminar todo el historial de forma permanente?",
"remove_request": "¿Estás seguro de que deseas eliminar esta solicitud de forma permanente?", "remove_request": "¿Estás seguro de que deseas eliminar esta solicitud de forma permanente?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "¿Estás seguro de que deseas eliminar este equipo?", "remove_team": "¿Estás seguro de que deseas eliminar este equipo?",
"remove_telemetry": "¿Estás seguro de que deseas darse de baja de la telemetría?", "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.", "request_change": "¿Estás seguro de que deseas descartar la solicitud actual, los cambios no guardados se perderán.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Encabezado {count}", "header": "Encabezado {count}",
"message": "Mensaje {count}", "message": "Mensaje {count}",
@@ -238,13 +192,11 @@
"profile": "Iniciar sesión para ver tu perfil", "profile": "Iniciar sesión para ver tu perfil",
"protocols": "Los protocolos están vacíos", "protocols": "Los protocolos están vacíos",
"schema": "Conectarse a un punto final de GraphQL", "schema": "Conectarse a un punto final de GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Aún no se han creado Shortcodes",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Nombre del equipo vacío", "team_name": "Nombre del equipo vacío",
"teams": "Los equipos están vacíos", "teams": "Los equipos están vacíos",
"tests": "No hay pruebas para esta solicitud", "tests": "No hay pruebas para esta solicitud"
"shortcodes": "Aún no se han creado Shortcodes"
}, },
"environment": { "environment": {
"add_to_global": "Añadir a Global", "add_to_global": "Añadir a Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Proporciona un nombre válido para el entorno.", "invalid_name": "Proporciona un nombre válido para el entorno.",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "Mis entornos", "my_environments": "Mis entornos",
@@ -281,10 +232,8 @@
"variable_list": "Lista de variables" "variable_list": "Lista de variables"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.", "browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.",
"check_console_details": "Consulta el registro de la consola para obtener más detalles.", "check_console_details": "Consulta el registro de la consola para obtener más detalles.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL no está formateado correctamente", "curl_invalid_format": "cURL no está formateado correctamente",
"danger_zone": "Zona de peligro", "danger_zone": "Zona de peligro",
"delete_account": "Tu cuenta es actualmente propietaria en estos equipos:", "delete_account": "Tu cuenta es actualmente propietaria en estos equipos:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "No se puede aplicar embellecedor a un cuerpo inválido, resuelve errores de sintaxis json y vuelve a intentarlo", "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.", "network_error": "Parece que hay un error de red. Por favor, inténtalo de nuevo.",
"network_fail": "No se pudo enviar la solicitud", "network_fail": "No se pudo enviar la solicitud",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Sin duración", "no_duration": "Sin duración",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No se han encontrado coincidencias", "no_results_found": "No se han encontrado coincidencias",
"page_not_found": "No se ha podido encontrar esta página", "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": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "No se pudo ejecutar el script de solicitud previa", "script_fail": "No se pudo ejecutar el script de solicitud previa",
"something_went_wrong": "Algo salió mal", "something_went_wrong": "Algo salió mal",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Exportar como JSON", "as_json": "Exportar como JSON",
"create_secret_gist": "Crear un Gist secreto", "create_secret_gist": "Crear un Gist secreto",
"failed": "Something went wrong while exporting",
"gist_created": "Gist creado", "gist_created": "Gist creado",
"require_github": "Iniciar sesión con GitHub para crear un Gist secreto", "require_github": "Iniciar sesión con GitHub para crear un Gist secreto",
"title": "Exportar" "title": "Exportar"
@@ -341,9 +286,6 @@
"subscriptions": "Suscripciones", "subscriptions": "Suscripciones",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Tiempo", "time": "Tiempo",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "El encabezado de autorización se generará automáticamente cuando se envía la solicitud.", "authorization": "El encabezado de autorización se generará automáticamente cuando se envía la solicitud.",
"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.",
"generate_documentation_first": "Generar la documentación primero", "generate_documentation_first": "Generar la documentación primero",
"network_fail": "No se puede acceder a la API. Comprueba tu conexión de red y vuelve a intentarlo.", "network_fail": "No se puede acceder a la API. Comprueba tu conexión de red y vuelve a intentarlo.",
"offline": "Parece estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.", "offline": "Parece estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Importar colecciones", "collections": "Importar colecciones",
"curl": "Importar cURL", "curl": "Importar cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Importación fallida", "failed": "Importación fallida",
"from_file": "Import from File",
"from_gist": "Importar desde Gist", "from_gist": "Importar desde Gist",
"from_gist_description": "Importar desde URL de Gist", "from_gist_description": "Importar desde URL de Gist",
"from_insomnia": "Importar desde Insomnia", "from_insomnia": "Importar desde Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Importar desde una colección de Postman", "from_postman_description": "Importar desde una colección de Postman",
"from_url": "Importar desde una URL", "from_url": "Importar desde una URL",
"gist_url": "Introduce la URL de Gist", "gist_url": "Introduce la URL de Gist",
"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_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "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_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Importar colecciones desde un archivo JSON de colecciones de Hoppscotch", "json_description": "Importar colecciones desde un archivo JSON de colecciones de Hoppscotch",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Importar" "title": "Importar"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "Tienes cambios sin guardar", "close_unsaved_tab": "Tienes cambios sin guardar",
"collections": "Colecciones", "collections": "Colecciones",
"confirm": "Confirmar", "confirm": "Confirmar",
"customize_request": "Customize Request",
"edit_request": "Editar solicitud", "edit_request": "Editar solicitud",
"import_export": "Importación y exportación", "import_export": "Importación y exportación"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "Ya estás suscrito a este tema.", "already_subscribed": "Ya estás suscrito a este tema.",
@@ -520,14 +449,13 @@
"structured": "Estructurado", "structured": "Estructurado",
"text": "Texto" "text": "Texto"
}, },
"copy_link": "Copiar enlace",
"different_collection": "No se pueden reordenar solicitudes de diferentes colecciones", "different_collection": "No se pueden reordenar solicitudes de diferentes colecciones",
"duplicated": "Solicitud duplicada", "duplicated": "Solicitud duplicada",
"duration": "Duración", "duration": "Duración",
"enter_curl": "Ingrese cURL", "enter_curl": "Ingrese cURL",
"generate_code": "Generar código", "generate_code": "Generar código",
"generated_code": "Código generado", "generated_code": "Código generado",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Lista de encabezados", "header_list": "Lista de encabezados",
"invalid_name": "Proporciona un nombre para la solicitud.", "invalid_name": "Proporciona un nombre para la solicitud.",
"method": "Método", "method": "Método",
@@ -552,14 +480,12 @@
"saved": "Solicitud guardada", "saved": "Solicitud guardada",
"share": "Compartir", "share": "Compartir",
"share_description": "Comparte Hoppscotch con tus amigos", "share_description": "Comparte Hoppscotch con tus amigos",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Solicitud", "title": "Solicitud",
"type": "Tipo de solicitud", "type": "Tipo de solicitud",
"url": "URL", "url": "URL",
"variables": "Variables", "variables": "Variables",
"view_my_links": "Ver mis enlaces", "view_my_links": "Ver mis enlaces"
"copy_link": "Copiar enlace"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Personaliza la configuración de tu cuenta.", "account_description": "Personaliza la configuración de tu cuenta.",
"account_email_description": "Tu dirección de correo electrónico principal.", "account_email_description": "Tu dirección de correo electrónico principal.",
"account_name_description": "Este es tu nombre para mostrar.", "account_name_description": "Este es tu nombre para mostrar.",
"additional": "Additional Settings",
"background": "Fondo", "background": "Fondo",
"black_mode": "Negro", "black_mode": "Negro",
"choose_language": "Elegir idioma", "choose_language": "Elegir idioma",
@@ -634,31 +559,14 @@
"verified_email": "Correo electrónico verificado", "verified_email": "Correo electrónico verificado",
"verify_email": "Verificar correo electrónico" "verify_email": "Verificar correo electrónico"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Acciones",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Creado el",
"copy_html": "Copy HTML", "deleted": "Código corto eliminado",
"copy_link": "Copy Link", "method": "Método",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode no encontrado",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"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",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Copiar enlace de solicitud",
"delete_method": "Seleccionar método DELETE", "delete_method": "Seleccionar método DELETE",
"get_method": "Seleccionar método GET", "get_method": "Seleccionar método GET",
"head_method": "Seleccionar método HEAD", "head_method": "Seleccionar método HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Guardar en colecciones", "save_to_collections": "Guardar en colecciones",
"send_request": "Enviar solicitud", "send_request": "Enviar solicitud",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Solicitud", "title": "Solicitud"
"copy_request_link": "Copiar enlace de solicitud"
}, },
"response": { "response": {
"copy": "Copiar la respuesta al portapapeles", "copy": "Copiar la respuesta al portapapeles",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Error de conexión", "connection_failed": "Error de conexión",
"connection_lost": "Conexión perdida", "connection_lost": "Conexión perdida",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Copiado al portapapeles", "copied_to_clipboard": "Copiado al portapapeles",
"deleted": "Eliminado", "deleted": "Eliminado",
"deprecated": "OBSOLETO", "deprecated": "OBSOLETO",
@@ -836,12 +742,10 @@
"disconnected": "Desconectado", "disconnected": "Desconectado",
"disconnected_from": "Desconectado de {name}", "disconnected_from": "Desconectado de {name}",
"docs_generated": "Documentación generada", "docs_generated": "Documentación generada",
"download_failed": "Download failed",
"download_started": "Descarga iniciada", "download_started": "Descarga iniciada",
"enabled": "Activado", "enabled": "Activado",
"file_imported": "Archivo importado", "file_imported": "Archivo importado",
"finished_in": "Terminado en {duration} ms", "finished_in": "Terminado en {duration} ms",
"hide": "Hide",
"history_deleted": "Historial eliminado", "history_deleted": "Historial eliminado",
"linewrap": "Envolver líneas", "linewrap": "Envolver líneas",
"loading": "Cargando...", "loading": "Cargando...",
@@ -852,7 +756,6 @@
"published_error": "Algo ha ido mal al publicar el mensaje: {topic} al tema: {message}", "published_error": "Algo ha ido mal al publicar el mensaje: {topic} al tema: {message}",
"published_message": "Mensaje publicado: {mensaje} al tema: {topic}", "published_message": "Mensaje publicado: {mensaje} al tema: {topic}",
"reconnection_error": "Fallo en la reconexión", "reconnection_error": "Fallo en la reconexión",
"show": "Show",
"subscribed_failed": "Error al suscribirse al tema: {topic}", "subscribed_failed": "Error al suscribirse al tema: {topic}",
"subscribed_success": "Suscrito con éxito al tema: {topic}", "subscribed_success": "Suscrito con éxito al tema: {topic}",
"unsubscribed_failed": "Error al darse de baja del tema: {topic}", "unsubscribed_failed": "Error al darse de baja del tema: {topic}",
@@ -888,7 +791,6 @@
"queries": "Consultas", "queries": "Consultas",
"query": "Consulta", "query": "Consulta",
"schema": "Esquema", "schema": "Esquema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Pruebas", "tests": "Pruebas",
@@ -905,7 +807,6 @@
"email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario de tu equipo.", "email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario de tu equipo.",
"exit": "Salir del equipo", "exit": "Salir del equipo",
"exit_disabled": "Solo el propietario puede salir del equipo", "exit_disabled": "Solo el propietario puede salir del equipo",
"failed_invites": "Failed invites",
"invalid_coll_id": "Identificador de colección no válido", "invalid_coll_id": "Identificador de colección no válido",
"invalid_email_format": "El formato de correo electrónico no es válido", "invalid_email_format": "El formato de correo electrónico no es válido",
"invalid_id": "Identificador de equipo inválido. Ponte en contacto con el propietario de tu equipo.", "invalid_id": "Identificador de equipo inválido. Ponte en contacto con el propietario de tu equipo.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Equipo guardado", "saved": "Equipo guardado",
"select_a_team": "Seleccionar un equipo", "select_a_team": "Seleccionar un equipo",
"success_invites": "Success invites",
"title": "Equipos", "title": "Equipos",
"we_sent_invite_link": "¡Hemos enviado un enlace de invitación a todos los invitados!", "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 tu bandeja de entrada. Haz clic en el enlace para unirse al equipo." "we_sent_invite_link_description": "Pide a todos los invitados que revisen tu bandeja de entrada. Haz clic en el enlace para unirse al equipo."
@@ -979,14 +879,5 @@
"personal": "Mi espacio de trabajo", "personal": "Mi espacio de trabajo",
"team": "Espacio de trabajo en equipo", "team": "Espacio de trabajo en equipo",
"title": "Espacios de trabajo" "title": "Espacios de trabajo"
},
"shortcodes": {
"actions": "Acciones",
"created_on": "Creado el",
"deleted": "Código corto eliminado",
"method": "Método",
"not_found": "Shortcode no encontrado",
"short_code": "Short code",
"url": "URL"
} }
} }

Some files were not shown because too many files have changed in this diff Show More