Skip to content

Commit

Permalink
Merge branch 'master' into update-design-system
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasEng authored Jan 2, 2024
2 parents 0bc8b5e + 58f9089 commit 4bf459b
Show file tree
Hide file tree
Showing 22 changed files with 295 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ public class AltinnAppGitRepository : AltinnGitRepository

private static string ProcessDefinitionFilePath => Path.Combine(PROCESS_DEFINITION_FOLDER_PATH, PROCESS_DEFINITION_FILENAME);

private const string _layoutSettingsSchemaUrl = "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json";
public const string LayoutSettingsSchemaUrl = "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json";

public const string LayoutSchemaUrl = "https://altinncdn.no/schemas/json/layout/layout.schema.v1.json";

private const string TextResourceFileNamePattern = "resource.??.json";

Expand Down Expand Up @@ -511,7 +513,7 @@ private async Task CreateLayoutSettings(string layoutSetName)
string[] layoutNames = MakePageOrder(GetLayoutNames(layoutSetName));

string defaultSettings = $@"{{
""schema"": ""{_layoutSettingsSchemaUrl}"",
""schema"": ""{LayoutSettingsSchemaUrl}"",
""pages"": {{
""order"": {JsonSerializer.Serialize(layoutNames)}
}}
Expand Down
42 changes: 41 additions & 1 deletion backend/src/Designer/Services/Implementation/RepositorySI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Net;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
Expand Down Expand Up @@ -33,6 +34,10 @@ namespace Altinn.Studio.Designer.Services.Implementation
/// </summary>
public class RepositorySI : IRepository
{
// Using Norwegian name of initial page to be consistent
// with automatic naming from frontend when adding new page
private const string InitialLayout = "Side1";

private readonly ServiceRepositorySettings _settings;
private readonly GeneralSettings _generalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
Expand All @@ -41,9 +46,10 @@ public class RepositorySI : IRepository
private readonly ILogger _logger;
private readonly IAltinnGitRepositoryFactory _altinnGitRepositoryFactory;
private readonly IApplicationMetadataService _applicationMetadataService;
private readonly IAppDevelopmentService _appDevelopmentService;
private readonly ITextsService _textsService;
private readonly IResourceRegistry _resourceRegistryService;
private readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions() { PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase, WriteIndented = true };
private readonly JsonSerializerOptions _serializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true };

/// <summary>
/// Initializes a new instance of the <see cref="RepositorySI"/> class
Expand All @@ -56,6 +62,7 @@ public class RepositorySI : IRepository
/// <param name="logger">The logger</param>
/// <param name="altinnGitRepositoryFactory">Factory class that knows how to create types of <see cref="AltinnGitRepository"/></param>
/// <param name="applicationMetadataService">The service for handling the application metadata file</param>
/// <param name="appDevelopmentService">The service for handling files concerning app-development</param>
/// <param name="textsService">The service for handling texts</param>
/// <param name="resourceRegistryService">The service for publishing resource in the ResourceRegistry</param>
public RepositorySI(
Expand All @@ -67,6 +74,7 @@ public RepositorySI(
ILogger<RepositorySI> logger,
IAltinnGitRepositoryFactory altinnGitRepositoryFactory,
IApplicationMetadataService applicationMetadataService,
IAppDevelopmentService appDevelopmentService,
ITextsService textsService,
IResourceRegistry resourceRegistryService)
{
Expand All @@ -78,6 +86,7 @@ public RepositorySI(
_logger = logger;
_altinnGitRepositoryFactory = altinnGitRepositoryFactory;
_applicationMetadataService = applicationMetadataService;
_appDevelopmentService = appDevelopmentService;
_textsService = textsService;
_resourceRegistryService = resourceRegistryService;
}
Expand Down Expand Up @@ -272,6 +281,9 @@ public bool DeleteLanguage(AltinnRepoEditingContext altinnRepoEditingContext, st
CreateServiceMetadata(metadata);
await _applicationMetadataService.CreateApplicationMetadata(org, serviceConfig.RepositoryName, serviceConfig.ServiceName);
await _textsService.CreateLanguageResources(org, serviceConfig.RepositoryName, developer);
var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, serviceConfig.RepositoryName, developer);
await _appDevelopmentService.SaveFormLayout(editingContext, null, InitialLayout, GetInitialLayout());
await _appDevelopmentService.SaveLayoutSettings(editingContext, GetInitialLayoutSettings(InitialLayout), null);
await CreateRepositorySettings(org, serviceConfig.RepositoryName, developer);

CommitInfo commitInfo = new() { Org = org, Repository = serviceConfig.RepositoryName, Message = "App created" };
Expand All @@ -282,6 +294,34 @@ public bool DeleteLanguage(AltinnRepoEditingContext altinnRepoEditingContext, st
return repository;
}

private static JsonNode GetInitialLayout()
{
var layout = new JsonObject
{
["schema"] = AltinnAppGitRepository.LayoutSchemaUrl,
["data"] = new JsonObject
{
["layout"] = new JsonArray()
}
};
return layout;
}

private static JsonNode GetInitialLayoutSettings(string initialLayout)
{

var layoutSettings = new JsonObject
{
["schema"] = AltinnAppGitRepository.LayoutSettingsSchemaUrl,
["pages"] = new JsonObject
{
["order"] = new JsonArray { initialLayout }
}
};

return layoutSettings;
}

private async Task CreateRepositorySettings(string org, string repository, string developer)
{
var altinnGitRepository = _altinnGitRepositoryFactory.GetAltinnGitRepository(org, repository, developer);
Expand Down
7 changes: 5 additions & 2 deletions backend/tests/Designer.Tests/Services/RepositorySITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public async Task CreateRepository_DoesNotExists_ShouldCreate()

try
{
var repository = await repositoryService.CreateService(org, new ServiceConfiguration() { RepositoryName = repositoryName, ServiceName = repositoryName });
await repositoryService.CreateService(org, new ServiceConfiguration() { RepositoryName = repositoryName, ServiceName = repositoryName });
var altinnStudioSettings = await new AltinnGitRepositoryFactory(repositoriesRootDirectory).GetAltinnGitRepository(org, repositoryName, developer).GetAltinnStudioSettings();
altinnStudioSettings.RepoType.Should().Be(AltinnRepositoryType.App);
}
Expand Down Expand Up @@ -329,9 +329,11 @@ private static RepositorySI GetServiceForTest(string developer, ISourceControl s

ApplicationMetadataService applicationInformationService = new(new Mock<ILogger<ApplicationMetadataService>>().Object, altinnStorageAppMetadataClient, altinnGitRepositoryFactory, httpContextAccessorMock.Object, new IGiteaMock());

AppDevelopmentService appDevelopmentService = new(altinnGitRepositoryFactory);

TextsService textsService = new(altinnGitRepositoryFactory, applicationInformationService);

ResourceRegistryService resourceRegistryService = new ResourceRegistryService();
ResourceRegistryService resourceRegistryService = new();

RepositorySI service = new(
repoSettings,
Expand All @@ -342,6 +344,7 @@ private static RepositorySI GetServiceForTest(string developer, ISourceControl s
new Mock<ILogger<RepositorySI>>().Object,
altinnGitRepositoryFactory,
applicationInformationService,
appDevelopmentService,
textsService,
resourceRegistryService);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants';
import { renderWithProviders } from '../../../test/testUtils';
import { queriesMock } from 'app-development/test/mocks';
import { textMock } from '../../../../testing/mocks/i18nMock';
import { privateRepositoryMock, repositoryMock } from '../../../test/repositoryMock';

// Test data
const org = 'org';
Expand All @@ -28,12 +29,13 @@ describe('Overview', () => {
it('renders component', async () => {
render({
getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])),
getOrgList: jest.fn().mockImplementation(() => Promise.resolve({ orgs: [] })),
getOrgList: jest.fn().mockImplementation(() => Promise.resolve({ orgs: [org] })),
getAppConfig: jest.fn().mockImplementation(() =>
Promise.resolve({
serviceName: title,
}),
),
getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(repositoryMock)),
});

expect(await screen.findByRole('heading', { name: title })).toBeInTheDocument();
Expand All @@ -55,14 +57,6 @@ describe('Overview', () => {

it('should display AppLogs if environments exist', async () => {
render({
getAppConfig: jest.fn().mockImplementation(() =>
Promise.resolve({
repositoryName: app,
serviceName: app,
serviceId: null,
serviceDescription: null,
}),
),
getOrgList: jest.fn().mockImplementation(() =>
Promise.resolve({
orgs: {
Expand All @@ -72,6 +66,7 @@ describe('Overview', () => {
},
}),
),
getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(repositoryMock)),
getDeployments: jest.fn().mockImplementation(() =>
Promise.resolve({
results: [
Expand All @@ -94,38 +89,44 @@ describe('Overview', () => {
],
}),
),
getEnvironments: jest.fn().mockImplementation(() =>
Promise.resolve([
{
appsUrl: '',
platformUrl: '',
hostname: '',
appPrefix: '',
platformPrefix: '',
name: '',
type: '',
},
]),
),
getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([{}])),
});
expect(
await screen.findByRole('heading', { name: textMock('overview.activity') }),
).toBeInTheDocument();
});

it('should not display AppLogs if environments do not exist', async () => {
it('should not display AppLogs if environments do not exist for repo owned by org', async () => {
render({
getAppConfig: jest.fn().mockImplementation(() => Promise.resolve({})),
getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(repositoryMock)),
getOrgList: jest.fn().mockImplementation(() =>
Promise.resolve({
orgs: {},
orgs: {
[org]: {
environments: [],
},
},
}),
),
getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])),
});
expect(await screen.findByText(textMock('app_publish.no_env_title'))).toBeInTheDocument();
expect(
screen.queryByRole('heading', { name: textMock('overview.activity') }),
).not.toBeInTheDocument();
});

it('should display RepoOwnedByPersonInfo if repo is not owned by an org', async () => {
render({
getOrgList: jest.fn().mockImplementation(() =>
Promise.resolve({
orgs: {},
}),
),
getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(privateRepositoryMock)),
});
expect(await screen.findByText(textMock('app_publish.private_app_owner'))).toBeInTheDocument();
});
});

const render = (queries = {}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { Navigation } from './Navigation';
import { News } from './News';
import { PageContainer } from 'app-shared/components/PageContainer/PageContainer';
import { StudioCenter, StudioSpinner } from '@studio/components';
import { useRepoMetadataQuery } from 'app-shared/hooks/queries';
import { RepoOwnedByPersonInfo } from './RepoOwnedByPersonInfo';

export const Overview = () => {
const { org, app } = useStudioUrlParams();
Expand All @@ -20,7 +22,7 @@ export const Overview = () => {
isPending: isPendingOrgs,
isError: isOrgsError,
} = useOrgListQuery({ hideDefaultError: true });

const { data: repository } = useRepoMetadataQuery(org, app);
const selectedOrg = orgs?.orgs[org];
const hasEnvironments = selectedOrg?.environments?.length > 0;

Expand All @@ -43,6 +45,9 @@ export const Overview = () => {
);
}

// If repo-owner is an organisation
const repoOwnerIsOrg = orgs && Object.keys(orgs.orgs).includes(repository?.owner.login);

return (
<PageContainer>
<div className={classes.container}>
Expand All @@ -53,9 +58,9 @@ export const Overview = () => {
<div className={classes.content}>
<main className={classes.main}>
<section className={classes.mainSection}>
<AppEnvironments />
{repoOwnerIsOrg ? <AppEnvironments /> : <RepoOwnedByPersonInfo />}
</section>
{hasEnvironments && (
{repoOwnerIsOrg && hasEnvironments && (
<section className={classes.mainSection}>
<AppLogs />
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.textContainer {
display: flex;
flex-direction: column;
width: 50%;
gap: 2rem;
}

.infoContainer {
background-image: url('/designer/img/Altinn-studio-2.svg');
background-size: 40% auto;
background-position: 90% 40%;
background-repeat: no-repeat;
padding: 1rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { RepoOwnedByPersonInfo } from './RepoOwnedByPersonInfo';
import { textMock } from '../../../../testing/mocks/i18nMock';

describe('RepoOwnedByPersonInfo', () => {
it('should show alert and info texts', () => {
render(<RepoOwnedByPersonInfo />);

const alert = screen.getByText(textMock('app_publish.private_app_owner'));
expect(alert).toBeInTheDocument();

const infoText1 = screen.getByText(textMock('app_publish.private_app_owner_info'));
expect(infoText1).toBeInTheDocument();

const infoText2 = screen.getByText(textMock('app_publish.private_app_owner_help'));
expect(infoText2).toBeInTheDocument();

const infoText3 = screen.getByText(textMock('app_publish.private_app_owner_options'));
expect(infoText3).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { Alert, Paragraph, Link } from '@digdir/design-system-react';
import { Trans, useTranslation } from 'react-i18next';
import classes from './RepoOwnedByPersonInfo.module.css';

export const RepoOwnedByPersonInfo = () => {
const { t } = useTranslation();
return (
<>
<Alert>{t('app_publish.private_app_owner')}</Alert>
<div className={classes.infoContainer}>
<div className={classes.textContainer}>
<Paragraph>{t('app_publish.private_app_owner_info')}</Paragraph>
<Paragraph>
<Trans
i18nKey={'app_publish.private_app_owner_help'}
components={{ a: <Link href='/contact'> </Link> }}
/>
</Paragraph>
<Paragraph>{t('app_publish.private_app_owner_options')}</Paragraph>
</div>
</div>
</>
);
};
11 changes: 10 additions & 1 deletion frontend/app-development/layout/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,18 @@ type PageHeaderProps = {
app: string;
showSubMenu: boolean;
user: User;
repoOwnerIsOrg: boolean;
isRepoError?: boolean;
};

export const PageHeader = ({ org, app, showSubMenu, user, isRepoError }: PageHeaderProps) => {
export const PageHeader = ({
org,
app,
showSubMenu,
user,
repoOwnerIsOrg,
isRepoError,
}: PageHeaderProps) => {
const repoType = getRepositoryType(org, app);
const repository = useAppSelector((state) => state.serviceInformation.repositoryInfo);
const menuItems = getFilteredTopBarMenu(repoType);
Expand All @@ -71,6 +79,7 @@ export const PageHeader = ({ org, app, showSubMenu, user, isRepoError }: PageHea
app={!isRepoError && app}
user={user}
repository={repository}
repoOwnerIsOrg={repoOwnerIsOrg}
buttonActions={!isRepoError && buttonActions(org, app)}
/>
);
Expand Down
Loading

0 comments on commit 4bf459b

Please sign in to comment.