mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 17:05:43 +01:00
Merge with 2.0.0-m3
This commit is contained in:
@@ -37,7 +37,6 @@ import com.github.sdorra.ssp.PermissionObject;
|
|||||||
import com.github.sdorra.ssp.StaticPermissions;
|
import com.github.sdorra.ssp.StaticPermissions;
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import sonia.scm.BasicPropertiesAware;
|
import sonia.scm.BasicPropertiesAware;
|
||||||
import sonia.scm.ModelObject;
|
import sonia.scm.ModelObject;
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
@@ -50,8 +49,11 @@ import javax.xml.bind.annotation.XmlElementWrapper;
|
|||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
import javax.xml.bind.annotation.XmlTransient;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source code repository.
|
* Source code repository.
|
||||||
@@ -79,7 +81,7 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
|
|||||||
private Long lastModified;
|
private Long lastModified;
|
||||||
private String namespace;
|
private String namespace;
|
||||||
private String name;
|
private String name;
|
||||||
private List<Permission> permissions;
|
private final Set<Permission> permissions = new HashSet<>();
|
||||||
@XmlElement(name = "public")
|
@XmlElement(name = "public")
|
||||||
private boolean publicReadable = false;
|
private boolean publicReadable = false;
|
||||||
private boolean archived = false;
|
private boolean archived = false;
|
||||||
@@ -127,7 +129,6 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.contact = contact;
|
this.contact = contact;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.permissions = Lists.newArrayList();
|
|
||||||
|
|
||||||
if (Util.isNotEmpty(permissions)) {
|
if (Util.isNotEmpty(permissions)) {
|
||||||
this.permissions.addAll(Arrays.asList(permissions));
|
this.permissions.addAll(Arrays.asList(permissions));
|
||||||
@@ -200,12 +201,8 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
|
|||||||
return new NamespaceAndName(getNamespace(), getName());
|
return new NamespaceAndName(getNamespace(), getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Permission> getPermissions() {
|
public Collection<Permission> getPermissions() {
|
||||||
if (permissions == null) {
|
return Collections.unmodifiableCollection(permissions);
|
||||||
permissions = Lists.newArrayList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return permissions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -300,8 +297,17 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPermissions(List<Permission> permissions) {
|
public void setPermissions(Collection<Permission> permissions) {
|
||||||
this.permissions = permissions;
|
this.permissions.clear();
|
||||||
|
this.permissions.addAll(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPermission(Permission newPermission) {
|
||||||
|
this.permissions.add(newPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePermission(Permission permission) {
|
||||||
|
this.permissions.remove(permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPublicReadable(boolean publicReadable) {
|
public void setPublicReadable(boolean publicReadable) {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
package sonia.scm.it;
|
package sonia.scm.it;
|
||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -49,8 +50,10 @@ import sonia.scm.web.VndMediaType;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static sonia.scm.it.utils.RepositoryUtil.addAndCommitRandomFile;
|
import static sonia.scm.it.utils.RepositoryUtil.addAndCommitRandomFile;
|
||||||
@@ -72,7 +75,7 @@ public class PermissionsITCase {
|
|||||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
private final String repositoryType;
|
private final String repositoryType;
|
||||||
private int createdPermissions;
|
private Collection<String> createdPermissions;
|
||||||
|
|
||||||
|
|
||||||
public PermissionsITCase(String repositoryType) {
|
public PermissionsITCase(String repositoryType) {
|
||||||
@@ -94,7 +97,7 @@ public class PermissionsITCase {
|
|||||||
TestData.createNotAdminUser(USER_OWNER, USER_PASS);
|
TestData.createNotAdminUser(USER_OWNER, USER_PASS);
|
||||||
TestData.createUserPermission(USER_OWNER, PermissionType.OWNER, repositoryType);
|
TestData.createUserPermission(USER_OWNER, PermissionType.OWNER, repositoryType);
|
||||||
TestData.createNotAdminUser(USER_OTHER, USER_PASS);
|
TestData.createNotAdminUser(USER_OTHER, USER_PASS);
|
||||||
createdPermissions = 3;
|
createdPermissions = asList(USER_READ, USER_WRITE, USER_OWNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -131,8 +134,8 @@ public class PermissionsITCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ownerShouldSeePermissions() {
|
public void ownerShouldSeePermissions() {
|
||||||
List<Object> userPermissions = TestData.getUserPermissions(USER_OWNER, USER_PASS, repositoryType);
|
List<Map> userPermissions = TestData.getUserPermissions(USER_OWNER, USER_PASS, repositoryType);
|
||||||
assertEquals(userPermissions.size(), createdPermissions);
|
Assertions.assertThat(userPermissions).extracting(e -> e.get("name")).containsAll(createdPermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -99,10 +99,10 @@ public class TestData {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Object> getUserPermissions(String username, String password, String repositoryType) {
|
public static List<Map> getUserPermissions(String username, String password, String repositoryType) {
|
||||||
return callUserPermissions(username, password, repositoryType, HttpStatus.SC_OK)
|
return callUserPermissions(username, password, repositoryType, HttpStatus.SC_OK)
|
||||||
.extract()
|
.extract()
|
||||||
.body().jsonPath().getList("_embedded.permissions");
|
.body().jsonPath().<Map>getList("_embedded.permissions");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidatableResponse callUserPermissions(String username, String password, String repositoryType, int expectedStatusCode) {
|
public static ValidatableResponse callUserPermissions(String username, String password, String repositoryType, int expectedStatusCode) {
|
||||||
|
|||||||
@@ -115,12 +115,6 @@
|
|||||||
<artifactId>smp-maven-plugin</artifactId>
|
<artifactId>smp-maven-plugin</artifactId>
|
||||||
<version>1.0.0-alpha-3</version>
|
<version>1.0.0-alpha-3</version>
|
||||||
<extensions>true</extensions>
|
<extensions>true</extensions>
|
||||||
<configuration>
|
|
||||||
<links>
|
|
||||||
<link>@scm-manager/ui-types</link>
|
|
||||||
<link>@scm-manager/ui-components</link>
|
|
||||||
</links>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|||||||
@@ -52,6 +52,10 @@
|
|||||||
<extensions>true</extensions>
|
<extensions>true</extensions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<corePlugin>true</corePlugin>
|
<corePlugin>true</corePlugin>
|
||||||
|
<links>
|
||||||
|
<link>@scm-manager/ui-types</link>
|
||||||
|
<link>@scm-manager/ui-components</link>
|
||||||
|
</links>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,10 @@
|
|||||||
<extensions>true</extensions>
|
<extensions>true</extensions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<corePlugin>true</corePlugin>
|
<corePlugin>true</corePlugin>
|
||||||
|
<links>
|
||||||
|
<link>@scm-manager/ui-types</link>
|
||||||
|
<link>@scm-manager/ui-components</link>
|
||||||
|
</links>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@
|
|||||||
<artifactId>smp-maven-plugin</artifactId>
|
<artifactId>smp-maven-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<corePlugin>true</corePlugin>
|
<corePlugin>true</corePlugin>
|
||||||
|
<links>
|
||||||
|
<link>@scm-manager/ui-types</link>
|
||||||
|
<link>@scm-manager/ui-components</link>
|
||||||
|
</links>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "scm-ui-components",
|
"name": "scm-ui-components",
|
||||||
|
"version": "0.0.3",
|
||||||
"description": "Lerna root for SCM-Manager UI Components",
|
"description": "Lerna root for SCM-Manager UI Components",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bootstrap": "lerna bootstrap",
|
"bootstrap": "lerna bootstrap",
|
||||||
"link": "lerna exec -- yarn link",
|
"link": "lerna exec -- yarn link",
|
||||||
"unlink": "lerna exec --no-bail -- yarn unlink || true"
|
"unlink": "lerna exec --no-bail -- yarn unlink || true",
|
||||||
|
"deploy": "node ./scripts/publish.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"lerna": "^3.2.1"
|
"lerna": "^3.4.3",
|
||||||
|
"xml2js": "^0.4.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@scm-manager/ui-components",
|
"name": "@scm-manager/ui-components",
|
||||||
"version": "0.0.1",
|
"version": "2.0.0-SNAPSHOT",
|
||||||
"description": "UI Components for SCM-Manager and its plugins",
|
"description": "UI Components for SCM-Manager and its plugins",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scm-manager/ui-extensions": "^0.1.1",
|
"@scm-manager/ui-extensions": "^0.1.1",
|
||||||
"@scm-manager/ui-types": "0.0.1",
|
"@scm-manager/ui-types": "2.0.0-SNAPSHOT",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"react": "^16.5.2",
|
"react": "^16.5.2",
|
||||||
|
|||||||
@@ -1,39 +1,33 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import injectSheet from "react-jss";
|
import injectSheet from "react-jss";
|
||||||
import classNames from "classnames";
|
import Tooltip from './Tooltip';
|
||||||
|
import HelpIcon from './HelpIcon';
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
img: {
|
tooltip: {
|
||||||
display: "block"
|
display: "inline-block",
|
||||||
},
|
paddingLeft: "3px"
|
||||||
q: {
|
|
||||||
float: "left",
|
|
||||||
paddingLeft: "3px",
|
|
||||||
float: "right"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
message: string,
|
message: string,
|
||||||
classes: any
|
classes: any
|
||||||
};
|
}
|
||||||
|
|
||||||
class Help extends React.Component<Props> {
|
class Help extends React.Component<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { message, classes } = this.props;
|
const { message, classes } = this.props;
|
||||||
const multiline = message.length > 60 ? "is-tooltip-multiline" : "";
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Tooltip className={classes.tooltip} message={message}>
|
||||||
className={classNames("tooltip is-tooltip-right", multiline, classes.q)}
|
<HelpIcon />
|
||||||
data-tooltip={message}
|
</Tooltip>
|
||||||
>
|
|
||||||
<i
|
|
||||||
className={classNames("fa fa-question has-text-info", classes.img)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectSheet(styles)(Help);
|
export default injectSheet(styles)(Help);
|
||||||
|
|
||||||
|
|||||||
14
scm-ui-components/packages/ui-components/src/HelpIcon.js
Normal file
14
scm-ui-components/packages/ui-components/src/HelpIcon.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
};
|
||||||
|
|
||||||
|
class HelpIcon extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
return <i className={classNames("fa fa-question has-text-info")} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HelpIcon;
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
//@flow
|
|
||||||
import React from "react";
|
|
||||||
import { Help } from "./index";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
label: string,
|
|
||||||
helpText?: string
|
|
||||||
};
|
|
||||||
|
|
||||||
class LabelWithHelpIcon extends React.Component<Props> {
|
|
||||||
renderLabel = () => {
|
|
||||||
const label = this.props.label;
|
|
||||||
if (label) {
|
|
||||||
return <label className="label">{label}</label>;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
renderHelp = () => {
|
|
||||||
const helpText = this.props.helpText;
|
|
||||||
if (helpText) {
|
|
||||||
return (
|
|
||||||
<div className="control columns is-vcentered">
|
|
||||||
<Help message={helpText} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderLabelWithHelpIcon = () => {
|
|
||||||
if (this.props.label) {
|
|
||||||
return (
|
|
||||||
<div className="field is-grouped">
|
|
||||||
<div className="control">{this.renderLabel()}</div>
|
|
||||||
{this.renderHelp()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return this.renderLabelWithHelpIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LabelWithHelpIcon;
|
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
//@flow
|
//@flow
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import injectSheet from "react-jss";
|
import injectSheet from "react-jss";
|
||||||
import Image from "./Image";
|
import Image from "./Image";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
|
minHeightContainer: {
|
||||||
|
minHeight: "256px"
|
||||||
|
},
|
||||||
wrapper: {
|
wrapper: {
|
||||||
position: "relative"
|
position: "relative"
|
||||||
},
|
},
|
||||||
@@ -34,6 +38,7 @@ class Loading extends React.Component<Props> {
|
|||||||
render() {
|
render() {
|
||||||
const { message, t, classes } = this.props;
|
const { message, t, classes } = this.props;
|
||||||
return (
|
return (
|
||||||
|
<div className={classes.minHeightContainer}>
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<div className={classes.loading}>
|
<div className={classes.loading}>
|
||||||
<Image
|
<Image
|
||||||
@@ -44,6 +49,7 @@ class Loading extends React.Component<Props> {
|
|||||||
<p className="has-text-centered">{message}</p>
|
<p className="has-text-centered">{message}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
scm-ui-components/packages/ui-components/src/Tooltip.js
Normal file
26
scm-ui-components/packages/ui-components/src/Tooltip.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//@flow
|
||||||
|
import * as React from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
message: string,
|
||||||
|
className: string,
|
||||||
|
children: React.Node
|
||||||
|
};
|
||||||
|
|
||||||
|
class Tooltip extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { className, message, children } = this.props;
|
||||||
|
const multiline = message.length > 60 ? "is-tooltip-multiline" : "";
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames("tooltip", "is-tooltip-right", multiline, className)}
|
||||||
|
data-tooltip={message}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tooltip;
|
||||||
@@ -10,7 +10,9 @@ type Props = {
|
|||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
helpText?: string
|
helpText?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
class Checkbox extends React.Component<Props> {
|
class Checkbox extends React.Component<Props> {
|
||||||
|
|
||||||
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
this.props.onChange(event.target.checked, this.props.name);
|
this.props.onChange(event.target.checked, this.props.name);
|
||||||
@@ -20,12 +22,8 @@ class Checkbox extends React.Component<Props> {
|
|||||||
renderHelp = () => {
|
renderHelp = () => {
|
||||||
const helpText = this.props.helpText;
|
const helpText = this.props.helpText;
|
||||||
if (helpText) {
|
if (helpText) {
|
||||||
return (
|
return <Help message={helpText} />;
|
||||||
<div className="control columns is-vcentered">
|
}
|
||||||
<Help message={helpText} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -39,10 +37,11 @@ class Checkbox extends React.Component<Props> {
|
|||||||
onChange={this.onCheckboxChange}
|
onChange={this.onCheckboxChange}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
/>
|
/>
|
||||||
|
{" "}
|
||||||
{this.props.label}
|
{this.props.label}
|
||||||
|
{this.renderHelp()}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{this.renderHelp()}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { LabelWithHelpIcon } from "../index";
|
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
label?: string,
|
label?: string,
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import Help from '../Help';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
label?: string,
|
||||||
|
helpText?: string
|
||||||
|
};
|
||||||
|
|
||||||
|
class LabelWithHelpIcon extends React.Component<Props> {
|
||||||
|
|
||||||
|
renderHelp() {
|
||||||
|
const { helpText } = this.props;
|
||||||
|
if (helpText) {
|
||||||
|
return (
|
||||||
|
<Help message={helpText} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {label } = this.props;
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
const help = this.renderHelp();
|
||||||
|
return (
|
||||||
|
<label className="label">
|
||||||
|
{label} { help }
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LabelWithHelpIcon;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { LabelWithHelpIcon } from "../index";
|
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||||
|
|
||||||
export type SelectItem = {
|
export type SelectItem = {
|
||||||
value: string,
|
value: string,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { LabelWithHelpIcon } from "../index";
|
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||||
|
|
||||||
export type SelectItem = {
|
export type SelectItem = {
|
||||||
value: string,
|
value: string,
|
||||||
@@ -8,10 +8,11 @@ export type SelectItem = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
name?: string,
|
||||||
label?: string,
|
label?: string,
|
||||||
placeholder?: SelectItem[],
|
placeholder?: SelectItem[],
|
||||||
value?: string,
|
value?: string,
|
||||||
onChange: string => void,
|
onChange: (value: string, name?: string) => void,
|
||||||
helpText?: string
|
helpText?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ class Textarea extends React.Component<Props> {
|
|||||||
field: ?HTMLTextAreaElement;
|
field: ?HTMLTextAreaElement;
|
||||||
|
|
||||||
handleInput = (event: SyntheticInputEvent<HTMLTextAreaElement>) => {
|
handleInput = (event: SyntheticInputEvent<HTMLTextAreaElement>) => {
|
||||||
this.props.onChange(event.target.value);
|
this.props.onChange(event.target.value, this.props.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ export { default as Checkbox } from "./Checkbox.js";
|
|||||||
export { default as InputField } from "./InputField.js";
|
export { default as InputField } from "./InputField.js";
|
||||||
export { default as Select } from "./Select.js";
|
export { default as Select } from "./Select.js";
|
||||||
export { default as Textarea } from "./Textarea.js";
|
export { default as Textarea } from "./Textarea.js";
|
||||||
|
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon";
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ export { default as Notification } from "./Notification.js";
|
|||||||
export { default as Paginator } from "./Paginator.js";
|
export { default as Paginator } from "./Paginator.js";
|
||||||
export { default as LinkPaginator } from "./LinkPaginator.js";
|
export { default as LinkPaginator } from "./LinkPaginator.js";
|
||||||
export { default as ProtectedRoute } from "./ProtectedRoute.js";
|
export { default as ProtectedRoute } from "./ProtectedRoute.js";
|
||||||
export { default as Help } from "./Help.js";
|
export { default as Help } from "./Help";
|
||||||
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon.js";
|
export { default as HelpIcon } from "./HelpIcon";
|
||||||
|
export { default as Tooltip } from "./Tooltip";
|
||||||
export { getPageFromMatch } from "./urls";
|
export { getPageFromMatch } from "./urls";
|
||||||
|
|
||||||
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR } from "./apiclient.js";
|
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR } from "./apiclient.js";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@scm-manager/ui-types",
|
"name": "@scm-manager/ui-types",
|
||||||
"version": "0.0.1",
|
"version": "2.0.0-SNAPSHOT",
|
||||||
"description": "Flow types for SCM-Manager related Objects",
|
"description": "Flow types for SCM-Manager related Objects",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
],
|
],
|
||||||
"repository": "https://bitbucket.org/sdorra/scm-manager",
|
"repository": "https://bitbucket.org/sdorra/scm-manager",
|
||||||
"author": "Sebastian Sdorra <sebastian.sdorra@cloudogu.com>",
|
"author": "Sebastian Sdorra <sebastian.sdorra@cloudogu.com>",
|
||||||
"license" : "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "ui-bunder lint",
|
"lint": "ui-bunder lint",
|
||||||
"check": "flow check"
|
"check": "flow check"
|
||||||
@@ -21,8 +21,14 @@
|
|||||||
[
|
[
|
||||||
"babelify",
|
"babelify",
|
||||||
{
|
{
|
||||||
"plugins": ["@babel/plugin-proposal-class-properties"],
|
"plugins": [
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-flow", "@babel/preset-react"]
|
"@babel/plugin-proposal-class-properties"
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env",
|
||||||
|
"@babel/preset-flow",
|
||||||
|
"@babel/preset-react"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
Arguments:
|
|
||||||
/usr/bin/node /home/ssdorra/.yarn/bin/yarn.js add --dev ui-bundler
|
|
||||||
|
|
||||||
PATH:
|
|
||||||
/home/ssdorra/.yarn/bin:/usr/local/go/bin:/home/ssdorra/Projects/go/bin:/home/ssdorra/.local/bin:/usr/local/google-cloud-sdk/bin:/usr/local/go/bin:/home/ssdorra/.sdkman/candidates/maven/current/bin:/home/ssdorra/.sdkman/candidates/groovy/current/bin:/home/ssdorra/bin:/home/ssdorra/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/home/ssdorra/Projects/go/bin
|
|
||||||
|
|
||||||
Yarn version:
|
|
||||||
1.9.2
|
|
||||||
|
|
||||||
Node version:
|
|
||||||
8.11.4
|
|
||||||
|
|
||||||
Platform:
|
|
||||||
linux x64
|
|
||||||
|
|
||||||
Trace:
|
|
||||||
Error: https://registry.yarnpkg.com/ui-bundler: Not found
|
|
||||||
at Request.params.callback [as _callback] (/home/ssdorra/.yarn/lib/cli.js:64150:18)
|
|
||||||
at Request.self.callback (/home/ssdorra/.yarn/lib/cli.js:137416:22)
|
|
||||||
at emitTwo (events.js:126:13)
|
|
||||||
at Request.emit (events.js:214:7)
|
|
||||||
at Request.<anonymous> (/home/ssdorra/.yarn/lib/cli.js:138388:10)
|
|
||||||
at emitOne (events.js:116:13)
|
|
||||||
at Request.emit (events.js:211:7)
|
|
||||||
at IncomingMessage.<anonymous> (/home/ssdorra/.yarn/lib/cli.js:138310:12)
|
|
||||||
at Object.onceWrapper (events.js:313:30)
|
|
||||||
at emitNone (events.js:111:20)
|
|
||||||
|
|
||||||
npm manifest:
|
|
||||||
{
|
|
||||||
"name": "@scm-manager/ui-types",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "Flow types for SCM-Manager related Objects",
|
|
||||||
"main": "src/index.js",
|
|
||||||
"files": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"repository": "https://bitbucket.org/sdorra/scm-manager",
|
|
||||||
"author": "Sebastian Sdorra <sebastian.sdorra@cloudogu.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"scripts": {
|
|
||||||
"check": "flow check"
|
|
||||||
},
|
|
||||||
"devDependencies": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
yarn manifest:
|
|
||||||
No manifest
|
|
||||||
|
|
||||||
Lockfile:
|
|
||||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
||||||
# yarn lockfile v1
|
|
||||||
@@ -68,6 +68,16 @@
|
|||||||
<script>link</script>
|
<script>link</script>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>deploy</id>
|
||||||
|
<phase>deploy</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<script>deploy</script>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|||||||
140
scm-ui-components/scripts/publish.js
Normal file
140
scm-ui-components/scripts/publish.js
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const {spawn} = require("child_process");
|
||||||
|
const parseString = require('xml2js').parseString;
|
||||||
|
|
||||||
|
function isSnapshot(version) {
|
||||||
|
return version.indexOf("SNAPSHOT") > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSnapshotVersion(version) {
|
||||||
|
const date = new Date();
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth().toString().padStart(2, "0");
|
||||||
|
const day = date.getDate().toString().padStart(2, "0");
|
||||||
|
|
||||||
|
const hours = date.getHours().toString().padStart(2, "0");
|
||||||
|
const minutes = date.getMinutes().toString().padStart(2, "0");
|
||||||
|
const seconds = date.getSeconds().toString().padStart(2, "0");
|
||||||
|
|
||||||
|
return version.replace("SNAPSHOT", `${year}${month}${day}-${hours}${minutes}${seconds}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createVersionForPublishing(version) {
|
||||||
|
if (isSnapshot(version)) {
|
||||||
|
return createSnapshotVersion(version);
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
function publishPackages(version) {
|
||||||
|
console.log(`publish ${version} of all packages`);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const lerna = spawn("lerna", [
|
||||||
|
"exec", "--", "yarn", "publish", "--new-version", version, "--access", "public"
|
||||||
|
], {
|
||||||
|
stdio: [
|
||||||
|
process.stdin,
|
||||||
|
process.stdout,
|
||||||
|
process.stderr
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
lerna.on('close', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(new Error("publishing of packages failed with status code: " + code));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVersion(package, packages, newVersion) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const packageJsonPath = path.join(package, "package.json");
|
||||||
|
fs.readFile(packageJsonPath, "utf-8" , (err, content) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
const packageJson = JSON.parse(content);
|
||||||
|
packageJson.version = newVersion;
|
||||||
|
|
||||||
|
for (let dep in packageJson.dependencies) {
|
||||||
|
if (packages.indexOf(dep) >= 0) {
|
||||||
|
packageJson.dependencies[ dep ] = newVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFile( packageJsonPath, JSON.stringify(packageJson, null, 2), (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
console.log("modified", packageJsonPath);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVersions(newVersion) {
|
||||||
|
console.log("set versions of packages to", newVersion);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.readdir("packages", (err, packages) => {
|
||||||
|
if ( err ) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
const actions = [];
|
||||||
|
const packagesWithOrg = packages.map((name) => `@scm-manager/${name}`);
|
||||||
|
for (let pkg of packages) {
|
||||||
|
const action = setVersion(path.join("packages", pkg), packagesWithOrg, newVersion);
|
||||||
|
actions.push(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(Promise.all(actions));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVersion() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.readFile("pom.xml", "utf8", (err, xml) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
parseString(xml, function (err, json) {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
const project = json.project;
|
||||||
|
|
||||||
|
let version = project.version;
|
||||||
|
if (!version) {
|
||||||
|
version = project.parent.version;
|
||||||
|
}
|
||||||
|
version = version[0];
|
||||||
|
|
||||||
|
resolve(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getVersion()
|
||||||
|
.then(version => {
|
||||||
|
const publishVersion = createVersionForPublishing(version);
|
||||||
|
return setVersions(publishVersion)
|
||||||
|
.then(() => publishPackages(publishVersion))
|
||||||
|
.then(() => setVersions(version));
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -124,7 +124,7 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
const { repositoryTypes, t } = this.props;
|
const { repositoryTypes, t } = this.props;
|
||||||
const repository = this.state.repository;
|
const repository = this.state.repository;
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<InputField
|
<InputField
|
||||||
label={t("repository.name")}
|
label={t("repository.name")}
|
||||||
onChange={this.handleNameChange}
|
onChange={this.handleNameChange}
|
||||||
@@ -140,7 +140,7 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
options={this.createSelectOptions(repositoryTypes)}
|
options={this.createSelectOptions(repositoryTypes)}
|
||||||
helpText={t("help.typeHelpText")}
|
helpText={t("help.typeHelpText")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import PermissionsNavLink from "../components/PermissionsNavLink";
|
|||||||
import Sources from "../sources/containers/Sources";
|
import Sources from "../sources/containers/Sources";
|
||||||
import RepositoryNavLink from "../components/RepositoryNavLink";
|
import RepositoryNavLink from "../components/RepositoryNavLink";
|
||||||
import { getRepositoriesLink } from "../../modules/indexResource";
|
import { getRepositoriesLink } from "../../modules/indexResource";
|
||||||
|
import {ExtensionPoint} from '@scm-manager/ui-extensions';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
namespace: string,
|
namespace: string,
|
||||||
@@ -104,6 +105,12 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = this.matchedUrl();
|
const url = this.matchedUrl();
|
||||||
|
|
||||||
|
const extensionProps = {
|
||||||
|
repository,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title={repository.namespace + "/" + repository.name}>
|
<Page title={repository.namespace + "/" + repository.name}>
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
@@ -165,6 +172,10 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<ExtensionPoint name="repository.route"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
@@ -186,11 +197,15 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
label={t("repository-root.sources")}
|
label={t("repository-root.sources")}
|
||||||
activeOnlyWhenExact={false}
|
activeOnlyWhenExact={false}
|
||||||
/>
|
/>
|
||||||
<EditNavLink repository={repository} editUrl={`${url}/edit`} />
|
<ExtensionPoint name="repository.navigation"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
<PermissionsNavLink
|
<PermissionsNavLink
|
||||||
permissionUrl={`${url}/permissions`}
|
permissionUrl={`${url}/permissions`}
|
||||||
repository={repository}
|
repository={repository}
|
||||||
/>
|
/>
|
||||||
|
<EditNavLink repository={repository} editUrl={`${url}/edit`} />
|
||||||
</Section>
|
</Section>
|
||||||
<Section label={t("repository-root.actions-label")}>
|
<Section label={t("repository-root.actions-label")}>
|
||||||
<DeleteNavAction repository={repository} delete={this.delete} />
|
<DeleteNavAction repository={repository} delete={this.delete} />
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class PermissionRootResource {
|
|||||||
Repository repository = load(namespace, name);
|
Repository repository = load(namespace, name);
|
||||||
RepositoryPermissions.permissionWrite(repository).check();
|
RepositoryPermissions.permissionWrite(repository).check();
|
||||||
checkPermissionAlreadyExists(permission, repository);
|
checkPermissionAlreadyExists(permission, repository);
|
||||||
repository.getPermissions().add(dtoToModelMapper.map(permission));
|
repository.addPermission(dtoToModelMapper.map(permission));
|
||||||
manager.modify(repository);
|
manager.modify(repository);
|
||||||
String urlPermissionName = modelToDtoMapper.getUrlPermissionName(permission);
|
String urlPermissionName = modelToDtoMapper.getUrlPermissionName(permission);
|
||||||
return Response.created(URI.create(resourceLinks.permission().self(namespace, name, urlPermissionName))).build();
|
return Response.created(URI.create(resourceLinks.permission().self(namespace, name, urlPermissionName))).build();
|
||||||
@@ -209,7 +209,7 @@ public class PermissionRootResource {
|
|||||||
.stream()
|
.stream()
|
||||||
.filter(filterPermission(permissionName))
|
.filter(filterPermission(permissionName))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(p -> repository.getPermissions().remove(p))
|
.ifPresent(repository::removePermission)
|
||||||
;
|
;
|
||||||
manager.modify(repository);
|
manager.modify(repository);
|
||||||
log.info("the permission with name: {} is updated.", permissionName);
|
log.info("the permission with name: {} is updated.", permissionName);
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
|
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import sonia.scm.repository.Permission;
|
||||||
|
import sonia.scm.repository.PermissionType;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryManager;
|
import sonia.scm.repository.RepositoryManager;
|
||||||
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DefaultValue;
|
import javax.ws.rs.DefaultValue;
|
||||||
@@ -21,6 +24,8 @@ import javax.ws.rs.Produces;
|
|||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
public class RepositoryCollectionResource {
|
public class RepositoryCollectionResource {
|
||||||
|
|
||||||
private static final int DEFAULT_PAGE_SIZE = 10;
|
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||||
@@ -89,7 +94,17 @@ public class RepositoryCollectionResource {
|
|||||||
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository"))
|
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository"))
|
||||||
public Response create(@Valid RepositoryDto repository) {
|
public Response create(@Valid RepositoryDto repository) {
|
||||||
return adapter.create(repository,
|
return adapter.create(repository,
|
||||||
() -> dtoToRepositoryMapper.map(repository, null),
|
() -> createModelObjectFromDto(repository),
|
||||||
r -> resourceLinks.repository().self(r.getNamespace(), r.getName()));
|
r -> resourceLinks.repository().self(r.getNamespace(), r.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Repository createModelObjectFromDto(@Valid RepositoryDto repositoryDto) {
|
||||||
|
Repository repository = dtoToRepositoryMapper.map(repositoryDto, null);
|
||||||
|
repository.setPermissions(singletonList(new Permission(currentUser(), PermissionType.OWNER)));
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String currentUser() {
|
||||||
|
return SecurityUtils.getSubject().getPrincipals().oneByType(User.class).getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ public class AuthorizationChangedEventProducer {
|
|||||||
private boolean isAuthorizationDataModified(Repository repository, Repository beforeModification) {
|
private boolean isAuthorizationDataModified(Repository repository, Repository beforeModification) {
|
||||||
return repository.isArchived() != beforeModification.isArchived()
|
return repository.isArchived() != beforeModification.isArchived()
|
||||||
|| repository.isPublicReadable() != beforeModification.isPublicReadable()
|
|| repository.isPublicReadable() != beforeModification.isPublicReadable()
|
||||||
|| ! repository.getPermissions().equals(beforeModification.getPermissions());
|
|| !(repository.getPermissions().containsAll(beforeModification.getPermissions()) && beforeModification.getPermissions().containsAll(repository.getPermissions()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireEventForEveryUser() {
|
private void fireEventForEveryUser() {
|
||||||
|
|||||||
@@ -54,13 +54,14 @@ import sonia.scm.cache.CacheManager;
|
|||||||
import sonia.scm.group.GroupNames;
|
import sonia.scm.group.GroupNames;
|
||||||
import sonia.scm.group.GroupPermissions;
|
import sonia.scm.group.GroupPermissions;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
|
import sonia.scm.repository.Permission;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryDAO;
|
import sonia.scm.repository.RepositoryDAO;
|
||||||
import sonia.scm.repository.RepositoryPermissions;
|
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.user.UserPermissions;
|
import sonia.scm.user.UserPermissions;
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -198,7 +199,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
private void collectRepositoryPermissions(Builder<String> builder,
|
private void collectRepositoryPermissions(Builder<String> builder,
|
||||||
Repository repository, User user, GroupNames groups)
|
Repository repository, User user, GroupNames groups)
|
||||||
{
|
{
|
||||||
List<sonia.scm.repository.Permission> repositoryPermissions
|
Collection<Permission> repositoryPermissions
|
||||||
= repository.getPermissions();
|
= repository.getPermissions();
|
||||||
|
|
||||||
if (Util.isNotEmpty(repositoryPermissions))
|
if (Util.isNotEmpty(repositoryPermissions))
|
||||||
|
|||||||
@@ -402,18 +402,17 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Repository createUserWithRepository(String userPermission) {
|
private Repository createUserWithRepository(String userPermission) {
|
||||||
Repository mockRepository = mock(Repository.class);
|
Repository mockRepository = new Repository();
|
||||||
when(mockRepository.getId()).thenReturn(REPOSITORY_NAME);
|
mockRepository.setId(REPOSITORY_NAME);
|
||||||
when(mockRepository.getNamespace()).thenReturn(REPOSITORY_NAMESPACE);
|
mockRepository.setNamespace(REPOSITORY_NAMESPACE);
|
||||||
when(mockRepository.getName()).thenReturn(REPOSITORY_NAME);
|
mockRepository.setName(REPOSITORY_NAME);
|
||||||
when(mockRepository.getNamespaceAndName()).thenReturn(new NamespaceAndName(REPOSITORY_NAMESPACE, REPOSITORY_NAME));
|
|
||||||
when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository);
|
when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository);
|
||||||
when(subject.isPermitted(userPermission != null ? eq(userPermission) : any(String.class))).thenReturn(true);
|
when(subject.isPermitted(userPermission != null ? eq(userPermission) : any(String.class))).thenReturn(true);
|
||||||
return mockRepository;
|
return mockRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createUserWithRepositoryAndPermissions(ArrayList<Permission> permissions, String userPermission) {
|
private void createUserWithRepositoryAndPermissions(ArrayList<Permission> permissions, String userPermission) {
|
||||||
when(createUserWithRepository(userPermission).getPermissions()).thenReturn(permissions);
|
createUserWithRepository(userPermission).setPermissions(permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<DynamicTest> createDynamicTestsToAssertResponses(ExpectedRequest... expectedRequests) {
|
private Stream<DynamicTest> createDynamicTestsToAssertResponses(ExpectedRequest... expectedRequests) {
|
||||||
@@ -421,10 +420,9 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
|
|||||||
.map(entry -> dynamicTest("the endpoint " + entry.description + " should return the status code " + entry.expectedResponseStatus, () -> assertExpectedRequest(entry)));
|
.map(entry -> dynamicTest("the endpoint " + entry.description + " should return the status code " + entry.expectedResponseStatus, () -> assertExpectedRequest(entry)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MockHttpResponse assertExpectedRequest(ExpectedRequest entry) throws URISyntaxException {
|
private void assertExpectedRequest(ExpectedRequest entry) throws URISyntaxException {
|
||||||
MockHttpResponse response = new MockHttpResponse();
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
HttpRequest request = null;
|
HttpRequest request = MockHttpRequest
|
||||||
request = MockHttpRequest
|
|
||||||
.create(entry.method, "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + entry.path)
|
.create(entry.method, "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + entry.path)
|
||||||
.content(entry.content)
|
.content(entry.content)
|
||||||
.contentType(VndMediaType.PERMISSION);
|
.contentType(VndMediaType.PERMISSION);
|
||||||
@@ -436,7 +434,6 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
|
|||||||
if (entry.responseValidator != null) {
|
if (entry.responseValidator != null) {
|
||||||
entry.responseValidator.accept(response);
|
entry.responseValidator.accept(response);
|
||||||
}
|
}
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ToString
|
@ToString
|
||||||
@@ -470,12 +467,12 @@ public class PermissionRootResourceTest extends RepositoryTestBase {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedRequest expectedResponseStatus(int expectedResponseStatus) {
|
ExpectedRequest expectedResponseStatus(int expectedResponseStatus) {
|
||||||
this.expectedResponseStatus = expectedResponseStatus;
|
this.expectedResponseStatus = expectedResponseStatus;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpectedRequest responseValidator(Consumer<MockHttpResponse> responseValidator) {
|
ExpectedRequest responseValidator(Consumer<MockHttpResponse> responseValidator) {
|
||||||
this.responseValidator = responseValidator;
|
this.responseValidator = responseValidator;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import com.github.sdorra.shiro.ShiroRule;
|
|||||||
import com.github.sdorra.shiro.SubjectAware;
|
import com.github.sdorra.shiro.SubjectAware;
|
||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import com.google.inject.util.Providers;
|
import com.google.inject.util.Providers;
|
||||||
|
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
import org.jboss.resteasy.core.Dispatcher;
|
import org.jboss.resteasy.core.Dispatcher;
|
||||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||||
@@ -22,6 +25,7 @@ import sonia.scm.repository.RepositoryIsNotArchivedException;
|
|||||||
import sonia.scm.repository.RepositoryManager;
|
import sonia.scm.repository.RepositoryManager;
|
||||||
import sonia.scm.repository.api.RepositoryService;
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@@ -37,6 +41,7 @@ import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
|||||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED;
|
import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED;
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -59,6 +64,8 @@ import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
|
|||||||
)
|
)
|
||||||
public class RepositoryRootResourceTest extends RepositoryTestBase {
|
public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||||
|
|
||||||
|
private static final String REALM = "AdminRealm";
|
||||||
|
|
||||||
private Dispatcher dispatcher;
|
private Dispatcher dispatcher;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@@ -96,6 +103,13 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
|||||||
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
|
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
|
||||||
when(scmPathInfoStore.get()).thenReturn(uriInfo);
|
when(scmPathInfoStore.get()).thenReturn(uriInfo);
|
||||||
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
|
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
|
||||||
|
SimplePrincipalCollection trillian = new SimplePrincipalCollection("trillian", REALM);
|
||||||
|
trillian.add(new User("trillian"), REALM);
|
||||||
|
shiro.setSubject(
|
||||||
|
new Subject.Builder()
|
||||||
|
.principals(trillian)
|
||||||
|
.authenticated(true)
|
||||||
|
.buildSubject());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -257,6 +271,34 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
|||||||
verify(repositoryManager).create(any(Repository.class));
|
verify(repositoryManager).create(any(Repository.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSetCurrentUserAsOwner() throws Exception {
|
||||||
|
ArgumentCaptor<Repository> createCaptor = ArgumentCaptor.forClass(Repository.class);
|
||||||
|
when(repositoryManager.create(createCaptor.capture())).thenAnswer(invocation -> {
|
||||||
|
Repository repository = (Repository) invocation.getArguments()[0];
|
||||||
|
repository.setNamespace("otherspace");
|
||||||
|
return repository;
|
||||||
|
});
|
||||||
|
|
||||||
|
URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json");
|
||||||
|
byte[] repositoryJson = Resources.toByteArray(url);
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2)
|
||||||
|
.contentType(VndMediaType.REPOSITORY)
|
||||||
|
.content(repositoryJson);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
Assertions.assertThat(createCaptor.getValue().getPermissions())
|
||||||
|
.hasSize(1)
|
||||||
|
.allSatisfy(p -> {
|
||||||
|
assertThat(p.getName()).isEqualTo("trillian");
|
||||||
|
assertThat(p.getType()).isEqualTo(PermissionType.OWNER);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldNotOverwriteExistingPermissionsOnUpdate() throws Exception {
|
public void shouldNotOverwriteExistingPermissionsOnUpdate() throws Exception {
|
||||||
Repository existingRepository = mockRepository("space", "repo");
|
Repository existingRepository = mockRepository("space", "repo");
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ private long calculateAverage(List<Long> times) {
|
|||||||
|
|
||||||
private Repository createTestRepository(int number) {
|
private Repository createTestRepository(int number) {
|
||||||
Repository repository = new Repository(keyGenerator.createKey(), REPOSITORY_TYPE, "namespace", "repo-" + number);
|
Repository repository = new Repository(keyGenerator.createKey(), REPOSITORY_TYPE, "namespace", "repo-" + number);
|
||||||
repository.getPermissions().add(new Permission("trillian", PermissionType.READ));
|
repository.addPermission(new Permission("trillian", PermissionType.READ));
|
||||||
return repository;
|
return repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user